123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- package dbstore
- import (
- "context"
- "database/sql"
- "encoding/json"
- "fmt"
- "time"
- "git.linuxforward.com/byop/byop-engine/models"
- "github.com/pkg/errors"
- )
- // App operations
- func (s *SQLiteStore) CreateApp(ctx context.Context, app *models.App) (int, error) {
- // Convert components slice to JSON
- componentsJSON, err := json.Marshal(app.Components)
- if err != nil {
- return 0, models.NewErrInternalServer("failed to marshal app components", err)
- }
- // Handle preview_id: if 0, pass NULL to database
- var previewID interface{}
- if app.PreviewID == 0 {
- previewID = nil
- } else {
- previewID = app.PreviewID
- }
- query := `INSERT INTO apps (user_id, name, description, status, components, preview_id, preview_url, current_image_tag, current_image_uri, error_msg, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
- now := time.Now().Format(time.RFC3339)
- result, err := s.db.ExecContext(ctx, query, app.UserID, app.Name, app.Description, app.Status, string(componentsJSON), previewID, app.PreviewURL, app.CurrentImageTag, app.CurrentImageURI, app.ErrorMsg, now, now)
- if err != nil {
- return 0, models.NewErrInternalServer("failed to create app", err)
- }
- id, err := result.LastInsertId()
- if err != nil {
- return 0, models.NewErrInternalServer("failed to get last insert ID for app", err)
- }
- return int(id), nil
- }
- func (s *SQLiteStore) GetAllApps(ctx context.Context) ([]*models.App, error) {
- query := `SELECT id, user_id, name, description, status, components, preview_id, preview_url, current_image_tag, current_image_uri, error_msg, created_at, updated_at FROM apps`
- rows, err := s.db.QueryContext(ctx, query)
- if err != nil {
- return nil, models.NewErrInternalServer("failed to query apps", err)
- }
- defer rows.Close()
- var apps []*models.App
- for rows.Next() {
- var app models.App
- var componentsJSON string
- var previewID sql.NullInt64
- err := rows.Scan(&app.ID, &app.UserID, &app.Name, &app.Description, &app.Status, &componentsJSON, &previewID, &app.PreviewURL, &app.CurrentImageTag, &app.CurrentImageURI, &app.ErrorMsg, &app.CreatedAt, &app.UpdatedAt)
- if err != nil {
- return nil, models.NewErrInternalServer("failed to scan app row", err)
- }
- // Handle nullable preview_id
- if previewID.Valid {
- app.PreviewID = int(previewID.Int64)
- } else {
- app.PreviewID = 0
- }
- // Parse components JSON
- if componentsJSON != "" {
- err := json.Unmarshal([]byte(componentsJSON), &app.Components)
- if err != nil {
- return nil, models.NewErrInternalServer("failed to unmarshal app components", err)
- }
- } else {
- app.Components = []int{} // Initialize as empty slice if null
- }
- apps = append(apps, &app)
- }
- if err = rows.Err(); err != nil {
- return nil, models.NewErrInternalServer("error iterating app rows", err)
- }
- return apps, nil
- }
- // GetAppByID retrieves a single app by ID
- func (s *SQLiteStore) GetAppByID(ctx context.Context, id int) (*models.App, error) {
- query := `SELECT id, user_id, name, description, status, components, preview_id, preview_url, current_image_tag, current_image_uri, error_msg, created_at, updated_at FROM apps WHERE id = ?`
- var app models.App
- var componentsJSON string
- var previewID sql.NullInt64
- err := s.db.QueryRowContext(ctx, query, id).Scan(&app.ID, &app.UserID, &app.Name, &app.Description, &app.Status, &componentsJSON, &previewID, &app.PreviewURL, &app.CurrentImageTag, &app.CurrentImageURI, &app.ErrorMsg, &app.CreatedAt, &app.UpdatedAt)
- if err != nil {
- if errors.Is(err, sql.ErrNoRows) {
- return nil, models.NewErrNotFound(fmt.Sprintf("app with ID %d not found", id), err)
- }
- return nil, models.NewErrInternalServer(fmt.Sprintf("failed to get app with ID %d", id), err)
- }
- // Handle nullable preview_id
- if previewID.Valid {
- app.PreviewID = int(previewID.Int64)
- } else {
- app.PreviewID = 0
- }
- // Parse components JSON
- if componentsJSON != "" {
- err := json.Unmarshal([]byte(componentsJSON), &app.Components)
- if err != nil {
- return nil, models.NewErrInternalServer("failed to unmarshal app components for app ID "+fmt.Sprint(id), err)
- }
- } else {
- app.Components = []int{} // Initialize as empty slice if null
- }
- return &app, nil
- }
- // UpdateApp updates an existing app
- func (s *SQLiteStore) UpdateApp(ctx context.Context, app *models.App) error {
- // Convert components slice to JSON
- componentsJSON, err := json.Marshal(app.Components)
- if err != nil {
- return models.NewErrInternalServer("failed to marshal app components for update", err)
- }
- // Handle preview_id: if 0, pass NULL to database
- var previewID interface{}
- if app.PreviewID == 0 {
- previewID = nil
- } else {
- previewID = app.PreviewID
- }
- query := `UPDATE apps SET user_id = ?, name = ?, description = ?, status = ?, components = ?, preview_id = ?, preview_url = ?, current_image_tag = ?, current_image_uri = ?, error_msg = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`
- result, err := s.db.ExecContext(ctx, query, app.UserID, app.Name, app.Description, app.Status, string(componentsJSON), previewID, app.PreviewURL, app.CurrentImageTag, app.CurrentImageURI, app.ErrorMsg, app.ID)
- if err != nil {
- return models.NewErrInternalServer(fmt.Sprintf("failed to update app with ID %d", app.ID), err)
- }
- rowsAffected, err := result.RowsAffected()
- if err != nil {
- return models.NewErrInternalServer(fmt.Sprintf("failed to get rows affected for app update ID %d", app.ID), err)
- }
- if rowsAffected == 0 {
- return models.NewErrNotFound(fmt.Sprintf("app with ID %d not found for update", app.ID), nil)
- }
- return nil
- }
- // DeleteApp deletes an app by ID with verification checks
- func (s *SQLiteStore) DeleteApp(ctx context.Context, id int) error {
- // First check if the app exists
- _, err := s.GetAppByID(ctx, id)
- if err != nil {
- return err
- }
- // Check if the app is used in any deployments
- deployments, err := s.GetDeploymentsByAppID(ctx, id)
- if err != nil {
- var nfErr *models.ErrNotFound
- if errors.As(err, &nfErr) {
- } else {
- return models.NewErrInternalServer(fmt.Sprintf("failed to check app deployments for app ID %d", id), err)
- }
- }
- if len(deployments) > 0 {
- return models.NewErrConflict(fmt.Sprintf("cannot delete app: it is used in %d deployment(s). Please delete the deployments first", len(deployments)), nil)
- }
- // If no deployments use this app, proceed with deletion
- query := `DELETE FROM apps WHERE id = ?`
- result, err := s.db.ExecContext(ctx, query, id)
- if err != nil {
- return models.NewErrInternalServer(fmt.Sprintf("failed to delete app with ID %d", id), err)
- }
- rowsAffected, err := result.RowsAffected()
- if err != nil {
- return models.NewErrInternalServer(fmt.Sprintf("failed to get rows affected for app deletion ID %d", id), err)
- }
- if rowsAffected == 0 {
- return models.NewErrNotFound(fmt.Sprintf("app with ID %d not found for deletion", id), nil)
- }
- return nil
- }
- func (s *SQLiteStore) UpdateAppStatus(ctx context.Context, appID int, status, errorMsg string) error {
- query := `UPDATE apps SET status = ?, error_msg = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`
- result, err := s.db.ExecContext(ctx, query, status, errorMsg, appID)
- if err != nil {
- return models.NewErrInternalServer(fmt.Sprintf("failed to update app status for ID %d", appID), err)
- }
- rowsAffected, err := result.RowsAffected()
- if err != nil {
- return models.NewErrInternalServer(fmt.Sprintf("failed to get rows affected for app status update ID %d", appID), err)
- }
- if rowsAffected == 0 {
- return models.NewErrNotFound(fmt.Sprintf("app with ID %d not found for status update", appID), nil)
- }
- return nil
- }
- // UpdateAppCurrentImage updates the current image tag and URI for an app.
- func (s *SQLiteStore) UpdateAppCurrentImage(ctx context.Context, appID int, imageTag string, imageURI string) error {
- query := `UPDATE apps SET current_image_tag = ?, current_image_uri = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`
- _, err := s.db.ExecContext(ctx, query, imageTag, imageURI, appID)
- if err != nil {
- return models.NewErrInternalServer(fmt.Sprintf("failed to update app current image for ID %d", appID), err)
- }
- return nil
- }
|