preview.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. package dbstore
  2. import (
  3. "context"
  4. "database/sql"
  5. "fmt"
  6. "git.linuxforward.com/byop/byop-engine/models"
  7. "github.com/pkg/errors"
  8. )
  9. // CreatePreview creates a new preview record
  10. func (s *SQLiteStore) CreatePreview(ctx context.Context, preview *models.Preview) (int, error) {
  11. query := `
  12. INSERT INTO previews (app_id, status, expires_at)
  13. VALUES (?, ?, ?)
  14. `
  15. result, err := s.db.ExecContext(ctx, query, preview.AppID, preview.Status, preview.ExpiresAt)
  16. if err != nil {
  17. return 0, models.NewErrInternalServer("failed to create preview", err)
  18. }
  19. id, err := result.LastInsertId()
  20. if err != nil {
  21. return 0, models.NewErrInternalServer("failed to get preview ID after creation", err)
  22. }
  23. return int(id), nil
  24. }
  25. // GetPreviewByID retrieves a preview by ID
  26. func (s *SQLiteStore) GetPreviewByID(ctx context.Context, id int) (*models.Preview, error) {
  27. preview := &models.Preview{}
  28. query := `
  29. SELECT id, app_id, status, url, vps_id, ip_address, error_msg,
  30. build_logs, deploy_logs, expires_at, created_at, updated_at
  31. FROM previews
  32. WHERE id = ?
  33. `
  34. err := s.db.QueryRowContext(ctx, query, id).Scan(
  35. &preview.ID, &preview.AppID, &preview.Status, &preview.URL,
  36. &preview.VPSID, &preview.IPAddress, &preview.ErrorMsg,
  37. &preview.BuildLogs, &preview.DeployLogs, &preview.ExpiresAt,
  38. &preview.CreatedAt, &preview.UpdatedAt,
  39. )
  40. if err != nil {
  41. if errors.Is(err, sql.ErrNoRows) {
  42. return nil, models.NewErrNotFound(fmt.Sprintf("preview with ID %d not found", id), err)
  43. }
  44. return nil, models.NewErrInternalServer(fmt.Sprintf("failed to get preview with ID %d", id), err)
  45. }
  46. return preview, nil
  47. }
  48. // GetPreviewsByAppID retrieves all previews for an app
  49. func (s *SQLiteStore) GetPreviewsByAppID(ctx context.Context, appID int) ([]*models.Preview, error) {
  50. query := `
  51. SELECT id, app_id, status, url, vps_id, ip_address, error_msg,
  52. build_logs, deploy_logs, expires_at, created_at, updated_at
  53. FROM previews
  54. WHERE app_id = ?
  55. ORDER BY created_at DESC
  56. `
  57. rows, err := s.db.QueryContext(ctx, query, appID)
  58. if err != nil {
  59. return nil, models.NewErrInternalServer(fmt.Sprintf("failed to get previews for app ID %d", appID), err)
  60. }
  61. defer rows.Close()
  62. var previews []*models.Preview
  63. for rows.Next() {
  64. var preview models.Preview
  65. err := rows.Scan(
  66. &preview.ID, &preview.AppID, &preview.Status, &preview.URL,
  67. &preview.VPSID, &preview.IPAddress, &preview.ErrorMsg,
  68. &preview.BuildLogs, &preview.DeployLogs, &preview.ExpiresAt,
  69. &preview.CreatedAt, &preview.UpdatedAt,
  70. )
  71. if err != nil {
  72. return nil, models.NewErrInternalServer("failed to scan preview row", err)
  73. }
  74. previews = append(previews, &preview)
  75. }
  76. if err = rows.Err(); err != nil {
  77. return nil, models.NewErrInternalServer(fmt.Sprintf("error iterating preview rows for app ID %d", appID), err)
  78. }
  79. return previews, nil
  80. }
  81. // GetAllPreviews retrieves all previews (for admin purposes)
  82. func (s *SQLiteStore) GetAllPreviews(ctx context.Context) ([]*models.Preview, error) {
  83. query := `
  84. SELECT id, app_id, status, url, vps_id, ip_address, error_msg,
  85. build_logs, deploy_logs, expires_at, created_at, updated_at
  86. FROM previews
  87. ORDER BY created_at DESC
  88. `
  89. rows, err := s.db.QueryContext(ctx, query)
  90. if err != nil {
  91. return nil, models.NewErrInternalServer("failed to get all previews", err)
  92. }
  93. defer rows.Close()
  94. var previews []*models.Preview
  95. for rows.Next() {
  96. var preview models.Preview
  97. err := rows.Scan(
  98. &preview.ID, &preview.AppID, &preview.Status, &preview.URL,
  99. &preview.VPSID, &preview.IPAddress, &preview.ErrorMsg,
  100. &preview.BuildLogs, &preview.DeployLogs, &preview.ExpiresAt,
  101. &preview.CreatedAt, &preview.UpdatedAt,
  102. )
  103. if err != nil {
  104. return nil, models.NewErrInternalServer("failed to scan preview row", err)
  105. }
  106. previews = append(previews, &preview)
  107. }
  108. if err = rows.Err(); err != nil {
  109. return nil, models.NewErrInternalServer("error iterating all preview rows", err)
  110. }
  111. return previews, nil
  112. }
  113. // UpdatePreviewStatus updates the status and error message of a preview
  114. func (s *SQLiteStore) UpdatePreviewStatus(ctx context.Context, previewID int, status, errorMsg string) error {
  115. query := `
  116. UPDATE previews
  117. SET status = ?, error_msg = ?, updated_at = CURRENT_TIMESTAMP
  118. WHERE id = ?
  119. `
  120. result, err := s.db.ExecContext(ctx, query, status, errorMsg, previewID)
  121. if err != nil {
  122. return models.NewErrInternalServer(fmt.Sprintf("failed to update preview status for ID %d", previewID), err)
  123. }
  124. rowsAffected, err := result.RowsAffected()
  125. if err != nil {
  126. return models.NewErrInternalServer(fmt.Sprintf("failed to get rows affected for preview status update ID %d", previewID), err)
  127. }
  128. if rowsAffected == 0 {
  129. return models.NewErrNotFound(fmt.Sprintf("preview with ID %d not found for status update", previewID), nil)
  130. }
  131. return nil
  132. }
  133. // UpdatePreviewVPS updates the VPS information for a preview
  134. func (s *SQLiteStore) UpdatePreviewVPS(ctx context.Context, previewID int, vpsID, ipAddress, url string) error {
  135. query := `
  136. UPDATE previews
  137. SET vps_id = ?, ip_address = ?, url = ?, updated_at = CURRENT_TIMESTAMP
  138. WHERE id = ?
  139. `
  140. result, err := s.db.ExecContext(ctx, query, vpsID, ipAddress, url, previewID)
  141. if err != nil {
  142. return models.NewErrInternalServer(fmt.Sprintf("failed to update preview VPS info for ID %d", previewID), err)
  143. }
  144. rowsAffected, err := result.RowsAffected()
  145. if err != nil {
  146. return models.NewErrInternalServer(fmt.Sprintf("failed to get rows affected for preview VPS update ID %d", previewID), err)
  147. }
  148. if rowsAffected == 0 {
  149. return models.NewErrNotFound(fmt.Sprintf("preview with ID %d not found for VPS update", previewID), nil)
  150. }
  151. return nil
  152. }
  153. // UpdatePreviewBuildLogs updates the build logs for a preview
  154. func (s *SQLiteStore) UpdatePreviewBuildLogs(ctx context.Context, previewID int, buildLogs string) error {
  155. query := `
  156. UPDATE previews
  157. SET build_logs = ?, updated_at = CURRENT_TIMESTAMP
  158. WHERE id = ?
  159. `
  160. result, err := s.db.ExecContext(ctx, query, buildLogs, previewID)
  161. if err != nil {
  162. return models.NewErrInternalServer(fmt.Sprintf("failed to update preview build logs for ID %d", previewID), err)
  163. }
  164. rowsAffected, err := result.RowsAffected()
  165. if err != nil {
  166. return models.NewErrInternalServer(fmt.Sprintf("failed to get rows affected for preview build logs update ID %d", previewID), err)
  167. }
  168. if rowsAffected == 0 {
  169. return models.NewErrNotFound(fmt.Sprintf("preview with ID %d not found for build logs update", previewID), nil)
  170. }
  171. return nil
  172. }
  173. // UpdatePreviewDeployLogs updates the deploy logs for a preview
  174. func (s *SQLiteStore) UpdatePreviewDeployLogs(ctx context.Context, previewID int, deployLogs string) error {
  175. query := `
  176. UPDATE previews
  177. SET deploy_logs = ?, updated_at = CURRENT_TIMESTAMP
  178. WHERE id = ?
  179. `
  180. result, err := s.db.ExecContext(ctx, query, deployLogs, previewID)
  181. if err != nil {
  182. return models.NewErrInternalServer(fmt.Sprintf("failed to update preview deploy logs for ID %d", previewID), err)
  183. }
  184. rowsAffected, err := result.RowsAffected()
  185. if err != nil {
  186. return models.NewErrInternalServer(fmt.Sprintf("failed to get rows affected for preview deploy logs update ID %d", previewID), err)
  187. }
  188. if rowsAffected == 0 {
  189. return models.NewErrNotFound(fmt.Sprintf("preview with ID %d not found for deploy logs update", previewID), nil)
  190. }
  191. return nil
  192. }
  193. // UpdatePreview updates a preview record
  194. func (s *SQLiteStore) UpdatePreview(ctx context.Context, preview models.Preview) error {
  195. query := `
  196. UPDATE previews
  197. SET app_id = ?, status = ?, url = ?, vps_id = ?, ip_address = ?,
  198. error_msg = ?, build_logs = ?, deploy_logs = ?, expires_at = ?,
  199. updated_at = CURRENT_TIMESTAMP
  200. WHERE id = ?
  201. `
  202. result, err := s.db.ExecContext(ctx, query,
  203. preview.AppID, preview.Status, preview.URL, preview.VPSID,
  204. preview.IPAddress, preview.ErrorMsg, preview.BuildLogs,
  205. preview.DeployLogs, preview.ExpiresAt, preview.ID,
  206. )
  207. if err != nil {
  208. return models.NewErrInternalServer(fmt.Sprintf("failed to update preview with ID %d", preview.ID), err)
  209. }
  210. rowsAffected, err := result.RowsAffected()
  211. if err != nil {
  212. return models.NewErrInternalServer(fmt.Sprintf("failed to get rows affected for preview update ID %d", preview.ID), err)
  213. }
  214. if rowsAffected == 0 {
  215. return models.NewErrNotFound(fmt.Sprintf("preview with ID %d not found for update", preview.ID), nil)
  216. }
  217. return nil
  218. }
  219. // DeletePreview deletes a preview record
  220. func (s *SQLiteStore) DeletePreview(ctx context.Context, previewID int) error {
  221. query := `DELETE FROM previews WHERE id = ?`
  222. result, err := s.db.ExecContext(ctx, query, previewID)
  223. if err != nil {
  224. return models.NewErrInternalServer(fmt.Sprintf("failed to delete preview with ID %d", previewID), err)
  225. }
  226. rowsAffected, err := result.RowsAffected()
  227. if err != nil {
  228. return models.NewErrInternalServer(fmt.Sprintf("failed to get rows affected for preview deletion ID %d", previewID), err)
  229. }
  230. if rowsAffected == 0 {
  231. return models.NewErrNotFound(fmt.Sprintf("preview with ID %d not found for deletion", previewID), nil)
  232. }
  233. return nil
  234. }
  235. // GetExpiredPreviews gets all previews that have expired (for cleanup jobs)
  236. func (s *SQLiteStore) GetExpiredPreviews(ctx context.Context) ([]*models.Preview, error) {
  237. query := `
  238. SELECT id, app_id, status, url, vps_id, ip_address, error_msg,
  239. build_logs, deploy_logs, expires_at, created_at, updated_at
  240. FROM previews
  241. WHERE expires_at < datetime('now') AND status != 'stopped'
  242. ORDER BY expires_at ASC
  243. `
  244. rows, err := s.db.QueryContext(ctx, query)
  245. if err != nil {
  246. return nil, models.NewErrInternalServer("failed to get expired previews", err)
  247. }
  248. defer rows.Close()
  249. var previews []*models.Preview
  250. for rows.Next() {
  251. var preview models.Preview
  252. err := rows.Scan(
  253. &preview.ID, &preview.AppID, &preview.Status, &preview.URL,
  254. &preview.VPSID, &preview.IPAddress, &preview.ErrorMsg,
  255. &preview.BuildLogs, &preview.DeployLogs, &preview.ExpiresAt,
  256. &preview.CreatedAt, &preview.UpdatedAt,
  257. )
  258. if err != nil {
  259. return nil, models.NewErrInternalServer("failed to scan expired preview row", err)
  260. }
  261. previews = append(previews, &preview)
  262. }
  263. if err = rows.Err(); err != nil {
  264. return nil, models.NewErrInternalServer("error iterating expired preview rows", err)
  265. }
  266. return previews, nil
  267. }
  268. // GetPreviewsByStatus retrieves all previews with a specific status
  269. func (s *SQLiteStore) GetPreviewsByStatus(ctx context.Context, status string) ([]*models.Preview, error) {
  270. query := `SELECT id, app_id, status, vps_id, ip_address, url, build_logs, deploy_logs, error_msg, expires_at, created_at, updated_at FROM previews WHERE status = ?`
  271. rows, err := s.db.QueryContext(ctx, query, status)
  272. if err != nil {
  273. return nil, models.NewErrInternalServer(fmt.Sprintf("failed to get previews with status %s", status), err)
  274. }
  275. defer rows.Close()
  276. var previews []*models.Preview
  277. for rows.Next() {
  278. var preview models.Preview
  279. err := rows.Scan(
  280. &preview.ID,
  281. &preview.AppID,
  282. &preview.Status,
  283. &preview.VPSID,
  284. &preview.IPAddress,
  285. &preview.URL,
  286. &preview.BuildLogs,
  287. &preview.DeployLogs,
  288. &preview.ErrorMsg,
  289. &preview.ExpiresAt,
  290. &preview.CreatedAt,
  291. &preview.UpdatedAt,
  292. )
  293. if err != nil {
  294. return nil, models.NewErrInternalServer("failed to scan preview row", err)
  295. }
  296. previews = append(previews, &preview)
  297. }
  298. if err = rows.Err(); err != nil {
  299. return nil, models.NewErrInternalServer(fmt.Sprintf("error iterating preview rows with status %s", status), err)
  300. }
  301. return previews, nil
  302. }
  303. // UpdateAppPreview updates the app with preview information
  304. func (s *SQLiteStore) UpdateAppPreview(ctx context.Context, appID, previewID int, previewURL string) error {
  305. query := `
  306. UPDATE apps
  307. SET preview_id = ?, preview_url = ?, status = 'ready', updated_at = CURRENT_TIMESTAMP
  308. WHERE id = ?
  309. `
  310. result, err := s.db.ExecContext(ctx, query, previewID, previewURL, appID)
  311. if err != nil {
  312. return models.NewErrInternalServer(fmt.Sprintf("failed to update app preview info for app ID %d", appID), err)
  313. }
  314. rowsAffected, err := result.RowsAffected()
  315. if err != nil {
  316. return models.NewErrInternalServer(fmt.Sprintf("failed to get rows affected for app preview update, app ID %d", appID), err)
  317. }
  318. if rowsAffected == 0 {
  319. return models.NewErrNotFound(fmt.Sprintf("app with ID %d not found for preview update", appID), nil)
  320. }
  321. return nil
  322. }
  323. // GetPreviewByAppID retrieves the latest preview for an app
  324. func (s *SQLiteStore) GetPreviewByAppID(ctx context.Context, appID int) (*models.Preview, error) {
  325. query := `
  326. SELECT id, app_id, status, url, vps_id, ip_address, error_msg,
  327. build_logs, deploy_logs, expires_at, created_at, updated_at
  328. FROM previews
  329. WHERE app_id = ?
  330. ORDER BY created_at DESC
  331. LIMIT 1
  332. `
  333. preview := &models.Preview{}
  334. err := s.db.QueryRowContext(ctx, query, appID).Scan(
  335. &preview.ID, &preview.AppID, &preview.Status, &preview.URL,
  336. &preview.VPSID, &preview.IPAddress, &preview.ErrorMsg,
  337. &preview.BuildLogs, &preview.DeployLogs, &preview.ExpiresAt,
  338. &preview.CreatedAt, &preview.UpdatedAt,
  339. )
  340. if err != nil {
  341. if errors.Is(err, sql.ErrNoRows) {
  342. return nil, nil
  343. }
  344. return nil, models.NewErrInternalServer(fmt.Sprintf("failed to get latest preview for app ID %d", appID), err)
  345. }
  346. return preview, nil
  347. }