@kyro-cms/admin 0.3.1 → 0.3.4

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 (242) hide show
  1. package/dist/EditorClient-XEUOVAAC.js +466 -0
  2. package/dist/EditorClient-XEUOVAAC.js.map +1 -0
  3. package/dist/EditorClient-YLCGVDXY.cjs +468 -0
  4. package/dist/EditorClient-YLCGVDXY.cjs.map +1 -0
  5. package/dist/chunk-7KPIUCGT.js +384 -0
  6. package/dist/chunk-7KPIUCGT.js.map +1 -0
  7. package/dist/chunk-GOACG6R7.cjs +473 -0
  8. package/dist/chunk-GOACG6R7.cjs.map +1 -0
  9. package/dist/index.cjs +14861 -0
  10. package/dist/index.cjs.map +1 -0
  11. package/dist/index.css +1661 -0
  12. package/dist/index.css.map +1 -0
  13. package/dist/index.d.ts +563 -0
  14. package/dist/index.js +14784 -0
  15. package/dist/index.js.map +1 -0
  16. package/package.json +19 -19
  17. package/src/components/ActionBar.tsx +7 -43
  18. package/src/components/Admin.tsx +138 -277
  19. package/src/components/ApiKeysManager.tsx +428 -419
  20. package/src/components/AuditLogsPage.tsx +35 -39
  21. package/src/components/AuthBridge.tsx +51 -0
  22. package/src/components/AutoForm.tsx +495 -1230
  23. package/src/components/BrandingHub.tsx +18 -19
  24. package/src/components/BulkActionsBar.tsx +1 -1
  25. package/src/components/CreateView.tsx +22 -36
  26. package/src/components/Dashboard.tsx +60 -84
  27. package/src/components/DetailView.tsx +113 -91
  28. package/src/components/DeveloperCenter.tsx +200 -198
  29. package/src/components/FieldRenderer.tsx +206 -0
  30. package/src/components/GraphQLPlayground.tsx +340 -480
  31. package/src/components/ListView.tsx +828 -254
  32. package/src/components/LoginPage.tsx +3 -4
  33. package/src/components/MarketplaceManager.tsx +254 -0
  34. package/src/components/MediaGallery.tsx +856 -1192
  35. package/src/components/PluginsManager.tsx +277 -0
  36. package/src/components/RestPlayground.tsx +398 -560
  37. package/src/components/SessionsManager.tsx +211 -0
  38. package/src/components/Sidebar.astro +179 -151
  39. package/src/components/ThemeProvider.tsx +7 -161
  40. package/src/components/UserManagement.tsx +162 -146
  41. package/src/components/UserMenu.tsx +110 -0
  42. package/src/components/WebhookManager.tsx +305 -367
  43. package/src/components/blocks/AccordionBlock.tsx +4 -4
  44. package/src/components/blocks/ArrayBlock.tsx +3 -3
  45. package/src/components/blocks/BlockEditModal.tsx +8 -8
  46. package/src/components/blocks/BlockWrapper.tsx +61 -0
  47. package/src/components/blocks/ButtonBlock.tsx +4 -4
  48. package/src/components/blocks/ChildBlocksTree.tsx +23 -25
  49. package/src/components/blocks/CodeBlock.tsx +15 -15
  50. package/src/components/blocks/ColumnsBlock.tsx +6 -44
  51. package/src/components/blocks/DividerBlock.tsx +3 -3
  52. package/src/components/blocks/FileBlock.tsx +4 -4
  53. package/src/components/blocks/HeadingBlock.tsx +6 -38
  54. package/src/components/blocks/HeroBlock.tsx +4 -4
  55. package/src/components/blocks/ImageBlock.tsx +4 -4
  56. package/src/components/blocks/LinkBlock.tsx +4 -4
  57. package/src/components/blocks/ListBlock.tsx +3 -3
  58. package/src/components/blocks/ParagraphBlock.tsx +12 -42
  59. package/src/components/blocks/RelationshipBlock.tsx +4 -4
  60. package/src/components/blocks/RichTextBlock.tsx +4 -4
  61. package/src/components/blocks/VStackBlock.tsx +5 -37
  62. package/src/components/blocks/VideoBlock.tsx +4 -4
  63. package/src/components/blocks/types.ts +11 -0
  64. package/src/components/fields/AccordionField.tsx +1 -1
  65. package/src/components/fields/ArrayField.tsx +2 -2
  66. package/src/components/fields/ArrayLayout.tsx +93 -0
  67. package/src/components/fields/BlocksField.tsx +122 -111
  68. package/src/components/fields/ButtonField.tsx +1 -1
  69. package/src/components/fields/CheckboxField.tsx +14 -15
  70. package/src/components/fields/ChildrenField.tsx +2 -2
  71. package/src/components/fields/CodeField.tsx +3 -3
  72. package/src/components/fields/ColumnsField.tsx +2 -2
  73. package/src/components/fields/DateField.tsx +13 -26
  74. package/src/components/fields/EditorClient.tsx +26 -28
  75. package/src/components/fields/FieldLayout.tsx +52 -0
  76. package/src/components/fields/GroupLayout.tsx +35 -0
  77. package/src/components/fields/JSONField.tsx +7 -7
  78. package/src/components/fields/LinkField.tsx +1 -1
  79. package/src/components/fields/MarkdownField.tsx +1 -1
  80. package/src/components/fields/NumberField.tsx +13 -26
  81. package/src/components/fields/PortableTextField.tsx +4 -4
  82. package/src/components/fields/PortableTextRenderer.tsx +1 -1
  83. package/src/components/fields/RelationshipBlockField.tsx +31 -23
  84. package/src/components/fields/RelationshipField.tsx +14 -14
  85. package/src/components/fields/SelectField.tsx +17 -26
  86. package/src/components/fields/TabsLayout.tsx +69 -0
  87. package/src/components/fields/TextField.tsx +85 -38
  88. package/src/components/fields/UploadField.tsx +71 -41
  89. package/src/components/fields/VideoField.tsx +1 -1
  90. package/src/components/fields/extensions/blockComponents.tsx +2 -2
  91. package/src/components/fields/extensions/blocksStore.ts +207 -193
  92. package/src/components/fields/types.ts +22 -0
  93. package/src/components/layout/Layout.tsx +1 -1
  94. package/src/components/ui/ActionMenu.tsx +63 -0
  95. package/src/components/ui/Badge.tsx +59 -5
  96. package/src/components/ui/BlockDrawer.tsx +4 -5
  97. package/src/components/ui/CommandPalette.tsx +58 -36
  98. package/src/components/ui/CommandPaletteWrapper.tsx +18 -17
  99. package/src/components/ui/Dropdown.tsx +18 -16
  100. package/src/components/ui/EmptyState.tsx +25 -0
  101. package/src/components/ui/GlobalModal.tsx +49 -0
  102. package/src/components/ui/IconButton.tsx +44 -0
  103. package/src/components/ui/Modal.tsx +19 -20
  104. package/src/components/ui/PageHeader.tsx +158 -0
  105. package/src/components/ui/Pagination.tsx +61 -0
  106. package/src/components/ui/PromptModal.tsx +1 -1
  107. package/src/components/ui/SearchInput.tsx +57 -0
  108. package/src/components/ui/SeoPreview.tsx +31 -0
  109. package/src/components/ui/SessionModal.tsx +0 -0
  110. package/src/components/ui/SlidePanel.tsx +2 -0
  111. package/src/components/ui/Toast.tsx +65 -122
  112. package/src/components/ui/Toaster.tsx +18 -0
  113. package/src/components/ui/icons.tsx +112 -0
  114. package/src/components/users/UserDetail.tsx +290 -0
  115. package/src/components/users/UserForm.tsx +242 -0
  116. package/src/components/users/UsersList.tsx +338 -0
  117. package/src/env.d.ts +13 -13
  118. package/src/fields/index.ts +2 -1
  119. package/src/global.d.ts +7 -0
  120. package/src/hooks/data.ts +2 -9
  121. package/src/hooks/useAsyncData.ts +36 -0
  122. package/src/hooks/useAutoFormState.ts +527 -0
  123. package/src/hooks/useSelection.ts +49 -0
  124. package/src/hooks/useSession.ts +0 -0
  125. package/src/index.ts +11 -1
  126. package/src/integration.ts +86 -11
  127. package/src/kyro-cms.d.ts +209 -0
  128. package/src/layouts/AdminLayout.astro +128 -11
  129. package/src/layouts/AuthLayout.astro +21 -5
  130. package/src/lib/api.ts +175 -55
  131. package/src/lib/autoform-store.ts +435 -0
  132. package/src/lib/config.ts +82 -34
  133. package/src/lib/createRegistry.ts +29 -0
  134. package/src/lib/default-kyro-config.ts +4 -0
  135. package/src/lib/globals.ts +50 -0
  136. package/src/lib/media-utils.ts +18 -0
  137. package/src/lib/object-utils.ts +77 -0
  138. package/src/lib/paths.ts +61 -0
  139. package/src/lib/stores/index.ts +370 -0
  140. package/src/lib/types.ts +43 -0
  141. package/src/lib/useResourceManager.ts +105 -0
  142. package/src/pages/403.astro +67 -0
  143. package/src/pages/[collection]/[id].astro +14 -180
  144. package/src/pages/[collection]/index.astro +11 -6
  145. package/src/pages/api-explorer.astro +173 -0
  146. package/src/pages/audit/index.astro +2 -0
  147. package/src/pages/auth/login.astro +122 -0
  148. package/src/pages/auth/register.astro +167 -0
  149. package/src/pages/graphql-explorer.astro +59 -0
  150. package/src/pages/{admin/graphql.astro → graphql.astro} +51 -17
  151. package/src/pages/index.astro +577 -0
  152. package/src/pages/index_ALT.astro +3 -0
  153. package/src/pages/keys.astro +11 -0
  154. package/src/pages/marketplace.astro +11 -0
  155. package/src/pages/media.astro +3 -0
  156. package/src/pages/plugins.astro +8 -0
  157. package/src/pages/preview/[collection]/[id].astro +188 -123
  158. package/src/pages/rest-playground.astro +62 -0
  159. package/src/pages/roles/index.astro +183 -76
  160. package/src/pages/sessions.astro +8 -0
  161. package/src/pages/settings/[slug].astro +92 -114
  162. package/src/pages/settings/index.astro +5 -3
  163. package/src/pages/users/[id].astro +25 -154
  164. package/src/pages/users/index.astro +19 -130
  165. package/src/pages/users/new.astro +9 -86
  166. package/src/pages/webhooks.astro +11 -0
  167. package/src/routes.ts +80 -0
  168. package/src/styles/main.css +119 -79
  169. package/src/theme/tokens.ts +1 -0
  170. package/src/vite-env.d.ts +14 -0
  171. package/src/collections/auth/index.ts +0 -155
  172. package/src/collections/portfolio/index.ts +0 -343
  173. package/src/components/ApiExplorer.tsx +0 -325
  174. package/src/components/EnhancedListView.tsx +0 -889
  175. package/src/components/GraphQLExplorer.tsx +0 -675
  176. package/src/components/Icons.tsx +0 -23
  177. package/src/components/StatusBadge.tsx +0 -76
  178. package/src/lib/MediaService.ts +0 -541
  179. package/src/lib/auth/sqlite-adapter.ts +0 -319
  180. package/src/lib/dataStore.ts +0 -226
  181. package/src/lib/db/adapter.ts +0 -54
  182. package/src/lib/db/drizzle-mysql-adapter.ts +0 -194
  183. package/src/lib/db/drizzle-mysql-auth-adapter.ts +0 -327
  184. package/src/lib/db/drizzle-postgres-adapter.ts +0 -202
  185. package/src/lib/db/drizzle-postgres-auth-adapter.ts +0 -304
  186. package/src/lib/db/drizzle-sqlite-adapter.ts +0 -227
  187. package/src/lib/db/drizzle-sqlite-auth-adapter.ts +0 -548
  188. package/src/lib/db/index.ts +0 -449
  189. package/src/lib/db/mongodb-adapter.ts +0 -207
  190. package/src/lib/db/mongodb-auth-adapter.ts +0 -305
  191. package/src/lib/db/schema/mysql-auth.ts +0 -113
  192. package/src/lib/db/schema/mysql-content.ts +0 -20
  193. package/src/lib/db/schema/postgres-auth.ts +0 -116
  194. package/src/lib/db/schema/postgres-content.ts +0 -35
  195. package/src/lib/db/schema/postgres-media.ts +0 -52
  196. package/src/lib/db/schema/postgres-settings.ts +0 -11
  197. package/src/lib/db/schema/sqlite-auth.ts +0 -112
  198. package/src/lib/db/schema/sqlite-content.ts +0 -20
  199. package/src/lib/db/version-adapter.ts +0 -248
  200. package/src/lib/graphql/index.ts +0 -1
  201. package/src/lib/graphql/schema.ts +0 -443
  202. package/src/lib/rate-limit.ts +0 -267
  203. package/src/lib/storage.ts +0 -374
  204. package/src/lib/store.ts +0 -85
  205. package/src/middleware.ts +0 -177
  206. package/src/pages/admin/api-explorer.astro +0 -98
  207. package/src/pages/admin/graphql-explorer.astro +0 -40
  208. package/src/pages/admin/index.astro +0 -286
  209. package/src/pages/admin/keys.astro +0 -8
  210. package/src/pages/admin/rest-playground.astro +0 -44
  211. package/src/pages/admin/webhooks.astro +0 -8
  212. package/src/pages/api/[collection]/[id]/publish.ts +0 -52
  213. package/src/pages/api/[collection]/[id]/unpublish.ts +0 -42
  214. package/src/pages/api/[collection]/[id]/versions.ts +0 -66
  215. package/src/pages/api/[collection]/[id].ts +0 -213
  216. package/src/pages/api/[collection]/index.ts +0 -209
  217. package/src/pages/api/auth/[id].ts +0 -121
  218. package/src/pages/api/auth/audit-logs.ts +0 -57
  219. package/src/pages/api/auth/login.ts +0 -211
  220. package/src/pages/api/auth/logout.ts +0 -66
  221. package/src/pages/api/auth/me.ts +0 -36
  222. package/src/pages/api/auth/refresh.ts +0 -119
  223. package/src/pages/api/auth/register.ts +0 -188
  224. package/src/pages/api/auth/users.ts +0 -97
  225. package/src/pages/api/collections.ts +0 -59
  226. package/src/pages/api/globals/[slug].ts +0 -42
  227. package/src/pages/api/graphql.ts +0 -90
  228. package/src/pages/api/health.ts +0 -426
  229. package/src/pages/api/keys/[id].ts +0 -26
  230. package/src/pages/api/keys/index.ts +0 -75
  231. package/src/pages/api/media/[id].ts +0 -309
  232. package/src/pages/api/media/folders.ts +0 -609
  233. package/src/pages/api/media/index.ts +0 -146
  234. package/src/pages/api/media/resize.ts +0 -267
  235. package/src/pages/api/search.ts +0 -82
  236. package/src/pages/api/slug-availability.ts +0 -70
  237. package/src/pages/api/storage-config.ts +0 -20
  238. package/src/pages/api/storage-status.ts +0 -206
  239. package/src/pages/api/upload.ts +0 -334
  240. package/src/pages/api/webhooks/index.ts +0 -71
  241. package/src/pages/login.astro +0 -82
  242. package/src/pages/register.astro +0 -102
@@ -1,26 +0,0 @@
1
- import type { APIRoute } from "astro";
2
- import { dataStore } from "../../../lib/dataStore";
3
-
4
- export const DELETE: APIRoute = async ({ params }) => {
5
- try {
6
- const { id } = params;
7
- if (!id) {
8
- return new Response(JSON.stringify({ error: "ID is required" }), {
9
- status: 400,
10
- headers: { "Content-Type": "application/json" },
11
- });
12
- }
13
-
14
- await dataStore.delete("_api_keys", id);
15
-
16
- return new Response(JSON.stringify({ success: true }), {
17
- status: 200,
18
- headers: { "Content-Type": "application/json" },
19
- });
20
- } catch (error: any) {
21
- return new Response(JSON.stringify({ error: error.message }), {
22
- status: 500,
23
- headers: { "Content-Type": "application/json" },
24
- });
25
- }
26
- };
@@ -1,75 +0,0 @@
1
- import type { APIRoute } from "astro";
2
- import { dataStore } from "../../../lib/dataStore";
3
-
4
- export const GET: APIRoute = async () => {
5
- try {
6
- const keys = await dataStore.find("_api_keys", { limit: 100, page: 1 });
7
-
8
- const docs = keys.docs.map((k: any) => ({
9
- id: k.id,
10
- name: k.name,
11
- key: k.key,
12
- keyPrefix: k.key?.substring(0, 8) || "",
13
- createdAt: k.createdAt,
14
- lastUsedAt: k.lastUsedAt,
15
- }));
16
-
17
- return new Response(JSON.stringify(docs), {
18
- status: 200,
19
- headers: { "Content-Type": "application/json" },
20
- });
21
- } catch (error: any) {
22
- return new Response(JSON.stringify({ error: error.message }), {
23
- status: 500,
24
- headers: { "Content-Type": "application/json" },
25
- });
26
- }
27
- };
28
-
29
- export const POST: APIRoute = async ({ request }) => {
30
- try {
31
- const body = await request.json();
32
- const { name } = body;
33
-
34
- if (!name) {
35
- return new Response(JSON.stringify({ error: "Name is required" }), {
36
- status: 400,
37
- headers: { "Content-Type": "application/json" },
38
- });
39
- }
40
-
41
- // Generate API key
42
- const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
43
- let suffix = "";
44
- for (let i = 0; i < 28; i++) {
45
- suffix += chars[Math.floor(Math.random() * chars.length)];
46
- }
47
- const key = `kyro_${suffix}`;
48
-
49
- const now = new Date().toISOString();
50
- const doc = await dataStore.create("_api_keys", {
51
- name,
52
- key,
53
- keyPrefix: key.substring(0, 8),
54
- createdAt: now,
55
- });
56
-
57
- return new Response(
58
- JSON.stringify({
59
- id: doc.id,
60
- name: doc.name,
61
- key: doc.key,
62
- createdAt: doc.createdAt,
63
- }),
64
- {
65
- status: 201,
66
- headers: { "Content-Type": "application/json" },
67
- },
68
- );
69
- } catch (error: any) {
70
- return new Response(JSON.stringify({ error: error.message }), {
71
- status: 500,
72
- headers: { "Content-Type": "application/json" },
73
- });
74
- }
75
- };
@@ -1,309 +0,0 @@
1
- import type { APIRoute } from "astro";
2
- import { getMediaService } from "../../../lib/MediaService";
3
- import { MediaService } from "@kyro-cms/core";
4
- import { getStorageConfig, constructMediaUrl } from "../../../lib/storage";
5
- import { getDatabaseConfig } from "../../../lib/db";
6
- import path from "path";
7
-
8
- export const GET: APIRoute = async ({ params }) => {
9
- try {
10
- const mediaService = await getMediaService();
11
- const { id } = params;
12
-
13
- if (!id) {
14
- return new Response(JSON.stringify({ error: "No file ID provided" }), {
15
- status: 400,
16
- headers: { "Content-Type": "application/json" },
17
- });
18
- }
19
-
20
- const doc = await mediaService.findById(id);
21
- if (!doc) {
22
- return new Response(JSON.stringify({ error: "Media not found" }), {
23
- status: 404,
24
- headers: { "Content-Type": "application/json" },
25
- });
26
- }
27
-
28
- // Compute URLs dynamically from current storage settings
29
- const storageConfig = await getStorageConfig();
30
- const isLocalStorage = storageConfig.provider === "local";
31
- const isCloudStorage = !isLocalStorage;
32
-
33
- // For cloud storage, use stored URL from DB; for local, construct from config
34
- const mediaUrl = isCloudStorage
35
- ? doc.url || (await constructMediaUrl(doc.filename, null))
36
- : await constructMediaUrl(doc.filename, doc.folder);
37
-
38
- // For local storage use resize API, for cloud use direct URL
39
- let thumbnailUrl: string | undefined;
40
- if (doc.mimeType?.startsWith("image/")) {
41
- if (isLocalStorage) {
42
- thumbnailUrl = `/api/media/resize?url=${encodeURIComponent(mediaUrl)}&w=400&h=400`;
43
- } else {
44
- thumbnailUrl = mediaUrl;
45
- }
46
- }
47
-
48
- return new Response(
49
- JSON.stringify({
50
- id: doc.id,
51
- title: doc.title || doc.filename,
52
- filename: doc.filename,
53
- originalName: doc.originalName,
54
- url: mediaUrl,
55
- thumbnailUrl,
56
- type: doc.mimeType?.split("/")[0] || "other",
57
- mimeType: doc.mimeType,
58
- fileSize: doc.fileSize,
59
- width: doc.width,
60
- height: doc.height,
61
- folder: doc.folder,
62
- alt: doc.alt,
63
- caption: doc.caption,
64
- createdAt: doc.createdAt,
65
- updatedAt: doc.updatedAt,
66
- }),
67
- {
68
- status: 200,
69
- headers: { "Content-Type": "application/json" },
70
- },
71
- );
72
- } catch (error) {
73
- console.error("Get media error:", error);
74
- return new Response(JSON.stringify({ error: "Failed to fetch media" }), {
75
- status: 500,
76
- headers: { "Content-Type": "application/json" },
77
- });
78
- }
79
- };
80
-
81
- export const PATCH: APIRoute = async ({ params, request }) => {
82
- try {
83
- const mediaService = await getMediaService();
84
- const { id } = params;
85
-
86
- if (!id) {
87
- return new Response(JSON.stringify({ error: "No file ID provided" }), {
88
- status: 400,
89
- headers: { "Content-Type": "application/json" },
90
- });
91
- }
92
-
93
- const body = await request.json();
94
-
95
- // Fetch current record to compare
96
- const current = await mediaService.findById(id);
97
- if (!current) {
98
- return new Response(JSON.stringify({ error: "Media not found" }), {
99
- status: 404,
100
- headers: { "Content-Type": "application/json" },
101
- });
102
- }
103
-
104
- const updateData: any = {
105
- alt: body.alt,
106
- caption: body.caption,
107
- title: body.title,
108
- };
109
-
110
- // If the title changed, rename the physical file and update url/filename
111
- const newTitle: string | undefined = body.title;
112
- if (
113
- newTitle &&
114
- newTitle !== current.title &&
115
- newTitle !== current.filename
116
- ) {
117
- const storageConfig = await getStorageConfig();
118
- const isLocalStorage = storageConfig.provider === "local";
119
-
120
- // Preserve original extension, apply new name
121
- const ext = path.extname(current.filename);
122
- const sanitized = newTitle
123
- .trim()
124
- .toLowerCase()
125
- .replace(/[^a-z0-9._-]/g, "_")
126
- .replace(/_+/g, "_");
127
- const newFilename = sanitized.endsWith(ext)
128
- ? sanitized
129
- : `${sanitized}${ext}`;
130
-
131
- const folder = current.folder ? `${current.folder}/` : "";
132
- const newKey = `${folder}${newFilename}`;
133
-
134
- if (isLocalStorage) {
135
- const uploadDir = storageConfig.uploadDir || "./public/uploads";
136
- const baseUrl = storageConfig.baseUrl || "/uploads";
137
- const folderPath = folder.replace("/", "");
138
-
139
- const oldPhysical = path.join(uploadDir, folderPath, current.filename);
140
- const newPhysical = path.join(uploadDir, folderPath, newFilename);
141
-
142
- const fs = await import("fs/promises");
143
- try {
144
- await fs.rename(oldPhysical, newPhysical);
145
- } catch (e: any) {
146
- console.warn("[rename] Could not rename physical file:", e.message);
147
- }
148
-
149
- const normalizedBase = baseUrl.endsWith("/")
150
- ? baseUrl.slice(0, -1)
151
- : baseUrl;
152
- updateData.url = `${normalizedBase}/${folder}${newFilename}`;
153
- } else {
154
- // Cloud storage - use MediaService rename
155
- try {
156
- const dbConfig = getDatabaseConfig();
157
- let db: any = null;
158
-
159
- if (dbConfig.type === "sqlite") {
160
- const Database = (await import("better-sqlite3")).default;
161
- db = new Database(dbConfig.contentDbPath || "./data/content.db");
162
- } else if (dbConfig.type === "postgres") {
163
- const { Pool } = await import("pg");
164
- db = new Pool({ connectionString: dbConfig.connectionString });
165
- } else if (dbConfig.type === "mysql") {
166
- const mysql = await import("mysql2/promise");
167
- db = await mysql.createPool({ uri: dbConfig.connectionString });
168
- }
169
-
170
- // Transform storageConfig to have 'type' field (for resolveProviderWithConfig)
171
- const cloudStorageConfig = {
172
- type: storageConfig.provider,
173
- [storageConfig.provider]: storageConfig.config,
174
- };
175
-
176
- const coreMediaService = await MediaService.init(db, {
177
- dialect: dbConfig.type as any,
178
- storageConfig: cloudStorageConfig,
179
- });
180
-
181
- const renamed = await coreMediaService.rename(id, newKey);
182
- if (renamed) {
183
- updateData.url = renamed.url;
184
- updateData.filename = renamed.filename;
185
- if (renamed.thumbnailUrl) {
186
- updateData.thumbnailUrl = renamed.thumbnailUrl;
187
- }
188
- }
189
-
190
- if (db && dbConfig.type === "sqlite") db.close();
191
- } catch (e: any) {
192
- console.error(
193
- "[rename] Cloud storage rename error:",
194
- e.message,
195
- e.stack,
196
- );
197
- // Continue anyway - still update DB metadata
198
- }
199
- }
200
-
201
- if (!updateData.filename) {
202
- updateData.filename = newFilename;
203
- }
204
- if (!updateData.url) {
205
- const { baseUrl } = await getStorageConfig();
206
- const normalizedBase = baseUrl.endsWith("/")
207
- ? baseUrl.slice(0, -1)
208
- : baseUrl;
209
- updateData.url = `${normalizedBase}/${folder}${newFilename}`;
210
- }
211
- }
212
-
213
- const updated = await mediaService.update(id, updateData);
214
-
215
- return new Response(JSON.stringify({ success: true, media: updated }), {
216
- status: 200,
217
- headers: { "Content-Type": "application/json" },
218
- });
219
- } catch (error) {
220
- console.error("Patch media error:", error);
221
- return new Response(
222
- JSON.stringify({ error: "Failed to update metadata" }),
223
- {
224
- status: 500,
225
- headers: { "Content-Type": "application/json" },
226
- },
227
- );
228
- }
229
- };
230
-
231
- export const DELETE: APIRoute = async ({ params }) => {
232
- try {
233
- const mediaService = await getMediaService();
234
- const { id } = params;
235
-
236
- if (!id) {
237
- return new Response(JSON.stringify({ error: "No file ID provided" }), {
238
- status: 400,
239
- headers: { "Content-Type": "application/json" },
240
- });
241
- }
242
-
243
- // Get the media item first to delete from storage
244
- const item = await mediaService.findById(id);
245
- if (!item) {
246
- return new Response(JSON.stringify({ error: "Media not found" }), {
247
- status: 404,
248
- headers: { "Content-Type": "application/json" },
249
- });
250
- }
251
-
252
- // Delete from cloud storage if not local
253
- const storageConfig = await getStorageConfig();
254
- if (storageConfig.provider !== "local") {
255
- try {
256
- const dbConfig = getDatabaseConfig();
257
- let db: any = null;
258
-
259
- if (dbConfig.type === "sqlite") {
260
- const Database = (await import("better-sqlite3")).default;
261
- db = new Database(dbConfig.contentDbPath || "./data/content.db");
262
- } else if (dbConfig.type === "postgres") {
263
- const { Pool } = await import("pg");
264
- db = new Pool({ connectionString: dbConfig.connectionString });
265
- } else if (dbConfig.type === "mysql") {
266
- const mysql = await import("mysql2/promise");
267
- db = await mysql.createPool({ uri: dbConfig.connectionString });
268
- }
269
-
270
- // Transform storageConfig to have 'type' field
271
- const cloudStorageConfig = {
272
- type: storageConfig.provider,
273
- [storageConfig.provider]: storageConfig.config,
274
- };
275
-
276
- const coreMediaService = await MediaService.init(db, {
277
- dialect: dbConfig.type as any,
278
- storageConfig: cloudStorageConfig,
279
- });
280
-
281
- // Delete the file from storage
282
- await coreMediaService.deleteFile(item.url);
283
- if (item.thumbnailUrl) {
284
- try {
285
- await coreMediaService.deleteFile(item.thumbnailUrl);
286
- } catch {}
287
- }
288
-
289
- if (db && dbConfig.type === "sqlite") db.close();
290
- } catch (e: any) {
291
- console.error("[delete] Cloud storage delete error:", e.message);
292
- }
293
- }
294
-
295
- // Delete from database
296
- await mediaService.delete(id);
297
-
298
- return new Response(JSON.stringify({ success: true }), {
299
- status: 200,
300
- headers: { "Content-Type": "application/json" },
301
- });
302
- } catch (error) {
303
- console.error("Delete media error:", error);
304
- return new Response(JSON.stringify({ error: "Failed to delete file" }), {
305
- status: 500,
306
- headers: { "Content-Type": "application/json" },
307
- });
308
- }
309
- };