@withwiz/gallery 0.1.0
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/README.ko.md +484 -0
- package/README.md +484 -0
- package/dist/chunk-B3MQL6ZR.mjs +79 -0
- package/dist/chunk-B3MQL6ZR.mjs.map +1 -0
- package/dist/chunk-BC3TPJQY.mjs +122 -0
- package/dist/chunk-BC3TPJQY.mjs.map +1 -0
- package/dist/chunk-BTLELFKC.mjs +11 -0
- package/dist/chunk-BTLELFKC.mjs.map +1 -0
- package/dist/chunk-GDUGKVDQ.mjs +50 -0
- package/dist/chunk-GDUGKVDQ.mjs.map +1 -0
- package/dist/chunk-GFOWZGJX.js +50 -0
- package/dist/chunk-GFOWZGJX.js.map +1 -0
- package/dist/chunk-ILSZPLME.js +47 -0
- package/dist/chunk-ILSZPLME.js.map +1 -0
- package/dist/chunk-M7CCTDUG.js +122 -0
- package/dist/chunk-M7CCTDUG.js.map +1 -0
- package/dist/chunk-WVRTM6E7.js +19 -0
- package/dist/chunk-WVRTM6E7.js.map +1 -0
- package/dist/chunk-XLPMNJJV.js +11 -0
- package/dist/chunk-XLPMNJJV.js.map +1 -0
- package/dist/chunk-XSPWM4DD.js +79 -0
- package/dist/chunk-XSPWM4DD.js.map +1 -0
- package/dist/chunk-YUMB3GBN.mjs +47 -0
- package/dist/chunk-YUMB3GBN.mjs.map +1 -0
- package/dist/chunk-ZEZCUJJD.mjs +19 -0
- package/dist/chunk-ZEZCUJJD.mjs.map +1 -0
- package/dist/components/gallery.css +1074 -0
- package/dist/components/index.d.mts +156 -0
- package/dist/components/index.d.ts +156 -0
- package/dist/components/index.js +1215 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +1215 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/errors-B9gzUsX-.d.ts +39 -0
- package/dist/errors-DYrlDCzP.d.mts +39 -0
- package/dist/gallery.css +1074 -0
- package/dist/hooks/index.d.mts +65 -0
- package/dist/hooks/index.d.ts +65 -0
- package/dist/hooks/index.js +14 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/index.mjs +14 -0
- package/dist/hooks/index.mjs.map +1 -0
- package/dist/index.d.mts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +37 -0
- package/dist/index.mjs.map +1 -0
- package/dist/presets/ballet.d.mts +64 -0
- package/dist/presets/ballet.d.ts +64 -0
- package/dist/presets/ballet.js +162 -0
- package/dist/presets/ballet.js.map +1 -0
- package/dist/presets/ballet.mjs +162 -0
- package/dist/presets/ballet.mjs.map +1 -0
- package/dist/server/index.d.mts +157 -0
- package/dist/server/index.d.ts +157 -0
- package/dist/server/index.js +619 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +619 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/types/index.d.mts +137 -0
- package/dist/types/index.d.ts +137 -0
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/index.mjs +1 -0
- package/dist/types/index.mjs.map +1 -0
- package/dist/useGalleryLightbox-BYzPyBYB.d.mts +22 -0
- package/dist/useGalleryLightbox-BYzPyBYB.d.ts +22 -0
- package/dist/validators/index.d.mts +68 -0
- package/dist/validators/index.d.ts +68 -0
- package/dist/validators/index.js +7 -0
- package/dist/validators/index.js.map +1 -0
- package/dist/validators/index.mjs +7 -0
- package/dist/validators/index.mjs.map +1 -0
- package/package.json +85 -0
- package/prisma/gallery.schema.prisma +62 -0
- package/prisma/migrations/2026-05-24-enum-to-table.sql +65 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/helpers.ts","../../src/services/category-service.ts","../../src/services/index.ts","../../src/server/route-handlers.ts","../../src/utils/api-helpers.ts","../../src/server/loaders.ts"],"sourcesContent":["import type { PaginatedResult } from \"../types/pagination\";\n\n/**\n * pagination math 를 일관되게 처리. ballet 의 `buildPaginatedResult` 와 동일 시맨틱.\n *\n * - total === 0 일 때 totalPages = 0 (clamp 안 함; UI 에서 \"결과 없음\" 로 처리)\n * - page > totalPages 여도 결과 자체는 caller 의 query 가 결정 — meta 만 계산\n */\nexport function buildPaginatedResult<T>(\n items: T[],\n page: number,\n limit: number,\n total: number,\n): PaginatedResult<T> {\n const totalPages = total === 0 ? 0 : Math.ceil(total / limit);\n return {\n items,\n meta: {\n page,\n limit,\n total,\n totalPages,\n hasNext: page < totalPages,\n hasPrev: page > 1,\n },\n };\n}\n","import type {\n GalleryConfig,\n GalleryCategoryItem,\n CreateCategoryInput,\n UpdateCategoryInput,\n} from \"../types\";\nimport { CategoryInUseError } from \"../errors\";\n\n/**\n * spec §9-2 의 categoryService.\n *\n * 호스트 독립성을 위해 Prisma delegate 는 `config.prisma[modelName]` 패턴으로 접근한다\n * (modelName 기본값 = \"galleryCategory\", gallery 본체 modelName 기본값 = \"gallery\").\n *\n * remove() 는 `onDelete: Restrict` 보강 — 사전 gallery.count() 후 throw.\n * admin UX 가 친화적 에러 메시지를 표시하도록 명시적 throw 를 사용.\n */\nexport function createCategoryService(config: GalleryConfig) {\n const categoryDelegate = () =>\n config.prisma[config.categoryModelName ?? \"galleryCategory\"];\n const galleryDelegate = () => config.prisma[config.modelName ?? \"gallery\"];\n\n async function list(opts?: { isActive?: boolean }): Promise<GalleryCategoryItem[]> {\n const where =\n opts?.isActive !== undefined ? { isActive: opts.isActive } : undefined;\n const rows = await categoryDelegate().findMany({\n ...(where ? { where } : {}),\n orderBy: [{ sortOrder: \"asc\" }, { createdAt: \"asc\" }],\n });\n return rows as GalleryCategoryItem[];\n }\n\n async function getBySlug(slug: string): Promise<GalleryCategoryItem | null> {\n const row = await categoryDelegate().findUnique({ where: { slug } });\n return (row as GalleryCategoryItem | null) ?? null;\n }\n\n async function getById(id: string): Promise<GalleryCategoryItem | null> {\n const row = await categoryDelegate().findUnique({ where: { id } });\n return (row as GalleryCategoryItem | null) ?? null;\n }\n\n async function create(data: CreateCategoryInput): Promise<GalleryCategoryItem> {\n const row = await categoryDelegate().create({ data });\n return row as GalleryCategoryItem;\n }\n\n async function update(\n id: string,\n data: UpdateCategoryInput,\n ): Promise<GalleryCategoryItem> {\n const row = await categoryDelegate().update({ where: { id }, data });\n return row as GalleryCategoryItem;\n }\n\n /**\n * 사용 중 카테고리 (galleries 가 참조) 는 삭제 차단.\n * DB 의 `onDelete: Restrict` 가 최종 방어선이지만, 친화적 에러를 위해 사전 count.\n */\n async function remove(id: string): Promise<void> {\n const inUse = await galleryDelegate().count({ where: { categoryId: id } });\n if (inUse > 0) {\n throw new CategoryInUseError(id, inUse);\n }\n await categoryDelegate().delete({ where: { id } });\n }\n\n /**\n * ids 배열 순서대로 sortOrder = 0..N-1 로 갱신.\n * 단일 mutation 묶음 — $transaction 사용 (배열 형태).\n */\n async function reorder(ids: string[]): Promise<void> {\n const ops = ids.map((id, idx) =>\n categoryDelegate().update({ where: { id }, data: { sortOrder: idx } }),\n );\n await config.prisma.$transaction(ops);\n }\n\n return { list, getBySlug, getById, create, update, remove, reorder };\n}\n\nexport type CategoryService = ReturnType<typeof createCategoryService>;\n","import type {\n GalleryConfig,\n GalleryDetail,\n GalleryListItem,\n CreateGalleryInput,\n UpdateGalleryInput,\n PaginatedResult,\n} from \"../types\";\nimport { GalleryNotFoundError } from \"../errors\";\nimport { buildPaginatedResult } from \"./helpers\";\n\nexport { buildPaginatedResult } from \"./helpers\";\nexport { createCategoryService } from \"./category-service\";\nexport type { CategoryService } from \"./category-service\";\n\ntype SortKey = \"sortOrder\" | \"createdAt\" | \"updatedAt\" | \"caption\";\n\n/**\n * spec §9-2 의 createGalleryService.\n *\n * 핵심 규칙:\n * - Prisma delegate 는 `config.prisma[config.modelName ?? \"gallery\"]` 패턴.\n * - `include: { category: true }` 만 사용. **author include 금지** (Gallery 모델에 author 관계 없음, §3 호스트 독립 원칙).\n * - 삭제 시 `config.storage` 가 isEnabled() true 면 collectKeys → deleteKeys.\n * - `revalidate` 는 service 에서 호출하지 않음 (route handler 책임).\n */\nexport function createGalleryService(config: GalleryConfig) {\n const galleryDelegate = () => config.prisma[config.modelName ?? \"gallery\"];\n const baseInclude = { category: true } as const;\n\n function orderByFor(sortBy: SortKey) {\n switch (sortBy) {\n case \"sortOrder\":\n return [{ sortOrder: \"asc\" as const }, { createdAt: \"desc\" as const }];\n case \"caption\":\n return { caption: \"asc\" as const };\n case \"createdAt\":\n case \"updatedAt\":\n return { [sortBy]: \"desc\" as const };\n }\n }\n\n // ── Public-facing read methods ─────────────────────\n\n async function listFeatured(limit?: number): Promise<GalleryListItem[]> {\n const rows = await galleryDelegate().findMany({\n where: { published: true, featured: true },\n include: baseInclude,\n orderBy: [{ sortOrder: \"asc\" }, { createdAt: \"desc\" }],\n ...(limit ? { take: limit } : {}),\n });\n return rows as GalleryListItem[];\n }\n\n async function listPublished(limit?: number): Promise<GalleryListItem[]> {\n const rows = await galleryDelegate().findMany({\n where: { published: true },\n include: baseInclude,\n orderBy: [{ sortOrder: \"asc\" }, { createdAt: \"desc\" }],\n ...(limit ? { take: limit } : {}),\n });\n return rows as GalleryListItem[];\n }\n\n async function listPublishedByCategory(\n categorySlug: string,\n limit?: number,\n ): Promise<GalleryListItem[]> {\n const rows = await galleryDelegate().findMany({\n where: { published: true, category: { slug: categorySlug } },\n include: baseInclude,\n orderBy: [{ sortOrder: \"asc\" }, { createdAt: \"desc\" }],\n ...(limit ? { take: limit } : {}),\n });\n return rows as GalleryListItem[];\n }\n\n // ── Admin list / detail ────────────────────────────\n\n async function listAll(opts: {\n page?: number;\n limit?: number;\n categoryId?: string;\n published?: boolean;\n search?: string;\n sortBy?: SortKey;\n }): Promise<PaginatedResult<GalleryListItem>> {\n const page = opts.page ?? 1;\n const limit = opts.limit ?? 20;\n const sortBy = opts.sortBy ?? \"sortOrder\";\n const skip = (page - 1) * limit;\n\n const where: Record<string, unknown> = {};\n if (opts.categoryId) where.categoryId = opts.categoryId;\n if (opts.published !== undefined) where.published = opts.published;\n if (opts.search) where.caption = { contains: opts.search, mode: \"insensitive\" };\n\n const [items, total] = await Promise.all([\n galleryDelegate().findMany({\n where,\n include: baseInclude,\n orderBy: orderByFor(sortBy),\n skip,\n take: limit,\n }),\n galleryDelegate().count({ where }),\n ]);\n\n return buildPaginatedResult(items as GalleryListItem[], page, limit, total);\n }\n\n async function getById(id: string): Promise<GalleryDetail | null> {\n const row = await galleryDelegate().findUnique({\n where: { id },\n include: baseInclude,\n });\n return (row as GalleryDetail | null) ?? null;\n }\n\n // ── Mutations ──────────────────────────────────────\n\n async function create(\n data: CreateGalleryInput,\n authorId: string,\n ): Promise<GalleryDetail> {\n const row = await galleryDelegate().create({\n data: {\n imageUrl: data.imageUrl,\n imageKey: data.imageKey ?? null,\n caption: data.caption ?? null,\n categoryId: data.categoryId,\n sortOrder: data.sortOrder ?? 0,\n featured: data.featured ?? false,\n published: data.published ?? false,\n authorId,\n },\n include: baseInclude,\n });\n return row as GalleryDetail;\n }\n\n async function createMany(\n items: CreateGalleryInput[],\n authorId: string,\n ): Promise<{ count: number }> {\n const result = await galleryDelegate().createMany({\n data: items.map((it) => ({\n imageUrl: it.imageUrl,\n imageKey: it.imageKey ?? null,\n caption: it.caption ?? null,\n categoryId: it.categoryId,\n sortOrder: it.sortOrder ?? 0,\n featured: it.featured ?? false,\n published: it.published ?? false,\n authorId,\n })),\n });\n return { count: result.count };\n }\n\n async function update(\n id: string,\n data: UpdateGalleryInput,\n ): Promise<GalleryDetail> {\n const patch: Record<string, unknown> = {};\n if (data.imageUrl !== undefined) patch.imageUrl = data.imageUrl;\n if (data.imageKey !== undefined) patch.imageKey = data.imageKey ?? null;\n if (data.caption !== undefined) patch.caption = data.caption ?? null;\n if (data.categoryId !== undefined) patch.categoryId = data.categoryId;\n if (data.sortOrder !== undefined) patch.sortOrder = data.sortOrder;\n if (data.featured !== undefined) patch.featured = data.featured;\n if (data.published !== undefined) patch.published = data.published;\n\n const row = await galleryDelegate().update({\n where: { id },\n data: patch,\n include: baseInclude,\n });\n return row as GalleryDetail;\n }\n\n async function remove(id: string): Promise<void> {\n const storageEnabled = config.storage?.isEnabled?.() === true;\n let imageKey: string | null = null;\n if (storageEnabled) {\n const row = (await galleryDelegate().findUnique({\n where: { id },\n select: { imageKey: true },\n })) as { imageKey: string | null } | null;\n imageKey = row?.imageKey ?? null;\n }\n await galleryDelegate().delete({ where: { id } });\n if (storageEnabled && imageKey && config.storage) {\n const keys = config.storage.collectKeys(imageKey);\n if (keys.length > 0) await config.storage.deleteKeys(keys);\n }\n }\n\n async function removeMany(ids: string[]): Promise<{ count: number }> {\n const storageEnabled = config.storage?.isEnabled?.() === true;\n let imageKeys: string[] = [];\n if (storageEnabled) {\n const rows = (await galleryDelegate().findMany({\n where: { id: { in: ids } },\n select: { imageKey: true },\n })) as Array<{ imageKey: string | null }>;\n imageKeys = rows\n .map((r) => r.imageKey)\n .filter((k): k is string => typeof k === \"string\" && k.length > 0);\n }\n const result = await galleryDelegate().deleteMany({ where: { id: { in: ids } } });\n if (storageEnabled && imageKeys.length > 0 && config.storage) {\n const allKeys = imageKeys.flatMap((k) => config.storage!.collectKeys(k));\n const uniqueKeys = [...new Set(allKeys)];\n if (uniqueKeys.length > 0) await config.storage.deleteKeys(uniqueKeys);\n }\n return { count: result.count };\n }\n\n async function bulkUpdatePublished(\n ids: string[],\n published: boolean,\n ): Promise<{ count: number }> {\n const result = await galleryDelegate().updateMany({\n where: { id: { in: ids } },\n data: { published, updatedAt: new Date() },\n });\n return { count: result.count };\n }\n\n async function bulkUpdateFeatured(\n ids: string[],\n featured: boolean,\n ): Promise<{ count: number }> {\n const result = await galleryDelegate().updateMany({\n where: { id: { in: ids } },\n data: { featured, updatedAt: new Date() },\n });\n return { count: result.count };\n }\n\n async function togglePublish(id: string): Promise<GalleryDetail> {\n const current = (await galleryDelegate().findUnique({\n where: { id },\n select: { published: true },\n })) as { published: boolean } | null;\n if (!current) {\n throw new GalleryNotFoundError(id);\n }\n const row = await galleryDelegate().update({\n where: { id },\n data: { published: !current.published },\n include: baseInclude,\n });\n return row as GalleryDetail;\n }\n\n async function count(opts?: { published?: boolean }): Promise<number> {\n const where = opts?.published !== undefined ? { published: opts.published } : undefined;\n const n = await galleryDelegate().count(where ? { where } : {});\n return n as number;\n }\n\n async function listRecent(limit: number): Promise<GalleryListItem[]> {\n const rows = await galleryDelegate().findMany({\n include: baseInclude,\n orderBy: { createdAt: \"desc\" },\n take: limit,\n });\n return rows as GalleryListItem[];\n }\n\n return {\n listFeatured,\n listPublished,\n listPublishedByCategory,\n listAll,\n getById,\n create,\n createMany,\n update,\n remove,\n removeMany,\n bulkUpdatePublished,\n bulkUpdateFeatured,\n togglePublish,\n count,\n listRecent,\n };\n}\n\nexport type GalleryService = ReturnType<typeof createGalleryService>;\n","/**\n * Route handler factory — spec §9-3.\n *\n * `createGalleryRoutes(config)` 가 host 의 `apiWrapper` 로 감싼 9 개 라우트를 반환한다.\n * host 는 각 라우트 파일에서 `export const { GET, POST } = galleryRoutes.collection` 식으로 사용.\n *\n * 설계 원칙:\n * - 모든 mutation 종료 직전 `config.revalidate?.(path)` 를 `config.revalidatePaths` 각 path 에 대해 호출.\n * - `config.permissions?.canEdit/canDelete` 가 함수면 호출 → false 시 403 반환.\n * 미설정이면 admin 전원 모든 권한 (spec §16 v0.1 정책).\n * - `ctx.params?.id` 는 host 미들웨어가 채워준다고 가정 (spec §6-1).\n * - 카테고리 `remove` 의 in-use 에러는 service throw → handler 가 잡아 409 반환.\n */\nimport { NextResponse } from \"next/server\";\nimport type {\n ApiContext,\n GalleryConfig,\n RouteHandler,\n} from \"../types\";\nimport { createGalleryService } from \"../services\";\nimport { createCategoryService } from \"../services/category-service\";\nimport { createGallerySchemas } from \"../validators\";\nimport {\n CategoryInUseError,\n GalleryNotFoundError,\n} from \"../errors\";\nimport {\n parsePagination,\n getSearchParam,\n parseSortKey,\n validateAndParse,\n validateIds,\n} from \"../utils/api-helpers\";\n\nconst VALID_SORT_KEYS = [\n \"sortOrder\",\n \"createdAt\",\n \"updatedAt\",\n \"caption\",\n] as const;\n\ntype SortKey = (typeof VALID_SORT_KEYS)[number];\n\nexport interface GalleryRoutes {\n collection: { GET: RouteHandler; POST: RouteHandler; DELETE: RouteHandler };\n item: { GET: RouteHandler; PUT: RouteHandler; DELETE: RouteHandler };\n publishToggle: { PATCH: RouteHandler };\n bulk: { POST: RouteHandler; PATCH: RouteHandler };\n categoryCollection: { GET: RouteHandler; POST: RouteHandler };\n categoryItem: {\n GET: RouteHandler;\n PUT: RouteHandler;\n DELETE: RouteHandler;\n };\n}\n\nfunction jsonError(message: string, status: number, error?: string): Response {\n return NextResponse.json(\n { success: false, error: error ?? \"Error\", message },\n { status },\n );\n}\n\nfunction callRevalidate(config: GalleryConfig): void {\n if (!config.revalidate || !config.revalidatePaths) return;\n for (const p of config.revalidatePaths) {\n config.revalidate(p);\n }\n}\n\nfunction getAuthorId(config: GalleryConfig, ctx: ApiContext): string | null {\n if (config.authorIdFromContext) {\n try {\n const id = config.authorIdFromContext(ctx);\n return id || null;\n } catch {\n return null;\n }\n }\n return ctx.user?.id ?? null;\n}\n\nfunction getRouteId(ctx: ApiContext): string | null {\n const id = ctx.params?.id;\n return typeof id === \"string\" && id.length > 0 ? id : null;\n}\n\nexport function createGalleryRoutes(config: GalleryConfig): GalleryRoutes {\n const service = createGalleryService(config);\n const categoryService = createCategoryService(config);\n const schemas = createGallerySchemas({\n batchMax: config.limits.batchMax,\n captionMaxLength: config.limits.captionMaxLength ?? 200,\n });\n\n // ── /api/admin/galleries ─────────────────────────────\n\n const collectionGET = config.apiWrapper(async (ctx: ApiContext) => {\n const { page, limit } = parsePagination(ctx.request);\n const { searchParams } = new URL(ctx.request.url);\n const categoryId = getSearchParam(ctx.request, \"categoryId\");\n const publishedRaw = getSearchParam(ctx.request, \"published\");\n const search = getSearchParam(ctx.request, \"search\");\n const sortBy = parseSortKey<SortKey>(\n searchParams,\n VALID_SORT_KEYS,\n \"sortOrder\",\n );\n\n const published =\n publishedRaw === \"true\"\n ? true\n : publishedRaw === \"false\"\n ? false\n : undefined;\n\n const result = await service.listAll({\n page,\n limit,\n categoryId,\n published,\n search,\n sortBy,\n });\n\n return NextResponse.json({ success: true, data: result });\n });\n\n const collectionPOST = config.apiWrapper(async (ctx: ApiContext) => {\n const authorId = getAuthorId(config, ctx);\n if (!authorId) return jsonError(\"authentication required\", 401, \"Unauthorized\");\n\n const body = await ctx.request.json();\n const validation = validateAndParse(schemas.CreateGallerySchema, body);\n if (!validation.success) return validation.response;\n\n const item = await service.create(validation.data, authorId);\n callRevalidate(config);\n return NextResponse.json({ success: true, data: item }, { status: 201 });\n });\n\n const collectionDELETE = config.apiWrapper(async (ctx: ApiContext) => {\n const body = await ctx.request.json();\n const check = validateIds(body?.ids);\n if (!check.valid) return check.response;\n\n const { count } = await service.removeMany(check.ids);\n callRevalidate(config);\n return NextResponse.json({ success: true, data: { count } });\n });\n\n // ── /api/admin/galleries/[id] ────────────────────────\n\n const itemGET = config.apiWrapper(async (ctx: ApiContext) => {\n const id = getRouteId(ctx);\n if (!id) return jsonError(\"missing route param: id\", 400, \"InvalidParams\");\n\n const item = await service.getById(id);\n if (!item) return jsonError(\"Gallery item not found\", 404, \"NotFound\");\n\n return NextResponse.json({ success: true, data: item });\n });\n\n const itemPUT = config.apiWrapper(async (ctx: ApiContext) => {\n const id = getRouteId(ctx);\n if (!id) return jsonError(\"missing route param: id\", 400, \"InvalidParams\");\n\n const body = await ctx.request.json();\n const validation = validateAndParse(schemas.UpdateGallerySchema, body);\n if (!validation.success) return validation.response;\n\n const existing = await service.getById(id);\n if (!existing) return jsonError(\"Gallery item not found\", 404, \"NotFound\");\n\n if (config.permissions?.canEdit) {\n const ok = config.permissions.canEdit(ctx, { authorId: existing.authorId });\n if (!ok) return jsonError(\"forbidden\", 403, \"Forbidden\");\n }\n\n const item = await service.update(id, validation.data);\n callRevalidate(config);\n return NextResponse.json({ success: true, data: item });\n });\n\n const itemDELETE = config.apiWrapper(async (ctx: ApiContext) => {\n const id = getRouteId(ctx);\n if (!id) return jsonError(\"missing route param: id\", 400, \"InvalidParams\");\n\n if (config.permissions?.canDelete) {\n const existing = await service.getById(id);\n if (!existing) return jsonError(\"Gallery item not found\", 404, \"NotFound\");\n const ok = config.permissions.canDelete(ctx, {\n authorId: existing.authorId,\n });\n if (!ok) return jsonError(\"forbidden\", 403, \"Forbidden\");\n }\n\n await service.remove(id);\n callRevalidate(config);\n return new NextResponse(null, { status: 204 });\n });\n\n // ── /api/admin/galleries/[id]/publish ────────────────\n\n const publishTogglePATCH = config.apiWrapper(async (ctx: ApiContext) => {\n const id = getRouteId(ctx);\n if (!id) return jsonError(\"missing route param: id\", 400, \"InvalidParams\");\n\n if (config.permissions?.canEdit) {\n const existing = await service.getById(id);\n if (!existing) return jsonError(\"Gallery item not found\", 404, \"NotFound\");\n const ok = config.permissions.canEdit(ctx, {\n authorId: existing.authorId,\n });\n if (!ok) return jsonError(\"forbidden\", 403, \"Forbidden\");\n }\n\n try {\n const result = await service.togglePublish(id);\n callRevalidate(config);\n return NextResponse.json({ success: true, data: result });\n } catch (err) {\n // service.togglePublish throws GalleryNotFoundError on missing id — map to 404\n if (err instanceof GalleryNotFoundError) {\n return jsonError(\"Gallery item not found\", 404, \"NotFound\");\n }\n throw err;\n }\n });\n\n // ── /api/admin/galleries/bulk ────────────────────────\n\n const bulkPOST = config.apiWrapper(async (ctx: ApiContext) => {\n const authorId = getAuthorId(config, ctx);\n if (!authorId) return jsonError(\"authentication required\", 401, \"Unauthorized\");\n\n const body = await ctx.request.json();\n const validation = validateAndParse(schemas.BatchCreateGallerySchema, body);\n if (!validation.success) return validation.response;\n\n const result = await service.createMany(validation.data.items, authorId);\n callRevalidate(config);\n return NextResponse.json(\n { success: true, data: result },\n { status: 201 },\n );\n });\n\n const bulkPATCH = config.apiWrapper(async (ctx: ApiContext) => {\n const body = await ctx.request.json();\n const validation = validateAndParse(schemas.BulkUpdateSchema, body);\n if (!validation.success) return validation.response;\n\n const { ids, published, featured } = validation.data;\n let count = 0;\n\n if (published !== undefined) {\n const r = await service.bulkUpdatePublished(ids, published);\n count = r.count;\n }\n if (featured !== undefined) {\n const r = await service.bulkUpdateFeatured(ids, featured);\n count = r.count;\n }\n\n callRevalidate(config);\n return NextResponse.json({ success: true, data: { count } });\n });\n\n // ── /api/admin/gallery-categories ────────────────────\n\n const categoryCollectionGET = config.apiWrapper(async (ctx: ApiContext) => {\n const isActiveRaw = getSearchParam(ctx.request, \"isActive\");\n const isActive =\n isActiveRaw === \"true\"\n ? true\n : isActiveRaw === \"false\"\n ? false\n : undefined;\n\n const items = await categoryService.list(\n isActive === undefined ? undefined : { isActive },\n );\n return NextResponse.json({ success: true, data: items });\n });\n\n const categoryCollectionPOST = config.apiWrapper(async (ctx: ApiContext) => {\n const body = await ctx.request.json();\n const validation = validateAndParse(schemas.CreateCategorySchema, body);\n if (!validation.success) return validation.response;\n\n const item = await categoryService.create(validation.data);\n callRevalidate(config);\n return NextResponse.json({ success: true, data: item }, { status: 201 });\n });\n\n // ── /api/admin/gallery-categories/[id] ───────────────\n\n const categoryItemGET = config.apiWrapper(async (ctx: ApiContext) => {\n const id = getRouteId(ctx);\n if (!id) return jsonError(\"missing route param: id\", 400, \"InvalidParams\");\n\n const item = await categoryService.getById(id);\n if (!item) return jsonError(\"Category not found\", 404, \"NotFound\");\n return NextResponse.json({ success: true, data: item });\n });\n\n const categoryItemPUT = config.apiWrapper(async (ctx: ApiContext) => {\n const id = getRouteId(ctx);\n if (!id) return jsonError(\"missing route param: id\", 400, \"InvalidParams\");\n\n const body = await ctx.request.json();\n const validation = validateAndParse(schemas.UpdateCategorySchema, body);\n if (!validation.success) return validation.response;\n\n const existing = await categoryService.getById(id);\n if (!existing) return jsonError(\"Category not found\", 404, \"NotFound\");\n\n const item = await categoryService.update(id, validation.data);\n callRevalidate(config);\n return NextResponse.json({ success: true, data: item });\n });\n\n const categoryItemDELETE = config.apiWrapper(async (ctx: ApiContext) => {\n const id = getRouteId(ctx);\n if (!id) return jsonError(\"missing route param: id\", 400, \"InvalidParams\");\n\n try {\n await categoryService.remove(id);\n } catch (err) {\n if (err instanceof CategoryInUseError) {\n return jsonError(err.message, 409, \"CategoryInUse\");\n }\n throw err;\n }\n callRevalidate(config);\n return new NextResponse(null, { status: 204 });\n });\n\n return {\n collection: {\n GET: collectionGET,\n POST: collectionPOST,\n DELETE: collectionDELETE,\n },\n item: { GET: itemGET, PUT: itemPUT, DELETE: itemDELETE },\n publishToggle: { PATCH: publishTogglePATCH },\n bulk: { POST: bulkPOST, PATCH: bulkPATCH },\n categoryCollection: {\n GET: categoryCollectionGET,\n POST: categoryCollectionPOST,\n },\n categoryItem: {\n GET: categoryItemGET,\n PUT: categoryItemPUT,\n DELETE: categoryItemDELETE,\n },\n };\n}\n","import { NextResponse } from \"next/server\";\nimport type { z } from \"zod\";\n\ninterface RequestLike {\n url: string;\n}\n\nexport function parsePagination(req: RequestLike): { page: number; limit: number } {\n const { searchParams } = new URL(req.url);\n const rawPage = Number.parseInt(searchParams.get(\"page\") ?? \"1\", 10);\n const rawLimit = Number.parseInt(searchParams.get(\"limit\") ?? \"20\", 10);\n const page = Number.isFinite(rawPage) && rawPage > 0 ? rawPage : 1;\n const limit = Number.isFinite(rawLimit) && rawLimit > 0 ? Math.min(rawLimit, 100) : 20;\n return { page, limit };\n}\n\nexport function getSearchParam(req: RequestLike, key: string): string | undefined {\n const { searchParams } = new URL(req.url);\n const v = searchParams.get(key);\n return v ?? undefined;\n}\n\nexport type ValidateAndParseResult<T> =\n | { success: true; data: T }\n | { success: false; response: Response };\n\nexport function validateAndParse<T>(\n schema: z.ZodSchema<T>,\n data: unknown,\n): ValidateAndParseResult<T> {\n const parsed = schema.safeParse(data);\n if (parsed.success) return { success: true, data: parsed.data };\n return {\n success: false,\n response: NextResponse.json(\n { success: false, error: \"ValidationError\", details: parsed.error.issues },\n { status: 400 },\n ),\n };\n}\n\nexport type ValidateIdsResult =\n | { valid: true; ids: string[] }\n | { valid: false; response: Response };\n\nexport function validateIds(input: unknown): ValidateIdsResult {\n if (!Array.isArray(input) || input.length === 0) {\n return {\n valid: false,\n response: NextResponse.json(\n { success: false, error: \"InvalidIds\", message: \"ids must be a non-empty array\" },\n { status: 400 },\n ),\n };\n }\n const ids = input.filter((x): x is string => typeof x === \"string\" && x.length > 0);\n if (ids.length === 0) {\n return {\n valid: false,\n response: NextResponse.json(\n { success: false, error: \"InvalidIds\", message: \"ids array contains no valid strings\" },\n { status: 400 },\n ),\n };\n }\n return { valid: true, ids };\n}\n\nexport function parseSortKey<T extends string>(\n searchParams: URLSearchParams,\n allowed: readonly T[],\n fallback: T,\n): T {\n const raw = searchParams.get(\"sortBy\");\n return raw && (allowed as readonly string[]).includes(raw) ? (raw as T) : fallback;\n}\n","/**\n * RSC loaders — spec §9-4.\n *\n * Server Components 가 직접 호출하는 함수. 각 함수는 service 의 대응 메서드를\n * 얇게 감싸기만 한다. host 가 매번 `config` 를 주입해 explicit DI 를 유지한다\n * (setGalleryConfig 의존 회피 — host 의 layout/page 에서 config 를 명시적으로\n * 주입하는 것이 RSC graph 캐싱과 호환성 면에서 더 안전).\n */\nimport type {\n GalleryConfig,\n GalleryListItem,\n PaginatedResult,\n} from \"../types\";\nimport { createGalleryService } from \"../services\";\n\ntype SortKey = \"sortOrder\" | \"createdAt\" | \"updatedAt\" | \"caption\";\n\n/**\n * spec §9-4: `getGalleryItems(config, opts) → PaginatedResult<GalleryListItem>`\n *\n * admin 페이지의 RSC 가 직접 호출. fetch 라운드트립 없이 service 호출.\n */\nexport async function getGalleryItems(\n config: GalleryConfig,\n opts: {\n page?: number;\n limit?: number;\n categoryId?: string;\n published?: boolean;\n search?: string;\n sortBy?: SortKey;\n },\n): Promise<PaginatedResult<GalleryListItem>> {\n const service = createGalleryService(config);\n return service.listAll(opts);\n}\n\n/**\n * spec §9-4: `getFeaturedGalleries(config, limit?) → GalleryListItem[]`\n *\n * 홈 페이지의 모자이크 그리드 등 featured 영역에서 호출.\n */\nexport async function getFeaturedGalleries(\n config: GalleryConfig,\n limit?: number,\n): Promise<GalleryListItem[]> {\n const service = createGalleryService(config);\n return service.listFeatured(limit);\n}\n\n/**\n * spec §9-4: `getRecentGalleries(config, limit) → GalleryListItem[]`\n *\n * 대시보드의 최근 항목 위젯 등에서 호출.\n */\nexport async function getRecentGalleries(\n config: GalleryConfig,\n limit: number,\n): Promise<GalleryListItem[]> {\n const service = createGalleryService(config);\n return service.listRecent(limit);\n}\n\n/**\n * spec §9-4: `getGalleryCount(config, opts?) → number`\n *\n * 대시보드 통계 등에서 호출. `opts.published` 미지정 시 전체 count.\n */\nexport async function getGalleryCount(\n config: GalleryConfig,\n opts?: { published?: boolean },\n): Promise<number> {\n const service = createGalleryService(config);\n return service.count(opts);\n}\n"],"mappings":"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","names":["NextResponse","NextResponse"]}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { NextRequest } from 'next/server';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
/** gallery 이 PrismaClient 에서 사용하는 메서드만 명시 (structural typing).
|
|
5
|
+
* host 는 자기 PrismaClient 를 그대로 주입. modelName 가변성을 위해 delegate 는 index 로 접근. */
|
|
6
|
+
interface PrismaLike {
|
|
7
|
+
$transaction<T>(operations: Promise<T>[]): Promise<T[]>;
|
|
8
|
+
$transaction<T>(fn: (tx: PrismaLike) => Promise<T>): Promise<T>;
|
|
9
|
+
readonly [delegate: string]: any;
|
|
10
|
+
}
|
|
11
|
+
/** gallery route handler 가 사용하는 컨텍스트.
|
|
12
|
+
* host 의 미들웨어가 IApiContext 호환 객체를 만들어 handler 에 전달한다고 가정. */
|
|
13
|
+
interface ApiContext {
|
|
14
|
+
request: NextRequest;
|
|
15
|
+
user?: {
|
|
16
|
+
id: string;
|
|
17
|
+
[key: string]: unknown;
|
|
18
|
+
};
|
|
19
|
+
/** Next 16 dynamic route 의 params (이미 await 된 형태) */
|
|
20
|
+
params?: Record<string, string>;
|
|
21
|
+
}
|
|
22
|
+
/** Next.js route handler 시그니처 (Next 16) */
|
|
23
|
+
type RouteHandler = (req: NextRequest, routeCtx?: {
|
|
24
|
+
params: Promise<Record<string, string>>;
|
|
25
|
+
}) => Promise<Response>;
|
|
26
|
+
/** host 가 제공하는 API 미들웨어 래퍼 형태 (ballet 의 withAdminApi 와 동일 패턴) */
|
|
27
|
+
type ApiWrapper = (handler: (ctx: ApiContext) => Promise<Response>) => RouteHandler;
|
|
28
|
+
|
|
29
|
+
type SortOrder = "asc" | "desc";
|
|
30
|
+
interface PaginationMeta {
|
|
31
|
+
page: number;
|
|
32
|
+
limit: number;
|
|
33
|
+
total: number;
|
|
34
|
+
totalPages: number;
|
|
35
|
+
hasNext: boolean;
|
|
36
|
+
hasPrev: boolean;
|
|
37
|
+
}
|
|
38
|
+
interface PaginatedResult<T> {
|
|
39
|
+
items: T[];
|
|
40
|
+
meta: PaginationMeta;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type GalleryI18nKey = "admin.title" | "admin.newButton" | "admin.searchPlaceholder" | "admin.emptyState" | "admin.bulkSelectAll" | "admin.bulkPublish" | "admin.bulkUnpublish" | "admin.bulkFeature" | "admin.bulkUnfeature" | "admin.bulkDelete" | "admin.featuredOverLimit" | "form.caption" | "form.captionPlaceholder" | "form.category" | "form.sortOrder" | "form.published" | "form.featured" | "form.save" | "form.cancel" | "form.imageDropPrompt" | "form.imageReplacePrompt" | "preview.title" | "preview.dragHint" | "preview.emptyState" | "category.title" | "category.newButton" | "category.slug" | "category.labelKo" | "category.labelEn" | "category.deleteConfirmInUse" | "public.galleryLabel" | "public.moments" | "public.expandAria" | "public.lbClose" | "public.lbPrev" | "public.lbNext";
|
|
44
|
+
type GallerySlotName = "managerRoot" | "managerToolbar" | "listRoot" | "listItem" | "editFormRoot" | "homePreviewRoot" | "imageDropZone" | "emptyState";
|
|
45
|
+
interface GalleryListItem {
|
|
46
|
+
id: string;
|
|
47
|
+
imageUrl: string;
|
|
48
|
+
imageKey: string | null;
|
|
49
|
+
caption: string | null;
|
|
50
|
+
categoryId: string;
|
|
51
|
+
category?: {
|
|
52
|
+
id: string;
|
|
53
|
+
slug: string;
|
|
54
|
+
labelKo: string;
|
|
55
|
+
labelEn: string | null;
|
|
56
|
+
};
|
|
57
|
+
sortOrder: number;
|
|
58
|
+
featured: boolean;
|
|
59
|
+
published: boolean;
|
|
60
|
+
authorId: string;
|
|
61
|
+
createdAt: Date;
|
|
62
|
+
updatedAt: Date;
|
|
63
|
+
}
|
|
64
|
+
type GalleryDetail = GalleryListItem;
|
|
65
|
+
interface GalleryCategoryItem {
|
|
66
|
+
id: string;
|
|
67
|
+
slug: string;
|
|
68
|
+
labelKo: string;
|
|
69
|
+
labelEn: string | null;
|
|
70
|
+
sortOrder: number;
|
|
71
|
+
isActive: boolean;
|
|
72
|
+
createdAt: Date;
|
|
73
|
+
updatedAt: Date;
|
|
74
|
+
}
|
|
75
|
+
interface CreateGalleryInput {
|
|
76
|
+
imageUrl: string;
|
|
77
|
+
imageKey?: string;
|
|
78
|
+
caption?: string;
|
|
79
|
+
categoryId: string;
|
|
80
|
+
sortOrder?: number;
|
|
81
|
+
featured?: boolean;
|
|
82
|
+
published?: boolean;
|
|
83
|
+
}
|
|
84
|
+
type UpdateGalleryInput = Partial<CreateGalleryInput>;
|
|
85
|
+
interface CreateCategoryInput {
|
|
86
|
+
slug: string;
|
|
87
|
+
labelKo: string;
|
|
88
|
+
labelEn?: string;
|
|
89
|
+
sortOrder?: number;
|
|
90
|
+
isActive?: boolean;
|
|
91
|
+
}
|
|
92
|
+
type UpdateCategoryInput = Partial<CreateCategoryInput>;
|
|
93
|
+
interface GalleryConfig {
|
|
94
|
+
prisma: PrismaLike;
|
|
95
|
+
modelName?: string;
|
|
96
|
+
categoryModelName?: string;
|
|
97
|
+
apiWrapper: ApiWrapper;
|
|
98
|
+
authorIdFromContext?: (ctx: ApiContext) => string;
|
|
99
|
+
permissions?: {
|
|
100
|
+
canEdit?: (ctx: ApiContext, gallery: {
|
|
101
|
+
authorId: string;
|
|
102
|
+
}) => boolean;
|
|
103
|
+
canDelete?: (ctx: ApiContext, gallery: {
|
|
104
|
+
authorId: string;
|
|
105
|
+
}) => boolean;
|
|
106
|
+
};
|
|
107
|
+
storage?: {
|
|
108
|
+
isEnabled: () => boolean;
|
|
109
|
+
collectKeys: (imageKey: string) => string[];
|
|
110
|
+
deleteKeys: (keys: string[]) => Promise<void>;
|
|
111
|
+
validateUpload?: (file: {
|
|
112
|
+
name: string;
|
|
113
|
+
size: number;
|
|
114
|
+
type: string;
|
|
115
|
+
}) => {
|
|
116
|
+
ok: true;
|
|
117
|
+
} | {
|
|
118
|
+
ok: false;
|
|
119
|
+
reason: string;
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
limits: {
|
|
123
|
+
maxFeatured: number;
|
|
124
|
+
mosaicCount: number;
|
|
125
|
+
batchMax: number;
|
|
126
|
+
captionMaxLength?: number;
|
|
127
|
+
};
|
|
128
|
+
revalidate?: (path: string, type?: "page" | "layout") => void;
|
|
129
|
+
revalidatePaths?: string[];
|
|
130
|
+
i18n?: Partial<Record<GalleryI18nKey, string>> & Record<string, string>;
|
|
131
|
+
ui?: {
|
|
132
|
+
classNames?: Partial<Record<GallerySlotName, string>>;
|
|
133
|
+
slots?: Partial<Record<GallerySlotName, React.ReactNode>>;
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export type { ApiContext, ApiWrapper, CreateCategoryInput, CreateGalleryInput, GalleryCategoryItem, GalleryConfig, GalleryDetail, GalleryI18nKey, GalleryListItem, GallerySlotName, PaginatedResult, PaginationMeta, PrismaLike, RouteHandler, SortOrder, UpdateCategoryInput, UpdateGalleryInput };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { NextRequest } from 'next/server';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
/** gallery 이 PrismaClient 에서 사용하는 메서드만 명시 (structural typing).
|
|
5
|
+
* host 는 자기 PrismaClient 를 그대로 주입. modelName 가변성을 위해 delegate 는 index 로 접근. */
|
|
6
|
+
interface PrismaLike {
|
|
7
|
+
$transaction<T>(operations: Promise<T>[]): Promise<T[]>;
|
|
8
|
+
$transaction<T>(fn: (tx: PrismaLike) => Promise<T>): Promise<T>;
|
|
9
|
+
readonly [delegate: string]: any;
|
|
10
|
+
}
|
|
11
|
+
/** gallery route handler 가 사용하는 컨텍스트.
|
|
12
|
+
* host 의 미들웨어가 IApiContext 호환 객체를 만들어 handler 에 전달한다고 가정. */
|
|
13
|
+
interface ApiContext {
|
|
14
|
+
request: NextRequest;
|
|
15
|
+
user?: {
|
|
16
|
+
id: string;
|
|
17
|
+
[key: string]: unknown;
|
|
18
|
+
};
|
|
19
|
+
/** Next 16 dynamic route 의 params (이미 await 된 형태) */
|
|
20
|
+
params?: Record<string, string>;
|
|
21
|
+
}
|
|
22
|
+
/** Next.js route handler 시그니처 (Next 16) */
|
|
23
|
+
type RouteHandler = (req: NextRequest, routeCtx?: {
|
|
24
|
+
params: Promise<Record<string, string>>;
|
|
25
|
+
}) => Promise<Response>;
|
|
26
|
+
/** host 가 제공하는 API 미들웨어 래퍼 형태 (ballet 의 withAdminApi 와 동일 패턴) */
|
|
27
|
+
type ApiWrapper = (handler: (ctx: ApiContext) => Promise<Response>) => RouteHandler;
|
|
28
|
+
|
|
29
|
+
type SortOrder = "asc" | "desc";
|
|
30
|
+
interface PaginationMeta {
|
|
31
|
+
page: number;
|
|
32
|
+
limit: number;
|
|
33
|
+
total: number;
|
|
34
|
+
totalPages: number;
|
|
35
|
+
hasNext: boolean;
|
|
36
|
+
hasPrev: boolean;
|
|
37
|
+
}
|
|
38
|
+
interface PaginatedResult<T> {
|
|
39
|
+
items: T[];
|
|
40
|
+
meta: PaginationMeta;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type GalleryI18nKey = "admin.title" | "admin.newButton" | "admin.searchPlaceholder" | "admin.emptyState" | "admin.bulkSelectAll" | "admin.bulkPublish" | "admin.bulkUnpublish" | "admin.bulkFeature" | "admin.bulkUnfeature" | "admin.bulkDelete" | "admin.featuredOverLimit" | "form.caption" | "form.captionPlaceholder" | "form.category" | "form.sortOrder" | "form.published" | "form.featured" | "form.save" | "form.cancel" | "form.imageDropPrompt" | "form.imageReplacePrompt" | "preview.title" | "preview.dragHint" | "preview.emptyState" | "category.title" | "category.newButton" | "category.slug" | "category.labelKo" | "category.labelEn" | "category.deleteConfirmInUse" | "public.galleryLabel" | "public.moments" | "public.expandAria" | "public.lbClose" | "public.lbPrev" | "public.lbNext";
|
|
44
|
+
type GallerySlotName = "managerRoot" | "managerToolbar" | "listRoot" | "listItem" | "editFormRoot" | "homePreviewRoot" | "imageDropZone" | "emptyState";
|
|
45
|
+
interface GalleryListItem {
|
|
46
|
+
id: string;
|
|
47
|
+
imageUrl: string;
|
|
48
|
+
imageKey: string | null;
|
|
49
|
+
caption: string | null;
|
|
50
|
+
categoryId: string;
|
|
51
|
+
category?: {
|
|
52
|
+
id: string;
|
|
53
|
+
slug: string;
|
|
54
|
+
labelKo: string;
|
|
55
|
+
labelEn: string | null;
|
|
56
|
+
};
|
|
57
|
+
sortOrder: number;
|
|
58
|
+
featured: boolean;
|
|
59
|
+
published: boolean;
|
|
60
|
+
authorId: string;
|
|
61
|
+
createdAt: Date;
|
|
62
|
+
updatedAt: Date;
|
|
63
|
+
}
|
|
64
|
+
type GalleryDetail = GalleryListItem;
|
|
65
|
+
interface GalleryCategoryItem {
|
|
66
|
+
id: string;
|
|
67
|
+
slug: string;
|
|
68
|
+
labelKo: string;
|
|
69
|
+
labelEn: string | null;
|
|
70
|
+
sortOrder: number;
|
|
71
|
+
isActive: boolean;
|
|
72
|
+
createdAt: Date;
|
|
73
|
+
updatedAt: Date;
|
|
74
|
+
}
|
|
75
|
+
interface CreateGalleryInput {
|
|
76
|
+
imageUrl: string;
|
|
77
|
+
imageKey?: string;
|
|
78
|
+
caption?: string;
|
|
79
|
+
categoryId: string;
|
|
80
|
+
sortOrder?: number;
|
|
81
|
+
featured?: boolean;
|
|
82
|
+
published?: boolean;
|
|
83
|
+
}
|
|
84
|
+
type UpdateGalleryInput = Partial<CreateGalleryInput>;
|
|
85
|
+
interface CreateCategoryInput {
|
|
86
|
+
slug: string;
|
|
87
|
+
labelKo: string;
|
|
88
|
+
labelEn?: string;
|
|
89
|
+
sortOrder?: number;
|
|
90
|
+
isActive?: boolean;
|
|
91
|
+
}
|
|
92
|
+
type UpdateCategoryInput = Partial<CreateCategoryInput>;
|
|
93
|
+
interface GalleryConfig {
|
|
94
|
+
prisma: PrismaLike;
|
|
95
|
+
modelName?: string;
|
|
96
|
+
categoryModelName?: string;
|
|
97
|
+
apiWrapper: ApiWrapper;
|
|
98
|
+
authorIdFromContext?: (ctx: ApiContext) => string;
|
|
99
|
+
permissions?: {
|
|
100
|
+
canEdit?: (ctx: ApiContext, gallery: {
|
|
101
|
+
authorId: string;
|
|
102
|
+
}) => boolean;
|
|
103
|
+
canDelete?: (ctx: ApiContext, gallery: {
|
|
104
|
+
authorId: string;
|
|
105
|
+
}) => boolean;
|
|
106
|
+
};
|
|
107
|
+
storage?: {
|
|
108
|
+
isEnabled: () => boolean;
|
|
109
|
+
collectKeys: (imageKey: string) => string[];
|
|
110
|
+
deleteKeys: (keys: string[]) => Promise<void>;
|
|
111
|
+
validateUpload?: (file: {
|
|
112
|
+
name: string;
|
|
113
|
+
size: number;
|
|
114
|
+
type: string;
|
|
115
|
+
}) => {
|
|
116
|
+
ok: true;
|
|
117
|
+
} | {
|
|
118
|
+
ok: false;
|
|
119
|
+
reason: string;
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
limits: {
|
|
123
|
+
maxFeatured: number;
|
|
124
|
+
mosaicCount: number;
|
|
125
|
+
batchMax: number;
|
|
126
|
+
captionMaxLength?: number;
|
|
127
|
+
};
|
|
128
|
+
revalidate?: (path: string, type?: "page" | "layout") => void;
|
|
129
|
+
revalidatePaths?: string[];
|
|
130
|
+
i18n?: Partial<Record<GalleryI18nKey, string>> & Record<string, string>;
|
|
131
|
+
ui?: {
|
|
132
|
+
classNames?: Partial<Record<GallerySlotName, string>>;
|
|
133
|
+
slots?: Partial<Record<GallerySlotName, React.ReactNode>>;
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export type { ApiContext, ApiWrapper, CreateCategoryInput, CreateGalleryInput, GalleryCategoryItem, GalleryConfig, GalleryDetail, GalleryI18nKey, GalleryListItem, GallerySlotName, PaginatedResult, PaginationMeta, PrismaLike, RouteHandler, SortOrder, UpdateCategoryInput, UpdateGalleryInput };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/types/index.js"],"names":[],"mappings":"AAAA","file":"/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/types/index.js"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
interface LightboxImage {
|
|
2
|
+
src: string;
|
|
3
|
+
alt?: string;
|
|
4
|
+
}
|
|
5
|
+
interface UseGalleryLightboxReturn {
|
|
6
|
+
isOpen: boolean;
|
|
7
|
+
currentIndex: number;
|
|
8
|
+
current: LightboxImage | null;
|
|
9
|
+
open: (index: number) => void;
|
|
10
|
+
close: () => void;
|
|
11
|
+
next: () => void;
|
|
12
|
+
prev: () => void;
|
|
13
|
+
}
|
|
14
|
+
/** headless 갤러리 라이트박스. open 상태에서 ESC / ←/→ 키바인딩 자동 등록.
|
|
15
|
+
*
|
|
16
|
+
* - 이미지 0개일 때 `open` 호출은 no-op.
|
|
17
|
+
* - `next` / `prev` 는 wrap-around (마지막 다음 = 처음).
|
|
18
|
+
* - SSR safe: window keydown listener 는 isOpen && typeof window !== "undefined" 일 때만 등록.
|
|
19
|
+
*/
|
|
20
|
+
declare function useGalleryLightbox(images: LightboxImage[]): UseGalleryLightboxReturn;
|
|
21
|
+
|
|
22
|
+
export { type LightboxImage as L, type UseGalleryLightboxReturn as U, useGalleryLightbox as u };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
interface LightboxImage {
|
|
2
|
+
src: string;
|
|
3
|
+
alt?: string;
|
|
4
|
+
}
|
|
5
|
+
interface UseGalleryLightboxReturn {
|
|
6
|
+
isOpen: boolean;
|
|
7
|
+
currentIndex: number;
|
|
8
|
+
current: LightboxImage | null;
|
|
9
|
+
open: (index: number) => void;
|
|
10
|
+
close: () => void;
|
|
11
|
+
next: () => void;
|
|
12
|
+
prev: () => void;
|
|
13
|
+
}
|
|
14
|
+
/** headless 갤러리 라이트박스. open 상태에서 ESC / ←/→ 키바인딩 자동 등록.
|
|
15
|
+
*
|
|
16
|
+
* - 이미지 0개일 때 `open` 호출은 no-op.
|
|
17
|
+
* - `next` / `prev` 는 wrap-around (마지막 다음 = 처음).
|
|
18
|
+
* - SSR safe: window keydown listener 는 isOpen && typeof window !== "undefined" 일 때만 등록.
|
|
19
|
+
*/
|
|
20
|
+
declare function useGalleryLightbox(images: LightboxImage[]): UseGalleryLightboxReturn;
|
|
21
|
+
|
|
22
|
+
export { type LightboxImage as L, type UseGalleryLightboxReturn as U, useGalleryLightbox as u };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* spec §9-1 충실 구현.
|
|
5
|
+
*
|
|
6
|
+
* 호스트가 `config.limits.batchMax` 와 `config.limits.captionMaxLength` 를 넘겨
|
|
7
|
+
* 패키지 내부 스키마를 생성한다. categoryId 는 cuid 형식만 검증하고,
|
|
8
|
+
* 실제 카테고리 존재 여부는 service 레이어가 Prisma FK 제약으로 자연스럽게 잡는다.
|
|
9
|
+
*/
|
|
10
|
+
declare function createGallerySchemas(opts: {
|
|
11
|
+
batchMax: number;
|
|
12
|
+
captionMaxLength: number;
|
|
13
|
+
}): {
|
|
14
|
+
CreateGallerySchema: z.ZodObject<{
|
|
15
|
+
imageUrl: z.ZodString;
|
|
16
|
+
imageKey: z.ZodOptional<z.ZodString>;
|
|
17
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
18
|
+
categoryId: z.ZodString;
|
|
19
|
+
sortOrder: z.ZodDefault<z.ZodNumber>;
|
|
20
|
+
featured: z.ZodDefault<z.ZodBoolean>;
|
|
21
|
+
published: z.ZodDefault<z.ZodBoolean>;
|
|
22
|
+
}, z.core.$strip>;
|
|
23
|
+
UpdateGallerySchema: z.ZodObject<{
|
|
24
|
+
imageUrl: z.ZodOptional<z.ZodString>;
|
|
25
|
+
imageKey: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
26
|
+
caption: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
27
|
+
categoryId: z.ZodOptional<z.ZodString>;
|
|
28
|
+
sortOrder: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
29
|
+
featured: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
30
|
+
published: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
31
|
+
}, z.core.$strip>;
|
|
32
|
+
BatchCreateGallerySchema: z.ZodObject<{
|
|
33
|
+
items: z.ZodArray<z.ZodObject<{
|
|
34
|
+
imageUrl: z.ZodString;
|
|
35
|
+
imageKey: z.ZodOptional<z.ZodString>;
|
|
36
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
37
|
+
categoryId: z.ZodString;
|
|
38
|
+
sortOrder: z.ZodDefault<z.ZodNumber>;
|
|
39
|
+
featured: z.ZodDefault<z.ZodBoolean>;
|
|
40
|
+
published: z.ZodDefault<z.ZodBoolean>;
|
|
41
|
+
}, z.core.$strip>>;
|
|
42
|
+
}, z.core.$strip>;
|
|
43
|
+
BulkUpdateSchema: z.ZodObject<{
|
|
44
|
+
ids: z.ZodArray<z.ZodString>;
|
|
45
|
+
published: z.ZodOptional<z.ZodBoolean>;
|
|
46
|
+
featured: z.ZodOptional<z.ZodBoolean>;
|
|
47
|
+
}, z.core.$strip>;
|
|
48
|
+
CreateCategorySchema: z.ZodObject<{
|
|
49
|
+
slug: z.ZodString;
|
|
50
|
+
labelKo: z.ZodString;
|
|
51
|
+
labelEn: z.ZodOptional<z.ZodString>;
|
|
52
|
+
sortOrder: z.ZodDefault<z.ZodNumber>;
|
|
53
|
+
isActive: z.ZodDefault<z.ZodBoolean>;
|
|
54
|
+
}, z.core.$strip>;
|
|
55
|
+
UpdateCategorySchema: z.ZodObject<{
|
|
56
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
57
|
+
labelKo: z.ZodOptional<z.ZodString>;
|
|
58
|
+
labelEn: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
59
|
+
sortOrder: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
60
|
+
isActive: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
61
|
+
}, z.core.$strip>;
|
|
62
|
+
ReorderCategorySchema: z.ZodObject<{
|
|
63
|
+
ids: z.ZodArray<z.ZodString>;
|
|
64
|
+
}, z.core.$strip>;
|
|
65
|
+
};
|
|
66
|
+
type GallerySchemas = ReturnType<typeof createGallerySchemas>;
|
|
67
|
+
|
|
68
|
+
export { type GallerySchemas, createGallerySchemas };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* spec §9-1 충실 구현.
|
|
5
|
+
*
|
|
6
|
+
* 호스트가 `config.limits.batchMax` 와 `config.limits.captionMaxLength` 를 넘겨
|
|
7
|
+
* 패키지 내부 스키마를 생성한다. categoryId 는 cuid 형식만 검증하고,
|
|
8
|
+
* 실제 카테고리 존재 여부는 service 레이어가 Prisma FK 제약으로 자연스럽게 잡는다.
|
|
9
|
+
*/
|
|
10
|
+
declare function createGallerySchemas(opts: {
|
|
11
|
+
batchMax: number;
|
|
12
|
+
captionMaxLength: number;
|
|
13
|
+
}): {
|
|
14
|
+
CreateGallerySchema: z.ZodObject<{
|
|
15
|
+
imageUrl: z.ZodString;
|
|
16
|
+
imageKey: z.ZodOptional<z.ZodString>;
|
|
17
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
18
|
+
categoryId: z.ZodString;
|
|
19
|
+
sortOrder: z.ZodDefault<z.ZodNumber>;
|
|
20
|
+
featured: z.ZodDefault<z.ZodBoolean>;
|
|
21
|
+
published: z.ZodDefault<z.ZodBoolean>;
|
|
22
|
+
}, z.core.$strip>;
|
|
23
|
+
UpdateGallerySchema: z.ZodObject<{
|
|
24
|
+
imageUrl: z.ZodOptional<z.ZodString>;
|
|
25
|
+
imageKey: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
26
|
+
caption: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
27
|
+
categoryId: z.ZodOptional<z.ZodString>;
|
|
28
|
+
sortOrder: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
29
|
+
featured: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
30
|
+
published: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
31
|
+
}, z.core.$strip>;
|
|
32
|
+
BatchCreateGallerySchema: z.ZodObject<{
|
|
33
|
+
items: z.ZodArray<z.ZodObject<{
|
|
34
|
+
imageUrl: z.ZodString;
|
|
35
|
+
imageKey: z.ZodOptional<z.ZodString>;
|
|
36
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
37
|
+
categoryId: z.ZodString;
|
|
38
|
+
sortOrder: z.ZodDefault<z.ZodNumber>;
|
|
39
|
+
featured: z.ZodDefault<z.ZodBoolean>;
|
|
40
|
+
published: z.ZodDefault<z.ZodBoolean>;
|
|
41
|
+
}, z.core.$strip>>;
|
|
42
|
+
}, z.core.$strip>;
|
|
43
|
+
BulkUpdateSchema: z.ZodObject<{
|
|
44
|
+
ids: z.ZodArray<z.ZodString>;
|
|
45
|
+
published: z.ZodOptional<z.ZodBoolean>;
|
|
46
|
+
featured: z.ZodOptional<z.ZodBoolean>;
|
|
47
|
+
}, z.core.$strip>;
|
|
48
|
+
CreateCategorySchema: z.ZodObject<{
|
|
49
|
+
slug: z.ZodString;
|
|
50
|
+
labelKo: z.ZodString;
|
|
51
|
+
labelEn: z.ZodOptional<z.ZodString>;
|
|
52
|
+
sortOrder: z.ZodDefault<z.ZodNumber>;
|
|
53
|
+
isActive: z.ZodDefault<z.ZodBoolean>;
|
|
54
|
+
}, z.core.$strip>;
|
|
55
|
+
UpdateCategorySchema: z.ZodObject<{
|
|
56
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
57
|
+
labelKo: z.ZodOptional<z.ZodString>;
|
|
58
|
+
labelEn: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
59
|
+
sortOrder: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
60
|
+
isActive: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
61
|
+
}, z.core.$strip>;
|
|
62
|
+
ReorderCategorySchema: z.ZodObject<{
|
|
63
|
+
ids: z.ZodArray<z.ZodString>;
|
|
64
|
+
}, z.core.$strip>;
|
|
65
|
+
};
|
|
66
|
+
type GallerySchemas = ReturnType<typeof createGallerySchemas>;
|
|
67
|
+
|
|
68
|
+
export { type GallerySchemas, createGallerySchemas };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/validators/index.js"],"names":[],"mappings":"AAAA;AACE;AACF,uDAA6B;AAC7B;AACE;AACF,qEAAC","file":"/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-gallery/dist/validators/index.js"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|