@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.
- package/dist/api.d.ts +40 -9
- package/dist/api.js +40 -8
- package/dist/api.js.map +1 -1
- package/dist/client.d.ts +0 -1
- package/dist/client.js +39 -7
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/lib/contracts/labels.d.ts +28 -1
- package/dist/lib/contracts/labels.js +28 -0
- package/dist/lib/contracts/labels.js.map +1 -1
- package/dist/server/entities/index.d.ts +0 -1
- package/dist/server/entities/index.js +36 -69
- package/dist/server/entities/index.js.map +1 -1
- package/dist/server/generators/index.js +43 -73
- package/dist/server/generators/index.js.map +1 -1
- package/dist/server/repositories/index.js +41 -71
- package/dist/server/repositories/index.js.map +1 -1
- package/dist/server/routes/labels/[id]/index.js +68 -71
- package/dist/server/routes/labels/[id]/index.js.map +1 -1
- package/dist/server/routes/labels/[labelId]/admin/index.js +68 -71
- package/dist/server/routes/labels/[labelId]/admin/index.js.map +1 -1
- package/dist/server/routes/labels/[labelId]/publish/index.js +68 -71
- package/dist/server/routes/labels/[labelId]/publish/index.js.map +1 -1
- package/dist/server/routes/labels/[labelId]/versions/index.js +554 -0
- package/dist/server/routes/labels/[labelId]/versions/index.js.map +1 -0
- package/dist/server/routes/labels/_labelId_/versions/index.d.ts +11 -0
- package/dist/server/routes/labels/by-key/[key]/index.js +68 -71
- package/dist/server/routes/labels/by-key/[key]/index.js.map +1 -1
- package/dist/server/routes/labels/index.js +68 -71
- package/dist/server/routes/labels/index.js.map +1 -1
- package/dist/server/routes/published-cache/index.js +37 -69
- package/dist/server/routes/published-cache/index.js.map +1 -1
- package/dist/server/routes/values/[labelId]/[version]/index.js +41 -71
- package/dist/server/routes/values/[labelId]/[version]/index.js.map +1 -1
- package/dist/server/routes/values/[labelId]/index.js +41 -71
- package/dist/server/routes/values/[labelId]/index.js.map +1 -1
- package/dist/server.d.ts +0 -1
- package/dist/server.js +45 -76
- package/dist/server.js.map +1 -1
- package/migrations/0003_rare_runaways.sql +1 -0
- package/migrations/meta/0003_snapshot.json +563 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/dist/server/entities/cms-label-versions.d.ts +0 -174
- package/dist/server/entities/cms-label-versions.js +0 -76
- 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 {
|
|
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-
|
|
85
|
-
import { serial as serial3,
|
|
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
|
|
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:
|
|
92
|
+
section: text3("section").notNull(),
|
|
125
93
|
// "home" | "why-futureplay" | "team" | "our-companies" | "apply"
|
|
126
94
|
// 언어
|
|
127
|
-
locale:
|
|
95
|
+
locale: text3("locale").notNull(),
|
|
128
96
|
// "ko" | "en" | "ja"
|
|
129
97
|
// 사용자 ID (핵심 필드!)
|
|
130
|
-
userId:
|
|
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:
|
|
109
|
+
updatedAt: timestamp3("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
142
110
|
}, (table) => [
|
|
143
111
|
// UNIQUE 제약: section + locale + userId 조합은 유일
|
|
144
|
-
|
|
112
|
+
unique2("cms_draft_cache_unique").on(table.section, table.locale, table.userId),
|
|
145
113
|
// 인덱스: section으로 조회 최적화
|
|
146
|
-
|
|
114
|
+
index3("cms_draft_cache_section_idx").on(table.section),
|
|
147
115
|
// 인덱스: userId로 사용자의 모든 draft 조회 최적화
|
|
148
|
-
|
|
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
|
|
153
|
-
import { createFunctionSchema as
|
|
154
|
-
var
|
|
155
|
-
var cmsPublishedCache =
|
|
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:
|
|
125
|
+
id: serial4("id").primaryKey(),
|
|
158
126
|
// 섹션 (페이지 단위)
|
|
159
|
-
section:
|
|
127
|
+
section: text4("section").notNull(),
|
|
160
128
|
// "home" | "why-futureplay" | "team" | "our-companies" | "apply"
|
|
161
129
|
// 언어
|
|
162
|
-
locale:
|
|
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:
|
|
174
|
-
publishedBy:
|
|
141
|
+
publishedAt: timestamp4("published_at", { withTimezone: true }).notNull(),
|
|
142
|
+
publishedBy: text4("published_by"),
|
|
175
143
|
// 캐시 버전 (클라이언트 캐싱용)
|
|
176
|
-
version:
|
|
144
|
+
version: integer3("version").notNull().default(1)
|
|
177
145
|
}, (table) => [
|
|
178
146
|
// UNIQUE 제약: section + locale 조합은 유일
|
|
179
|
-
|
|
147
|
+
unique3("cms_published_cache_unique").on(table.section, table.locale),
|
|
180
148
|
// 인덱스: section으로 조회 최적화
|
|
181
|
-
|
|
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
|
|
186
|
-
import { createFunctionSchema as
|
|
187
|
-
var
|
|
188
|
-
var cmsAuditLogs =
|
|
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:
|
|
158
|
+
id: serial5("id").primaryKey(),
|
|
191
159
|
// Foreign Key: cms_labels (nullable - 라벨 삭제 시 로그는 유지)
|
|
192
|
-
labelId:
|
|
160
|
+
labelId: integer4("label_id").references(() => cmsLabels.id, { onDelete: "set null" }),
|
|
193
161
|
// 작업 유형
|
|
194
|
-
action:
|
|
162
|
+
action: text5("action").notNull(),
|
|
195
163
|
// "create" | "update" | "publish" | "unpublish" | "archive" | "delete" | "rollback" | "duplicate"
|
|
196
164
|
// 사용자 정보
|
|
197
|
-
userId:
|
|
198
|
-
userName:
|
|
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:
|
|
174
|
+
createdAt: timestamp5("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
207
175
|
}, (table) => [
|
|
208
176
|
// 인덱스: labelId로 이력 조회 최적화
|
|
209
|
-
|
|
177
|
+
index5("cms_audit_logs_label_id_idx").on(table.labelId),
|
|
210
178
|
// 인덱스: userId로 사용자 활동 조회 최적화
|
|
211
|
-
|
|
179
|
+
index5("cms_audit_logs_user_id_idx").on(table.userId),
|
|
212
180
|
// 인덱스: action 필터링 최적화
|
|
213
|
-
|
|
181
|
+
index5("cms_audit_logs_action_idx").on(table.action),
|
|
214
182
|
// 인덱스: 시간순 조회 최적화
|
|
215
|
-
|
|
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:
|
|
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:
|
|
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";
|