Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

316 Zeilen
7.6KB

  1. package memory
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "sort"
  7. "sync"
  8. "time"
  9. "qctextbuilder/internal/domain"
  10. "qctextbuilder/internal/store"
  11. )
  12. type Store struct {
  13. mu sync.RWMutex
  14. templates map[int64]domain.Template
  15. manifests map[int64]domain.TemplateManifest
  16. manifestField map[string][]domain.TemplateField
  17. builds map[string]domain.SiteBuild
  18. drafts map[string]domain.BuildDraft
  19. settings domain.AppSettings
  20. hasSettings bool
  21. }
  22. func New() *Store {
  23. return &Store{
  24. templates: make(map[int64]domain.Template),
  25. manifests: make(map[int64]domain.TemplateManifest),
  26. manifestField: make(map[string][]domain.TemplateField),
  27. builds: make(map[string]domain.SiteBuild),
  28. drafts: make(map[string]domain.BuildDraft),
  29. }
  30. }
  31. func (s *Store) UpsertTemplates(_ context.Context, templates []domain.Template) error {
  32. s.mu.Lock()
  33. defer s.mu.Unlock()
  34. for _, t := range templates {
  35. existing, ok := s.templates[t.ID]
  36. if ok {
  37. t.IsOnboarded = existing.IsOnboarded
  38. t.ManifestStatus = existing.ManifestStatus
  39. t.LastDiscoveredAt = existing.LastDiscoveredAt
  40. }
  41. s.templates[t.ID] = t
  42. }
  43. return nil
  44. }
  45. func (s *Store) GetTemplateByID(_ context.Context, id int64) (*domain.Template, error) {
  46. s.mu.RLock()
  47. defer s.mu.RUnlock()
  48. t, ok := s.templates[id]
  49. if !ok {
  50. return nil, store.ErrNotFound
  51. }
  52. copy := t
  53. return &copy, nil
  54. }
  55. func (s *Store) ListTemplates(_ context.Context) ([]domain.Template, error) {
  56. s.mu.RLock()
  57. defer s.mu.RUnlock()
  58. out := make([]domain.Template, 0, len(s.templates))
  59. for _, t := range s.templates {
  60. out = append(out, t)
  61. }
  62. return out, nil
  63. }
  64. func (s *Store) SetTemplateManifestStatus(_ context.Context, templateID int64, status string, onboarded bool) error {
  65. s.mu.Lock()
  66. defer s.mu.Unlock()
  67. t, ok := s.templates[templateID]
  68. if !ok {
  69. return store.ErrNotFound
  70. }
  71. t.ManifestStatus = status
  72. t.IsOnboarded = onboarded
  73. s.templates[templateID] = t
  74. return nil
  75. }
  76. func (s *Store) CreateManifest(_ context.Context, manifest domain.TemplateManifest, fields []domain.TemplateField) error {
  77. s.mu.Lock()
  78. defer s.mu.Unlock()
  79. s.manifests[manifest.TemplateID] = manifest
  80. s.manifestField[manifest.ID] = fields
  81. return nil
  82. }
  83. func (s *Store) GetActiveManifestByTemplateID(_ context.Context, templateID int64) (*domain.TemplateManifest, error) {
  84. s.mu.RLock()
  85. defer s.mu.RUnlock()
  86. m, ok := s.manifests[templateID]
  87. if !ok {
  88. return nil, store.ErrNotFound
  89. }
  90. copy := m
  91. return &copy, nil
  92. }
  93. func (s *Store) ListFieldsByManifestID(_ context.Context, manifestID string) ([]domain.TemplateField, error) {
  94. s.mu.RLock()
  95. defer s.mu.RUnlock()
  96. fields, ok := s.manifestField[manifestID]
  97. if !ok {
  98. return nil, store.ErrNotFound
  99. }
  100. out := make([]domain.TemplateField, 0, len(fields))
  101. out = append(out, fields...)
  102. return out, nil
  103. }
  104. func (s *Store) UpdateFields(_ context.Context, manifestID string, fields []domain.TemplateField) error {
  105. s.mu.Lock()
  106. defer s.mu.Unlock()
  107. if _, ok := s.manifestField[manifestID]; !ok {
  108. return store.ErrNotFound
  109. }
  110. next := make([]domain.TemplateField, len(fields))
  111. copy(next, fields)
  112. s.manifestField[manifestID] = next
  113. return nil
  114. }
  115. func (s *Store) CreateBuild(_ context.Context, build domain.SiteBuild) error {
  116. s.mu.Lock()
  117. defer s.mu.Unlock()
  118. if _, exists := s.builds[build.ID]; exists {
  119. return errors.New("build already exists")
  120. }
  121. s.builds[build.ID] = build
  122. return nil
  123. }
  124. func (s *Store) GetBuildByID(_ context.Context, id string) (*domain.SiteBuild, error) {
  125. s.mu.RLock()
  126. defer s.mu.RUnlock()
  127. build, ok := s.builds[id]
  128. if !ok {
  129. return nil, store.ErrNotFound
  130. }
  131. copy := build
  132. return &copy, nil
  133. }
  134. func (s *Store) ListBuildsByStatuses(_ context.Context, statuses []string, limit int) ([]domain.SiteBuild, error) {
  135. s.mu.RLock()
  136. defer s.mu.RUnlock()
  137. allowed := make(map[string]struct{}, len(statuses))
  138. for _, status := range statuses {
  139. allowed[status] = struct{}{}
  140. }
  141. out := make([]domain.SiteBuild, 0)
  142. for _, build := range s.builds {
  143. if len(allowed) > 0 {
  144. if _, ok := allowed[build.QCStatus]; !ok {
  145. continue
  146. }
  147. }
  148. out = append(out, build)
  149. if limit > 0 && len(out) >= limit {
  150. break
  151. }
  152. }
  153. return out, nil
  154. }
  155. func (s *Store) MarkBuildSubmitted(_ context.Context, buildID string, jobID int64, status string, qcResult json.RawMessage, startedAt time.Time) error {
  156. s.mu.Lock()
  157. defer s.mu.Unlock()
  158. build, ok := s.builds[buildID]
  159. if !ok {
  160. return store.ErrNotFound
  161. }
  162. build.QCJobID = &jobID
  163. build.QCStatus = status
  164. build.QCResultJSON = cloneRaw(qcResult)
  165. build.StartedAt = &startedAt
  166. s.builds[buildID] = build
  167. return nil
  168. }
  169. func (s *Store) UpdateBuildFromJob(_ context.Context, buildID string, status string, siteID *int64, previewURL string, qcResult json.RawMessage, qcError json.RawMessage, finishedAt *time.Time) error {
  170. s.mu.Lock()
  171. defer s.mu.Unlock()
  172. build, ok := s.builds[buildID]
  173. if !ok {
  174. return store.ErrNotFound
  175. }
  176. build.QCStatus = status
  177. build.QCResultJSON = cloneRaw(qcResult)
  178. build.QCErrorJSON = cloneRaw(qcError)
  179. build.QCPreviewURL = previewURL
  180. if siteID != nil {
  181. id := *siteID
  182. build.QCSiteID = &id
  183. }
  184. build.FinishedAt = finishedAt
  185. s.builds[buildID] = build
  186. return nil
  187. }
  188. func (s *Store) UpdateBuildEditorURL(_ context.Context, buildID string, editorURL string, qcResult json.RawMessage) error {
  189. s.mu.Lock()
  190. defer s.mu.Unlock()
  191. build, ok := s.builds[buildID]
  192. if !ok {
  193. return store.ErrNotFound
  194. }
  195. build.QCEditorURL = editorURL
  196. build.QCResultJSON = cloneRaw(qcResult)
  197. s.builds[buildID] = build
  198. return nil
  199. }
  200. func cloneRaw(raw json.RawMessage) json.RawMessage {
  201. if raw == nil {
  202. return nil
  203. }
  204. out := make([]byte, len(raw))
  205. copy(out, raw)
  206. return json.RawMessage(out)
  207. }
  208. func (s *Store) UpsertSettings(_ context.Context, settings domain.AppSettings) error {
  209. s.mu.Lock()
  210. defer s.mu.Unlock()
  211. s.settings = settings
  212. s.hasSettings = true
  213. return nil
  214. }
  215. func (s *Store) GetSettings(_ context.Context) (*domain.AppSettings, error) {
  216. s.mu.RLock()
  217. defer s.mu.RUnlock()
  218. if !s.hasSettings {
  219. return nil, store.ErrNotFound
  220. }
  221. value := s.settings
  222. return &value, nil
  223. }
  224. func (s *Store) CreateDraft(_ context.Context, draft domain.BuildDraft) error {
  225. s.mu.Lock()
  226. defer s.mu.Unlock()
  227. if _, exists := s.drafts[draft.ID]; exists {
  228. return errors.New("draft already exists")
  229. }
  230. s.drafts[draft.ID] = draft
  231. return nil
  232. }
  233. func (s *Store) UpdateDraft(_ context.Context, draft domain.BuildDraft) error {
  234. s.mu.Lock()
  235. defer s.mu.Unlock()
  236. if _, exists := s.drafts[draft.ID]; !exists {
  237. return store.ErrNotFound
  238. }
  239. s.drafts[draft.ID] = draft
  240. return nil
  241. }
  242. func (s *Store) GetDraftByID(_ context.Context, id string) (*domain.BuildDraft, error) {
  243. s.mu.RLock()
  244. defer s.mu.RUnlock()
  245. draft, ok := s.drafts[id]
  246. if !ok {
  247. return nil, store.ErrNotFound
  248. }
  249. copy := draft
  250. copy.GlobalDataJSON = cloneRaw(draft.GlobalDataJSON)
  251. copy.FieldValuesJSON = cloneRaw(draft.FieldValuesJSON)
  252. copy.DraftContextJSON = cloneRaw(draft.DraftContextJSON)
  253. copy.SuggestionStateJSON = cloneRaw(draft.SuggestionStateJSON)
  254. return &copy, nil
  255. }
  256. func (s *Store) ListDrafts(_ context.Context, limit int) ([]domain.BuildDraft, error) {
  257. s.mu.RLock()
  258. defer s.mu.RUnlock()
  259. out := make([]domain.BuildDraft, 0, len(s.drafts))
  260. for _, draft := range s.drafts {
  261. copy := draft
  262. copy.GlobalDataJSON = cloneRaw(draft.GlobalDataJSON)
  263. copy.FieldValuesJSON = cloneRaw(draft.FieldValuesJSON)
  264. copy.DraftContextJSON = cloneRaw(draft.DraftContextJSON)
  265. copy.SuggestionStateJSON = cloneRaw(draft.SuggestionStateJSON)
  266. out = append(out, copy)
  267. }
  268. sort.Slice(out, func(i, j int) bool {
  269. return out[i].UpdatedAt.After(out[j].UpdatedAt)
  270. })
  271. if limit > 0 && len(out) > limit {
  272. out = out[:limit]
  273. }
  274. return out, nil
  275. }