@kyro-cms/admin 0.3.2 → 0.3.5

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,374 +0,0 @@
1
- import path from "path";
2
- import { getDatabaseConfig } from "./db";
3
-
4
- /**
5
- * Extract the Public Dev URL ID from either a full URL or just the ID.
6
- * Handles formats like:
7
- * - https://bucket.pub-xxx.r2.dev -> pub-xxx
8
- * - pub-xxx -> pub-xxx
9
- * - empty/undefined -> ""
10
- */
11
- function extractPublicDevUrlId(url?: string): string {
12
- if (!url) return "";
13
- if (url.startsWith("pub-")) return url; // Already just the ID
14
-
15
- // Extract from URL like https://bucket.pub-xxx.r2.dev
16
- const match = url.match(/pub-[a-zA-Z0-9]+/i);
17
- return match ? match[0] : "";
18
- }
19
-
20
- export type StorageProviderType =
21
- | "local"
22
- | "aws"
23
- | "r2"
24
- | "gcs"
25
- | "digitalocean"
26
- | "backblaze"
27
- | "wasabi"
28
- | "bunny"
29
- | "cloudinary"
30
- | "imgix"
31
- | "ftp";
32
-
33
- export interface StorageConfig {
34
- /** The storage provider type */
35
- provider: StorageProviderType;
36
- /** The base URL for accessing files */
37
- baseUrl: string;
38
- /** The filesystem directory (for local storage only) */
39
- uploadDir?: string;
40
- /** Provider-specific config */
41
- config: {
42
- bucket?: string;
43
- region?: string;
44
- accountId?: string;
45
- accessKeyId?: string;
46
- secretAccessKey?: string;
47
- cdnUrl?: string;
48
- prefix?: string;
49
- publicDevUrl?: string;
50
- cloudName?: string;
51
- apiKey?: string;
52
- apiSecret?: string;
53
- folder?: string;
54
- uploadPreset?: string;
55
- domain?: string;
56
- signKey?: string;
57
- host?: string;
58
- port?: number;
59
- user?: string;
60
- password?: string;
61
- secure?: boolean;
62
- storageZone?: string;
63
- projectId?: string;
64
- type?: string;
65
- };
66
- }
67
-
68
- interface RawStorageSettings {
69
- provider?: string;
70
- local?: {
71
- uploadDir?: string;
72
- baseUrl?: string;
73
- };
74
- aws?: Record<string, any>;
75
- r2?: Record<string, any>;
76
- gcs?: Record<string, any>;
77
- digitalocean?: Record<string, any>;
78
- backblaze?: Record<string, any>;
79
- wasabi?: Record<string, any>;
80
- bunny?: Record<string, any>;
81
- cloudinary?: Record<string, any>;
82
- imgix?: Record<string, any>;
83
- ftp?: Record<string, any>;
84
- limits?: Record<string, any>;
85
- [key: string]: any;
86
- }
87
-
88
- /**
89
- * Read the complete storage configuration from the globals table.
90
- */
91
- export async function getStorageConfig(): Promise<StorageConfig> {
92
- const dbConfig = getDatabaseConfig();
93
- let rawSettings: RawStorageSettings = { provider: "local" };
94
-
95
- try {
96
- if (dbConfig.type === "sqlite") {
97
- const Database = (await import("better-sqlite3")).default;
98
- const db = new Database(dbConfig.contentDbPath || "./data/content.db");
99
-
100
- try {
101
- const row = db
102
- .prepare("SELECT data FROM globals WHERE slug = ?")
103
- .get("storage-settings") as { data: string } | undefined;
104
-
105
- if (row) {
106
- rawSettings =
107
- typeof row.data === "string" ? JSON.parse(row.data) : row.data;
108
- }
109
- } finally {
110
- db.close();
111
- }
112
- }
113
- } catch {
114
- // Use defaults if anything fails
115
- }
116
-
117
- const provider = (rawSettings.provider || "local") as StorageProviderType;
118
-
119
- // Build config based on provider type
120
- const config: StorageConfig = {
121
- provider,
122
- baseUrl: "",
123
- config: {},
124
- };
125
-
126
- switch (provider) {
127
- case "local": {
128
- const localConfig = rawSettings.local || {};
129
- const uploadDirRaw = localConfig.uploadDir || "./public/uploads";
130
- const baseUrlRaw = localConfig.baseUrl || "/uploads";
131
-
132
- // Resolve uploadDir
133
- let uploadDir: string;
134
- if (path.isAbsolute(uploadDirRaw)) {
135
- uploadDir = uploadDirRaw;
136
- } else if (uploadDirRaw.includes("/") || uploadDirRaw.includes("\\")) {
137
- uploadDir = path.resolve(process.cwd(), uploadDirRaw);
138
- } else {
139
- uploadDir = path.join(process.cwd(), "public", uploadDirRaw);
140
- }
141
- config.uploadDir = uploadDir;
142
-
143
- // Resolve baseUrl
144
- config.baseUrl = baseUrlRaw.startsWith("/")
145
- ? baseUrlRaw
146
- : `/${baseUrlRaw}`;
147
- if (config.baseUrl.length > 1) {
148
- config.baseUrl = config.baseUrl.replace(/\/+$/, "");
149
- }
150
- break;
151
- }
152
-
153
- case "aws": {
154
- const awsConfig = rawSettings.aws || {};
155
- config.baseUrl =
156
- awsConfig.cdnUrl ||
157
- `https://${awsConfig.bucket}.s3.${awsConfig.region || "us-east-1"}.amazonaws.com`;
158
- config.config = {
159
- bucket: awsConfig.bucket,
160
- region: awsConfig.region || "us-east-1",
161
- accessKeyId: awsConfig.accessKeyId,
162
- secretAccessKey: awsConfig.secretAccessKey,
163
- cdnUrl: awsConfig.cdnUrl,
164
- prefix: awsConfig.prefix,
165
- };
166
- break;
167
- }
168
-
169
- case "r2": {
170
- const r2Config = rawSettings.r2 || {};
171
- // Priority: cdnUrl > publicDevUrl > accountId-based URL
172
- let baseUrl: string;
173
- if (r2Config.cdnUrl) {
174
- baseUrl = r2Config.cdnUrl.replace(/\/$/, "");
175
- } else if (r2Config.publicDevUrl) {
176
- // Handle both full URL and just the ID
177
- const pubId = extractPublicDevUrlId(r2Config.publicDevUrl);
178
- baseUrl = pubId ? `https://${pubId}.r2.dev` : "";
179
- } else if (r2Config.accountId) {
180
- baseUrl = `https://${r2Config.bucket}.${r2Config.accountId}.r2.cloudflarestorage.com`;
181
- } else {
182
- baseUrl = "";
183
- }
184
- config.baseUrl = baseUrl;
185
- config.config = {
186
- bucket: r2Config.bucket,
187
- accountId: r2Config.accountId,
188
- publicDevUrl: extractPublicDevUrlId(r2Config.publicDevUrl),
189
- accessKeyId: r2Config.accessKeyId,
190
- secretAccessKey: r2Config.secretAccessKey,
191
- cdnUrl: r2Config.cdnUrl,
192
- prefix: r2Config.prefix,
193
- };
194
- break;
195
- }
196
-
197
- case "gcs": {
198
- const gcsConfig = rawSettings.gcs || {};
199
- config.baseUrl =
200
- gcsConfig.cdnUrl ||
201
- `https://storage.googleapis.com/${gcsConfig.bucket}`;
202
- config.config = {
203
- bucket: gcsConfig.bucket,
204
- projectId: gcsConfig.projectId,
205
- accessKeyId: gcsConfig.clientEmail,
206
- secretAccessKey: gcsConfig.privateKey,
207
- cdnUrl: gcsConfig.cdnUrl,
208
- prefix: gcsConfig.prefix,
209
- };
210
- break;
211
- }
212
-
213
- case "digitalocean": {
214
- const doConfig = rawSettings.digitalocean || {};
215
- const region = doConfig.region || "nyc3";
216
- config.baseUrl =
217
- doConfig.cdnUrl ||
218
- `https://${doConfig.bucket}.${region}.cdn.digitaloceanspaces.com`;
219
- config.config = {
220
- bucket: doConfig.bucket,
221
- region,
222
- accessKeyId: doConfig.accessKeyId,
223
- secretAccessKey: doConfig.secretAccessKey,
224
- cdnUrl: doConfig.cdnUrl,
225
- prefix: doConfig.prefix,
226
- };
227
- break;
228
- }
229
-
230
- case "backblaze": {
231
- const b2Config = rawSettings.backblaze || {};
232
- config.baseUrl = b2Config.cdnUrl || `https://f000.backblazeb2.com`;
233
- config.config = {
234
- bucket: b2Config.bucket,
235
- accountId: b2Config.accountId,
236
- accessKeyId: b2Config.applicationKeyId,
237
- secretAccessKey: b2Config.applicationKey,
238
- cdnUrl: b2Config.cdnUrl,
239
- prefix: b2Config.prefix,
240
- };
241
- break;
242
- }
243
-
244
- case "wasabi": {
245
- const wasabiConfig = rawSettings.wasabi || {};
246
- const region = wasabiConfig.region || "us-east-1";
247
- config.baseUrl =
248
- wasabiConfig.cdnUrl || `https://s3.${region}.wasabisys.com`;
249
- config.config = {
250
- bucket: wasabiConfig.bucket,
251
- region,
252
- accessKeyId: wasabiConfig.accessKeyId,
253
- secretAccessKey: wasabiConfig.secretAccessKey,
254
- cdnUrl: wasabiConfig.cdnUrl,
255
- prefix: wasabiConfig.prefix,
256
- };
257
- break;
258
- }
259
-
260
- case "bunny": {
261
- const bunnyConfig = rawSettings.bunny || {};
262
- config.baseUrl =
263
- bunnyConfig.cdnUrl || `https://${bunnyConfig.storageZone}.b-cdn.net`;
264
- config.config = {
265
- storageZone: bunnyConfig.storageZone,
266
- apiKey: bunnyConfig.apiKey,
267
- cdnUrl: bunnyConfig.cdnUrl,
268
- prefix: bunnyConfig.prefix,
269
- };
270
- break;
271
- }
272
-
273
- case "cloudinary": {
274
- const cloudinaryConfig = rawSettings.cloudinary || {};
275
- config.baseUrl = `https://res.cloudinary.com/${cloudinaryConfig.cloudName}/image/upload`;
276
- config.config = {
277
- cloudName: cloudinaryConfig.cloudName,
278
- apiKey: cloudinaryConfig.apiKey,
279
- apiSecret: cloudinaryConfig.apiSecret,
280
- folder: cloudinaryConfig.folder,
281
- uploadPreset: cloudinaryConfig.uploadPreset,
282
- };
283
- break;
284
- }
285
-
286
- case "imgix": {
287
- const imgixConfig = rawSettings.imgix || {};
288
- config.baseUrl = `https://${imgixConfig.domain}`;
289
- config.config = {
290
- domain: imgixConfig.domain,
291
- signKey: imgixConfig.signKey,
292
- };
293
- break;
294
- }
295
-
296
- case "ftp": {
297
- const ftpConfig = rawSettings.ftp || {};
298
- config.baseUrl = ftpConfig.baseUrl || "";
299
- config.config = {
300
- host: ftpConfig.host,
301
- port: ftpConfig.port || 22,
302
- user: ftpConfig.user,
303
- password: ftpConfig.password,
304
- secure: ftpConfig.secure,
305
- prefix: ftpConfig.prefix,
306
- type: "sftp",
307
- };
308
- break;
309
- }
310
-
311
- default:
312
- // Fallback to local
313
- config.provider = "local";
314
- config.uploadDir = path.join(process.cwd(), "public", "uploads");
315
- config.baseUrl = "/uploads";
316
- }
317
-
318
- return config;
319
- }
320
-
321
- /**
322
- * Construct a media URL from filename and optional folder using current storage settings.
323
- * For cloud providers, this includes the full base URL. For local, it uses relative path.
324
- */
325
- export async function constructMediaUrl(
326
- filename: string,
327
- folder?: string | null,
328
- ): Promise<string> {
329
- const config = await getStorageConfig();
330
- return constructMediaUrlSync(config.baseUrl, filename, folder);
331
- }
332
-
333
- /**
334
- * Construct a media URL synchronously.
335
- */
336
- export function constructMediaUrlSync(
337
- baseUrl: string,
338
- filename: string,
339
- folder?: string | null,
340
- ): string {
341
- // Cloudinary stores full URL in the database - just return it if baseUrl matches
342
- // The filename for cloud storage already contains folder path
343
- if (baseUrl.startsWith("http") && filename.startsWith("http")) {
344
- // Already a full URL (e.g., from Cloudinary upload response)
345
- return filename;
346
- }
347
-
348
- // Ensure baseUrl has trailing slash for proper URL construction
349
- const normalizedBaseUrl = baseUrl.endsWith("/") ? baseUrl : baseUrl + "/";
350
- const folderPrefix = folder ? `${folder}/` : "";
351
-
352
- // For local/relative URLs
353
- return `${normalizedBaseUrl}${folderPrefix}${filename}`;
354
- }
355
-
356
- /**
357
- * Get provider display name
358
- */
359
- export function getProviderDisplayName(provider: string): string {
360
- const names: Record<string, string> = {
361
- local: "Local Server",
362
- aws: "AWS S3",
363
- r2: "Cloudflare R2",
364
- gcs: "Google Cloud Storage",
365
- digitalocean: "DigitalOcean Spaces",
366
- backblaze: "Backblaze B2",
367
- wasabi: "Wasabi",
368
- bunny: "Bunny.net",
369
- cloudinary: "Cloudinary",
370
- imgix: "Imgix",
371
- ftp: "FTP",
372
- };
373
- return names[provider] || provider;
374
- }
package/src/lib/store.ts DELETED
@@ -1,85 +0,0 @@
1
- import { create } from 'zustand';
2
-
3
- interface EditorState {
4
- editor: any;
5
- setEditor: (editor: any) => void;
6
-
7
- blockDrawerOpen: boolean;
8
- openBlockDrawer: (options?: { targetColumn?: number; targetNodePos?: number }) => void;
9
- closeBlockDrawer: () => void;
10
- toggleBlockDrawer: () => void;
11
-
12
- selectedBlock: string | null;
13
- setSelectedBlock: (block: string | null) => void;
14
-
15
- pendingInsertPos: number | null;
16
- pendingTargetColumn: number | null;
17
- pendingTargetStack: number | null;
18
- pendingTargetGroup: number | null;
19
- pendingTargetCard: number | null;
20
- pendingTargetRepeater: number | null;
21
- setPendingInsert: (pos: number | null, column?: number | null) => void;
22
- clearPendingTargets: () => void;
23
- }
24
-
25
- export const useEditorStore = create<EditorState>((set) => ({
26
- editor: null,
27
- setEditor: (editor) => set({ editor }),
28
-
29
- blockDrawerOpen: false,
30
- openBlockDrawer: (options) => set({
31
- blockDrawerOpen: true,
32
- pendingTargetColumn: options?.targetColumn ?? null
33
- }),
34
- closeBlockDrawer: () => set({
35
- blockDrawerOpen: false,
36
- pendingTargetColumn: null,
37
- pendingInsertPos: null,
38
- pendingTargetStack: null,
39
- pendingTargetGroup: null,
40
- pendingTargetCard: null,
41
- pendingTargetRepeater: null,
42
- }),
43
- toggleBlockDrawer: () => set((state) => ({ blockDrawerOpen: !state.blockDrawerOpen })),
44
-
45
- selectedBlock: null,
46
- setSelectedBlock: (block) => set({ selectedBlock: block }),
47
-
48
- pendingInsertPos: null,
49
- pendingTargetColumn: null,
50
- pendingTargetStack: null,
51
- pendingTargetGroup: null,
52
- pendingTargetCard: null,
53
- pendingTargetRepeater: null,
54
- setPendingInsert: (pos, column) => set({
55
- pendingInsertPos: pos,
56
- pendingTargetColumn: column ?? null
57
- }),
58
- clearPendingTargets: () => set({
59
- pendingTargetColumn: null,
60
- pendingTargetStack: null,
61
- pendingTargetGroup: null,
62
- pendingTargetCard: null,
63
- pendingTargetRepeater: null,
64
- }),
65
- }));
66
-
67
- interface UIState {
68
- sidebarOpen: boolean;
69
- toggleSidebar: () => void;
70
- setSidebarOpen: (open: boolean) => void;
71
-
72
- activeModal: string | null;
73
- openModal: (modal: string) => void;
74
- closeModal: () => void;
75
- }
76
-
77
- export const useUIStore = create<UIState>((set) => ({
78
- sidebarOpen: true,
79
- toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),
80
- setSidebarOpen: (open) => set({ sidebarOpen: open }),
81
-
82
- activeModal: null,
83
- openModal: (modal) => set({ activeModal: modal }),
84
- closeModal: () => set({ activeModal: null }),
85
- }));
package/src/middleware.ts DELETED
@@ -1,177 +0,0 @@
1
- import type { MiddlewareHandler } from "astro";
2
- import jwt from "jsonwebtoken";
3
-
4
- const JWT_SECRET = process.env.JWT_SECRET || "change-me-in-production";
5
-
6
- const PUBLIC_PATHS = [
7
- "/api/auth/login",
8
- "/api/auth/logout",
9
- "/api/auth/register",
10
- "/api/auth/me",
11
- "/api/auth/users",
12
- "/api/auth/refresh",
13
- "/api/users",
14
- "/api/health",
15
- "/api/search",
16
- "/api/upload",
17
- "/api/media",
18
- "/login",
19
- "/register",
20
- "/favicon.svg",
21
- ];
22
-
23
- const PUBLIC_PREFIXES = [
24
- "/api/collections/",
25
- "/api/auth/",
26
- "/api/globals/",
27
- "/api/media/",
28
- ];
29
-
30
- const isApiRequest = (pathname: string): boolean => {
31
- return pathname.startsWith("/api/");
32
- };
33
-
34
- const redirectToLogin = (): Response => {
35
- return new Response(null, {
36
- status: 302,
37
- headers: {
38
- Location: "/login",
39
- },
40
- });
41
- };
42
-
43
- export const onRequest: MiddlewareHandler = async (
44
- { request, url, locals },
45
- next,
46
- ) => {
47
- const pathname = new URL(url).pathname;
48
-
49
- // Helper to extract token from cookie or header
50
- const getToken = (): string | null => {
51
- // Check Authorization header first
52
- const authHeader = request.headers.get("authorization");
53
- if (authHeader?.startsWith("Bearer ")) {
54
- return authHeader.slice(7);
55
- }
56
- // Check cookie
57
- const cookies = request.headers.get("cookie") || "";
58
- const match = cookies.match(/auth_token=([^;]+)/);
59
- return match ? match[1] : null;
60
- };
61
-
62
- const token = getToken();
63
-
64
- // Set user in locals if token is valid
65
- if (token) {
66
- try {
67
- const payload = jwt.verify(token, JWT_SECRET) as jwt.JwtPayload;
68
- locals.user = {
69
- id: payload.sub || "",
70
- email: (payload as any).email || "",
71
- role: (payload as any).role || "guest",
72
- tenantId: (payload as any).tenantId,
73
- };
74
- } catch {
75
- // Token invalid, leave user undefined
76
- }
77
- }
78
-
79
- // Handle root path - redirect to admin for authenticated users
80
- if (pathname === "/") {
81
- if (!token) {
82
- return new Response(null, {
83
- status: 302,
84
- headers: {
85
- Location: "/login",
86
- },
87
- });
88
- }
89
-
90
- // Token exists - redirect to admin dashboard
91
- try {
92
- jwt.verify(token, JWT_SECRET);
93
- return new Response(null, {
94
- status: 302,
95
- headers: {
96
- Location: "/admin",
97
- },
98
- });
99
- } catch {
100
- return new Response(null, {
101
- status: 302,
102
- headers: {
103
- Location: "/login",
104
- },
105
- });
106
- }
107
- }
108
-
109
- // Handle /admin path - main dashboard
110
- if (pathname === "/admin") {
111
- if (!token) {
112
- return new Response(null, {
113
- status: 302,
114
- headers: {
115
- Location: "/login",
116
- },
117
- });
118
- }
119
-
120
- try {
121
- jwt.verify(token, JWT_SECRET);
122
- return next();
123
- } catch {
124
- return new Response(null, {
125
- status: 302,
126
- headers: {
127
- Location: "/login",
128
- },
129
- });
130
- }
131
- }
132
-
133
- if (PUBLIC_PATHS.includes(pathname)) {
134
- return next();
135
- }
136
-
137
- for (const prefix of PUBLIC_PREFIXES) {
138
- if (pathname.startsWith(prefix)) {
139
- return next();
140
- }
141
- }
142
-
143
- if (!token) {
144
- if (!isApiRequest(pathname)) {
145
- return redirectToLogin();
146
- }
147
- return new Response(
148
- JSON.stringify({ error: "Authentication required. Please log in." }),
149
- {
150
- status: 401,
151
- headers: { "Content-Type": "application/json" },
152
- },
153
- );
154
- }
155
-
156
- try {
157
- jwt.verify(token, JWT_SECRET);
158
- return next();
159
- } catch (err) {
160
- const isExpired = err instanceof jwt.TokenExpiredError;
161
- if (!isApiRequest(pathname)) {
162
- return redirectToLogin();
163
- }
164
- return new Response(
165
- JSON.stringify({
166
- error: isExpired
167
- ? "Token expired. Please refresh your session or log in again."
168
- : "Invalid token. Please log in again.",
169
- code: isExpired ? "TOKEN_EXPIRED" : "TOKEN_INVALID",
170
- }),
171
- {
172
- status: 401,
173
- headers: { "Content-Type": "application/json" },
174
- },
175
- );
176
- }
177
- };