@spfn/cms 0.1.0-alpha.8 → 0.1.0-alpha.81

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 (233) hide show
  1. package/README.md +28 -416
  2. package/dist/{helpers/locale.actions.d.ts → actions-BEFWwQsh.d.ts} +70 -7
  3. package/dist/actions.d.ts +2 -9
  4. package/dist/actions.js +99 -10
  5. package/dist/actions.js.map +1 -1
  6. package/dist/api.d.ts +319 -0
  7. package/dist/api.js +467 -0
  8. package/dist/api.js.map +1 -0
  9. package/dist/client.d.ts +135 -127
  10. package/dist/client.js +1318 -59
  11. package/dist/client.js.map +1 -1
  12. package/dist/{types.d.ts → index-Dh5FjWzR.d.ts} +45 -7
  13. package/dist/index.d.ts +112 -16
  14. package/dist/index.js +625 -23
  15. package/dist/index.js.map +1 -1
  16. package/dist/label-sync-generator-B0EmvtWM.d.ts +32 -0
  17. package/dist/lib/contracts/labels.d.ts +244 -0
  18. package/dist/lib/contracts/labels.js +269 -0
  19. package/dist/lib/contracts/labels.js.map +1 -0
  20. package/dist/lib/contracts/published-cache.d.ts +48 -0
  21. package/dist/lib/contracts/published-cache.js +49 -0
  22. package/dist/lib/contracts/published-cache.js.map +1 -0
  23. package/dist/lib/contracts/values.d.ts +71 -0
  24. package/dist/lib/contracts/values.js +104 -0
  25. package/dist/lib/contracts/values.js.map +1 -0
  26. package/dist/locale.constants-BNkSdNP1.d.ts +108 -0
  27. package/dist/{entities → server/entities}/cms-audit-logs.d.ts +15 -70
  28. package/dist/server/entities/cms-audit-logs.js +78 -0
  29. package/dist/server/entities/cms-audit-logs.js.map +1 -0
  30. package/dist/{entities → server/entities}/cms-draft-cache.d.ts +13 -73
  31. package/dist/server/entities/cms-draft-cache.js +38 -0
  32. package/dist/server/entities/cms-draft-cache.js.map +1 -0
  33. package/dist/{entities → server/entities}/cms-label-values.d.ts +16 -67
  34. package/dist/server/entities/cms-label-values.js +81 -0
  35. package/dist/server/entities/cms-label-values.js.map +1 -0
  36. package/dist/{entities → server/entities}/cms-labels.d.ts +17 -14
  37. package/dist/server/entities/cms-labels.js +42 -0
  38. package/dist/server/entities/cms-labels.js.map +1 -0
  39. package/dist/{entities → server/entities}/cms-published-cache.d.ts +14 -69
  40. package/dist/server/entities/cms-published-cache.js +36 -0
  41. package/dist/server/entities/cms-published-cache.js.map +1 -0
  42. package/dist/server/entities/index.d.ts +6 -0
  43. package/dist/server/entities/index.js +185 -0
  44. package/dist/server/entities/index.js.map +1 -0
  45. package/dist/server/generators/index.d.ts +19 -0
  46. package/dist/server/generators/index.js +731 -0
  47. package/dist/server/generators/index.js.map +1 -0
  48. package/dist/server/labels/index.d.ts +1 -0
  49. package/dist/server/labels/index.js +33 -0
  50. package/dist/server/labels/index.js.map +1 -0
  51. package/dist/server/repositories/index.d.ts +212 -0
  52. package/dist/server/repositories/index.js +418 -0
  53. package/dist/server/repositories/index.js.map +1 -0
  54. package/dist/server/routes/labels/[id]/admin/index.js +679 -0
  55. package/dist/server/routes/labels/[id]/admin/index.js.map +1 -0
  56. package/dist/server/routes/labels/[id]/index.js +576 -0
  57. package/dist/server/routes/labels/[id]/index.js.map +1 -0
  58. package/dist/server/routes/labels/[id]/publish/index.js +720 -0
  59. package/dist/server/routes/labels/[id]/publish/index.js.map +1 -0
  60. package/dist/server/routes/labels/[id]/versions/index.js +548 -0
  61. package/dist/server/routes/labels/[id]/versions/index.js.map +1 -0
  62. package/dist/server/routes/labels/_id_/admin/index.d.ts +11 -0
  63. package/dist/{routes/labels/[id] → server/routes/labels/_id_}/index.d.ts +5 -3
  64. package/dist/server/routes/labels/_id_/publish/index.d.ts +11 -0
  65. package/dist/server/routes/labels/_id_/versions/index.d.ts +11 -0
  66. package/dist/server/routes/labels/by-key/[key]/index.js +525 -0
  67. package/dist/server/routes/labels/by-key/[key]/index.js.map +1 -0
  68. package/dist/server/routes/labels/by-key/_key_/index.d.ts +10 -0
  69. package/dist/server/routes/labels/index.d.ts +12 -0
  70. package/dist/server/routes/labels/index.js +684 -0
  71. package/dist/server/routes/labels/index.js.map +1 -0
  72. package/dist/server/routes/published-cache/index.d.ts +11 -0
  73. package/dist/server/routes/published-cache/index.js +337 -0
  74. package/dist/server/routes/published-cache/index.js.map +1 -0
  75. package/dist/server/routes/values/[labelId]/[version]/index.js +457 -0
  76. package/dist/server/routes/values/[labelId]/[version]/index.js.map +1 -0
  77. package/dist/server/routes/values/[labelId]/index.js +452 -0
  78. package/dist/server/routes/values/[labelId]/index.js.map +1 -0
  79. package/dist/server/routes/values/_labelId_/_version_/index.d.ts +10 -0
  80. package/dist/server/routes/values/_labelId_/index.d.ts +10 -0
  81. package/dist/server.d.ts +77 -7
  82. package/dist/server.js +1747 -247
  83. package/dist/server.js.map +1 -1
  84. package/migrations/0000_init.sql +3 -0
  85. package/migrations/0001_far_lady_vermin.sql +86 -0
  86. package/migrations/0002_heavy_the_enforcers.sql +2 -0
  87. package/migrations/0003_rare_runaways.sql +1 -0
  88. package/migrations/meta/0000_snapshot.json +15 -0
  89. package/migrations/meta/0001_snapshot.json +687 -0
  90. package/migrations/meta/0002_snapshot.json +686 -0
  91. package/migrations/meta/0003_snapshot.json +563 -0
  92. package/migrations/meta/_journal.json +34 -0
  93. package/package.json +55 -36
  94. package/dist/actions.d.ts.map +0 -1
  95. package/dist/client.d.ts.map +0 -1
  96. package/dist/cms.config.d.ts +0 -77
  97. package/dist/cms.config.d.ts.map +0 -1
  98. package/dist/cms.config.js +0 -111
  99. package/dist/cms.config.js.map +0 -1
  100. package/dist/entities/cms-audit-logs.d.ts.map +0 -1
  101. package/dist/entities/cms-audit-logs.js +0 -103
  102. package/dist/entities/cms-audit-logs.js.map +0 -1
  103. package/dist/entities/cms-draft-cache.d.ts.map +0 -1
  104. package/dist/entities/cms-draft-cache.js +0 -112
  105. package/dist/entities/cms-draft-cache.js.map +0 -1
  106. package/dist/entities/cms-label-values.d.ts.map +0 -1
  107. package/dist/entities/cms-label-values.js +0 -105
  108. package/dist/entities/cms-label-values.js.map +0 -1
  109. package/dist/entities/cms-label-versions.d.ts +0 -207
  110. package/dist/entities/cms-label-versions.d.ts.map +0 -1
  111. package/dist/entities/cms-label-versions.js +0 -80
  112. package/dist/entities/cms-label-versions.js.map +0 -1
  113. package/dist/entities/cms-labels.d.ts.map +0 -1
  114. package/dist/entities/cms-labels.js +0 -48
  115. package/dist/entities/cms-labels.js.map +0 -1
  116. package/dist/entities/cms-published-cache.d.ts.map +0 -1
  117. package/dist/entities/cms-published-cache.js +0 -103
  118. package/dist/entities/cms-published-cache.js.map +0 -1
  119. package/dist/entities/index.d.ts +0 -10
  120. package/dist/entities/index.d.ts.map +0 -1
  121. package/dist/entities/index.js +0 -10
  122. package/dist/entities/index.js.map +0 -1
  123. package/dist/generators/index.d.ts +0 -19
  124. package/dist/generators/index.d.ts.map +0 -1
  125. package/dist/generators/index.js +0 -19
  126. package/dist/generators/index.js.map +0 -1
  127. package/dist/generators/label-sync-generator.d.ts +0 -33
  128. package/dist/generators/label-sync-generator.d.ts.map +0 -1
  129. package/dist/generators/label-sync-generator.js +0 -86
  130. package/dist/generators/label-sync-generator.js.map +0 -1
  131. package/dist/helpers/locale.actions.d.ts.map +0 -1
  132. package/dist/helpers/locale.actions.js +0 -210
  133. package/dist/helpers/locale.actions.js.map +0 -1
  134. package/dist/helpers/locale.constants.d.ts +0 -10
  135. package/dist/helpers/locale.constants.d.ts.map +0 -1
  136. package/dist/helpers/locale.constants.js +0 -10
  137. package/dist/helpers/locale.constants.js.map +0 -1
  138. package/dist/helpers/locale.d.ts +0 -17
  139. package/dist/helpers/locale.d.ts.map +0 -1
  140. package/dist/helpers/locale.js +0 -20
  141. package/dist/helpers/locale.js.map +0 -1
  142. package/dist/helpers/sync.d.ts +0 -41
  143. package/dist/helpers/sync.d.ts.map +0 -1
  144. package/dist/helpers/sync.js +0 -309
  145. package/dist/helpers/sync.js.map +0 -1
  146. package/dist/index.d.ts.map +0 -1
  147. package/dist/init.d.ts +0 -31
  148. package/dist/init.d.ts.map +0 -1
  149. package/dist/init.js +0 -36
  150. package/dist/init.js.map +0 -1
  151. package/dist/labels/helpers.d.ts +0 -31
  152. package/dist/labels/helpers.d.ts.map +0 -1
  153. package/dist/labels/helpers.js +0 -60
  154. package/dist/labels/helpers.js.map +0 -1
  155. package/dist/labels/index.d.ts +0 -7
  156. package/dist/labels/index.d.ts.map +0 -1
  157. package/dist/labels/index.js +0 -7
  158. package/dist/labels/index.js.map +0 -1
  159. package/dist/repositories/cms-draft-cache.repository.d.ts +0 -62
  160. package/dist/repositories/cms-draft-cache.repository.d.ts.map +0 -1
  161. package/dist/repositories/cms-draft-cache.repository.js +0 -56
  162. package/dist/repositories/cms-draft-cache.repository.js.map +0 -1
  163. package/dist/repositories/cms-label-values.repository.d.ts +0 -32
  164. package/dist/repositories/cms-label-values.repository.d.ts.map +0 -1
  165. package/dist/repositories/cms-label-values.repository.js +0 -72
  166. package/dist/repositories/cms-label-values.repository.js.map +0 -1
  167. package/dist/repositories/cms-labels.repository.d.ts +0 -53
  168. package/dist/repositories/cms-labels.repository.d.ts.map +0 -1
  169. package/dist/repositories/cms-labels.repository.js +0 -77
  170. package/dist/repositories/cms-labels.repository.js.map +0 -1
  171. package/dist/repositories/cms-published-cache.repository.d.ts +0 -53
  172. package/dist/repositories/cms-published-cache.repository.d.ts.map +0 -1
  173. package/dist/repositories/cms-published-cache.repository.js +0 -54
  174. package/dist/repositories/cms-published-cache.repository.js.map +0 -1
  175. package/dist/repositories/index.d.ts +0 -8
  176. package/dist/repositories/index.d.ts.map +0 -1
  177. package/dist/repositories/index.js +0 -9
  178. package/dist/repositories/index.js.map +0 -1
  179. package/dist/routes/labels/[id]/contract.d.ts +0 -68
  180. package/dist/routes/labels/[id]/contract.d.ts.map +0 -1
  181. package/dist/routes/labels/[id]/contract.js +0 -84
  182. package/dist/routes/labels/[id]/contract.js.map +0 -1
  183. package/dist/routes/labels/[id]/index.d.ts.map +0 -1
  184. package/dist/routes/labels/[id]/index.js +0 -96
  185. package/dist/routes/labels/[id]/index.js.map +0 -1
  186. package/dist/routes/labels/by-key/[key]/contract.d.ts +0 -24
  187. package/dist/routes/labels/by-key/[key]/contract.d.ts.map +0 -1
  188. package/dist/routes/labels/by-key/[key]/contract.js +0 -28
  189. package/dist/routes/labels/by-key/[key]/contract.js.map +0 -1
  190. package/dist/routes/labels/by-key/[key]/index.d.ts +0 -8
  191. package/dist/routes/labels/by-key/[key]/index.d.ts.map +0 -1
  192. package/dist/routes/labels/by-key/[key]/index.js +0 -32
  193. package/dist/routes/labels/by-key/[key]/index.js.map +0 -1
  194. package/dist/routes/labels/contract.d.ts +0 -59
  195. package/dist/routes/labels/contract.d.ts.map +0 -1
  196. package/dist/routes/labels/contract.js +0 -75
  197. package/dist/routes/labels/contract.js.map +0 -1
  198. package/dist/routes/labels/index.d.ts +0 -10
  199. package/dist/routes/labels/index.d.ts.map +0 -1
  200. package/dist/routes/labels/index.js +0 -73
  201. package/dist/routes/labels/index.js.map +0 -1
  202. package/dist/routes/published-cache/contract.d.ts +0 -25
  203. package/dist/routes/published-cache/contract.d.ts.map +0 -1
  204. package/dist/routes/published-cache/contract.js +0 -35
  205. package/dist/routes/published-cache/contract.js.map +0 -1
  206. package/dist/routes/published-cache/index.d.ts +0 -8
  207. package/dist/routes/published-cache/index.d.ts.map +0 -1
  208. package/dist/routes/published-cache/index.js +0 -33
  209. package/dist/routes/published-cache/index.js.map +0 -1
  210. package/dist/routes/values/[labelId]/[version]/contract.d.ts +0 -29
  211. package/dist/routes/values/[labelId]/[version]/contract.d.ts.map +0 -1
  212. package/dist/routes/values/[labelId]/[version]/contract.js +0 -33
  213. package/dist/routes/values/[labelId]/[version]/contract.js.map +0 -1
  214. package/dist/routes/values/[labelId]/[version]/index.d.ts +0 -8
  215. package/dist/routes/values/[labelId]/[version]/index.d.ts.map +0 -1
  216. package/dist/routes/values/[labelId]/[version]/index.js +0 -45
  217. package/dist/routes/values/[labelId]/[version]/index.js.map +0 -1
  218. package/dist/routes/values/[labelId]/contract.d.ts +0 -38
  219. package/dist/routes/values/[labelId]/contract.d.ts.map +0 -1
  220. package/dist/routes/values/[labelId]/contract.js +0 -59
  221. package/dist/routes/values/[labelId]/contract.js.map +0 -1
  222. package/dist/routes/values/[labelId]/index.d.ts +0 -8
  223. package/dist/routes/values/[labelId]/index.d.ts.map +0 -1
  224. package/dist/routes/values/[labelId]/index.js +0 -42
  225. package/dist/routes/values/[labelId]/index.js.map +0 -1
  226. package/dist/server.d.ts.map +0 -1
  227. package/dist/store.d.ts +0 -87
  228. package/dist/store.d.ts.map +0 -1
  229. package/dist/store.js +0 -205
  230. package/dist/store.js.map +0 -1
  231. package/dist/types.d.ts.map +0 -1
  232. package/dist/types.js +0 -7
  233. package/dist/types.js.map +0 -1
@@ -0,0 +1,576 @@
1
+ // src/server/routes/labels/[id]/index.ts
2
+ import { createApp } from "@spfn/core/route";
3
+ import { Transactional } from "@spfn/core/db";
4
+
5
+ // src/server/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 { asc } from "drizzle-orm";
8
+
9
+ // src/server/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/server/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
+ // 버전 번호 (null = draft, number = published version)
58
+ version: integer2("version"),
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/server/entities/cms-draft-cache.ts
86
+ import { serial as serial3, text as text3, jsonb as jsonb2, 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 cmsDraftCache = schema3.table("draft_cache", {
90
+ // Primary Key
91
+ id: serial3("id").primaryKey(),
92
+ // 섹션 (페이지 단위)
93
+ section: text3("section").notNull(),
94
+ // "home" | "why-futureplay" | "team" | "our-companies" | "apply"
95
+ // 언어
96
+ locale: text3("locale").notNull(),
97
+ // "ko" | "en" | "ja"
98
+ // 사용자 ID (핵심 필드!)
99
+ userId: text3("user_id").notNull(),
100
+ // 각 관리자의 독립적인 작업 공간
101
+ // Draft 콘텐츠 (JSONB)
102
+ content: jsonb2("content").notNull(),
103
+ // Record<string, LabelValue>
104
+ // {
105
+ // "home.hero.title": { type: "text", content: "수정 중..." },
106
+ // "home.hero.subtitle": { type: "text", content: "새로운 문구" },
107
+ // ...
108
+ // }
109
+ // 최종 수정 시각
110
+ updatedAt: timestamp3("updated_at", { withTimezone: true }).notNull().defaultNow()
111
+ }, (table) => [
112
+ // UNIQUE 제약: section + locale + userId 조합은 유일
113
+ unique2("cms_draft_cache_unique").on(table.section, table.locale, table.userId),
114
+ // 인덱스: section으로 조회 최적화
115
+ index3("cms_draft_cache_section_idx").on(table.section),
116
+ // 인덱스: userId로 사용자의 모든 draft 조회 최적화
117
+ index3("cms_draft_cache_user_idx").on(table.userId)
118
+ ]);
119
+
120
+ // src/server/entities/cms-published-cache.ts
121
+ import { serial as serial4, text as text4, jsonb as jsonb3, integer as integer3, timestamp as timestamp4, index as index4, unique as unique3 } from "drizzle-orm/pg-core";
122
+ import { createFunctionSchema as createFunctionSchema4 } from "@spfn/core/db";
123
+ var schema4 = createFunctionSchema4("@spfn/cms");
124
+ var cmsPublishedCache = schema4.table("published_cache", {
125
+ // Primary Key
126
+ id: serial4("id").primaryKey(),
127
+ // 섹션 (페이지 단위)
128
+ section: text4("section").notNull(),
129
+ // "home" | "why-futureplay" | "team" | "our-companies" | "apply"
130
+ // 언어
131
+ locale: text4("locale").notNull(),
132
+ // "ko" | "en" | "ja"
133
+ // 캐시된 콘텐츠 (JSONB)
134
+ content: jsonb3("content").notNull(),
135
+ // Record<string, LabelValue>
136
+ // {
137
+ // "home.hero.title": { type: "text", content: "..." },
138
+ // "home.hero.image": { type: "image", url: "...", alt: "..." },
139
+ // ...
140
+ // }
141
+ // 발행 정보
142
+ publishedAt: timestamp4("published_at", { withTimezone: true }).notNull(),
143
+ publishedBy: text4("published_by"),
144
+ // 캐시 버전 (클라이언트 캐싱용)
145
+ version: integer3("version").notNull().default(1)
146
+ }, (table) => [
147
+ // UNIQUE 제약: section + locale 조합은 유일
148
+ unique3("cms_published_cache_unique").on(table.section, table.locale),
149
+ // 인덱스: section으로 조회 최적화
150
+ index4("cms_published_cache_section_idx").on(table.section)
151
+ ]);
152
+
153
+ // src/server/entities/cms-audit-logs.ts
154
+ import { serial as serial5, integer as integer4, text as text5, jsonb as jsonb4, timestamp as timestamp5, index as index5 } from "drizzle-orm/pg-core";
155
+ import { createFunctionSchema as createFunctionSchema5 } from "@spfn/core/db";
156
+ var schema5 = createFunctionSchema5("@spfn/cms");
157
+ var cmsAuditLogs = schema5.table("audit_logs", {
158
+ // Primary Key
159
+ id: serial5("id").primaryKey(),
160
+ // Foreign Key: cms_labels (nullable - 라벨 삭제 시 로그는 유지)
161
+ labelId: integer4("label_id").references(() => cmsLabels.id, { onDelete: "set null" }),
162
+ // 작업 유형
163
+ action: text5("action").notNull(),
164
+ // "create" | "update" | "publish" | "unpublish" | "archive" | "delete" | "rollback" | "duplicate"
165
+ // 사용자 정보
166
+ userId: text5("user_id").notNull(),
167
+ userName: text5("user_name"),
168
+ // 변경 내용 (before/after)
169
+ changes: jsonb4("changes"),
170
+ // { before: {...}, after: {...} }
171
+ // 추가 메타데이터
172
+ metadata: jsonb4("metadata"),
173
+ // { version: number, ip: string, userAgent: string, ... }
174
+ // 작업 시각
175
+ createdAt: timestamp5("created_at", { withTimezone: true }).notNull().defaultNow()
176
+ }, (table) => [
177
+ // 인덱스: labelId로 이력 조회 최적화
178
+ index5("cms_audit_logs_label_id_idx").on(table.labelId),
179
+ // 인덱스: userId로 사용자 활동 조회 최적화
180
+ index5("cms_audit_logs_user_id_idx").on(table.userId),
181
+ // 인덱스: action 필터링 최적화
182
+ index5("cms_audit_logs_action_idx").on(table.action),
183
+ // 인덱스: 시간순 조회 최적화
184
+ index5("cms_audit_logs_created_at_idx").on(table.createdAt)
185
+ ]);
186
+
187
+ // src/server/repositories/cms-labels.repository.ts
188
+ async function findMany(options) {
189
+ const { section } = options || {};
190
+ return findManyHelper(cmsLabels, {
191
+ where: section ? { section } : void 0,
192
+ orderBy: asc(cmsLabels.key)
193
+ // key 오름차순 정렬 (JSON 파일의 순서 유지)
194
+ });
195
+ }
196
+ async function count(section) {
197
+ return countHelper(cmsLabels, section ? { section } : void 0);
198
+ }
199
+ async function findById(id) {
200
+ return findOne(cmsLabels, { id });
201
+ }
202
+ async function findByKey(key) {
203
+ return findOne(cmsLabels, { key });
204
+ }
205
+ async function findBySection(section) {
206
+ return findManyHelper(cmsLabels, {
207
+ where: { section },
208
+ orderBy: asc(cmsLabels.key)
209
+ // key 오름차순 정렬 (JSON 파일의 순서 유지)
210
+ });
211
+ }
212
+ async function create(data) {
213
+ return createHelper(cmsLabels, data);
214
+ }
215
+ async function updateById(id, data) {
216
+ return updateOne(cmsLabels, { id }, { ...data, updatedAt: /* @__PURE__ */ new Date() });
217
+ }
218
+ async function deleteById(id) {
219
+ return deleteOne(cmsLabels, { id });
220
+ }
221
+ var cmsLabelsRepository = {
222
+ findMany,
223
+ count,
224
+ findById,
225
+ findByKey,
226
+ findBySection,
227
+ create,
228
+ updateById,
229
+ deleteById
230
+ };
231
+
232
+ // src/server/repositories/cms-label-values.repository.ts
233
+ import { findOne as findOne2, findMany as findMany2, create as create2, updateOne as updateOne2, deleteMany } from "@spfn/core/db";
234
+ import { eq, and, isNull } from "drizzle-orm";
235
+
236
+ // src/server/repositories/cms-draft-cache.repository.ts
237
+ import { findOne as findOne3, findMany as findMany3, deleteOne as deleteOne2, deleteMany as deleteMany2, upsert as upsertHelper } from "@spfn/core/db";
238
+ import { eq as eq2, and as and2, lt } from "drizzle-orm";
239
+
240
+ // src/server/repositories/cms-published-cache.repository.ts
241
+ import { findOne as findOne4, findMany as findMany4, deleteOne as deleteOne3, deleteMany as deleteMany3, upsert as upsertHelper2 } from "@spfn/core/db";
242
+ import { eq as eq3, and as and3, sql } from "drizzle-orm";
243
+
244
+ // src/lib/contracts/labels.ts
245
+ import { Type } from "@sinclair/typebox";
246
+ var getLabelsContract = {
247
+ method: "GET",
248
+ path: "/_cms/labels",
249
+ query: Type.Object({
250
+ section: Type.Optional(Type.String({ description: "\uC139\uC158\uC73C\uB85C \uD544\uD130\uB9C1 (\uC608: home, why-futureplay)" })),
251
+ includeDefaultValues: Type.Optional(Type.Boolean({ description: "\uAE30\uBCF8\uAC12 \uD3EC\uD568 \uC5EC\uBD80" }))
252
+ }),
253
+ response: Type.Object({
254
+ labels: Type.Array(Type.Object({
255
+ id: Type.Number(),
256
+ key: Type.String(),
257
+ section: Type.String(),
258
+ type: Type.String(),
259
+ description: Type.Union([Type.String(), Type.Null()], { description: "\uB77C\uBCA8 \uC124\uBA85" }),
260
+ publishedVersion: Type.Union([Type.Number(), Type.Null()]),
261
+ createdBy: Type.Union([Type.String(), Type.Null()]),
262
+ createdAt: Type.String(),
263
+ updatedAt: Type.String(),
264
+ defaultValue: Type.Optional(Type.Any({ description: "\uB77C\uBCA8 \uC815\uC758 \uD30C\uC77C\uC758 \uAE30\uBCF8\uAC12" }))
265
+ })),
266
+ total: Type.Number()
267
+ })
268
+ };
269
+ var createLabelContract = {
270
+ method: "POST",
271
+ path: "/_cms/labels",
272
+ body: Type.Object({
273
+ key: Type.String({
274
+ description: "\uACE0\uC720 \uD0A4 (\uC608: home.hero.title)",
275
+ pattern: "^[a-z0-9-]+\\.[a-z0-9-]+\\.[a-z0-9-]+$"
276
+ }),
277
+ section: Type.String({
278
+ description: "\uC139\uC158 \uC774\uB984 (\uC608: home, why-futureplay)",
279
+ pattern: "^[a-z0-9-]+$"
280
+ }),
281
+ type: Type.Union([
282
+ Type.Literal("text"),
283
+ Type.Literal("image"),
284
+ Type.Literal("video"),
285
+ Type.Literal("file"),
286
+ Type.Literal("object")
287
+ ], { description: "\uAC12 \uD0C0\uC785" }),
288
+ createdBy: Type.Optional(Type.String({ description: "\uC0DD\uC131\uC790 ID" }))
289
+ }),
290
+ response: Type.Union([
291
+ Type.Object({
292
+ id: Type.Number(),
293
+ key: Type.String(),
294
+ section: Type.String(),
295
+ type: Type.String(),
296
+ publishedVersion: Type.Union([Type.Number(), Type.Null()]),
297
+ createdBy: Type.Union([Type.String(), Type.Null()]),
298
+ createdAt: Type.String(),
299
+ updatedAt: Type.String()
300
+ }),
301
+ Type.Object({
302
+ error: Type.String(),
303
+ key: Type.Optional(Type.String())
304
+ })
305
+ ])
306
+ };
307
+ var getLabelContract = {
308
+ method: "GET",
309
+ path: "/_cms/labels/:id",
310
+ params: Type.Object({
311
+ id: Type.String({ description: "\uB77C\uBCA8 ID" })
312
+ }),
313
+ response: Type.Union([
314
+ Type.Object({
315
+ id: Type.Number(),
316
+ key: Type.String(),
317
+ section: Type.String(),
318
+ type: Type.String(),
319
+ description: Type.Union([Type.String(), Type.Null()]),
320
+ publishedVersion: Type.Union([Type.Number(), Type.Null()]),
321
+ createdBy: Type.Union([Type.String(), Type.Null()]),
322
+ createdAt: Type.String(),
323
+ updatedAt: Type.String()
324
+ }),
325
+ Type.Object({
326
+ error: Type.String()
327
+ })
328
+ ])
329
+ };
330
+ var updateLabelContract = {
331
+ method: "PATCH",
332
+ path: "/_cms/labels/:id",
333
+ params: Type.Object({
334
+ id: Type.String({ description: "\uB77C\uBCA8 ID" })
335
+ }),
336
+ body: Type.Object({
337
+ section: Type.Optional(Type.String({ description: "\uC139\uC158 \uBCC0\uACBD" })),
338
+ type: Type.Optional(Type.Union([
339
+ Type.Literal("text"),
340
+ Type.Literal("image"),
341
+ Type.Literal("video"),
342
+ Type.Literal("file"),
343
+ Type.Literal("object")
344
+ ]))
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
+ description: Type.Union([Type.String(), Type.Null()]),
353
+ publishedVersion: Type.Union([Type.Number(), Type.Null()]),
354
+ createdBy: Type.Union([Type.String(), Type.Null()]),
355
+ createdAt: Type.String(),
356
+ updatedAt: Type.String()
357
+ }),
358
+ Type.Object({
359
+ error: Type.String()
360
+ })
361
+ ])
362
+ };
363
+ var deleteLabelContract = {
364
+ method: "DELETE",
365
+ path: "/_cms/labels/:id",
366
+ params: Type.Object({
367
+ id: Type.String({ description: "\uB77C\uBCA8 ID" })
368
+ }),
369
+ response: Type.Union([
370
+ Type.Object({
371
+ success: Type.Boolean(),
372
+ id: Type.Number()
373
+ }),
374
+ Type.Object({
375
+ error: Type.String()
376
+ })
377
+ ])
378
+ };
379
+ var getLabelByKeyContract = {
380
+ method: "GET",
381
+ path: "/_cms/labels/by-key/:key",
382
+ params: Type.Object({
383
+ key: Type.String({ description: "\uB77C\uBCA8 Key (\uC608: home.hero.title)" })
384
+ }),
385
+ response: Type.Union([
386
+ Type.Object({
387
+ id: Type.Number(),
388
+ key: Type.String(),
389
+ section: Type.String(),
390
+ type: Type.String(),
391
+ description: Type.Union([Type.String(), Type.Null()]),
392
+ publishedVersion: Type.Union([Type.Number(), Type.Null()]),
393
+ createdBy: Type.Union([Type.String(), Type.Null()]),
394
+ createdAt: Type.String(),
395
+ updatedAt: Type.String()
396
+ }),
397
+ Type.Object({
398
+ error: Type.String(),
399
+ key: Type.Optional(Type.String())
400
+ })
401
+ ])
402
+ };
403
+ var publishLabelContract = {
404
+ method: "POST",
405
+ path: "/_cms/labels/:id/publish",
406
+ params: Type.Object({
407
+ id: Type.String({ description: "\uB77C\uBCA8 ID" })
408
+ }),
409
+ body: Type.Object({
410
+ notes: Type.Optional(Type.String({ description: "\uBC1C\uD589 \uB178\uD2B8 (\uBC84\uC804 \uC124\uBA85)" })),
411
+ publishedBy: Type.Optional(Type.String({ description: "\uBC1C\uD589\uC790 ID" }))
412
+ }),
413
+ response: Type.Union([
414
+ Type.Object({
415
+ success: Type.Boolean(),
416
+ id: Type.Number(),
417
+ version: Type.Number(),
418
+ message: Type.String()
419
+ }),
420
+ Type.Object({
421
+ error: Type.String()
422
+ })
423
+ ])
424
+ };
425
+ var getAdminLabelContract = {
426
+ method: "GET",
427
+ path: "/_cms/labels/:id/admin",
428
+ params: Type.Object({
429
+ id: Type.String({ description: "\uB77C\uBCA8 ID" })
430
+ }),
431
+ response: Type.Union([
432
+ Type.Object({
433
+ label: Type.Object({
434
+ id: Type.Number(),
435
+ key: Type.String(),
436
+ section: Type.String(),
437
+ type: Type.String(),
438
+ description: Type.Union([Type.String(), Type.Null()]),
439
+ publishedVersion: Type.Union([Type.Number(), Type.Null()]),
440
+ createdBy: Type.Union([Type.String(), Type.Null()]),
441
+ createdAt: Type.String(),
442
+ updatedAt: Type.String()
443
+ }),
444
+ draft: Type.Array(Type.Object({
445
+ id: Type.Number(),
446
+ labelId: Type.Number(),
447
+ version: Type.Null(),
448
+ locale: Type.String(),
449
+ breakpoint: Type.Union([Type.String(), Type.Null()]),
450
+ value: Type.Any(),
451
+ createdAt: Type.String()
452
+ })),
453
+ published: Type.Array(Type.Object({
454
+ id: Type.Number(),
455
+ labelId: Type.Number(),
456
+ version: Type.Number(),
457
+ locale: Type.String(),
458
+ breakpoint: Type.Union([Type.String(), Type.Null()]),
459
+ value: Type.Any(),
460
+ createdAt: Type.String()
461
+ })),
462
+ status: Type.Union([
463
+ Type.Literal("default-only"),
464
+ Type.Literal("unpublished"),
465
+ Type.Literal("published"),
466
+ Type.Literal("modified")
467
+ ])
468
+ }),
469
+ Type.Object({
470
+ error: Type.String()
471
+ })
472
+ ])
473
+ };
474
+ var getLabelVersionsContract = {
475
+ method: "GET",
476
+ path: "/_cms/labels/:id/versions",
477
+ params: Type.Object({
478
+ id: Type.String({ description: "\uB77C\uBCA8 ID" })
479
+ }),
480
+ response: Type.Union([
481
+ Type.Object({
482
+ versions: Type.Array(Type.Object({
483
+ version: Type.Number({ description: "\uBC84\uC804 \uBC88\uD638" }),
484
+ publishedAt: Type.String({ description: "\uBC1C\uD589 \uC2DC\uAC01 (ISO 8601)" }),
485
+ publishedBy: Type.Union([Type.String(), Type.Null()], { description: "\uBC1C\uD589\uC790 ID" }),
486
+ notes: Type.Union([Type.String(), Type.Null()], { description: "\uBC1C\uD589 \uB178\uD2B8" }),
487
+ values: Type.Array(Type.Object({
488
+ id: Type.Number(),
489
+ locale: Type.String(),
490
+ breakpoint: Type.Union([Type.String(), Type.Null()]),
491
+ value: Type.Any(),
492
+ createdAt: Type.String()
493
+ }))
494
+ }))
495
+ }),
496
+ Type.Object({
497
+ error: Type.String()
498
+ })
499
+ ])
500
+ };
501
+
502
+ // src/server/routes/labels/[id]/index.ts
503
+ var app = createApp();
504
+ app.bind(getLabelContract, async (c) => {
505
+ const { id } = c.params;
506
+ const labelId = parseInt(id, 10);
507
+ if (isNaN(labelId)) {
508
+ return c.json({ error: "Invalid label ID" }, 400);
509
+ }
510
+ const label = await cmsLabelsRepository.findById(labelId);
511
+ if (!label) {
512
+ return c.json({ error: "Label not found" }, 404);
513
+ }
514
+ return c.json({
515
+ id: label.id,
516
+ key: label.key,
517
+ section: label.section,
518
+ type: label.type,
519
+ description: label.description,
520
+ publishedVersion: label.publishedVersion,
521
+ createdBy: label.createdBy,
522
+ createdAt: label.createdAt.toISOString(),
523
+ updatedAt: label.updatedAt.toISOString()
524
+ });
525
+ });
526
+ app.bind(updateLabelContract, [Transactional()], async (c) => {
527
+ const { id } = c.params;
528
+ const labelId = parseInt(id, 10);
529
+ if (isNaN(labelId)) {
530
+ return c.json({ error: "Invalid label ID" }, 400);
531
+ }
532
+ const body = await c.data();
533
+ const existing = await cmsLabelsRepository.findById(labelId);
534
+ if (!existing) {
535
+ return c.json({ error: "Label not found" }, 404);
536
+ }
537
+ const updated = await cmsLabelsRepository.updateById(labelId, body);
538
+ if (!updated) {
539
+ return c.json({ error: "Failed to update label" }, 500);
540
+ }
541
+ return c.json({
542
+ id: updated.id,
543
+ key: updated.key,
544
+ section: updated.section,
545
+ type: updated.type,
546
+ description: updated.description,
547
+ publishedVersion: updated.publishedVersion,
548
+ createdBy: updated.createdBy,
549
+ createdAt: updated.createdAt.toISOString(),
550
+ updatedAt: updated.updatedAt.toISOString()
551
+ });
552
+ });
553
+ app.bind(deleteLabelContract, [Transactional()], async (c) => {
554
+ const { id } = c.params;
555
+ const labelId = parseInt(id, 10);
556
+ if (isNaN(labelId)) {
557
+ return c.json({ error: "Invalid label ID" }, 400);
558
+ }
559
+ const existing = await cmsLabelsRepository.findById(labelId);
560
+ if (!existing) {
561
+ return c.json({ error: "Label not found" }, 404);
562
+ }
563
+ const deleted = await cmsLabelsRepository.deleteById(labelId);
564
+ if (!deleted) {
565
+ return c.json({ error: "Failed to delete label" }, 500);
566
+ }
567
+ return c.json({
568
+ success: true,
569
+ id: deleted.id
570
+ });
571
+ });
572
+ var id_default = app;
573
+ export {
574
+ id_default as default
575
+ };
576
+ //# sourceMappingURL=index.js.map