@brightsideoy/kama 0.1.0-alpha.16 → 0.1.0-alpha.161

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 (89) hide show
  1. package/README.md +169 -0
  2. package/bin/cli.js +686 -155
  3. package/content/definitions/block-layouts/three-column.yaml +20 -0
  4. package/content/definitions/block-layouts/two-column.yaml +29 -0
  5. package/content/definitions/blocks/button.yaml +24 -0
  6. package/content/definitions/blocks/header.yaml +13 -0
  7. package/content/definitions/blocks/header_image.yaml +21 -0
  8. package/content/definitions/blocks/hero.yaml +28 -0
  9. package/content/definitions/blocks/html.yaml +13 -0
  10. package/content/definitions/blocks/image_card.yaml +56 -0
  11. package/content/definitions/blocks/single_image.yaml +27 -0
  12. package/content/definitions/blocks/text.yaml +15 -0
  13. package/content/definitions/channels/ecommerce.yaml +28 -0
  14. package/content/definitions/channels/website.yaml +41 -0
  15. package/content/definitions/field-sets/access.yaml +27 -0
  16. package/content/definitions/field-sets/channel-base.yaml +50 -0
  17. package/content/definitions/field-sets/channel-seo.yaml +21 -0
  18. package/content/definitions/field-sets/comments.yaml +11 -0
  19. package/content/definitions/field-sets/content-block.yaml +41 -0
  20. package/content/definitions/field-sets/page-base.yaml +44 -0
  21. package/content/definitions/field-sets/page-seo.yaml +34 -0
  22. package/content/definitions/field-sets/page-social.yaml +78 -0
  23. package/content/definitions/field-sets/schema-type.yaml +43 -0
  24. package/content/definitions/field-sets/tree-base.yaml +31 -0
  25. package/content/definitions/field-sets/visibility.yaml +8 -0
  26. package/content/definitions/node-layouts/content.yml +12 -0
  27. package/content/definitions/node-layouts/landing-page.yml +27 -0
  28. package/content/definitions/node-layouts/single-column.yml +43 -0
  29. package/content/definitions/node-layouts/two-column.yml +48 -0
  30. package/content/definitions/nodes/address.yaml +40 -0
  31. package/content/definitions/nodes/article.yaml +37 -0
  32. package/content/definitions/nodes/event.yaml +55 -0
  33. package/content/definitions/nodes/external-link.yaml +29 -0
  34. package/content/definitions/nodes/page-skeleton.yaml +48 -0
  35. package/content/definitions/nodes/page.yaml +54 -0
  36. package/content/definitions/nodes/person.yaml +41 -0
  37. package/content/definitions/nodes/product.yaml +89 -0
  38. package/content/definitions/trees/navigation.yaml +24 -0
  39. package/definitions/editor/button.ts +11 -0
  40. package/definitions/editor/default.ts +21 -0
  41. package/definitions/editor/header.ts +36 -0
  42. package/definitions/editor/minimal.ts +15 -0
  43. package/definitions/validators/iban.ts +12 -0
  44. package/definitions/validators/magic-word.ts +7 -0
  45. package/dist/assets/index-C5TmJVER.css +1 -0
  46. package/dist/assets/index-jpMh2BZv.js +164 -0
  47. package/dist/dist-BNkCx-o-.js +8912 -0
  48. package/dist/index.html +15 -0
  49. package/dist/index.js +7646 -1562
  50. package/dist/schema-compiler.js +48 -0
  51. package/documentation/SCHEMAS.md +182 -0
  52. package/package.json +19 -17
  53. package/schema.sql +206 -0
  54. package/templates/astro-starter/astro.config.mjs +24 -0
  55. package/templates/astro-starter/package.json.template +20 -0
  56. package/templates/astro-starter/public/robots.txt +23 -0
  57. package/templates/astro-starter/src/components/blocks/BlockRenderer.astro +134 -0
  58. package/templates/astro-starter/src/components/blocks/ColumnLayout.astro +67 -0
  59. package/templates/astro-starter/src/components/blocks/HeaderImage.astro +132 -0
  60. package/templates/astro-starter/src/components/blocks/Hero.astro +218 -0
  61. package/templates/astro-starter/src/components/blocks/HtmlBlock.astro +25 -0
  62. package/templates/astro-starter/src/components/blocks/ImageBlock.astro +92 -0
  63. package/templates/astro-starter/src/components/blocks/TextBlock.astro +15 -0
  64. package/templates/astro-starter/src/components/global/Breadcrumb.astro +67 -0
  65. package/templates/astro-starter/src/components/global/Footer.astro +50 -0
  66. package/templates/astro-starter/src/components/global/Header.astro +177 -0
  67. package/templates/astro-starter/src/components/global/MetaHead.astro +266 -0
  68. package/templates/astro-starter/src/components/global/NavigationMenu.astro +318 -0
  69. package/templates/astro-starter/src/components/ui/KamaImage.astro +172 -0
  70. package/templates/astro-starter/src/layouts/SkeletonLayout.astro +47 -0
  71. package/templates/astro-starter/src/layouts/StandardLayout.astro +62 -0
  72. package/templates/astro-starter/src/lib/cms.ts +197 -0
  73. package/templates/astro-starter/src/lib/media/kama-image-service.ts +56 -0
  74. package/templates/astro-starter/src/lib/media/media-proxy.ts +129 -0
  75. package/templates/astro-starter/src/lib/media/media-url.ts +98 -0
  76. package/templates/astro-starter/src/middleware.ts +406 -0
  77. package/templates/astro-starter/src/pages/404.astro +46 -0
  78. package/templates/astro-starter/src/pages/[...slug].astro +238 -0
  79. package/templates/astro-starter/src/pages/llms-full.txt.ts +171 -0
  80. package/templates/astro-starter/src/pages/llms.txt.ts +113 -0
  81. package/templates/astro-starter/src/pages/media/[...path].ts +14 -0
  82. package/templates/astro-starter/src/pages/partials/footer.astro +69 -0
  83. package/templates/astro-starter/src/pages/partials/nav.astro +141 -0
  84. package/templates/astro-starter/src/styles/standard-theme.css +157 -0
  85. package/templates/astro-starter/src/utils/tiptap.ts +113 -0
  86. package/templates/astro-starter/tsconfig.json +0 -0
  87. package/templates/kama-worker/index.js +19 -0
  88. package/templates/kama-worker/server.js +98 -0
  89. package/wrangler.json +22 -0
@@ -0,0 +1,48 @@
1
+ import { fileURLToPath as e, pathToFileURL as t } from "node:url";
2
+ import { existsSync as n, mkdirSync as r, readFileSync as i, writeFileSync as a } from "node:fs";
3
+ import { dirname as o, join as s, resolve as c } from "node:path";
4
+ //#region scripts/schema-compiler.ts
5
+ var l = e(import.meta.url), u = o(l);
6
+ function d(e = process.cwd()) {
7
+ let t = e;
8
+ for (; t !== c(t, "..");) {
9
+ if (n(s(t, "content", "definitions"))) return t;
10
+ t = c(t, "..");
11
+ }
12
+ return process.cwd();
13
+ }
14
+ async function f() {
15
+ let e = d();
16
+ process.chdir(e);
17
+ let o = !1;
18
+ try {
19
+ JSON.parse(i(s(e, "package.json"), "utf-8")).name === "@brightsideoy/kama" && (o = !0);
20
+ } catch {}
21
+ let c;
22
+ if (o) {
23
+ let t = s(e, "server", "db");
24
+ n(t) || r(t, { recursive: !0 }), c = s(t, "bundled-definitions.js");
25
+ } else {
26
+ let t = s(e, "kama-worker");
27
+ n(t) || r(t, { recursive: !0 }), c = s(t, "bundled-definitions.js");
28
+ }
29
+ let l, f = s(u, "../server/schema/engine.ts"), p = s(u, "../dist/index.js");
30
+ if (n(f)) l = (await import(t(f).href)).compileDefinitions;
31
+ else if (n(p)) l = (await import(t(p).href)).compileDefinitions;
32
+ else try {
33
+ l = (await import("./dist-BNkCx-o-.js")).compileDefinitions;
34
+ } catch (e) {
35
+ console.error("❌ Could not resolve Kama Schema Engine:", e), process.exit(1);
36
+ }
37
+ l || (console.error("❌ compileDefinitions function missing from resolved module"), process.exit(1));
38
+ let m = l(), h = Object.keys(m?.nodes || {}).length, g = Object.keys(m?.blocks || {}).length, _ = `// AUTO-GENERATED BUNDLE - DO NOT EDIT
39
+ const defs = ${JSON.stringify(m, null, 2)};
40
+ export const BUNDLED_DEFINITIONS = defs;
41
+ export default defs;
42
+ `;
43
+ a(c, _, "utf-8"), console.log(`✅ Schema securely compiled using engine.ts to ${c} (${h} nodes, ${g} blocks)`);
44
+ }
45
+ f().catch((e) => {
46
+ console.error("❌ Schema Compiler Runtime Error:", e), process.exit(1);
47
+ });
48
+ //#endregion
@@ -0,0 +1,182 @@
1
+ # Kama CMS Schema Documentation
2
+
3
+ Kama CMS uses a highly dynamic, YAML-driven schema system. Instead of hardcoding database tables and React/Vue forms, you define your content models, layouts, and components using simple YAML files. The Kama build pipeline automatically generates the admin UI, database indexes, and API endpoints based on these definitions.
4
+
5
+ Here is the complete guide to understanding, overriding, and creating custom schemas in your Kama project.
6
+
7
+ ## 1. Directory Structure: What Goes Where?
8
+
9
+ Your project’s schemas live in the `content/definitions/` (or `schemas/`) directory. The folder structure dictates how the CMS interprets the YAML file:
10
+
11
+ | Directory | Purpose | Example Use Case |
12
+ | :---------------- | :---------------------------------------------------------------- | :----------------------------------------------------------- |
13
+ | `/nodes/` | Standalone content entities (Pages, Articles, Products, Persons). | A blog post, a product page, or an office location. |
14
+ | `/blocks/` | Reusable content components placed on a page canvas. | A hero banner, a text block, a call-to-action button. |
15
+ | `/node-layouts/` | Page-level CSS Grid templates that define regions (drop zones). | A standard landing page, a 3-column layout with a sidebar. |
16
+ | `/block-layouts/` | Component-level layout wrappers for grouping blocks. | A 50/50 split column block. |
17
+ | `/channels/` | Top-level endpoints (websites, apps, stores). | A primary marketing website or a secondary e-commerce store. |
18
+ | `/trees/` | Hierarchical structures for navigation and routing. | Main menu, footer links, social links. |
19
+ | `/field-sets/` | Reusable groupings of fields to keep your YAML DRY. | Standard SEO fields, Access/Visibility settings. |
20
+
21
+ ## 2. Overriding and Adding Custom Schemas
22
+
23
+ The Kama schema pipeline merges core package defaults with your local project files.
24
+
25
+ ### How to Override a Core Schema
26
+
27
+ To modify a default Kama schema (e.g., `page.yaml` or `address.yaml`), create a file with the **exact same name** in your local workspace:
28
+ `your-project/content/definitions/nodes/page.yaml`
29
+
30
+ During the build process (`npm run bundle:schemas`), the local file completely overwrites the core package file.
31
+
32
+ ### How to Add a Custom Schema
33
+
34
+ To create a completely new content type (e.g., a Job Posting), simply add a new file:
35
+ `your-project/content/definitions/nodes/job-posting.yaml`
36
+
37
+ Run `npm run deploy:cms` to deploy the worker. The CMS will automatically register the new schema, create database indexes, and generate the UI.
38
+
39
+ ## 3. Schema Anatomy (Top-Level Parameters)
40
+
41
+ Every YAML file represents a Schema object. Below are the top-level parameters you can define at the root of your YAML file.
42
+
43
+ ### Base Parameters
44
+
45
+ - `id` _(string)_: Unique identifier. Should match the filename (e.g., `image_card`).
46
+ - `label` _(string)_: Human-readable name displayed in the CMS Admin UI.
47
+ - `description` _(string)_: Optional subtext shown in block pickers or menus.
48
+
49
+ ### Node/Content Behaviors
50
+
51
+ - `inTree` _(boolean)_: If `true`, this node can be created in the "Site Tree" (usually for pages/routes).
52
+ - `inVault` _(boolean)_: If `true`, this node appears in the "Vault" (usually for structured data like Products, Addresses, Persons).
53
+ - `hasCanvas` _(boolean)_: If `true`, enables the visual drag-and-drop page builder for this node.
54
+ - `directLinking` _(boolean)_: If `true`, the node is resolvable to a frontend URL/slug (e.g., an Article or Page).
55
+
56
+ ### Layout & Canvas Restrictions
57
+
58
+ - `defaultLayout` _(string)_: The fallback layout ID if none is selected (e.g., `"single-column"`).
59
+ - `allowedLayouts` _(array)_: List of layout IDs this node is permitted to use.
60
+ - `allowedBlocks` _(array)_: Restricts which blocks can be dropped onto this node's canvas (e.g., `[header, hero, text, image_card]`).
61
+
62
+ ### Grid & Regions (For Layouts)
63
+
64
+ Used primarily in `/node-layouts/` and `/block-layouts/`.
65
+
66
+ - `layout`: Defines the CSS Grid structure.
67
+ - `columns` _(string)_: CSS grid-template-columns (e.g., `"10fr 4fr"`).
68
+ - `areas` _(array of arrays)_: Maps regions to the grid layout.
69
+ - `regions`: Configuration for the specific drop zones defined in the `areas`.
70
+ - `label` _(string)_: UI label for the drop zone.
71
+ - `showLabel` _(boolean)_: Shows/hides the header in the editor.
72
+ - `inherit` _(string)_: Can inherit content from `"parent"`.
73
+
74
+ ## 4. Tabs & Fields Architecture
75
+
76
+ Content input in Kama is organized into **Tabs**, which contain **Fields**.
77
+
78
+ ```yaml
79
+ tabs:
80
+ settings: # Tab ID
81
+ label: "Settings" # Tab UI Name
82
+ fields:
83
+ # Key-Value map of fields
84
+ title:
85
+ label: "Title"
86
+ type: "text"
87
+ required: true
88
+ ```
89
+
90
+ ## 5. Field Types (`type`)
91
+
92
+ The `type` parameter determines the UI component used in the CMS and how data is handled.
93
+
94
+ - `text`: Standard single-line text input.
95
+ - `textarea`: Multi-line text input.
96
+ - `richtext`: WYSIWYG editor. (Use `editor: "minimal"`, `"default"`, or `"button"` to configure toolbars).
97
+ - `number`: Numeric input.
98
+ - `boolean`: Toggle switch.
99
+ - `date` / `datetime`: Date/Time picker.
100
+ - `slug`: URL-friendly string generator. Often paired with `linkedTo: "title"`.
101
+ - `select`: Dropdown menu. (Requires an `options` array).
102
+ - `layout-select`: Specialized dropdown that queries available layout schemas.
103
+ - `language-select`: Specialized dropdown for locales.
104
+ - `link`: A unified link picker (internal nodes, trees, URLs, files, emails, tel).
105
+ - `icon`: Icon picker. (Configure with `packages: ["heroicons", "lucide"]`).
106
+ - `asset` / `image`: Cloudflare R2 media uploader.
107
+ - `hidden`: Hidden from the UI, used for programmatic data.
108
+
109
+ ## 6. Field Configuration Parameters
110
+
111
+ Inside a specific field, you can define how it looks, validates, and behaves in the database.
112
+
113
+ ### UI & Display
114
+
115
+ - `label` _(string)_: Field label.
116
+ - `description` _(string)_: Helper text below the input.
117
+ - `default` _(any)_: Default value when a new entry is created.
118
+ - `position` _(string)_: Set to `"sidebar"` to move the field out of the main content column.
119
+ - `showAsEditorTitle` _(boolean)_: Uses this field's value as the display title in Vault/Tree lists.
120
+ - `public` _(boolean)_: **CRITICAL.** If `true`, this field is exposed in the public frontend API. If omitted or `false`, the data remains private to the admin backend.
121
+
122
+ ### Database & Indexing
123
+
124
+ - `dbType` _(string)_: The SQLite data type (e.g., `text`, `integer`, `json`). Most custom fields live inside a JSON blob, but native columns require this.
125
+ - `index` _(boolean)_: Instructs the build pipeline to generate a SQLite index for this field for fast querying.
126
+ - `primaryKey` _(boolean)_: Defines the ID column.
127
+
128
+ ### Validation (`rules`)
129
+
130
+ - `required` _(boolean)_: Makes the field mandatory. (Can also be set outside of `rules`).
131
+ - `unique` / `uniqueSibling` _(boolean)_: Enforces uniqueness across the DB or tree branch.
132
+ - `pattern` _(string)_: Regex validation (e.g., `"^[a-z0-9_-]+$"`).
133
+ - `customMessage` _(string)_: Error message when validation fails.
134
+
135
+ ### Linking & Inheritance
136
+
137
+ - `linkedTo` _(string)_: Automatically populates this field based on another (e.g., auto-generating a slug from the title).
138
+ - `inherits` / `inheritsFromParent` _(string/boolean)_: Pulls a fallback value from a parent node if left blank.
139
+
140
+ ## 7. Media & Asset Specifics (`asset` / `image`)
141
+
142
+ Kama features a powerful R2-backed media engine with automated image cropping.
143
+
144
+ ```yaml
145
+ featuredImage:
146
+ label: Main Image
147
+ type: asset
148
+ multiple: false
149
+ maxSizeMB: 15
150
+ maxPreviewWidth: 300px
151
+ allowedTypes: ["image/jpeg", "image/png", "image/webp"]
152
+ crops:
153
+ list_thumb:
154
+ label: "List View (4:3)"
155
+ aspectRatio: 1.333
156
+ catalog_card:
157
+ label: "Portrait Card (3:4)"
158
+ aspectRatio: 0.75
159
+ ```
160
+
161
+ - `multiple` _(boolean)_: Enables multi-file upload (Gallery).
162
+ - `allowedTypes` _(array)_: MIME type restrictions.
163
+ - `crops`: A map of required crop aspect ratios. The CMS will prompt the editor to crop the image to these exact ratios upon upload. The frontend API returns optimized URLs for each crop key.
164
+
165
+ ## 8. Field Sets (Inclusions)
166
+
167
+ To keep your schemas DRY, Kama allows you to group common fields in `/field-sets/` and import them into other schemas.
168
+
169
+ For example, `content/definitions/field-sets/page-meta.yaml` defines SEO fields.
170
+
171
+ **How to include a Field Set in a Schema:**
172
+ Use the `fieldSet` keyword inside your fields list.
173
+
174
+ ```yaml
175
+ tabs:
176
+ settings:
177
+ fields:
178
+ title:
179
+ label: "Title"
180
+ # Imports the entire page-base YAML here
181
+ fieldSet: page-base
182
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightsideoy/kama",
3
- "version": "0.1.0-alpha.16",
3
+ "version": "0.1.0-alpha.161",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -11,21 +11,23 @@
11
11
  }
12
12
  },
13
13
  "bin": {
14
- "kama": "./bin/cli.js",
15
- "create-kama": "./bin/cli.js"
14
+ "kama": "bin/cli.js",
15
+ "create-kama": "bin/cli.js"
16
16
  },
17
- "files": [
18
- "dist",
19
- "bin"
20
- ],
21
17
  "scripts": {
22
- "dev": "concurrently \"vite\" \"tsx watch server/index.ts\"",
23
- "build": "vite build",
24
- "build:cms": "vite build",
25
- "build:seed-sql": "tsx src/db/generate-sql.ts",
26
- "db:seed": "npm run build:seed-sql && tsx -e \"import Database from 'better-sqlite3'; import fs from 'node:fs'; const db = new Database('data/cms.db'); db.exec(fs.readFileSync('seed.sql', 'utf8')); console.log('✅ Database seeded successfully!');\"",
27
- "d1:seed:remote": "npm run build:seed-sql && npx wrangler d1 execute YOUR_D1_NAME --remote --file=./seed.sql",
28
- "serve": "tsx server/index.ts"
18
+ "predev": "npm run bundle:definitions && npm run build:schema",
19
+ "dev": "npm run dev:local",
20
+ "dev:local": "concurrently \"vite\" \"KAMA_RUNTIME=node tsx server/dev.ts\"",
21
+ "dev:cf": "concurrently \"vite\" \"wrangler dev server/index.ts\"",
22
+ "bundle:definitions": "node bin/cli.js bundle",
23
+ "build:schema": "tsx scripts/build-schema.ts",
24
+ "build:seed": "tsx scripts/build-seed.ts",
25
+ "build:server": "vite build",
26
+ "build:ui": "vite build --config vite.config.ui.ts",
27
+ "build": "npm run bundle:definitions && npm run build:schema && npm run build:server && npm run build:ui",
28
+ "build:cms": "npm run build",
29
+ "prepublishOnly": "npm run build",
30
+ "serve": "wrangler dev server/index.ts"
29
31
  },
30
32
  "publishConfig": {
31
33
  "access": "public"
@@ -43,7 +45,6 @@
43
45
  "@tiptap/pm": "^3.30.0",
44
46
  "@tiptap/starter-kit": "^3.30.0",
45
47
  "@tiptap/vue-3": "^3.30.0",
46
- "better-sqlite3": "^13.0.3",
47
48
  "drizzle-orm": "^0.45.2",
48
49
  "hono": "^4.13.2",
49
50
  "locale-codes": "^1.3.1",
@@ -58,7 +59,7 @@
58
59
  "devDependencies": {
59
60
  "@iconify-json/heroicons": "^1.2.3",
60
61
  "@iconify-json/lucide": "^1.2.123",
61
- "@types/better-sqlite3": "^9.6.0",
62
+ "@libsql/client": "^0.14.0",
62
63
  "@types/node": "^26.2.0",
63
64
  "@types/sharp": "^0.31.1",
64
65
  "@types/yaml": "^1.9.6",
@@ -69,6 +70,7 @@
69
70
  "typescript": "~5.7.2",
70
71
  "unplugin-icons": "^23.0.1",
71
72
  "vite": "^8.2.1",
72
- "vue-tsc": "^2.2.0"
73
+ "vue-tsc": "^2.2.0",
74
+ "wrangler": "^4.134.0"
73
75
  }
74
76
  }
package/schema.sql ADDED
@@ -0,0 +1,206 @@
1
+ -- Kama Core System Schema
2
+ -- Auto-generated from server/db/schema.ts
3
+
4
+ CREATE TABLE IF NOT EXISTS "content_schemas" (
5
+ id TEXT PRIMARY KEY,
6
+ name TEXT NOT NULL,
7
+ type TEXT UNIQUE NOT NULL,
8
+ schemaJson TEXT NOT NULL,
9
+ isSystem INTEGER DEFAULT 1,
10
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
11
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
12
+ );
13
+
14
+ CREATE TABLE IF NOT EXISTS "users" (
15
+ id TEXT PRIMARY KEY,
16
+ email TEXT UNIQUE NOT NULL,
17
+ name TEXT NOT NULL,
18
+ globalRole TEXT DEFAULT 'editor',
19
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
20
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
21
+ );
22
+
23
+ CREATE TABLE IF NOT EXISTS "user_accounts" (
24
+ id TEXT PRIMARY KEY,
25
+ userId TEXT NOT NULL,
26
+ provider TEXT NOT NULL,
27
+ providerAccountId TEXT NOT NULL,
28
+ passwordHash TEXT NOT NULL,
29
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
30
+ FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE,
31
+ UNIQUE(provider, providerAccountId)
32
+ );
33
+
34
+ CREATE TABLE IF NOT EXISTS "sessions" (
35
+ id TEXT PRIMARY KEY,
36
+ userId TEXT NOT NULL,
37
+ expiresAt TEXT NOT NULL,
38
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
39
+ FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE
40
+ );
41
+
42
+ CREATE TABLE IF NOT EXISTS "channels" (
43
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
44
+ key TEXT UNIQUE NOT NULL,
45
+ label TEXT NOT NULL,
46
+ channelType TEXT,
47
+ domain TEXT,
48
+ baseUrl TEXT,
49
+ defaultLanguage TEXT,
50
+ isHidden INTEGER NOT NULL DEFAULT 0,
51
+ dataJson TEXT,
52
+ createdById TEXT,
53
+ updatedById TEXT,
54
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
55
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
56
+ FOREIGN KEY (createdById) REFERENCES users(id) ON DELETE SET NULL,
57
+ FOREIGN KEY (updatedById) REFERENCES users(id) ON DELETE SET NULL
58
+ );
59
+
60
+ CREATE TABLE IF NOT EXISTS "trees" (
61
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
62
+ key TEXT UNIQUE NOT NULL,
63
+ label TEXT NOT NULL,
64
+ treeType TEXT,
65
+ channelId INTEGER,
66
+ isHidden INTEGER NOT NULL DEFAULT 0,
67
+ dataJson TEXT,
68
+ createdById TEXT,
69
+ updatedById TEXT,
70
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
71
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
72
+ FOREIGN KEY (channelId) REFERENCES channels(id) ON DELETE SET NULL,
73
+ FOREIGN KEY (createdById) REFERENCES users(id) ON DELETE SET NULL,
74
+ FOREIGN KEY (updatedById) REFERENCES users(id) ON DELETE SET NULL
75
+ );
76
+
77
+ CREATE TABLE IF NOT EXISTS "channel_trees" (
78
+ channelId INTEGER NOT NULL,
79
+ treeId INTEGER NOT NULL,
80
+ key TEXT NOT NULL DEFAULT 'main',
81
+ metaJson TEXT,
82
+ PRIMARY KEY (channelId, treeId, key),
83
+ FOREIGN KEY (channelId) REFERENCES channels(id) ON DELETE CASCADE,
84
+ FOREIGN KEY (treeId) REFERENCES trees(id) ON DELETE CASCADE
85
+ );
86
+
87
+ CREATE TABLE IF NOT EXISTS "content_nodes" (
88
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
89
+ contentType TEXT NOT NULL,
90
+ title TEXT NOT NULL,
91
+ slug TEXT,
92
+ language TEXT,
93
+ isHidden INTEGER NOT NULL DEFAULT 0,
94
+ status TEXT DEFAULT 'draft',
95
+ hasUnpublishedChanges INTEGER DEFAULT 0,
96
+ disabledRegionsJson TEXT DEFAULT '{}',
97
+ dataJson TEXT,
98
+ draftDataJson TEXT,
99
+ publishedDataJson TEXT,
100
+ publishedAt TEXT,
101
+ createdById TEXT,
102
+ updatedById TEXT,
103
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
104
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
105
+ deletedAt TEXT,
106
+ FOREIGN KEY (createdById) REFERENCES users(id) ON DELETE SET NULL,
107
+ FOREIGN KEY (updatedById) REFERENCES users(id) ON DELETE SET NULL
108
+ );
109
+
110
+ CREATE TABLE IF NOT EXISTS "node_channels" (
111
+ nodeId INTEGER NOT NULL,
112
+ channelId INTEGER NOT NULL,
113
+ PRIMARY KEY (nodeId, channelId),
114
+ FOREIGN KEY (nodeId) REFERENCES content_nodes(id) ON DELETE CASCADE,
115
+ FOREIGN KEY (channelId) REFERENCES channels(id) ON DELETE CASCADE
116
+ );
117
+
118
+ CREATE TABLE IF NOT EXISTS "tree_nodes" (
119
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
120
+ treeId INTEGER NOT NULL,
121
+ contentId INTEGER NOT NULL,
122
+ parentId INTEGER,
123
+ sortOrder INTEGER DEFAULT 0,
124
+ FOREIGN KEY (treeId) REFERENCES trees(id) ON DELETE CASCADE,
125
+ FOREIGN KEY (contentId) REFERENCES content_nodes(id) ON DELETE CASCADE,
126
+ FOREIGN KEY (parentId) REFERENCES tree_nodes(id) ON DELETE CASCADE
127
+ );
128
+
129
+ CREATE TABLE IF NOT EXISTS "content_blocks" (
130
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
131
+ contentId INTEGER NOT NULL,
132
+ region TEXT DEFAULT 'main',
133
+ elementType TEXT NOT NULL,
134
+ dataJson TEXT,
135
+ sortOrder INTEGER DEFAULT 0,
136
+ isDraft INTEGER NOT NULL DEFAULT 1,
137
+ FOREIGN KEY (contentId) REFERENCES content_nodes(id) ON DELETE CASCADE
138
+ );
139
+
140
+ CREATE TABLE IF NOT EXISTS "node_revisions" (
141
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
142
+ nodeId INTEGER NOT NULL,
143
+ version INTEGER NOT NULL DEFAULT 1,
144
+ snapshotJson TEXT NOT NULL,
145
+ createdById TEXT,
146
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
147
+ FOREIGN KEY (nodeId) REFERENCES content_nodes(id) ON DELETE CASCADE,
148
+ FOREIGN KEY (createdById) REFERENCES users(id) ON DELETE SET NULL
149
+ );
150
+
151
+ CREATE TABLE IF NOT EXISTS "assets" (
152
+ id TEXT PRIMARY KEY,
153
+ filename TEXT NOT NULL,
154
+ mimeType TEXT NOT NULL,
155
+ sizeBytes INTEGER NOT NULL,
156
+ storageKeyPrivate TEXT,
157
+ cdnUrl TEXT,
158
+ parentAssetId TEXT,
159
+ cropCoordinatesJson TEXT,
160
+ isSystemVariant INTEGER DEFAULT 0,
161
+ dataJson TEXT DEFAULT '{}',
162
+ createdById TEXT,
163
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
164
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
165
+ deletedAt TEXT,
166
+ FOREIGN KEY (parentAssetId) REFERENCES assets(id) ON DELETE SET NULL,
167
+ FOREIGN KEY (createdById) REFERENCES users(id) ON DELETE SET NULL
168
+ );
169
+
170
+ CREATE TABLE IF NOT EXISTS "node_assets" (
171
+ nodeId INTEGER NOT NULL,
172
+ assetId TEXT NOT NULL,
173
+ fieldKey TEXT NOT NULL,
174
+ sortOrder INTEGER DEFAULT 0,
175
+ PRIMARY KEY (nodeId, assetId, fieldKey),
176
+ FOREIGN KEY (nodeId) REFERENCES content_nodes(id) ON DELETE CASCADE,
177
+ FOREIGN KEY (assetId) REFERENCES assets(id) ON DELETE CASCADE
178
+ );
179
+
180
+ CREATE INDEX IF NOT EXISTS idx_tree_nodes_tree_parent ON tree_nodes(treeId, parentId);
181
+ CREATE INDEX IF NOT EXISTS idx_tree_nodes_contentId ON tree_nodes(contentId);
182
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_slug_status ON content_nodes(slug, status);
183
+ CREATE INDEX IF NOT EXISTS idx_content_blocks_node ON content_blocks(contentId, isDraft);
184
+ CREATE INDEX IF NOT EXISTS idx_node_assets_nodeId ON node_assets(nodeId);
185
+ CREATE INDEX IF NOT EXISTS idx_node_assets_assetId ON node_assets(assetId);
186
+ CREATE INDEX IF NOT EXISTS idx_content_schemas_type ON content_schemas(type);
187
+ CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(userId);
188
+ CREATE INDEX IF NOT EXISTS idx_sessions_expires ON sessions(expiresAt);
189
+ CREATE INDEX IF NOT EXISTS idx_channels_visibility ON channels(isHidden);
190
+ CREATE INDEX IF NOT EXISTS idx_trees_visibility ON trees(isHidden);
191
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_visibility ON content_nodes(isHidden, status);
192
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_slug ON content_nodes(slug);
193
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_type ON content_nodes(contentType);
194
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_deleted_updated ON content_nodes(deletedAt, updatedAt DESC);
195
+ CREATE INDEX IF NOT EXISTS idx_tree_nodes_hierarchy ON tree_nodes(treeId, parentId, sortOrder);
196
+ CREATE INDEX IF NOT EXISTS idx_tree_nodes_content ON tree_nodes(contentId, treeId);
197
+ CREATE INDEX IF NOT EXISTS idx_content_blocks_content ON content_blocks(contentId, isDraft, region, sortOrder);
198
+ CREATE INDEX IF NOT EXISTS idx_node_channels_channel ON node_channels(channelId, nodeId);
199
+ CREATE INDEX IF NOT EXISTS idx_node_channels_node ON node_channels(nodeId, channelId);
200
+ CREATE INDEX IF NOT EXISTS idx_node_assets_node_asset ON node_assets(nodeId, assetId);
201
+ CREATE INDEX IF NOT EXISTS idx_node_assets_asset_node ON node_assets(assetId, nodeId);
202
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_node_revisions_version ON node_revisions(nodeId, version);
203
+ CREATE INDEX IF NOT EXISTS idx_assets_system_created ON assets(isSystemVariant, createdAt DESC);
204
+ CREATE INDEX IF NOT EXISTS idx_assets_system_mime ON assets(isSystemVariant, mimeType);
205
+ CREATE INDEX IF NOT EXISTS idx_assets_deleted ON assets(deletedAt);
206
+ CREATE INDEX IF NOT EXISTS idx_assets_parent ON assets(parentAssetId);
@@ -0,0 +1,24 @@
1
+ import { defineConfig } from 'astro/config';
2
+ import node from '@astrojs/node';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+
8
+ export default defineConfig({
9
+ output: 'server',
10
+ trailingSlash: 'ignore',
11
+ image: { service: { entrypoint: './src/lib/media/kama-image-service.ts' } },
12
+ adapter: node({ mode: "standalone" }),
13
+ vite: {
14
+ resolve: { alias: { '@': path.resolve(__dirname, './src') } },
15
+ ssr: {
16
+ external: ['cloudflare:workers', 'tinyexec', 'node:child_process', 'node:fs', 'node:url', 'node:path', 'node:stream', 'node:readline', 'node:stream/promises']
17
+ },
18
+ environments: {
19
+ ssr: {
20
+ external: ['cloudflare:workers', 'tinyexec', 'node:child_process', 'node:fs', 'node:url', 'node:path', 'node:stream', 'node:readline', 'node:stream/promises']
21
+ }
22
+ }
23
+ }
24
+ });
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "{{SITE_NAME}}",
3
+ "type": "module",
4
+ "version": "0.0.1",
5
+ "engines": {
6
+ "node": ">=22.12.0"
7
+ },
8
+ "scripts": {
9
+ "dev": "astro dev",
10
+ "build": "astro build",
11
+ "preview": "astro preview",
12
+ "deploy": "astro build && wrangler deploy"
13
+ },
14
+ "dependencies": {
15
+ "@astrojs/cloudflare": "^14.2.5",
16
+ "@brightsideoy/kama": "{{KAMA_VERSION}}",
17
+ "astro": "^7.2.0",
18
+ "wrangler": "^4.0.0"
19
+ }
20
+ }
@@ -0,0 +1,23 @@
1
+ # Allow standard search engine crawlers
2
+ User-agent: *
3
+ Allow: /
4
+
5
+ # Block sensitive or irrelevant paths
6
+ Disallow: /api/
7
+ Disallow: /*?*search=
8
+
9
+ # Explicitly block AI scrapers / Training Bots (Optional)
10
+ # User-agent: GPTBot
11
+ # Disallow: /
12
+
13
+ # User-agent: CCBot
14
+ # Disallow: /
15
+
16
+ # User-agent: Anthropic-ai
17
+ # Disallow: /
18
+
19
+ # User-agent: Google-Extended
20
+ # Disallow: /
21
+
22
+ # XML Sitemap location
23
+ # Sitemap: https://yourdomain.com/sitemap-index.xml