@spfn/cms 0.1.0-alpha.6 → 0.1.0-alpha.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/dist/actions.d.ts +140 -6
  2. package/dist/actions.js +97 -10
  3. package/dist/actions.js.map +1 -1
  4. package/dist/client.d.ts +43 -9
  5. package/dist/client.js +406 -56
  6. package/dist/client.js.map +1 -1
  7. package/dist/contracts/labels.d.ts +149 -0
  8. package/dist/contracts/labels.js +166 -0
  9. package/dist/contracts/labels.js.map +1 -0
  10. package/dist/contracts/published-cache.d.ts +25 -0
  11. package/dist/contracts/published-cache.js +32 -0
  12. package/dist/contracts/published-cache.js.map +1 -0
  13. package/dist/contracts/values.d.ts +69 -0
  14. package/dist/contracts/values.js +100 -0
  15. package/dist/contracts/values.js.map +1 -0
  16. package/dist/entities/cms-audit-logs.d.ts +15 -70
  17. package/dist/entities/cms-audit-logs.js +75 -100
  18. package/dist/entities/cms-audit-logs.js.map +1 -1
  19. package/dist/entities/cms-draft-cache.d.ts +13 -73
  20. package/dist/entities/cms-draft-cache.js +35 -109
  21. package/dist/entities/cms-draft-cache.js.map +1 -1
  22. package/dist/entities/cms-label-values.d.ts +14 -65
  23. package/dist/entities/cms-label-values.js +78 -102
  24. package/dist/entities/cms-label-values.js.map +1 -1
  25. package/dist/entities/cms-label-versions.d.ts +16 -49
  26. package/dist/entities/cms-label-versions.js +73 -77
  27. package/dist/entities/cms-label-versions.js.map +1 -1
  28. package/dist/entities/cms-labels.d.ts +17 -14
  29. package/dist/entities/cms-labels.js +39 -45
  30. package/dist/entities/cms-labels.js.map +1 -1
  31. package/dist/entities/cms-published-cache.d.ts +14 -69
  32. package/dist/entities/cms-published-cache.js +33 -100
  33. package/dist/entities/cms-published-cache.js.map +1 -1
  34. package/dist/entities/index.d.ts +7 -10
  35. package/dist/entities/index.js +217 -9
  36. package/dist/entities/index.js.map +1 -1
  37. package/dist/generators/index.d.ts +8 -7
  38. package/dist/generators/index.js +657 -17
  39. package/dist/generators/index.js.map +1 -1
  40. package/dist/index.d.ts +134 -20
  41. package/dist/index.js +1115 -23
  42. package/dist/index.js.map +1 -1
  43. package/dist/{generators/label-sync-generator.d.ts → label-sync-generator-lQrcVfja.d.ts} +9 -6
  44. package/dist/labels/index.d.ts +31 -4
  45. package/dist/labels/index.js +31 -6
  46. package/dist/labels/index.js.map +1 -1
  47. package/dist/repositories/index.d.ts +205 -6
  48. package/dist/repositories/index.js +435 -8
  49. package/dist/repositories/index.js.map +1 -1
  50. package/dist/routes/labels/[id]/index.js +499 -89
  51. package/dist/routes/labels/[id]/index.js.map +1 -1
  52. package/dist/routes/labels/{[id] → _id_}/index.d.ts +5 -3
  53. package/dist/routes/labels/by-key/[key]/index.js +453 -29
  54. package/dist/routes/labels/by-key/[key]/index.js.map +1 -1
  55. package/dist/routes/labels/by-key/_key_/index.d.ts +10 -0
  56. package/dist/routes/labels/index.d.ts +5 -3
  57. package/dist/routes/labels/index.js +488 -68
  58. package/dist/routes/labels/index.js.map +1 -1
  59. package/dist/routes/published-cache/index.d.ts +5 -3
  60. package/dist/routes/published-cache/index.js +325 -30
  61. package/dist/routes/published-cache/index.js.map +1 -1
  62. package/dist/routes/values/[labelId]/[version]/index.js +467 -41
  63. package/dist/routes/values/[labelId]/[version]/index.js.map +1 -1
  64. package/dist/routes/values/[labelId]/index.js +463 -39
  65. package/dist/routes/values/[labelId]/index.js.map +1 -1
  66. package/dist/routes/values/_labelId_/_version_/index.d.ts +10 -0
  67. package/dist/routes/values/_labelId_/index.d.ts +10 -0
  68. package/dist/server.d.ts +17 -7
  69. package/dist/server.js +263 -252
  70. package/dist/server.js.map +1 -1
  71. package/dist/store.d.ts +8 -14
  72. package/dist/store.js +396 -198
  73. package/dist/store.js.map +1 -1
  74. package/dist/types.d.ts +14 -7
  75. package/dist/types.js +0 -6
  76. package/dist/types.js.map +1 -1
  77. package/migrations/0000_condemned_centennial.sql +89 -0
  78. package/migrations/meta/0000_snapshot.json +687 -0
  79. package/migrations/meta/_journal.json +13 -0
  80. package/package.json +33 -16
  81. package/dist/actions.d.ts.map +0 -1
  82. package/dist/client.d.ts.map +0 -1
  83. package/dist/cms.config.d.ts +0 -77
  84. package/dist/cms.config.d.ts.map +0 -1
  85. package/dist/cms.config.js +0 -111
  86. package/dist/cms.config.js.map +0 -1
  87. package/dist/entities/cms-audit-logs.d.ts.map +0 -1
  88. package/dist/entities/cms-draft-cache.d.ts.map +0 -1
  89. package/dist/entities/cms-label-values.d.ts.map +0 -1
  90. package/dist/entities/cms-label-versions.d.ts.map +0 -1
  91. package/dist/entities/cms-labels.d.ts.map +0 -1
  92. package/dist/entities/cms-published-cache.d.ts.map +0 -1
  93. package/dist/entities/index.d.ts.map +0 -1
  94. package/dist/generators/index.d.ts.map +0 -1
  95. package/dist/generators/label-sync-generator.d.ts.map +0 -1
  96. package/dist/generators/label-sync-generator.js +0 -87
  97. package/dist/generators/label-sync-generator.js.map +0 -1
  98. package/dist/helpers/locale.actions.d.ts +0 -132
  99. package/dist/helpers/locale.actions.d.ts.map +0 -1
  100. package/dist/helpers/locale.actions.js +0 -210
  101. package/dist/helpers/locale.actions.js.map +0 -1
  102. package/dist/helpers/locale.constants.d.ts +0 -10
  103. package/dist/helpers/locale.constants.d.ts.map +0 -1
  104. package/dist/helpers/locale.constants.js +0 -10
  105. package/dist/helpers/locale.constants.js.map +0 -1
  106. package/dist/helpers/locale.d.ts +0 -17
  107. package/dist/helpers/locale.d.ts.map +0 -1
  108. package/dist/helpers/locale.js +0 -20
  109. package/dist/helpers/locale.js.map +0 -1
  110. package/dist/helpers/sync.d.ts +0 -41
  111. package/dist/helpers/sync.d.ts.map +0 -1
  112. package/dist/helpers/sync.js +0 -309
  113. package/dist/helpers/sync.js.map +0 -1
  114. package/dist/index.d.ts.map +0 -1
  115. package/dist/init.d.ts +0 -31
  116. package/dist/init.d.ts.map +0 -1
  117. package/dist/init.js +0 -36
  118. package/dist/init.js.map +0 -1
  119. package/dist/labels/helpers.d.ts +0 -31
  120. package/dist/labels/helpers.d.ts.map +0 -1
  121. package/dist/labels/helpers.js +0 -60
  122. package/dist/labels/helpers.js.map +0 -1
  123. package/dist/labels/index.d.ts.map +0 -1
  124. package/dist/repositories/cms-draft-cache.repository.d.ts +0 -62
  125. package/dist/repositories/cms-draft-cache.repository.d.ts.map +0 -1
  126. package/dist/repositories/cms-draft-cache.repository.js +0 -56
  127. package/dist/repositories/cms-draft-cache.repository.js.map +0 -1
  128. package/dist/repositories/cms-label-values.repository.d.ts +0 -32
  129. package/dist/repositories/cms-label-values.repository.d.ts.map +0 -1
  130. package/dist/repositories/cms-label-values.repository.js +0 -72
  131. package/dist/repositories/cms-label-values.repository.js.map +0 -1
  132. package/dist/repositories/cms-labels.repository.d.ts +0 -53
  133. package/dist/repositories/cms-labels.repository.d.ts.map +0 -1
  134. package/dist/repositories/cms-labels.repository.js +0 -77
  135. package/dist/repositories/cms-labels.repository.js.map +0 -1
  136. package/dist/repositories/cms-published-cache.repository.d.ts +0 -53
  137. package/dist/repositories/cms-published-cache.repository.d.ts.map +0 -1
  138. package/dist/repositories/cms-published-cache.repository.js +0 -54
  139. package/dist/repositories/cms-published-cache.repository.js.map +0 -1
  140. package/dist/repositories/index.d.ts.map +0 -1
  141. package/dist/routes/labels/[id]/contract.d.ts +0 -68
  142. package/dist/routes/labels/[id]/contract.d.ts.map +0 -1
  143. package/dist/routes/labels/[id]/contract.js +0 -84
  144. package/dist/routes/labels/[id]/contract.js.map +0 -1
  145. package/dist/routes/labels/[id]/index.d.ts.map +0 -1
  146. package/dist/routes/labels/by-key/[key]/contract.d.ts +0 -24
  147. package/dist/routes/labels/by-key/[key]/contract.d.ts.map +0 -1
  148. package/dist/routes/labels/by-key/[key]/contract.js +0 -28
  149. package/dist/routes/labels/by-key/[key]/contract.js.map +0 -1
  150. package/dist/routes/labels/by-key/[key]/index.d.ts +0 -8
  151. package/dist/routes/labels/by-key/[key]/index.d.ts.map +0 -1
  152. package/dist/routes/labels/contract.d.ts +0 -59
  153. package/dist/routes/labels/contract.d.ts.map +0 -1
  154. package/dist/routes/labels/contract.js +0 -75
  155. package/dist/routes/labels/contract.js.map +0 -1
  156. package/dist/routes/labels/index.d.ts.map +0 -1
  157. package/dist/routes/published-cache/contract.d.ts +0 -25
  158. package/dist/routes/published-cache/contract.d.ts.map +0 -1
  159. package/dist/routes/published-cache/contract.js +0 -35
  160. package/dist/routes/published-cache/contract.js.map +0 -1
  161. package/dist/routes/published-cache/index.d.ts.map +0 -1
  162. package/dist/routes/values/[labelId]/[version]/contract.d.ts +0 -29
  163. package/dist/routes/values/[labelId]/[version]/contract.d.ts.map +0 -1
  164. package/dist/routes/values/[labelId]/[version]/contract.js +0 -33
  165. package/dist/routes/values/[labelId]/[version]/contract.js.map +0 -1
  166. package/dist/routes/values/[labelId]/[version]/index.d.ts +0 -8
  167. package/dist/routes/values/[labelId]/[version]/index.d.ts.map +0 -1
  168. package/dist/routes/values/[labelId]/contract.d.ts +0 -38
  169. package/dist/routes/values/[labelId]/contract.d.ts.map +0 -1
  170. package/dist/routes/values/[labelId]/contract.js +0 -59
  171. package/dist/routes/values/[labelId]/contract.js.map +0 -1
  172. package/dist/routes/values/[labelId]/index.d.ts +0 -8
  173. package/dist/routes/values/[labelId]/index.d.ts.map +0 -1
  174. package/dist/server.d.ts.map +0 -1
  175. package/dist/store.d.ts.map +0 -1
  176. package/dist/types.d.ts.map +0 -1
@@ -1,73 +1,493 @@
1
- /**
2
- * CMS Labels Routes
3
- *
4
- * 라벨 메타데이터 관리 API
5
- * - GET /cms/labels - 라벨 목록 조회
6
- * - POST /cms/labels - 라벨 생성
7
- */
8
- import { createApp } from '@spfn/core/route';
9
- import { Transactional } from '@spfn/core/db';
10
- import { cmsLabelsRepository } from '../../repositories/index.js';
11
- import { getLabelsContract, createLabelContract } from './contract.js';
12
- const app = createApp();
13
- /**
14
- * GET /cms/labels
15
- * 라벨 목록 조회 (페이지네이션, 섹션 필터)
16
- */
1
+ // src/routes/labels/index.ts
2
+ import { createApp } from "@spfn/core/route";
3
+ import { Transactional } from "@spfn/core/db";
4
+
5
+ // src/repositories/cms-labels.repository.ts
6
+ import { findOne, findMany as findManyHelper, create as createHelper, updateOne, deleteOne, count as countHelper } from "@spfn/core/db";
7
+ import { desc } from "drizzle-orm";
8
+
9
+ // src/entities/cms-labels.ts
10
+ import { index, integer, serial, text, timestamp } from "drizzle-orm/pg-core";
11
+ import { createFunctionSchema } from "@spfn/core/db";
12
+ var schema = createFunctionSchema("@spfn/cms");
13
+ var cmsLabels = schema.table("labels", {
14
+ // Primary Key
15
+ id: serial("id").primaryKey(),
16
+ // 라벨 식별자
17
+ key: text("key").notNull().unique(),
18
+ // 예: "home.hero.title", "why-futureplay.hero.subtitle"
19
+ // 구조: {section}.{component}.{property}
20
+ // 섹션 분류 (페이지 단위)
21
+ section: text("section").notNull(),
22
+ // 예: "home", "why-futureplay", "team"
23
+ // 값 타입
24
+ type: text("type").notNull(),
25
+ // "text" | "image" | "video" | "file" | "object"
26
+ // 기본값
27
+ defaultValue: text("default_value"),
28
+ // 라벨의 기본값 (sync 시 설정)
29
+ // 설명
30
+ description: text("description"),
31
+ // 라벨에 대한 설명 (optional)
32
+ // 현재 발행된 버전 번호
33
+ publishedVersion: integer("published_version"),
34
+ // null = 미발행 상태
35
+ // 1, 2, 3... = 발행된 버전 번호
36
+ // 생성자 추적
37
+ createdBy: text("created_by"),
38
+ // 타임스탬프
39
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
40
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
41
+ }, (table) => [
42
+ // 인덱스: 섹션별 조회 최적화
43
+ index("cms_labels_section_idx").on(table.section),
44
+ // 인덱스: key로 조회 최적화 (unique 제약으로 자동 생성되지만 명시)
45
+ index("cms_labels_key_idx").on(table.key)
46
+ ]);
47
+
48
+ // src/entities/cms-label-values.ts
49
+ import { serial as serial2, integer as integer2, text as text2, jsonb, timestamp as timestamp2, index as index2, unique } from "drizzle-orm/pg-core";
50
+ import { createFunctionSchema as createFunctionSchema2 } from "@spfn/core/db";
51
+ var schema2 = createFunctionSchema2("@spfn/cms");
52
+ var cmsLabelValues = schema2.table("label_values", {
53
+ // Primary Key
54
+ id: serial2("id").primaryKey(),
55
+ // Foreign Key: cms_labels
56
+ labelId: integer2("label_id").notNull().references(() => cmsLabels.id, { onDelete: "cascade" }),
57
+ // 버전 번호
58
+ version: integer2("version").notNull().default(1),
59
+ // 언어 코드
60
+ locale: text2("locale").notNull().default("ko"),
61
+ // "ko" | "en" | "ja"
62
+ // 반응형 브레이크포인트
63
+ breakpoint: text2("breakpoint"),
64
+ // null = 기본값 (모든 화면 크기)
65
+ // "sm" | "md" | "lg" | "xl" | "2xl"
66
+ // 실제 값 (JSONB)
67
+ value: jsonb("value").notNull(),
68
+ // LabelValue 타입:
69
+ // - TextValue: { type: "text", content: string }
70
+ // - ImageValue: { type: "image", url: string, alt?: string, width?: number, height?: number }
71
+ // - VideoValue: { type: "video", url: string, thumbnail?: string, duration?: number }
72
+ // - FileValue: { type: "file", url: string, filename: string, size?: number }
73
+ // - ObjectValue: { type: "object", fields: Record<string, LabelValue> }
74
+ // 생성 시각
75
+ createdAt: timestamp2("created_at", { withTimezone: true }).notNull().defaultNow()
76
+ }, (table) => [
77
+ // UNIQUE 제약: 같은 버전에서 locale + breakpoint 조합은 유일
78
+ unique("cms_label_values_locale_breakpoint_unique").on(table.labelId, table.version, table.locale, table.breakpoint),
79
+ // 인덱스: labelId + version 복합 조회 최적화
80
+ index2("cms_label_values_label_version_idx").on(table.labelId, table.version),
81
+ // 인덱스: locale 필터링 최적화
82
+ index2("cms_label_values_locale_idx").on(table.locale)
83
+ ]);
84
+
85
+ // src/entities/cms-label-versions.ts
86
+ import { serial as serial3, integer as integer3, text as text3, timestamp as timestamp3, index as index3, unique as unique2 } from "drizzle-orm/pg-core";
87
+ import { createFunctionSchema as createFunctionSchema3 } from "@spfn/core/db";
88
+ var schema3 = createFunctionSchema3("@spfn/cms");
89
+ var cmsLabelVersions = schema3.table("label_versions", {
90
+ // Primary Key
91
+ id: serial3("id").primaryKey(),
92
+ // Foreign Key: cms_labels
93
+ labelId: integer3("label_id").notNull().references(() => cmsLabels.id, { onDelete: "cascade" }),
94
+ // 버전 번호
95
+ version: integer3("version").notNull(),
96
+ // 버전 상태
97
+ status: text3("status").notNull(),
98
+ // "draft" | "published" | "archived"
99
+ // 발행 정보
100
+ publishedAt: timestamp3("published_at", { withTimezone: true }),
101
+ publishedBy: text3("published_by"),
102
+ // 버전 노트 (변경사항 설명)
103
+ notes: text3("notes"),
104
+ // 버전 생성자
105
+ createdBy: text3("created_by"),
106
+ // 생성 시각
107
+ createdAt: timestamp3("created_at", { withTimezone: true }).notNull().defaultNow()
108
+ }, (table) => [
109
+ // UNIQUE 제약: 각 라벨의 버전 번호는 고유
110
+ unique2("cms_label_versions_label_version_unique").on(table.labelId, table.version),
111
+ // 인덱스: labelId로 버전 목록 조회 최적화
112
+ index3("cms_label_versions_label_id_idx").on(table.labelId),
113
+ // 인덱스: status 필터링 최적화
114
+ index3("cms_label_versions_status_idx").on(table.status)
115
+ ]);
116
+
117
+ // src/entities/cms-draft-cache.ts
118
+ import { serial as serial4, text as text4, jsonb as jsonb2, timestamp as timestamp4, index as index4, unique as unique3 } from "drizzle-orm/pg-core";
119
+ import { createFunctionSchema as createFunctionSchema4 } from "@spfn/core/db";
120
+ var schema4 = createFunctionSchema4("@spfn/cms");
121
+ var cmsDraftCache = schema4.table("draft_cache", {
122
+ // Primary Key
123
+ id: serial4("id").primaryKey(),
124
+ // 섹션 (페이지 단위)
125
+ section: text4("section").notNull(),
126
+ // "home" | "why-futureplay" | "team" | "our-companies" | "apply"
127
+ // 언어
128
+ locale: text4("locale").notNull(),
129
+ // "ko" | "en" | "ja"
130
+ // 사용자 ID (핵심 필드!)
131
+ userId: text4("user_id").notNull(),
132
+ // 각 관리자의 독립적인 작업 공간
133
+ // Draft 콘텐츠 (JSONB)
134
+ content: jsonb2("content").notNull(),
135
+ // Record<string, LabelValue>
136
+ // {
137
+ // "home.hero.title": { type: "text", content: "수정 중..." },
138
+ // "home.hero.subtitle": { type: "text", content: "새로운 문구" },
139
+ // ...
140
+ // }
141
+ // 최종 수정 시각
142
+ updatedAt: timestamp4("updated_at", { withTimezone: true }).notNull().defaultNow()
143
+ }, (table) => [
144
+ // UNIQUE 제약: section + locale + userId 조합은 유일
145
+ unique3("cms_draft_cache_unique").on(table.section, table.locale, table.userId),
146
+ // 인덱스: section으로 조회 최적화
147
+ index4("cms_draft_cache_section_idx").on(table.section),
148
+ // 인덱스: userId로 사용자의 모든 draft 조회 최적화
149
+ index4("cms_draft_cache_user_idx").on(table.userId)
150
+ ]);
151
+
152
+ // src/entities/cms-published-cache.ts
153
+ import { serial as serial5, text as text5, jsonb as jsonb3, integer as integer4, timestamp as timestamp5, index as index5, unique as unique4 } from "drizzle-orm/pg-core";
154
+ import { createFunctionSchema as createFunctionSchema5 } from "@spfn/core/db";
155
+ var schema5 = createFunctionSchema5("@spfn/cms");
156
+ var cmsPublishedCache = schema5.table("published_cache", {
157
+ // Primary Key
158
+ id: serial5("id").primaryKey(),
159
+ // 섹션 (페이지 단위)
160
+ section: text5("section").notNull(),
161
+ // "home" | "why-futureplay" | "team" | "our-companies" | "apply"
162
+ // 언어
163
+ locale: text5("locale").notNull(),
164
+ // "ko" | "en" | "ja"
165
+ // 캐시된 콘텐츠 (JSONB)
166
+ content: jsonb3("content").notNull(),
167
+ // Record<string, LabelValue>
168
+ // {
169
+ // "home.hero.title": { type: "text", content: "..." },
170
+ // "home.hero.image": { type: "image", url: "...", alt: "..." },
171
+ // ...
172
+ // }
173
+ // 발행 정보
174
+ publishedAt: timestamp5("published_at", { withTimezone: true }).notNull(),
175
+ publishedBy: text5("published_by"),
176
+ // 캐시 버전 (클라이언트 캐싱용)
177
+ version: integer4("version").notNull().default(1)
178
+ }, (table) => [
179
+ // UNIQUE 제약: section + locale 조합은 유일
180
+ unique4("cms_published_cache_unique").on(table.section, table.locale),
181
+ // 인덱스: section으로 조회 최적화
182
+ index5("cms_published_cache_section_idx").on(table.section)
183
+ ]);
184
+
185
+ // src/entities/cms-audit-logs.ts
186
+ import { serial as serial6, integer as integer5, text as text6, jsonb as jsonb4, timestamp as timestamp6, index as index6 } from "drizzle-orm/pg-core";
187
+ import { createFunctionSchema as createFunctionSchema6 } from "@spfn/core/db";
188
+ var schema6 = createFunctionSchema6("@spfn/cms");
189
+ var cmsAuditLogs = schema6.table("audit_logs", {
190
+ // Primary Key
191
+ id: serial6("id").primaryKey(),
192
+ // Foreign Key: cms_labels (nullable - 라벨 삭제 시 로그는 유지)
193
+ labelId: integer5("label_id").references(() => cmsLabels.id, { onDelete: "set null" }),
194
+ // 작업 유형
195
+ action: text6("action").notNull(),
196
+ // "create" | "update" | "publish" | "unpublish" | "archive" | "delete" | "rollback" | "duplicate"
197
+ // 사용자 정보
198
+ userId: text6("user_id").notNull(),
199
+ userName: text6("user_name"),
200
+ // 변경 내용 (before/after)
201
+ changes: jsonb4("changes"),
202
+ // { before: {...}, after: {...} }
203
+ // 추가 메타데이터
204
+ metadata: jsonb4("metadata"),
205
+ // { version: number, ip: string, userAgent: string, ... }
206
+ // 작업 시각
207
+ createdAt: timestamp6("created_at", { withTimezone: true }).notNull().defaultNow()
208
+ }, (table) => [
209
+ // 인덱스: labelId로 이력 조회 최적화
210
+ index6("cms_audit_logs_label_id_idx").on(table.labelId),
211
+ // 인덱스: userId로 사용자 활동 조회 최적화
212
+ index6("cms_audit_logs_user_id_idx").on(table.userId),
213
+ // 인덱스: action 필터링 최적화
214
+ index6("cms_audit_logs_action_idx").on(table.action),
215
+ // 인덱스: 시간순 조회 최적화
216
+ index6("cms_audit_logs_created_at_idx").on(table.createdAt)
217
+ ]);
218
+
219
+ // src/repositories/cms-labels.repository.ts
220
+ async function findMany(options) {
221
+ const { section, limit = 20, offset = 0 } = options || {};
222
+ return findManyHelper(cmsLabels, {
223
+ where: section ? { section } : void 0,
224
+ orderBy: desc(cmsLabels.updatedAt),
225
+ limit,
226
+ offset
227
+ });
228
+ }
229
+ async function count(section) {
230
+ return countHelper(cmsLabels, section ? { section } : void 0);
231
+ }
232
+ async function findById(id) {
233
+ return findOne(cmsLabels, { id });
234
+ }
235
+ async function findByKey(key) {
236
+ return findOne(cmsLabels, { key });
237
+ }
238
+ async function findBySection(section) {
239
+ return findManyHelper(cmsLabels, {
240
+ where: { section },
241
+ orderBy: desc(cmsLabels.updatedAt)
242
+ });
243
+ }
244
+ async function create(data) {
245
+ return createHelper(cmsLabels, data);
246
+ }
247
+ async function updateById(id, data) {
248
+ return updateOne(cmsLabels, { id }, { ...data, updatedAt: /* @__PURE__ */ new Date() });
249
+ }
250
+ async function deleteById(id) {
251
+ return deleteOne(cmsLabels, { id });
252
+ }
253
+ var cmsLabelsRepository = {
254
+ findMany,
255
+ count,
256
+ findById,
257
+ findByKey,
258
+ findBySection,
259
+ create,
260
+ updateById,
261
+ deleteById
262
+ };
263
+
264
+ // src/repositories/cms-label-values.repository.ts
265
+ import { findOne as findOne2, findMany as findMany2, create as create2, updateOne as updateOne2, deleteMany } from "@spfn/core/db";
266
+ import { eq, and, isNull } from "drizzle-orm";
267
+
268
+ // src/repositories/cms-draft-cache.repository.ts
269
+ import { findOne as findOne3, findMany as findMany3, deleteOne as deleteOne2, deleteMany as deleteMany2, upsert as upsertHelper } from "@spfn/core/db";
270
+ import { eq as eq2, and as and2, lt } from "drizzle-orm";
271
+
272
+ // src/repositories/cms-published-cache.repository.ts
273
+ import { findOne as findOne4, findMany as findMany4, deleteOne as deleteOne3, deleteMany as deleteMany3, upsert as upsertHelper2 } from "@spfn/core/db";
274
+ import { eq as eq3, and as and3, sql } from "drizzle-orm";
275
+
276
+ // src/contracts/labels.ts
277
+ import { Type } from "@sinclair/typebox";
278
+ var getLabelsContract = {
279
+ method: "GET",
280
+ path: "/cms/labels",
281
+ query: Type.Object({
282
+ section: Type.Optional(Type.String({ description: "\uC139\uC158\uC73C\uB85C \uD544\uD130\uB9C1 (\uC608: home, why-futureplay)" })),
283
+ limit: Type.Optional(Type.Number({ minimum: 1, maximum: 100, default: 20, description: "\uD398\uC774\uC9C0\uB2F9 \uD56D\uBAA9 \uC218" })),
284
+ offset: Type.Optional(Type.Number({ minimum: 0, default: 0, description: "\uC2DC\uC791 \uC624\uD504\uC14B" }))
285
+ }),
286
+ response: Type.Object({
287
+ labels: Type.Array(Type.Object({
288
+ id: Type.Number(),
289
+ key: Type.String(),
290
+ section: Type.String(),
291
+ type: Type.String(),
292
+ publishedVersion: Type.Union([Type.Number(), Type.Null()]),
293
+ createdBy: Type.Union([Type.String(), Type.Null()]),
294
+ createdAt: Type.String(),
295
+ updatedAt: Type.String()
296
+ })),
297
+ total: Type.Number(),
298
+ limit: Type.Number(),
299
+ offset: Type.Number()
300
+ })
301
+ };
302
+ var createLabelContract = {
303
+ method: "POST",
304
+ path: "/cms/labels",
305
+ body: Type.Object({
306
+ key: Type.String({
307
+ description: "\uACE0\uC720 \uD0A4 (\uC608: home.hero.title)",
308
+ pattern: "^[a-z0-9-]+\\.[a-z0-9-]+\\.[a-z0-9-]+$"
309
+ }),
310
+ section: Type.String({
311
+ description: "\uC139\uC158 \uC774\uB984 (\uC608: home, why-futureplay)",
312
+ pattern: "^[a-z0-9-]+$"
313
+ }),
314
+ type: Type.Union([
315
+ Type.Literal("text"),
316
+ Type.Literal("image"),
317
+ Type.Literal("video"),
318
+ Type.Literal("file"),
319
+ Type.Literal("object")
320
+ ], { description: "\uAC12 \uD0C0\uC785" }),
321
+ createdBy: Type.Optional(Type.String({ description: "\uC0DD\uC131\uC790 ID" }))
322
+ }),
323
+ response: Type.Union([
324
+ Type.Object({
325
+ id: Type.Number(),
326
+ key: Type.String(),
327
+ section: Type.String(),
328
+ type: Type.String(),
329
+ publishedVersion: Type.Union([Type.Number(), Type.Null()]),
330
+ createdBy: Type.Union([Type.String(), Type.Null()]),
331
+ createdAt: Type.String(),
332
+ updatedAt: Type.String()
333
+ }),
334
+ Type.Object({
335
+ error: Type.String(),
336
+ key: Type.Optional(Type.String())
337
+ })
338
+ ])
339
+ };
340
+ var getLabelContract = {
341
+ method: "GET",
342
+ path: "/cms/labels/:id",
343
+ params: Type.Object({
344
+ id: Type.String({ description: "\uB77C\uBCA8 ID" })
345
+ }),
346
+ response: Type.Union([
347
+ Type.Object({
348
+ id: Type.Number(),
349
+ key: Type.String(),
350
+ section: Type.String(),
351
+ type: Type.String(),
352
+ publishedVersion: Type.Union([Type.Number(), Type.Null()]),
353
+ createdBy: Type.Union([Type.String(), Type.Null()]),
354
+ createdAt: Type.String(),
355
+ updatedAt: Type.String()
356
+ }),
357
+ Type.Object({
358
+ error: Type.String()
359
+ })
360
+ ])
361
+ };
362
+ var updateLabelContract = {
363
+ method: "PATCH",
364
+ path: "/cms/labels/:id",
365
+ params: Type.Object({
366
+ id: Type.String({ description: "\uB77C\uBCA8 ID" })
367
+ }),
368
+ body: Type.Object({
369
+ section: Type.Optional(Type.String({ description: "\uC139\uC158 \uBCC0\uACBD" })),
370
+ type: Type.Optional(Type.Union([
371
+ Type.Literal("text"),
372
+ Type.Literal("image"),
373
+ Type.Literal("video"),
374
+ Type.Literal("file"),
375
+ Type.Literal("object")
376
+ ]))
377
+ }),
378
+ response: Type.Union([
379
+ Type.Object({
380
+ id: Type.Number(),
381
+ key: Type.String(),
382
+ section: Type.String(),
383
+ type: Type.String(),
384
+ publishedVersion: Type.Union([Type.Number(), Type.Null()]),
385
+ createdBy: Type.Union([Type.String(), Type.Null()]),
386
+ createdAt: Type.String(),
387
+ updatedAt: Type.String()
388
+ }),
389
+ Type.Object({
390
+ error: Type.String()
391
+ })
392
+ ])
393
+ };
394
+ var deleteLabelContract = {
395
+ method: "DELETE",
396
+ path: "/cms/labels/:id",
397
+ params: Type.Object({
398
+ id: Type.String({ description: "\uB77C\uBCA8 ID" })
399
+ }),
400
+ response: Type.Union([
401
+ Type.Object({
402
+ success: Type.Boolean(),
403
+ id: Type.Number()
404
+ }),
405
+ Type.Object({
406
+ error: Type.String()
407
+ })
408
+ ])
409
+ };
410
+ var getLabelByKeyContract = {
411
+ method: "GET",
412
+ path: "/cms/labels/by-key/:key",
413
+ params: Type.Object({
414
+ key: Type.String({ description: "\uB77C\uBCA8 Key (\uC608: home.hero.title)" })
415
+ }),
416
+ response: Type.Union([
417
+ Type.Object({
418
+ id: Type.Number(),
419
+ key: Type.String(),
420
+ section: Type.String(),
421
+ type: Type.String(),
422
+ publishedVersion: Type.Union([Type.Number(), Type.Null()]),
423
+ createdBy: Type.Union([Type.String(), Type.Null()]),
424
+ createdAt: Type.String(),
425
+ updatedAt: Type.String()
426
+ }),
427
+ Type.Object({
428
+ error: Type.String(),
429
+ key: Type.Optional(Type.String())
430
+ })
431
+ ])
432
+ };
433
+
434
+ // src/routes/labels/index.ts
435
+ var app = createApp();
17
436
  app.bind(getLabelsContract, async (c) => {
18
- const { section, limit = 20, offset = 0 } = c.query;
19
- // 라벨 목록 조회
20
- const labels = await cmsLabelsRepository.findMany({
21
- section,
22
- limit,
23
- offset,
24
- });
25
- // 전체 개수 조회
26
- const total = await cmsLabelsRepository.count(section);
27
- return c.json({
28
- labels: labels.map((label) => ({
29
- id: label.id,
30
- key: label.key,
31
- section: label.section,
32
- type: label.type,
33
- publishedVersion: label.publishedVersion,
34
- createdBy: label.createdBy,
35
- createdAt: label.createdAt.toISOString(),
36
- updatedAt: label.updatedAt.toISOString(),
37
- })),
38
- total,
39
- limit,
40
- offset,
41
- });
437
+ const { section, limit = 20, offset = 0 } = c.query;
438
+ const labels = await cmsLabelsRepository.findMany({
439
+ section,
440
+ limit,
441
+ offset
442
+ });
443
+ const total = await cmsLabelsRepository.count(section);
444
+ return c.json({
445
+ labels: labels.map((label) => ({
446
+ id: label.id,
447
+ key: label.key,
448
+ section: label.section,
449
+ type: label.type,
450
+ publishedVersion: label.publishedVersion,
451
+ createdBy: label.createdBy,
452
+ createdAt: label.createdAt.toISOString(),
453
+ updatedAt: label.updatedAt.toISOString()
454
+ })),
455
+ total,
456
+ limit,
457
+ offset
458
+ });
42
459
  });
43
- /**
44
- * POST /cms/labels
45
- * 새 라벨 생성
46
- */
47
460
  app.bind(createLabelContract, [Transactional()], async (c) => {
48
- const body = await c.data();
49
- // 중복 key 체크
50
- const existing = await cmsLabelsRepository.findByKey(body.key);
51
- if (existing) {
52
- return c.json({ error: 'Label with this key already exists', key: body.key }, 409);
53
- }
54
- // 라벨 생성
55
- const label = await cmsLabelsRepository.create({
56
- key: body.key,
57
- section: body.section,
58
- type: body.type,
59
- createdBy: body.createdBy,
60
- });
61
- return c.json({
62
- id: label.id,
63
- key: label.key,
64
- section: label.section,
65
- type: label.type,
66
- publishedVersion: label.publishedVersion,
67
- createdBy: label.createdBy,
68
- createdAt: label.createdAt.toISOString(),
69
- updatedAt: label.updatedAt.toISOString(),
70
- }, 201);
461
+ const body = await c.data();
462
+ const existing = await cmsLabelsRepository.findByKey(body.key);
463
+ if (existing) {
464
+ return c.json(
465
+ { error: "Label with this key already exists", key: body.key },
466
+ 409
467
+ );
468
+ }
469
+ const label = await cmsLabelsRepository.create({
470
+ key: body.key,
471
+ section: body.section,
472
+ type: body.type,
473
+ createdBy: body.createdBy
474
+ });
475
+ return c.json(
476
+ {
477
+ id: label.id,
478
+ key: label.key,
479
+ section: label.section,
480
+ type: label.type,
481
+ publishedVersion: label.publishedVersion,
482
+ createdBy: label.createdBy,
483
+ createdAt: label.createdAt.toISOString(),
484
+ updatedAt: label.updatedAt.toISOString()
485
+ },
486
+ 201
487
+ );
71
488
  });
72
- export default app;
489
+ var labels_default = app;
490
+ export {
491
+ labels_default as default
492
+ };
73
493
  //# sourceMappingURL=index.js.map