@spfn/cms 0.1.0-alpha.75 → 0.1.0-alpha.77

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 (46) hide show
  1. package/dist/api.d.ts +40 -9
  2. package/dist/api.js +40 -8
  3. package/dist/api.js.map +1 -1
  4. package/dist/client.d.ts +0 -1
  5. package/dist/client.js +39 -7
  6. package/dist/client.js.map +1 -1
  7. package/dist/index.d.ts +0 -1
  8. package/dist/lib/contracts/labels.d.ts +28 -1
  9. package/dist/lib/contracts/labels.js +28 -0
  10. package/dist/lib/contracts/labels.js.map +1 -1
  11. package/dist/server/entities/index.d.ts +0 -1
  12. package/dist/server/entities/index.js +36 -69
  13. package/dist/server/entities/index.js.map +1 -1
  14. package/dist/server/generators/index.js +43 -73
  15. package/dist/server/generators/index.js.map +1 -1
  16. package/dist/server/repositories/index.js +41 -71
  17. package/dist/server/repositories/index.js.map +1 -1
  18. package/dist/server/routes/labels/[id]/index.js +68 -71
  19. package/dist/server/routes/labels/[id]/index.js.map +1 -1
  20. package/dist/server/routes/labels/[labelId]/admin/index.js +68 -71
  21. package/dist/server/routes/labels/[labelId]/admin/index.js.map +1 -1
  22. package/dist/server/routes/labels/[labelId]/publish/index.js +68 -71
  23. package/dist/server/routes/labels/[labelId]/publish/index.js.map +1 -1
  24. package/dist/server/routes/labels/[labelId]/versions/index.js +554 -0
  25. package/dist/server/routes/labels/[labelId]/versions/index.js.map +1 -0
  26. package/dist/server/routes/labels/_labelId_/versions/index.d.ts +11 -0
  27. package/dist/server/routes/labels/by-key/[key]/index.js +68 -71
  28. package/dist/server/routes/labels/by-key/[key]/index.js.map +1 -1
  29. package/dist/server/routes/labels/index.js +68 -71
  30. package/dist/server/routes/labels/index.js.map +1 -1
  31. package/dist/server/routes/published-cache/index.js +37 -69
  32. package/dist/server/routes/published-cache/index.js.map +1 -1
  33. package/dist/server/routes/values/[labelId]/[version]/index.js +41 -71
  34. package/dist/server/routes/values/[labelId]/[version]/index.js.map +1 -1
  35. package/dist/server/routes/values/[labelId]/index.js +41 -71
  36. package/dist/server/routes/values/[labelId]/index.js.map +1 -1
  37. package/dist/server.d.ts +0 -1
  38. package/dist/server.js +45 -76
  39. package/dist/server.js.map +1 -1
  40. package/migrations/0003_rare_runaways.sql +1 -0
  41. package/migrations/meta/0003_snapshot.json +563 -0
  42. package/migrations/meta/_journal.json +7 -0
  43. package/package.json +1 -1
  44. package/dist/server/entities/cms-label-versions.d.ts +0 -174
  45. package/dist/server/entities/cms-label-versions.js +0 -76
  46. package/dist/server/entities/cms-label-versions.js.map +0 -1
@@ -3,7 +3,7 @@ import { createApp } from "@spfn/core/route";
3
3
 
4
4
  // src/server/repositories/cms-labels.repository.ts
5
5
  import { findOne, findMany as findManyHelper, create as createHelper, updateOne, deleteOne, count as countHelper } from "@spfn/core/db";
6
- import { desc } from "drizzle-orm";
6
+ import { asc } from "drizzle-orm";
7
7
 
8
8
  // src/server/entities/cms-labels.ts
9
9
  import { index, integer, serial, text, timestamp } from "drizzle-orm/pg-core";
@@ -81,53 +81,21 @@ var cmsLabelValues = schema2.table("label_values", {
81
81
  index2("cms_label_values_locale_idx").on(table.locale)
82
82
  ]);
83
83
 
84
- // src/server/entities/cms-label-versions.ts
85
- import { serial as serial3, integer as integer3, text as text3, timestamp as timestamp3, index as index3, unique as unique2 } from "drizzle-orm/pg-core";
84
+ // src/server/entities/cms-draft-cache.ts
85
+ import { serial as serial3, text as text3, jsonb as jsonb2, timestamp as timestamp3, index as index3, unique as unique2 } from "drizzle-orm/pg-core";
86
86
  import { createFunctionSchema as createFunctionSchema3 } from "@spfn/core/db";
87
87
  var schema3 = createFunctionSchema3("@spfn/cms");
88
- var cmsLabelVersions = schema3.table("label_versions", {
88
+ var cmsDraftCache = schema3.table("draft_cache", {
89
89
  // Primary Key
90
90
  id: serial3("id").primaryKey(),
91
- // Foreign Key: cms_labels
92
- labelId: integer3("label_id").notNull().references(() => cmsLabels.id, { onDelete: "cascade" }),
93
- // 버전 번호
94
- version: integer3("version").notNull(),
95
- // 버전 상태
96
- status: text3("status").notNull(),
97
- // "draft" | "published" | "archived"
98
- // 발행 정보
99
- publishedAt: timestamp3("published_at", { withTimezone: true }),
100
- publishedBy: text3("published_by"),
101
- // 버전 노트 (변경사항 설명)
102
- notes: text3("notes"),
103
- // 버전 생성자
104
- createdBy: text3("created_by"),
105
- // 생성 시각
106
- createdAt: timestamp3("created_at", { withTimezone: true }).notNull().defaultNow()
107
- }, (table) => [
108
- // UNIQUE 제약: 각 라벨의 버전 번호는 고유
109
- unique2("cms_label_versions_label_version_unique").on(table.labelId, table.version),
110
- // 인덱스: labelId로 버전 목록 조회 최적화
111
- index3("cms_label_versions_label_id_idx").on(table.labelId),
112
- // 인덱스: status 필터링 최적화
113
- index3("cms_label_versions_status_idx").on(table.status)
114
- ]);
115
-
116
- // src/server/entities/cms-draft-cache.ts
117
- import { serial as serial4, text as text4, jsonb as jsonb2, timestamp as timestamp4, index as index4, unique as unique3 } from "drizzle-orm/pg-core";
118
- import { createFunctionSchema as createFunctionSchema4 } from "@spfn/core/db";
119
- var schema4 = createFunctionSchema4("@spfn/cms");
120
- var cmsDraftCache = schema4.table("draft_cache", {
121
- // Primary Key
122
- id: serial4("id").primaryKey(),
123
91
  // 섹션 (페이지 단위)
124
- section: text4("section").notNull(),
92
+ section: text3("section").notNull(),
125
93
  // "home" | "why-futureplay" | "team" | "our-companies" | "apply"
126
94
  // 언어
127
- locale: text4("locale").notNull(),
95
+ locale: text3("locale").notNull(),
128
96
  // "ko" | "en" | "ja"
129
97
  // 사용자 ID (핵심 필드!)
130
- userId: text4("user_id").notNull(),
98
+ userId: text3("user_id").notNull(),
131
99
  // 각 관리자의 독립적인 작업 공간
132
100
  // Draft 콘텐츠 (JSONB)
133
101
  content: jsonb2("content").notNull(),
@@ -138,28 +106,28 @@ var cmsDraftCache = schema4.table("draft_cache", {
138
106
  // ...
139
107
  // }
140
108
  // 최종 수정 시각
141
- updatedAt: timestamp4("updated_at", { withTimezone: true }).notNull().defaultNow()
109
+ updatedAt: timestamp3("updated_at", { withTimezone: true }).notNull().defaultNow()
142
110
  }, (table) => [
143
111
  // UNIQUE 제약: section + locale + userId 조합은 유일
144
- unique3("cms_draft_cache_unique").on(table.section, table.locale, table.userId),
112
+ unique2("cms_draft_cache_unique").on(table.section, table.locale, table.userId),
145
113
  // 인덱스: section으로 조회 최적화
146
- index4("cms_draft_cache_section_idx").on(table.section),
114
+ index3("cms_draft_cache_section_idx").on(table.section),
147
115
  // 인덱스: userId로 사용자의 모든 draft 조회 최적화
148
- index4("cms_draft_cache_user_idx").on(table.userId)
116
+ index3("cms_draft_cache_user_idx").on(table.userId)
149
117
  ]);
150
118
 
151
119
  // src/server/entities/cms-published-cache.ts
152
- 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";
153
- import { createFunctionSchema as createFunctionSchema5 } from "@spfn/core/db";
154
- var schema5 = createFunctionSchema5("@spfn/cms");
155
- var cmsPublishedCache = schema5.table("published_cache", {
120
+ 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";
121
+ import { createFunctionSchema as createFunctionSchema4 } from "@spfn/core/db";
122
+ var schema4 = createFunctionSchema4("@spfn/cms");
123
+ var cmsPublishedCache = schema4.table("published_cache", {
156
124
  // Primary Key
157
- id: serial5("id").primaryKey(),
125
+ id: serial4("id").primaryKey(),
158
126
  // 섹션 (페이지 단위)
159
- section: text5("section").notNull(),
127
+ section: text4("section").notNull(),
160
128
  // "home" | "why-futureplay" | "team" | "our-companies" | "apply"
161
129
  // 언어
162
- locale: text5("locale").notNull(),
130
+ locale: text4("locale").notNull(),
163
131
  // "ko" | "en" | "ja"
164
132
  // 캐시된 콘텐츠 (JSONB)
165
133
  content: jsonb3("content").notNull(),
@@ -170,32 +138,32 @@ var cmsPublishedCache = schema5.table("published_cache", {
170
138
  // ...
171
139
  // }
172
140
  // 발행 정보
173
- publishedAt: timestamp5("published_at", { withTimezone: true }).notNull(),
174
- publishedBy: text5("published_by"),
141
+ publishedAt: timestamp4("published_at", { withTimezone: true }).notNull(),
142
+ publishedBy: text4("published_by"),
175
143
  // 캐시 버전 (클라이언트 캐싱용)
176
- version: integer4("version").notNull().default(1)
144
+ version: integer3("version").notNull().default(1)
177
145
  }, (table) => [
178
146
  // UNIQUE 제약: section + locale 조합은 유일
179
- unique4("cms_published_cache_unique").on(table.section, table.locale),
147
+ unique3("cms_published_cache_unique").on(table.section, table.locale),
180
148
  // 인덱스: section으로 조회 최적화
181
- index5("cms_published_cache_section_idx").on(table.section)
149
+ index4("cms_published_cache_section_idx").on(table.section)
182
150
  ]);
183
151
 
184
152
  // src/server/entities/cms-audit-logs.ts
185
- import { serial as serial6, integer as integer5, text as text6, jsonb as jsonb4, timestamp as timestamp6, index as index6 } from "drizzle-orm/pg-core";
186
- import { createFunctionSchema as createFunctionSchema6 } from "@spfn/core/db";
187
- var schema6 = createFunctionSchema6("@spfn/cms");
188
- var cmsAuditLogs = schema6.table("audit_logs", {
153
+ import { serial as serial5, integer as integer4, text as text5, jsonb as jsonb4, timestamp as timestamp5, index as index5 } from "drizzle-orm/pg-core";
154
+ import { createFunctionSchema as createFunctionSchema5 } from "@spfn/core/db";
155
+ var schema5 = createFunctionSchema5("@spfn/cms");
156
+ var cmsAuditLogs = schema5.table("audit_logs", {
189
157
  // Primary Key
190
- id: serial6("id").primaryKey(),
158
+ id: serial5("id").primaryKey(),
191
159
  // Foreign Key: cms_labels (nullable - 라벨 삭제 시 로그는 유지)
192
- labelId: integer5("label_id").references(() => cmsLabels.id, { onDelete: "set null" }),
160
+ labelId: integer4("label_id").references(() => cmsLabels.id, { onDelete: "set null" }),
193
161
  // 작업 유형
194
- action: text6("action").notNull(),
162
+ action: text5("action").notNull(),
195
163
  // "create" | "update" | "publish" | "unpublish" | "archive" | "delete" | "rollback" | "duplicate"
196
164
  // 사용자 정보
197
- userId: text6("user_id").notNull(),
198
- userName: text6("user_name"),
165
+ userId: text5("user_id").notNull(),
166
+ userName: text5("user_name"),
199
167
  // 변경 내용 (before/after)
200
168
  changes: jsonb4("changes"),
201
169
  // { before: {...}, after: {...} }
@@ -203,16 +171,16 @@ var cmsAuditLogs = schema6.table("audit_logs", {
203
171
  metadata: jsonb4("metadata"),
204
172
  // { version: number, ip: string, userAgent: string, ... }
205
173
  // 작업 시각
206
- createdAt: timestamp6("created_at", { withTimezone: true }).notNull().defaultNow()
174
+ createdAt: timestamp5("created_at", { withTimezone: true }).notNull().defaultNow()
207
175
  }, (table) => [
208
176
  // 인덱스: labelId로 이력 조회 최적화
209
- index6("cms_audit_logs_label_id_idx").on(table.labelId),
177
+ index5("cms_audit_logs_label_id_idx").on(table.labelId),
210
178
  // 인덱스: userId로 사용자 활동 조회 최적화
211
- index6("cms_audit_logs_user_id_idx").on(table.userId),
179
+ index5("cms_audit_logs_user_id_idx").on(table.userId),
212
180
  // 인덱스: action 필터링 최적화
213
- index6("cms_audit_logs_action_idx").on(table.action),
181
+ index5("cms_audit_logs_action_idx").on(table.action),
214
182
  // 인덱스: 시간순 조회 최적화
215
- index6("cms_audit_logs_created_at_idx").on(table.createdAt)
183
+ index5("cms_audit_logs_created_at_idx").on(table.createdAt)
216
184
  ]);
217
185
 
218
186
  // src/server/repositories/cms-labels.repository.ts
@@ -220,7 +188,8 @@ async function findMany(options) {
220
188
  const { section, limit = 20, offset = 0 } = options || {};
221
189
  return findManyHelper(cmsLabels, {
222
190
  where: section ? { section } : void 0,
223
- orderBy: desc(cmsLabels.updatedAt),
191
+ orderBy: asc(cmsLabels.key),
192
+ // key 오름차순 정렬 (JSON 파일의 순서 유지)
224
193
  limit,
225
194
  offset
226
195
  });
@@ -237,7 +206,8 @@ async function findByKey(key) {
237
206
  async function findBySection(section) {
238
207
  return findManyHelper(cmsLabels, {
239
208
  where: { section },
240
- orderBy: desc(cmsLabels.updatedAt)
209
+ orderBy: asc(cmsLabels.key)
210
+ // key 오름차순 정렬 (JSON 파일의 순서 유지)
241
211
  });
242
212
  }
243
213
  async function create(data) {
@@ -506,6 +476,33 @@ var getAdminLabelContract = {
506
476
  })
507
477
  ])
508
478
  };
479
+ var getLabelVersionsContract = {
480
+ method: "GET",
481
+ path: "/_cms/labels/:labelId/versions",
482
+ params: Type.Object({
483
+ labelId: Type.String({ description: "\uB77C\uBCA8 ID" })
484
+ }),
485
+ response: Type.Union([
486
+ Type.Object({
487
+ versions: Type.Array(Type.Object({
488
+ version: Type.Number({ description: "\uBC84\uC804 \uBC88\uD638" }),
489
+ publishedAt: Type.String({ description: "\uBC1C\uD589 \uC2DC\uAC01 (ISO 8601)" }),
490
+ publishedBy: Type.Union([Type.String(), Type.Null()], { description: "\uBC1C\uD589\uC790 ID" }),
491
+ notes: Type.Union([Type.String(), Type.Null()], { description: "\uBC1C\uD589 \uB178\uD2B8" }),
492
+ values: Type.Array(Type.Object({
493
+ id: Type.Number(),
494
+ locale: Type.String(),
495
+ breakpoint: Type.Union([Type.String(), Type.Null()]),
496
+ value: Type.Any(),
497
+ createdAt: Type.String()
498
+ }))
499
+ }))
500
+ }),
501
+ Type.Object({
502
+ error: Type.String()
503
+ })
504
+ ])
505
+ };
509
506
 
510
507
  // src/server/helpers/sync.ts
511
508
  import { existsSync, readdirSync, readFileSync, statSync } from "fs";