@kyro-cms/admin 0.1.6 → 0.1.8

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 (179) hide show
  1. package/README.md +149 -51
  2. package/package.json +54 -5
  3. package/src/collections/auth/index.ts +2 -2
  4. package/src/collections/portfolio/index.ts +343 -0
  5. package/src/components/ActionBar.tsx +153 -16
  6. package/src/components/Admin.tsx +137 -28
  7. package/src/components/ApiExplorer.tsx +325 -0
  8. package/src/components/ApiKeysManager.tsx +563 -0
  9. package/src/components/AuditLogsPage.tsx +664 -0
  10. package/src/components/AutoForm.tsx +2155 -770
  11. package/src/components/BrandingHub.tsx +267 -0
  12. package/src/components/BulkActionsBar.tsx +3 -3
  13. package/src/components/CreateView.tsx +4 -4
  14. package/src/components/Dashboard.tsx +393 -0
  15. package/src/components/DetailView.tsx +200 -58
  16. package/src/components/DeveloperCenter.tsx +403 -0
  17. package/src/components/EnhancedListView.tsx +890 -0
  18. package/src/components/GraphQLExplorer.tsx +675 -0
  19. package/src/components/GraphQLPlayground.tsx +627 -0
  20. package/src/components/ListView.tsx +192 -54
  21. package/src/components/MediaGallery.tsx +1569 -0
  22. package/src/components/Modal.tsx +206 -0
  23. package/src/components/RestPlayground.tsx +951 -0
  24. package/src/components/Sidebar.astro +237 -0
  25. package/src/components/ThemeProvider.tsx +8 -2
  26. package/src/components/UserManagement.tsx +204 -0
  27. package/src/components/VersionHistoryPanel.tsx +3 -3
  28. package/src/components/WebhookManager.tsx +608 -0
  29. package/src/components/blocks/AccordionBlock.tsx +65 -0
  30. package/src/components/blocks/ArrayBlock.tsx +84 -0
  31. package/src/components/blocks/BlockEditModal.tsx +363 -0
  32. package/src/components/blocks/ButtonBlock.tsx +64 -0
  33. package/src/components/blocks/ChildBlocksTree.tsx +551 -0
  34. package/src/components/blocks/CodeBlock.tsx +114 -0
  35. package/src/components/blocks/ColumnsBlock.tsx +93 -0
  36. package/src/components/blocks/DividerBlock.tsx +43 -0
  37. package/src/components/blocks/FileBlock.tsx +63 -0
  38. package/src/components/blocks/HeadingBlock.tsx +59 -0
  39. package/src/components/blocks/HeroBlock.tsx +99 -0
  40. package/src/components/blocks/ImageBlock.tsx +82 -0
  41. package/src/components/blocks/LinkBlock.tsx +65 -0
  42. package/src/components/blocks/ListBlock.tsx +60 -0
  43. package/src/components/blocks/ParagraphBlock.tsx +61 -0
  44. package/src/components/blocks/RelationshipBlock.tsx +72 -0
  45. package/src/components/blocks/RichTextBlock.tsx +66 -0
  46. package/src/components/blocks/VStackBlock.tsx +61 -0
  47. package/src/components/blocks/VideoBlock.tsx +65 -0
  48. package/src/components/blocks/index.ts +10 -0
  49. package/src/components/fields/AccordionField.tsx +213 -0
  50. package/src/components/fields/ArrayField.tsx +241 -0
  51. package/src/components/fields/BlocksField.tsx +323 -0
  52. package/src/components/fields/ButtonField.tsx +53 -0
  53. package/src/components/fields/CheckboxField.tsx +18 -8
  54. package/src/components/fields/ChildrenField.tsx +48 -0
  55. package/src/components/fields/CodeField.tsx +294 -0
  56. package/src/components/fields/ColumnsField.tsx +137 -0
  57. package/src/components/fields/DateField.tsx +24 -12
  58. package/src/components/fields/EditorClient.tsx +537 -0
  59. package/src/components/fields/HeadingField.tsx +31 -0
  60. package/src/components/fields/HeroField.tsx +101 -0
  61. package/src/components/fields/JSONField.tsx +341 -0
  62. package/src/components/fields/LinkField.tsx +81 -0
  63. package/src/components/fields/ListField.tsx +74 -0
  64. package/src/components/fields/MarkdownField.tsx +260 -0
  65. package/src/components/fields/NumberField.tsx +25 -13
  66. package/src/components/fields/PortableTextField.tsx +155 -0
  67. package/src/components/fields/PortableTextRenderer.tsx +68 -0
  68. package/src/components/fields/RelationshipBlockField.tsx +233 -0
  69. package/src/components/fields/RelationshipField.tsx +278 -60
  70. package/src/components/fields/SelectField.tsx +28 -16
  71. package/src/components/fields/TextField.tsx +31 -15
  72. package/src/components/fields/UploadField.tsx +613 -0
  73. package/src/components/fields/VideoField.tsx +73 -0
  74. package/src/components/fields/extensions/blockComponents.tsx +247 -0
  75. package/src/components/fields/extensions/blocksStore.ts +273 -0
  76. package/src/components/fields/index.ts +24 -0
  77. package/src/components/index.ts +1 -2
  78. package/src/components/layout/Header.tsx +2 -2
  79. package/src/components/layout/Layout.tsx +3 -3
  80. package/src/components/ui/Badge.tsx +9 -4
  81. package/src/components/ui/BlockDrawer.tsx +79 -0
  82. package/src/components/ui/Button.tsx +1 -1
  83. package/src/components/ui/CommandPalette.tsx +362 -0
  84. package/src/components/ui/CommandPaletteWrapper.tsx +97 -0
  85. package/src/components/ui/Dropdown.tsx +1 -1
  86. package/src/components/ui/Modal.tsx +37 -12
  87. package/src/components/ui/PromptModal.tsx +94 -0
  88. package/src/components/ui/SlidePanel.tsx +43 -16
  89. package/src/components/ui/Toast.tsx +80 -14
  90. package/src/env.d.ts +16 -0
  91. package/src/env.ts +20 -0
  92. package/src/index.ts +0 -1
  93. package/src/layouts/AdminLayout.astro +164 -170
  94. package/src/layouts/AuthLayout.astro +23 -6
  95. package/src/lib/MediaService.ts +541 -0
  96. package/src/lib/api.ts +163 -0
  97. package/src/lib/auth/sqlite-adapter.ts +319 -0
  98. package/src/lib/config.ts +23 -7
  99. package/src/lib/dataStore.ts +188 -73
  100. package/src/lib/date-utils.ts +69 -0
  101. package/src/lib/db/adapter.ts +54 -0
  102. package/src/lib/db/drizzle-mysql-adapter.ts +194 -0
  103. package/src/lib/db/drizzle-mysql-auth-adapter.ts +327 -0
  104. package/src/lib/db/drizzle-postgres-adapter.ts +202 -0
  105. package/src/lib/db/drizzle-postgres-auth-adapter.ts +304 -0
  106. package/src/lib/db/drizzle-sqlite-adapter.ts +227 -0
  107. package/src/lib/db/drizzle-sqlite-auth-adapter.ts +548 -0
  108. package/src/lib/db/index.ts +449 -0
  109. package/src/lib/db/mongodb-adapter.ts +207 -0
  110. package/src/lib/db/mongodb-auth-adapter.ts +305 -0
  111. package/src/lib/db/schema/mysql-auth.ts +113 -0
  112. package/src/lib/db/schema/mysql-content.ts +20 -0
  113. package/src/lib/db/schema/postgres-auth.ts +116 -0
  114. package/src/lib/db/schema/postgres-content.ts +35 -0
  115. package/src/lib/db/schema/postgres-media.ts +52 -0
  116. package/src/lib/db/schema/postgres-settings.ts +11 -0
  117. package/src/lib/db/schema/sqlite-auth.ts +112 -0
  118. package/src/lib/db/schema/sqlite-content.ts +20 -0
  119. package/src/lib/db/version-adapter.ts +248 -0
  120. package/src/lib/graphql/index.ts +1 -0
  121. package/src/lib/graphql/schema.ts +443 -0
  122. package/src/lib/i18n.tsx +353 -0
  123. package/src/lib/rate-limit.ts +267 -0
  124. package/src/lib/slugify.ts +15 -0
  125. package/src/lib/storage.ts +374 -0
  126. package/src/lib/store.ts +85 -0
  127. package/src/lib/validation.ts +250 -0
  128. package/src/middleware.ts +70 -11
  129. package/src/pages/[collection]/[id].astro +178 -122
  130. package/src/pages/[collection]/index.astro +24 -156
  131. package/src/pages/admin/api-explorer.astro +98 -0
  132. package/src/pages/admin/graphql-explorer.astro +40 -0
  133. package/src/pages/admin/graphql.astro +97 -0
  134. package/src/pages/admin/index.astro +200 -139
  135. package/src/pages/admin/keys.astro +8 -0
  136. package/src/pages/admin/rest-playground.astro +44 -0
  137. package/src/pages/admin/webhooks.astro +8 -0
  138. package/src/pages/api/[collection]/[id]/publish.ts +52 -0
  139. package/src/pages/api/[collection]/[id]/unpublish.ts +42 -0
  140. package/src/pages/api/[collection]/[id]/versions.ts +66 -0
  141. package/src/pages/api/[collection]/[id].ts +114 -159
  142. package/src/pages/api/[collection]/index.ts +150 -230
  143. package/src/pages/api/auth/[id].ts +48 -69
  144. package/src/pages/api/auth/audit-logs.ts +20 -43
  145. package/src/pages/api/auth/login.ts +159 -45
  146. package/src/pages/api/auth/logout.ts +42 -24
  147. package/src/pages/api/auth/refresh.ts +119 -0
  148. package/src/pages/api/auth/register.ts +110 -40
  149. package/src/pages/api/auth/users.ts +22 -97
  150. package/src/pages/api/collections.ts +59 -0
  151. package/src/pages/api/globals/[slug]/test.ts +172 -0
  152. package/src/pages/api/globals/[slug].ts +42 -0
  153. package/src/pages/api/graphql.ts +90 -0
  154. package/src/pages/api/health.ts +417 -40
  155. package/src/pages/api/keys/[id].ts +26 -0
  156. package/src/pages/api/keys/index.ts +75 -0
  157. package/src/pages/api/media/[id].ts +309 -0
  158. package/src/pages/api/media/folders.ts +609 -0
  159. package/src/pages/api/media/index.ts +146 -0
  160. package/src/pages/api/media/resize.ts +267 -0
  161. package/src/pages/api/search.ts +82 -0
  162. package/src/pages/api/slug-availability.ts +70 -0
  163. package/src/pages/api/storage-config.ts +20 -0
  164. package/src/pages/api/storage-status.ts +206 -0
  165. package/src/pages/api/upload.ts +334 -0
  166. package/src/pages/api/webhooks/index.ts +71 -0
  167. package/src/pages/audit/index.astro +2 -104
  168. package/src/pages/login.astro +11 -11
  169. package/src/pages/media.astro +10 -0
  170. package/src/pages/preview/[collection]/[id].astro +178 -0
  171. package/src/pages/register.astro +13 -13
  172. package/src/pages/roles/index.astro +21 -21
  173. package/src/pages/settings/[slug].astro +162 -0
  174. package/src/pages/settings/index.astro +9 -0
  175. package/src/pages/users/[id].astro +29 -21
  176. package/src/pages/users/index.astro +22 -17
  177. package/src/pages/users/new.astro +18 -17
  178. package/src/styles/main.css +563 -128
  179. package/src/components/layout/Sidebar.tsx +0 -497
@@ -0,0 +1,227 @@
1
+ import type { CollectionConfig } from "@kyro-cms/core";
2
+ import type { DatabaseAdapter, DatabaseConfig } from "./adapter";
3
+ import Database from "better-sqlite3";
4
+ import path from "path";
5
+ import fs from "fs";
6
+ import { randomBytes } from "crypto";
7
+ import { eq, desc, sql, and } from "drizzle-orm";
8
+ import * as contentSchema from "./schema/sqlite-content";
9
+
10
+ function getContentDbPath(): string {
11
+ return (
12
+ process.env.CONTENT_DB_PATH ||
13
+ path.join(process.cwd(), "data", "content.db")
14
+ );
15
+ }
16
+
17
+ function ensureDbDir(dbPath: string): void {
18
+ const dir = path.dirname(dbPath);
19
+ if (!fs.existsSync(dir)) {
20
+ fs.mkdirSync(dir, { recursive: true });
21
+ }
22
+ }
23
+
24
+ export class DrizzleSQLiteAdapter implements DatabaseAdapter {
25
+ private db: Database.Database;
26
+ private initialized = false;
27
+
28
+ constructor(config?: DatabaseConfig) {
29
+ const dbPath = config?.contentDbPath || getContentDbPath();
30
+ ensureDbDir(dbPath);
31
+ this.db = new Database(dbPath);
32
+ this.db.pragma("journal_mode = WAL");
33
+ this.db.pragma("synchronous = NORMAL");
34
+ this.db.pragma("cache_size = -64000");
35
+ }
36
+
37
+ initialize(collections: Record<string, CollectionConfig>) {
38
+ if (this.initialized) return;
39
+
40
+ this.db.exec(`
41
+ CREATE TABLE IF NOT EXISTS documents (
42
+ id TEXT PRIMARY KEY,
43
+ collection TEXT NOT NULL,
44
+ data TEXT NOT NULL DEFAULT '{}',
45
+ created_at TEXT NOT NULL,
46
+ updated_at TEXT NOT NULL
47
+ );
48
+ CREATE TABLE IF NOT EXISTS globals (
49
+ slug TEXT PRIMARY KEY,
50
+ data TEXT NOT NULL DEFAULT '{}',
51
+ updated_at TEXT NOT NULL
52
+ );
53
+ CREATE INDEX IF NOT EXISTS idx_documents_collection ON documents(collection);
54
+ CREATE INDEX IF NOT EXISTS idx_documents_created_at ON documents(created_at);
55
+ `);
56
+
57
+ this.initialized = true;
58
+ }
59
+
60
+ private getTimestamp(): string {
61
+ return new Date().toISOString();
62
+ }
63
+
64
+ private generateId(): string {
65
+ return randomBytes(16).toString("hex");
66
+ }
67
+
68
+ async find<T = any>(
69
+ slug: string,
70
+ options: { page?: number; limit?: number } = {},
71
+ ): Promise<{
72
+ docs: T[];
73
+ totalDocs: number;
74
+ totalPages: number;
75
+ page: number;
76
+ }> {
77
+ const page = options.page || 1;
78
+ const limit = options.limit || 25;
79
+ const start = (page - 1) * limit;
80
+
81
+ const totalResult = this.db
82
+ .prepare("SELECT COUNT(*) as cnt FROM documents WHERE collection = ?")
83
+ .get(slug) as { cnt: number };
84
+ const count = totalResult?.cnt || 0;
85
+
86
+ const docs = this.db
87
+ .prepare(
88
+ "SELECT data FROM documents WHERE collection = ? ORDER BY created_at DESC LIMIT ? OFFSET ?",
89
+ )
90
+ .all(slug, limit, start) as { data: string }[];
91
+
92
+ return {
93
+ docs: docs.map((d) => JSON.parse(d.data) as T),
94
+ totalDocs: count,
95
+ totalPages: Math.ceil(count / limit),
96
+ page,
97
+ };
98
+ }
99
+
100
+ async findById<T = any>(slug: string, id: string): Promise<T | null> {
101
+ const doc = this.db
102
+ .prepare("SELECT data FROM documents WHERE collection = ? AND id = ?")
103
+ .get(slug, id) as { data: string } | undefined;
104
+ return doc ? (JSON.parse(doc.data) as T) : null;
105
+ }
106
+
107
+ async create<T = any>(slug: string, data: Partial<T>): Promise<T> {
108
+ const now = this.getTimestamp();
109
+ const id = (data as any)?.id || this.generateId();
110
+ const newDoc = {
111
+ ...(data as Record<string, unknown>),
112
+ id,
113
+ createdAt: now,
114
+ updatedAt: now,
115
+ };
116
+
117
+ this.db
118
+ .prepare(
119
+ "INSERT INTO documents (id, collection, data, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
120
+ )
121
+ .run(id, slug, JSON.stringify(newDoc), now, now);
122
+
123
+ return newDoc as T;
124
+ }
125
+
126
+ async update<T = any>(
127
+ slug: string,
128
+ id: string,
129
+ data: Partial<T>,
130
+ ): Promise<T | null> {
131
+ const existing = await this.findById<T>(slug, id);
132
+ if (!existing) return null;
133
+
134
+ const now = this.getTimestamp();
135
+ const updated = { ...existing, ...data, id, updatedAt: now };
136
+
137
+ this.db
138
+ .prepare(
139
+ "UPDATE documents SET data = ?, updated_at = ? WHERE collection = ? AND id = ?",
140
+ )
141
+ .run(JSON.stringify(updated), now, slug, id);
142
+
143
+ return updated as T;
144
+ }
145
+
146
+ async delete(slug: string, id: string): Promise<boolean> {
147
+ this.db
148
+ .prepare("DELETE FROM documents WHERE collection = ? AND id = ?")
149
+ .run(slug, id);
150
+ return true;
151
+ }
152
+
153
+ async findGlobal<T = any>(slug: string): Promise<T> {
154
+ const global = this.db
155
+ .prepare("SELECT data FROM globals WHERE slug = ?")
156
+ .get(slug) as { data: string } | undefined;
157
+ return global ? (JSON.parse(global.data) as T) : ({} as T);
158
+ }
159
+
160
+ async updateGlobal<T = any>(slug: string, data: Partial<T>): Promise<T> {
161
+ const now = this.getTimestamp();
162
+ const current = await this.findGlobal<T>(slug);
163
+ const updated = { ...current, ...data };
164
+
165
+ this.db
166
+ .prepare(
167
+ "INSERT OR REPLACE INTO globals (slug, data, updated_at) VALUES (?, ?, ?)",
168
+ )
169
+ .run(slug, JSON.stringify(updated), now);
170
+
171
+ return updated as T;
172
+ }
173
+
174
+ async seedGlobal(slug: string, data: any): Promise<void> {
175
+ const now = this.getTimestamp();
176
+ this.db
177
+ .prepare(
178
+ "INSERT OR IGNORE INTO globals (slug, data, updated_at) VALUES (?, ?, ?)",
179
+ )
180
+ .run(slug, JSON.stringify(data), now);
181
+ }
182
+
183
+ async count(slug: string): Promise<number> {
184
+ const result = this.db
185
+ .prepare("SELECT COUNT(*) as cnt FROM documents WHERE collection = ?")
186
+ .get(slug) as { cnt: number };
187
+ return result?.cnt || 0;
188
+ }
189
+
190
+ async seed(slug: string, docs: any[]): Promise<void> {
191
+ const now = this.getTimestamp();
192
+ const stmt = this.db.prepare(
193
+ "INSERT INTO documents (id, collection, data, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
194
+ );
195
+
196
+ for (let i = 0; i < docs.length; i++) {
197
+ const doc = docs[i];
198
+ const id = doc.id || `${slug}-${i + 1}`;
199
+ stmt.run(
200
+ id,
201
+ slug,
202
+ JSON.stringify({
203
+ ...doc,
204
+ createdAt: doc.createdAt || now,
205
+ updatedAt: doc.updatedAt || now,
206
+ }),
207
+ now,
208
+ now,
209
+ );
210
+ }
211
+ }
212
+
213
+ async isSeeded(slug: string): Promise<boolean> {
214
+ return (await this.count(slug)) > 0;
215
+ }
216
+ }
217
+
218
+ let sqliteContentAdapter: DrizzleSQLiteAdapter | null = null;
219
+
220
+ export function getSQLiteContentAdapter(
221
+ config?: DatabaseConfig,
222
+ ): DrizzleSQLiteAdapter {
223
+ if (!sqliteContentAdapter) {
224
+ sqliteContentAdapter = new DrizzleSQLiteAdapter(config);
225
+ }
226
+ return sqliteContentAdapter;
227
+ }