@kyro-cms/admin 0.3.2 → 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.
- package/dist/EditorClient-XEUOVAAC.js +466 -0
- package/dist/EditorClient-XEUOVAAC.js.map +1 -0
- package/dist/EditorClient-YLCGVDXY.cjs +468 -0
- package/dist/EditorClient-YLCGVDXY.cjs.map +1 -0
- package/dist/chunk-7KPIUCGT.js +384 -0
- package/dist/chunk-7KPIUCGT.js.map +1 -0
- package/dist/chunk-GOACG6R7.cjs +473 -0
- package/dist/chunk-GOACG6R7.cjs.map +1 -0
- package/dist/index.cjs +14861 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +1661 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +563 -0
- package/dist/index.js +14784 -0
- package/dist/index.js.map +1 -0
- package/package.json +19 -19
- package/src/components/ActionBar.tsx +7 -43
- package/src/components/Admin.tsx +138 -277
- package/src/components/ApiKeysManager.tsx +428 -419
- package/src/components/AuditLogsPage.tsx +35 -39
- package/src/components/AuthBridge.tsx +51 -0
- package/src/components/AutoForm.tsx +495 -1230
- package/src/components/BrandingHub.tsx +18 -19
- package/src/components/BulkActionsBar.tsx +1 -1
- package/src/components/CreateView.tsx +22 -36
- package/src/components/Dashboard.tsx +60 -84
- package/src/components/DetailView.tsx +113 -91
- package/src/components/DeveloperCenter.tsx +200 -198
- package/src/components/FieldRenderer.tsx +206 -0
- package/src/components/GraphQLPlayground.tsx +340 -480
- package/src/components/ListView.tsx +828 -254
- package/src/components/LoginPage.tsx +3 -4
- package/src/components/MarketplaceManager.tsx +254 -0
- package/src/components/MediaGallery.tsx +856 -1192
- package/src/components/PluginsManager.tsx +277 -0
- package/src/components/RestPlayground.tsx +398 -560
- package/src/components/SessionsManager.tsx +211 -0
- package/src/components/Sidebar.astro +179 -151
- package/src/components/ThemeProvider.tsx +7 -161
- package/src/components/UserManagement.tsx +162 -146
- package/src/components/UserMenu.tsx +110 -0
- package/src/components/WebhookManager.tsx +305 -367
- package/src/components/blocks/AccordionBlock.tsx +4 -4
- package/src/components/blocks/ArrayBlock.tsx +3 -3
- package/src/components/blocks/BlockEditModal.tsx +8 -8
- package/src/components/blocks/BlockWrapper.tsx +61 -0
- package/src/components/blocks/ButtonBlock.tsx +4 -4
- package/src/components/blocks/ChildBlocksTree.tsx +23 -25
- package/src/components/blocks/CodeBlock.tsx +15 -15
- package/src/components/blocks/ColumnsBlock.tsx +6 -44
- package/src/components/blocks/DividerBlock.tsx +3 -3
- package/src/components/blocks/FileBlock.tsx +4 -4
- package/src/components/blocks/HeadingBlock.tsx +6 -38
- package/src/components/blocks/HeroBlock.tsx +4 -4
- package/src/components/blocks/ImageBlock.tsx +4 -4
- package/src/components/blocks/LinkBlock.tsx +4 -4
- package/src/components/blocks/ListBlock.tsx +3 -3
- package/src/components/blocks/ParagraphBlock.tsx +12 -42
- package/src/components/blocks/RelationshipBlock.tsx +4 -4
- package/src/components/blocks/RichTextBlock.tsx +4 -4
- package/src/components/blocks/VStackBlock.tsx +5 -37
- package/src/components/blocks/VideoBlock.tsx +4 -4
- package/src/components/blocks/types.ts +11 -0
- package/src/components/fields/AccordionField.tsx +1 -1
- package/src/components/fields/ArrayField.tsx +2 -2
- package/src/components/fields/ArrayLayout.tsx +93 -0
- package/src/components/fields/BlocksField.tsx +122 -111
- package/src/components/fields/ButtonField.tsx +1 -1
- package/src/components/fields/CheckboxField.tsx +14 -15
- package/src/components/fields/ChildrenField.tsx +2 -2
- package/src/components/fields/CodeField.tsx +3 -3
- package/src/components/fields/ColumnsField.tsx +2 -2
- package/src/components/fields/DateField.tsx +13 -26
- package/src/components/fields/EditorClient.tsx +26 -28
- package/src/components/fields/FieldLayout.tsx +52 -0
- package/src/components/fields/GroupLayout.tsx +35 -0
- package/src/components/fields/JSONField.tsx +7 -7
- package/src/components/fields/LinkField.tsx +1 -1
- package/src/components/fields/MarkdownField.tsx +1 -1
- package/src/components/fields/NumberField.tsx +13 -26
- package/src/components/fields/PortableTextField.tsx +4 -4
- package/src/components/fields/PortableTextRenderer.tsx +1 -1
- package/src/components/fields/RelationshipBlockField.tsx +31 -23
- package/src/components/fields/RelationshipField.tsx +14 -14
- package/src/components/fields/SelectField.tsx +17 -26
- package/src/components/fields/TabsLayout.tsx +69 -0
- package/src/components/fields/TextField.tsx +85 -38
- package/src/components/fields/UploadField.tsx +71 -41
- package/src/components/fields/VideoField.tsx +1 -1
- package/src/components/fields/extensions/blockComponents.tsx +2 -2
- package/src/components/fields/extensions/blocksStore.ts +207 -193
- package/src/components/fields/types.ts +22 -0
- package/src/components/layout/Layout.tsx +1 -1
- package/src/components/ui/ActionMenu.tsx +63 -0
- package/src/components/ui/Badge.tsx +59 -5
- package/src/components/ui/BlockDrawer.tsx +4 -5
- package/src/components/ui/CommandPalette.tsx +58 -36
- package/src/components/ui/CommandPaletteWrapper.tsx +18 -17
- package/src/components/ui/Dropdown.tsx +18 -16
- package/src/components/ui/EmptyState.tsx +25 -0
- package/src/components/ui/GlobalModal.tsx +49 -0
- package/src/components/ui/IconButton.tsx +44 -0
- package/src/components/ui/Modal.tsx +19 -20
- package/src/components/ui/PageHeader.tsx +158 -0
- package/src/components/ui/Pagination.tsx +61 -0
- package/src/components/ui/PromptModal.tsx +1 -1
- package/src/components/ui/SearchInput.tsx +57 -0
- package/src/components/ui/SeoPreview.tsx +31 -0
- package/src/components/ui/SessionModal.tsx +0 -0
- package/src/components/ui/SlidePanel.tsx +2 -0
- package/src/components/ui/Toast.tsx +65 -122
- package/src/components/ui/Toaster.tsx +18 -0
- package/src/components/ui/icons.tsx +112 -0
- package/src/components/users/UserDetail.tsx +290 -0
- package/src/components/users/UserForm.tsx +242 -0
- package/src/components/users/UsersList.tsx +338 -0
- package/src/env.d.ts +13 -13
- package/src/fields/index.ts +2 -1
- package/src/global.d.ts +7 -0
- package/src/hooks/data.ts +2 -9
- package/src/hooks/useAsyncData.ts +36 -0
- package/src/hooks/useAutoFormState.ts +527 -0
- package/src/hooks/useSelection.ts +49 -0
- package/src/hooks/useSession.ts +0 -0
- package/src/index.ts +11 -1
- package/src/integration.ts +86 -11
- package/src/kyro-cms.d.ts +209 -0
- package/src/layouts/AdminLayout.astro +128 -11
- package/src/layouts/AuthLayout.astro +21 -5
- package/src/lib/api.ts +175 -55
- package/src/lib/autoform-store.ts +435 -0
- package/src/lib/config.ts +82 -34
- package/src/lib/createRegistry.ts +29 -0
- package/src/lib/default-kyro-config.ts +4 -0
- package/src/lib/globals.ts +50 -0
- package/src/lib/media-utils.ts +18 -0
- package/src/lib/object-utils.ts +77 -0
- package/src/lib/paths.ts +61 -0
- package/src/lib/stores/index.ts +370 -0
- package/src/lib/types.ts +43 -0
- package/src/lib/useResourceManager.ts +105 -0
- package/src/pages/403.astro +67 -0
- package/src/pages/[collection]/[id].astro +14 -180
- package/src/pages/[collection]/index.astro +11 -6
- package/src/pages/api-explorer.astro +173 -0
- package/src/pages/audit/index.astro +2 -0
- package/src/pages/auth/login.astro +122 -0
- package/src/pages/auth/register.astro +167 -0
- package/src/pages/graphql-explorer.astro +59 -0
- package/src/pages/{admin/graphql.astro → graphql.astro} +51 -17
- package/src/pages/index.astro +577 -0
- package/src/pages/index_ALT.astro +3 -0
- package/src/pages/keys.astro +11 -0
- package/src/pages/marketplace.astro +11 -0
- package/src/pages/media.astro +3 -0
- package/src/pages/plugins.astro +8 -0
- package/src/pages/preview/[collection]/[id].astro +188 -123
- package/src/pages/rest-playground.astro +62 -0
- package/src/pages/roles/index.astro +183 -76
- package/src/pages/sessions.astro +8 -0
- package/src/pages/settings/[slug].astro +92 -114
- package/src/pages/settings/index.astro +5 -3
- package/src/pages/users/[id].astro +25 -154
- package/src/pages/users/index.astro +19 -130
- package/src/pages/users/new.astro +9 -86
- package/src/pages/webhooks.astro +11 -0
- package/src/routes.ts +80 -0
- package/src/styles/main.css +119 -79
- package/src/theme/tokens.ts +1 -0
- package/src/vite-env.d.ts +14 -0
- package/src/collections/auth/index.ts +0 -155
- package/src/collections/portfolio/index.ts +0 -343
- package/src/components/ApiExplorer.tsx +0 -325
- package/src/components/EnhancedListView.tsx +0 -889
- package/src/components/GraphQLExplorer.tsx +0 -675
- package/src/components/Icons.tsx +0 -23
- package/src/components/StatusBadge.tsx +0 -76
- package/src/lib/MediaService.ts +0 -541
- package/src/lib/auth/sqlite-adapter.ts +0 -319
- package/src/lib/dataStore.ts +0 -226
- package/src/lib/db/adapter.ts +0 -54
- package/src/lib/db/drizzle-mysql-adapter.ts +0 -194
- package/src/lib/db/drizzle-mysql-auth-adapter.ts +0 -327
- package/src/lib/db/drizzle-postgres-adapter.ts +0 -202
- package/src/lib/db/drizzle-postgres-auth-adapter.ts +0 -304
- package/src/lib/db/drizzle-sqlite-adapter.ts +0 -227
- package/src/lib/db/drizzle-sqlite-auth-adapter.ts +0 -548
- package/src/lib/db/index.ts +0 -449
- package/src/lib/db/mongodb-adapter.ts +0 -207
- package/src/lib/db/mongodb-auth-adapter.ts +0 -305
- package/src/lib/db/schema/mysql-auth.ts +0 -113
- package/src/lib/db/schema/mysql-content.ts +0 -20
- package/src/lib/db/schema/postgres-auth.ts +0 -116
- package/src/lib/db/schema/postgres-content.ts +0 -35
- package/src/lib/db/schema/postgres-media.ts +0 -52
- package/src/lib/db/schema/postgres-settings.ts +0 -11
- package/src/lib/db/schema/sqlite-auth.ts +0 -112
- package/src/lib/db/schema/sqlite-content.ts +0 -20
- package/src/lib/db/version-adapter.ts +0 -248
- package/src/lib/graphql/index.ts +0 -1
- package/src/lib/graphql/schema.ts +0 -443
- package/src/lib/rate-limit.ts +0 -267
- package/src/lib/storage.ts +0 -374
- package/src/lib/store.ts +0 -85
- package/src/middleware.ts +0 -177
- package/src/pages/admin/api-explorer.astro +0 -98
- package/src/pages/admin/graphql-explorer.astro +0 -40
- package/src/pages/admin/index.astro +0 -286
- package/src/pages/admin/keys.astro +0 -8
- package/src/pages/admin/rest-playground.astro +0 -44
- package/src/pages/admin/webhooks.astro +0 -8
- package/src/pages/api/[collection]/[id]/publish.ts +0 -52
- package/src/pages/api/[collection]/[id]/unpublish.ts +0 -42
- package/src/pages/api/[collection]/[id]/versions.ts +0 -66
- package/src/pages/api/[collection]/[id].ts +0 -213
- package/src/pages/api/[collection]/index.ts +0 -209
- package/src/pages/api/auth/[id].ts +0 -121
- package/src/pages/api/auth/audit-logs.ts +0 -57
- package/src/pages/api/auth/login.ts +0 -211
- package/src/pages/api/auth/logout.ts +0 -66
- package/src/pages/api/auth/me.ts +0 -36
- package/src/pages/api/auth/refresh.ts +0 -119
- package/src/pages/api/auth/register.ts +0 -188
- package/src/pages/api/auth/users.ts +0 -97
- package/src/pages/api/collections.ts +0 -59
- package/src/pages/api/globals/[slug].ts +0 -42
- package/src/pages/api/graphql.ts +0 -90
- package/src/pages/api/health.ts +0 -426
- package/src/pages/api/keys/[id].ts +0 -26
- package/src/pages/api/keys/index.ts +0 -75
- package/src/pages/api/media/[id].ts +0 -309
- package/src/pages/api/media/folders.ts +0 -609
- package/src/pages/api/media/index.ts +0 -146
- package/src/pages/api/media/resize.ts +0 -267
- package/src/pages/api/search.ts +0 -82
- package/src/pages/api/slug-availability.ts +0 -70
- package/src/pages/api/storage-config.ts +0 -20
- package/src/pages/api/storage-status.ts +0 -206
- package/src/pages/api/upload.ts +0 -334
- package/src/pages/api/webhooks/index.ts +0 -71
- package/src/pages/login.astro +0 -82
- package/src/pages/register.astro +0 -102
package/src/integration.ts
CHANGED
|
@@ -4,30 +4,105 @@ import fs from "fs";
|
|
|
4
4
|
|
|
5
5
|
export interface KyroAdminOptions {
|
|
6
6
|
basePath?: string;
|
|
7
|
+
apiPath?: string;
|
|
7
8
|
configPath?: string;
|
|
8
|
-
disableAuth?: boolean;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export function kyroAdmin(options: KyroAdminOptions = {}): AstroIntegration {
|
|
12
12
|
const {
|
|
13
13
|
basePath = "/admin",
|
|
14
|
+
apiPath = "/api",
|
|
14
15
|
configPath = "kyro.config.ts",
|
|
15
|
-
disableAuth = false,
|
|
16
16
|
} = options;
|
|
17
17
|
|
|
18
18
|
return {
|
|
19
19
|
name: "@kyro-cms/admin",
|
|
20
20
|
hooks: {
|
|
21
|
-
"astro:config:setup": ({ config, updateConfig, logger }) => {
|
|
22
|
-
logger.info(`Kyro Admin mounted at ${basePath}`);
|
|
21
|
+
"astro:config:setup": ({ config, updateConfig, injectRoute, logger }) => {
|
|
22
|
+
logger.info(`Kyro Admin mounted at ${basePath} (API: ${apiPath})`);
|
|
23
23
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const fallbackConfig = path.resolve(
|
|
25
|
+
new URL(".", import.meta.url).pathname,
|
|
26
|
+
"lib/default-kyro-config.ts",
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// Try to resolve config from root first, then admin local
|
|
30
|
+
const rootConfig = path.resolve(config.root.pathname, "..", configPath);
|
|
31
|
+
const localConfig = path.resolve(config.root.pathname, configPath);
|
|
32
|
+
|
|
33
|
+
const resolvedConfig = fs.existsSync(rootConfig)
|
|
34
|
+
? rootConfig
|
|
35
|
+
: fs.existsSync(localConfig)
|
|
36
|
+
? localConfig
|
|
37
|
+
: fallbackConfig;
|
|
38
|
+
|
|
39
|
+
if (resolvedConfig !== fallbackConfig) {
|
|
40
|
+
logger.info(`Loaded config from ${resolvedConfig}`);
|
|
27
41
|
} else {
|
|
28
|
-
logger.warn(
|
|
29
|
-
|
|
42
|
+
logger.warn(`Config file not found. Using defaults.`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Inject API Routes
|
|
46
|
+
if (apiPath) {
|
|
47
|
+
const apiHandlerPath = path.resolve(
|
|
48
|
+
config.root.pathname,
|
|
49
|
+
"..",
|
|
50
|
+
"src/api-handler.ts",
|
|
30
51
|
);
|
|
52
|
+
injectRoute({
|
|
53
|
+
pattern: `${apiPath}/[...path]`,
|
|
54
|
+
entrypoint: apiHandlerPath,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Inject Admin UI Routes
|
|
59
|
+
const pages = [
|
|
60
|
+
{ pattern: "", entrypoint: "./pages/index.astro" },
|
|
61
|
+
{ pattern: "/login", entrypoint: "./pages/auth/login.astro" },
|
|
62
|
+
{ pattern: "/register", entrypoint: "./pages/auth/register.astro" },
|
|
63
|
+
{ pattern: "/media", entrypoint: "./pages/media.astro" },
|
|
64
|
+
{ pattern: "/users", entrypoint: "./pages/users/index.astro" },
|
|
65
|
+
{ pattern: "/users/[id]", entrypoint: "./pages/users/[id].astro" },
|
|
66
|
+
{ pattern: "/roles", entrypoint: "./pages/roles/index.astro" },
|
|
67
|
+
{ pattern: "/settings", entrypoint: "./pages/settings/index.astro" },
|
|
68
|
+
{
|
|
69
|
+
pattern: "/settings/[slug]",
|
|
70
|
+
entrypoint: "./pages/settings/[slug].astro",
|
|
71
|
+
},
|
|
72
|
+
{ pattern: "/audit", entrypoint: "./pages/audit/index.astro" },
|
|
73
|
+
{ pattern: "/sessions", entrypoint: "./pages/sessions.astro" },
|
|
74
|
+
{ pattern: "/keys", entrypoint: "./pages/keys.astro" },
|
|
75
|
+
{ pattern: "/webhooks", entrypoint: "./pages/webhooks.astro" },
|
|
76
|
+
{ pattern: "/plugins", entrypoint: "./pages/plugins.astro" },
|
|
77
|
+
{ pattern: "/marketplace", entrypoint: "./pages/marketplace.astro" },
|
|
78
|
+
{
|
|
79
|
+
pattern: "/api-explorer",
|
|
80
|
+
entrypoint: "./pages/api-explorer.astro",
|
|
81
|
+
},
|
|
82
|
+
{ pattern: "/graphql", entrypoint: "./pages/graphql.astro" },
|
|
83
|
+
{
|
|
84
|
+
pattern: "/rest-playground",
|
|
85
|
+
entrypoint: "./pages/rest-playground.astro",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
pattern: "/[collection]",
|
|
89
|
+
entrypoint: "./pages/[collection]/index.astro",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
pattern: "/[collection]/[id]",
|
|
93
|
+
entrypoint: "./pages/[collection]/[id].astro",
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
for (const page of pages) {
|
|
98
|
+
const pattern = `${basePath}${page.pattern}`.replace(/\/$/, "");
|
|
99
|
+
injectRoute({
|
|
100
|
+
pattern: pattern || "/",
|
|
101
|
+
entrypoint: path.resolve(
|
|
102
|
+
new URL(".", import.meta.url).pathname,
|
|
103
|
+
page.entrypoint,
|
|
104
|
+
),
|
|
105
|
+
});
|
|
31
106
|
}
|
|
32
107
|
|
|
33
108
|
updateConfig({
|
|
@@ -38,8 +113,8 @@ export function kyroAdmin(options: KyroAdminOptions = {}): AstroIntegration {
|
|
|
38
113
|
},
|
|
39
114
|
},
|
|
40
115
|
define: {
|
|
41
|
-
|
|
42
|
-
|
|
116
|
+
__KYRO_ADMIN_PATH__: JSON.stringify(basePath),
|
|
117
|
+
__KYRO_API_PATH__: JSON.stringify(apiPath),
|
|
43
118
|
},
|
|
44
119
|
},
|
|
45
120
|
});
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
declare module '@kyro-cms/core' {
|
|
2
|
+
export interface CollectionConfig {
|
|
3
|
+
slug: string;
|
|
4
|
+
label?: string;
|
|
5
|
+
fields: FieldConfig[];
|
|
6
|
+
admin?: {
|
|
7
|
+
layout?: 'single' | 'split';
|
|
8
|
+
autoGenerate?: string;
|
|
9
|
+
defaultColumns?: string[];
|
|
10
|
+
description?: string;
|
|
11
|
+
useAsTitle?: string;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
};
|
|
14
|
+
timestamps?: boolean;
|
|
15
|
+
singularLabel?: string;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface GlobalConfig {
|
|
20
|
+
slug: string;
|
|
21
|
+
label?: string;
|
|
22
|
+
fields: FieldConfig[];
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface FieldConfig {
|
|
27
|
+
name: string;
|
|
28
|
+
label?: string;
|
|
29
|
+
type: string;
|
|
30
|
+
required?: boolean;
|
|
31
|
+
admin?: {
|
|
32
|
+
hidden?: boolean;
|
|
33
|
+
layout?: 'single' | 'split';
|
|
34
|
+
autoGenerate?: string;
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
};
|
|
37
|
+
tabs?: Array<{ label?: string; fields?: FieldConfig[]; [key: string]: unknown }>;
|
|
38
|
+
fields?: FieldConfig[];
|
|
39
|
+
options?: Array<{ label: string; value: string | number }>;
|
|
40
|
+
blocks?: Block[];
|
|
41
|
+
relationTo?: string | string[];
|
|
42
|
+
hasMany?: boolean;
|
|
43
|
+
condition?: unknown;
|
|
44
|
+
rowWidth?: string;
|
|
45
|
+
width?: string;
|
|
46
|
+
[key: string]: unknown;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type FieldType = string;
|
|
50
|
+
export const ALL_FIELD_TYPES: string[];
|
|
51
|
+
export type ALL_FIELD_TYPES = string[];
|
|
52
|
+
|
|
53
|
+
export interface TextField extends FieldConfig { type: 'text' }
|
|
54
|
+
export interface NumberField extends FieldConfig { type: 'number' }
|
|
55
|
+
export interface CheckboxField extends FieldConfig { type: 'checkbox' }
|
|
56
|
+
export interface DateField extends FieldConfig { type: 'date' }
|
|
57
|
+
export interface SelectField extends FieldConfig { type: 'select'; options?: { label: string; value: string | number }[] }
|
|
58
|
+
export interface TextareaField extends FieldConfig { type: 'textarea' }
|
|
59
|
+
export interface MarkdownField extends FieldConfig { type: 'markdown' }
|
|
60
|
+
export interface RichTextField extends FieldConfig { type: 'richText' }
|
|
61
|
+
export interface CodeField extends FieldConfig { type: 'code'; language?: string }
|
|
62
|
+
export interface JSONField extends FieldConfig { type: 'json' }
|
|
63
|
+
export interface ImageField extends FieldConfig { type: 'image' }
|
|
64
|
+
export interface UploadField extends FieldConfig { type: 'upload' }
|
|
65
|
+
export interface RelationshipField extends FieldConfig { type: 'relationship'; relationTo: string }
|
|
66
|
+
export interface BlocksField extends FieldConfig { type: 'blocks'; blocks: Block[] }
|
|
67
|
+
export interface ArrayField extends FieldConfig { type: 'array'; fields: FieldConfig[] }
|
|
68
|
+
export interface GroupField extends FieldConfig { type: 'group'; fields: FieldConfig[] }
|
|
69
|
+
|
|
70
|
+
export type Field = FieldConfig;
|
|
71
|
+
|
|
72
|
+
export interface Block {
|
|
73
|
+
slug: string;
|
|
74
|
+
label?: string;
|
|
75
|
+
fields: FieldConfig[];
|
|
76
|
+
[key: string]: unknown;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface RichTextBlock extends Block {}
|
|
80
|
+
|
|
81
|
+
export interface KyroConfig {
|
|
82
|
+
collections?: CollectionConfig[] | Record<string, CollectionConfig>;
|
|
83
|
+
globals?: GlobalConfig[] | Record<string, GlobalConfig>;
|
|
84
|
+
[key: string]: unknown;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface Permissions {
|
|
88
|
+
collections?: {
|
|
89
|
+
[key: string]: {
|
|
90
|
+
read?: boolean;
|
|
91
|
+
create?: boolean;
|
|
92
|
+
update?: boolean;
|
|
93
|
+
delete?: boolean;
|
|
94
|
+
[key: string]: unknown;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
globals?: {
|
|
98
|
+
[key: string]: {
|
|
99
|
+
read?: boolean;
|
|
100
|
+
update?: boolean;
|
|
101
|
+
[key: string]: unknown;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
media?: {
|
|
105
|
+
read?: boolean;
|
|
106
|
+
create?: boolean;
|
|
107
|
+
update?: boolean;
|
|
108
|
+
delete?: boolean;
|
|
109
|
+
};
|
|
110
|
+
users?: {
|
|
111
|
+
read?: boolean;
|
|
112
|
+
create?: boolean;
|
|
113
|
+
update?: boolean;
|
|
114
|
+
delete?: boolean;
|
|
115
|
+
};
|
|
116
|
+
[key: string]: unknown;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface FilterConfig {
|
|
120
|
+
field: string;
|
|
121
|
+
operator: string;
|
|
122
|
+
value: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface SortConfig {
|
|
126
|
+
field: string;
|
|
127
|
+
direction: 'asc' | 'desc';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface PaginationConfig {
|
|
131
|
+
page: number;
|
|
132
|
+
limit: number;
|
|
133
|
+
total: number;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface ColumnConfig {
|
|
137
|
+
name: string;
|
|
138
|
+
label: string;
|
|
139
|
+
sortable?: boolean;
|
|
140
|
+
width?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface BlockData {
|
|
144
|
+
id: string;
|
|
145
|
+
type: string;
|
|
146
|
+
data?: Record<string, unknown>;
|
|
147
|
+
children?: BlockData[];
|
|
148
|
+
order?: number;
|
|
149
|
+
[key: string]: unknown;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface ApiListResponse {
|
|
153
|
+
docs?: Record<string, unknown>[];
|
|
154
|
+
totalDocs?: number;
|
|
155
|
+
[key: string]: unknown;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface ApiDocResponse {
|
|
159
|
+
data?: Record<string, unknown>;
|
|
160
|
+
status?: string;
|
|
161
|
+
createdAt?: string;
|
|
162
|
+
updatedAt?: string;
|
|
163
|
+
publishedAt?: string;
|
|
164
|
+
[key: string]: unknown;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export interface Version {
|
|
168
|
+
id: string;
|
|
169
|
+
status?: string;
|
|
170
|
+
changeDescription?: string;
|
|
171
|
+
createdAt?: string;
|
|
172
|
+
updatedAt?: string;
|
|
173
|
+
createdBy?: string;
|
|
174
|
+
[key: string]: unknown;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface VersionDiff {
|
|
178
|
+
field?: string;
|
|
179
|
+
oldValue?: unknown;
|
|
180
|
+
newValue?: unknown;
|
|
181
|
+
[key: string]: unknown;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
declare module '@kyro-cms/core/client' {
|
|
186
|
+
export * from '@kyro-cms/core';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Ambient module for template collections (no type declarations in the package)
|
|
190
|
+
declare module '@kyro-cms/core/templates' {
|
|
191
|
+
import type { CollectionConfig, GlobalConfig } from '@kyro-cms/core';
|
|
192
|
+
|
|
193
|
+
export const blogCollections: Record<string, CollectionConfig>;
|
|
194
|
+
export const ecommerceCollections: Record<string, CollectionConfig>;
|
|
195
|
+
export const minimalCollections: Record<string, CollectionConfig>;
|
|
196
|
+
export const kitchenSinkCollections: Record<string, CollectionConfig>;
|
|
197
|
+
export const mediaCollections: Record<string, CollectionConfig>;
|
|
198
|
+
export const authCollections: Record<string, CollectionConfig>;
|
|
199
|
+
export const allSettingsGlobals: GlobalConfig[];
|
|
200
|
+
export const coreSettingsGlobals: GlobalConfig[];
|
|
201
|
+
export const ecommerceSettingsGlobals: GlobalConfig[];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Ambient module for the Astro virtual import
|
|
205
|
+
declare module 'kyro:config' {
|
|
206
|
+
import type { KyroConfig } from '@kyro-cms/core';
|
|
207
|
+
const config: KyroConfig;
|
|
208
|
+
export default config;
|
|
209
|
+
}
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
---
|
|
2
2
|
import "../styles/main.css";
|
|
3
|
-
import { nonAuthCollections, collections, globals } from "
|
|
4
|
-
import { CommandPaletteWrapper } from "
|
|
3
|
+
import { nonAuthCollections, collections, globals } from "../lib/config";
|
|
4
|
+
import { CommandPaletteWrapper } from "../components/ui/CommandPaletteWrapper";
|
|
5
5
|
import Sidebar from "../components/Sidebar.astro";
|
|
6
|
+
import { adminPath, apiPath } from "../lib/paths";
|
|
7
|
+
import { AuthBridge } from "../components/AuthBridge";
|
|
8
|
+
import { GlobalModal } from "../components/ui/GlobalModal";
|
|
9
|
+
import { Toaster } from "../components/ui/Toaster";
|
|
10
|
+
import { getSiteSettings } from "../lib/globals";
|
|
6
11
|
|
|
7
12
|
interface Props {
|
|
8
13
|
title: string;
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
const { title } = Astro.props;
|
|
12
|
-
const
|
|
17
|
+
const siteSettings = await getSiteSettings();
|
|
18
|
+
const siteName = siteSettings?.siteName || "Kyro CMS";
|
|
19
|
+
const siteFavicon = siteSettings?.siteFavicon;
|
|
13
20
|
---
|
|
14
21
|
|
|
15
22
|
<!doctype html>
|
|
@@ -17,9 +24,100 @@ const user = Astro.locals.user;
|
|
|
17
24
|
<head>
|
|
18
25
|
<meta charset="UTF-8" />
|
|
19
26
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
20
|
-
<title>{title} -
|
|
21
|
-
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
22
|
-
<
|
|
27
|
+
<title>{title} - {siteName}</title>
|
|
28
|
+
<link rel="icon" type={siteFavicon?.mimeType || "image/svg+xml"} href={siteFavicon?.url || "/favicon.svg"} />
|
|
29
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
30
|
+
<link
|
|
31
|
+
rel="preconnect"
|
|
32
|
+
href="https://fonts.gstatic.com"
|
|
33
|
+
crossorigin
|
|
34
|
+
/>
|
|
35
|
+
<link
|
|
36
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;900&display=swap"
|
|
37
|
+
rel="stylesheet"
|
|
38
|
+
/>
|
|
39
|
+
<script is:inline define:vars={{ adminPath, apiPath }}>
|
|
40
|
+
// Simple in-memory auth state (alternative to Zustand for SSR)
|
|
41
|
+
window.__kyroAuth = window.__kyroAuth || { user: null, permissions: null, verified: false };
|
|
42
|
+
|
|
43
|
+
// Verify auth - redirect to login if not authenticated
|
|
44
|
+
(async () => {
|
|
45
|
+
try {
|
|
46
|
+
// Fetch user and permissions using cookies
|
|
47
|
+
const [meRes, accessRes] = await Promise.all([
|
|
48
|
+
fetch(apiPath + '/auth/me', { credentials: 'include' }),
|
|
49
|
+
fetch(apiPath + '/auth/access', { credentials: 'include' })
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
if (!meRes.ok) {
|
|
53
|
+
window.location.href = adminPath + "/login";
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const meData = await meRes.json();
|
|
58
|
+
if (!meData.user) {
|
|
59
|
+
console.log('[AdminLayout] No user in data, redirecting to login');
|
|
60
|
+
window.location.href = adminPath + "/login";
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const permissions = accessRes.ok ? await accessRes.json() : null;
|
|
65
|
+
|
|
66
|
+
// Store user in memory (not localStorage)
|
|
67
|
+
window.__kyroAuth.user = meData.user;
|
|
68
|
+
window.__kyroAuth.permissions = permissions;
|
|
69
|
+
window.__kyroAuth.verified = true;
|
|
70
|
+
|
|
71
|
+
// Dispatch event for components to update
|
|
72
|
+
window.dispatchEvent(new CustomEvent('kyro:auth-ready', {
|
|
73
|
+
detail: {
|
|
74
|
+
user: meData.user,
|
|
75
|
+
permissions
|
|
76
|
+
}
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
// Navigation Guard - Check if user has read access to current page
|
|
80
|
+
if (permissions) {
|
|
81
|
+
const path = window.location.pathname;
|
|
82
|
+
const relativePath = path.replace(adminPath, '');
|
|
83
|
+
|
|
84
|
+
// Extract slug and type
|
|
85
|
+
let slug = '';
|
|
86
|
+
let type = '';
|
|
87
|
+
|
|
88
|
+
if (relativePath.startsWith('/users')) { slug = 'users'; type = 'collection'; }
|
|
89
|
+
else if (relativePath.startsWith('/audit')) { slug = 'audit_logs'; type = 'collection'; }
|
|
90
|
+
else if (relativePath.startsWith('/media')) { slug = 'media'; type = 'collection'; }
|
|
91
|
+
else if (relativePath.startsWith('/settings/')) { slug = relativePath.split('/')[2]; type = 'global'; }
|
|
92
|
+
else if (relativePath.includes('/') && !relativePath.startsWith('/login') && !relativePath.startsWith('/403')) {
|
|
93
|
+
// Dynamic collections: /[adminPath]/[collectionSlug]/...
|
|
94
|
+
slug = relativePath.split('/')[1];
|
|
95
|
+
type = 'collection';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (slug && type) {
|
|
99
|
+
let hasAccess = true;
|
|
100
|
+
if (type === 'collection' && permissions.collections) {
|
|
101
|
+
const p = permissions.collections[slug];
|
|
102
|
+
if (p && p.read === false) hasAccess = false;
|
|
103
|
+
} else if (type === 'global' && permissions.globals) {
|
|
104
|
+
const p = permissions.globals[slug];
|
|
105
|
+
if (p && p.read === false) hasAccess = false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!hasAccess) {
|
|
109
|
+
console.log('[AdminLayout] Access denied for', slug, 'redirecting to 403');
|
|
110
|
+
window.location.href = adminPath + "/403";
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
} catch (err) {
|
|
115
|
+
console.error('[AdminLayout] Auth check error:', err);
|
|
116
|
+
window.location.href = adminPath + "/login";
|
|
117
|
+
}
|
|
118
|
+
})();
|
|
119
|
+
|
|
120
|
+
// Theme init (only theme persisted)
|
|
23
121
|
(() => {
|
|
24
122
|
const getTheme = () => {
|
|
25
123
|
const stored = localStorage.getItem("theme");
|
|
@@ -38,8 +136,9 @@ const user = Astro.locals.user;
|
|
|
38
136
|
</script>
|
|
39
137
|
</head>
|
|
40
138
|
<body class="bg-[var(--kyro-bg)] antialiased text-[var(--kyro-text-primary)]">
|
|
139
|
+
<div id="kyro-user-data" data-user=""></div>
|
|
41
140
|
<div class="flex h-screen p-6 gap-6 overflow-hidden">
|
|
42
|
-
<Sidebar title={title}
|
|
141
|
+
<Sidebar title={title} />
|
|
43
142
|
|
|
44
143
|
<!-- Main Content Column -->
|
|
45
144
|
<main class="flex-1 flex flex-col gap-6 overflow-hidden">
|
|
@@ -89,6 +188,9 @@ const user = Astro.locals.user;
|
|
|
89
188
|
</div>
|
|
90
189
|
</div>
|
|
91
190
|
|
|
191
|
+
<!-- Auth Bridge: Hydrates Zustand useAuthStore with user + permissions -->
|
|
192
|
+
<AuthBridge client:load />
|
|
193
|
+
|
|
92
194
|
<!-- Command Palette (React) -->
|
|
93
195
|
<CommandPaletteWrapper
|
|
94
196
|
client:only="react"
|
|
@@ -96,8 +198,14 @@ const user = Astro.locals.user;
|
|
|
96
198
|
globals={globals}
|
|
97
199
|
/>
|
|
98
200
|
|
|
201
|
+
<!-- Global Modals (React) -->
|
|
202
|
+
<GlobalModal client:only="react" />
|
|
203
|
+
|
|
204
|
+
<!-- Toast Notifications (React) -->
|
|
205
|
+
<Toaster client:only="react" />
|
|
206
|
+
|
|
99
207
|
<!-- Theme UI Logic -->
|
|
100
|
-
<script is:inline>
|
|
208
|
+
<script is:inline define:vars={{ adminPath, apiPath }}>
|
|
101
209
|
const lightBtn = document.getElementById("theme-light-btn");
|
|
102
210
|
const darkBtn = document.getElementById("theme-dark-btn");
|
|
103
211
|
|
|
@@ -183,9 +291,18 @@ const user = Astro.locals.user;
|
|
|
183
291
|
|
|
184
292
|
logoutBackdrop?.addEventListener("click", closeLogoutModal);
|
|
185
293
|
logoutCancel?.addEventListener("click", closeLogoutModal);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
294
|
+
logoutConfirm?.addEventListener("click", async () => {
|
|
295
|
+
try {
|
|
296
|
+
await fetch(apiPath + '/auth/logout', {
|
|
297
|
+
method: 'POST',
|
|
298
|
+
credentials: 'include'
|
|
299
|
+
});
|
|
300
|
+
} finally {
|
|
301
|
+
// Clear auth state
|
|
302
|
+
window.__kyroAuth = { user: null, verified: false };
|
|
303
|
+
window.location.href = adminPath + "/login";
|
|
304
|
+
}
|
|
305
|
+
});
|
|
189
306
|
</script>
|
|
190
307
|
</body>
|
|
191
308
|
</html>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import "../styles/main.css";
|
|
3
|
+
import { adminPath, apiPath } from "../lib/paths";
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
6
|
title: string;
|
|
@@ -15,12 +16,24 @@ const { title } = Astro.props;
|
|
|
15
16
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
16
17
|
<title>{title} - Kyro CMS</title>
|
|
17
18
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
18
|
-
<script is:inline>
|
|
19
|
-
(() => {
|
|
19
|
+
<script is:inline define:vars={{ adminPath, apiPath }}>
|
|
20
|
+
(async () => {
|
|
21
|
+
try {
|
|
22
|
+
const res = await fetch(apiPath + "/auth/me", { credentials: "include" });
|
|
23
|
+
if (res.ok) {
|
|
24
|
+
const data = await res.json();
|
|
25
|
+
if (data?.user) {
|
|
26
|
+
window.location.href = adminPath;
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
} catch {}
|
|
20
31
|
const getTheme = () => {
|
|
21
32
|
const stored = localStorage.getItem("theme");
|
|
22
33
|
if (stored) return stored;
|
|
23
|
-
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
34
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
35
|
+
? "dark"
|
|
36
|
+
: "light";
|
|
24
37
|
};
|
|
25
38
|
const theme = getTheme();
|
|
26
39
|
if (theme === "dark") {
|
|
@@ -37,10 +50,13 @@ const { title } = Astro.props;
|
|
|
37
50
|
<!-- Logo -->
|
|
38
51
|
<div class="w-full text-center mb-8">
|
|
39
52
|
<a href="/" class="inline-block">
|
|
40
|
-
<span
|
|
53
|
+
<span
|
|
54
|
+
class="text-xl font-bold tracking-tighter text-[var(--kyro-text-primary)]"
|
|
55
|
+
>KYRO.</span
|
|
56
|
+
>
|
|
41
57
|
</a>
|
|
42
58
|
</div>
|
|
43
|
-
|
|
59
|
+
|
|
44
60
|
<div class="w-full flex justify-center">
|
|
45
61
|
<slot />
|
|
46
62
|
</div>
|