@alstar/studio 0.0.0-beta.1 → 0.0.0-beta.11

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 (69) hide show
  1. package/api/api-key.ts +69 -0
  2. package/api/auth.ts +66 -0
  3. package/api/backup.ts +40 -0
  4. package/api/block.ts +131 -66
  5. package/api/index.ts +19 -1
  6. package/api/mcp.ts +50 -0
  7. package/components/AdminPanel.ts +87 -0
  8. package/components/Backup.ts +13 -0
  9. package/components/BlockFieldRenderer.ts +125 -0
  10. package/components/BlockRenderer.ts +22 -0
  11. package/components/Entries.ts +20 -12
  12. package/components/Entry.ts +13 -21
  13. package/components/FieldRenderer.ts +35 -0
  14. package/components/Render.ts +46 -0
  15. package/components/Settings.ts +104 -0
  16. package/components/SiteLayout.ts +61 -0
  17. package/components/Users.ts +46 -0
  18. package/components/fields/Markdown.ts +44 -0
  19. package/components/fields/Slug.ts +113 -0
  20. package/components/fields/Text.ts +42 -0
  21. package/components/fields/index.ts +7 -0
  22. package/components/icons.ts +136 -7
  23. package/index.ts +94 -34
  24. package/package.json +10 -7
  25. package/pages/entry/[id].ts +15 -0
  26. package/pages/error.ts +14 -0
  27. package/{components → pages}/index.ts +7 -4
  28. package/pages/login.ts +21 -0
  29. package/pages/register.ts +33 -0
  30. package/pages/settings.ts +8 -0
  31. package/public/studio/css/admin-panel.css +103 -0
  32. package/public/studio/css/blocks-field.css +53 -0
  33. package/public/studio/css/settings.css +28 -0
  34. package/public/studio/js/markdown-editor.js +34 -0
  35. package/public/studio/js/sortable-list.js +50 -0
  36. package/public/studio/main.css +166 -0
  37. package/public/studio/main.js +21 -0
  38. package/queries/block-2.ts +339 -0
  39. package/queries/block-with-children.ts +74 -0
  40. package/queries/block.ts +289 -0
  41. package/queries/db-types.ts +15 -0
  42. package/queries/getBlockTrees-2.ts +71 -0
  43. package/queries/getBlockTrees.ts +316 -0
  44. package/queries/getBlocks.ts +214 -0
  45. package/queries/index.ts +2 -98
  46. package/queries/structure-types.ts +97 -0
  47. package/readme.md +205 -0
  48. package/schema.sql +18 -0
  49. package/schemas.ts +23 -52
  50. package/types.ts +144 -5
  51. package/utils/auth.ts +54 -0
  52. package/utils/buildBlocksTree.ts +4 -4
  53. package/utils/create-hash.ts +9 -0
  54. package/utils/define.ts +39 -0
  55. package/utils/file-based-router.ts +11 -2
  56. package/utils/get-config.ts +8 -9
  57. package/utils/get-or-create-row.ts +41 -0
  58. package/utils/html.ts +247 -0
  59. package/utils/startup-log.ts +19 -0
  60. package/components/AdminPanel/AdminPanel.css +0 -59
  61. package/components/AdminPanel/AdminPanel.ts +0 -57
  62. package/components/Block.ts +0 -116
  63. package/components/Entry.css +0 -7
  64. package/components/Field.ts +0 -164
  65. package/components/Fields.ts +0 -43
  66. package/components/layout.ts +0 -53
  67. package/public/main.css +0 -92
  68. package/public/main.js +0 -43
  69. /package/public/{favicon.svg → studio/favicon.svg} +0 -0
@@ -0,0 +1,97 @@
1
+ // structure-types.ts
2
+ import { type DBBase } from "./db-types.ts";
3
+
4
+ /**
5
+ * Extract helpers
6
+ */
7
+ type ArrayElement<T> = T extends readonly (infer U)[] ? U : never;
8
+
9
+ /**
10
+ * Field definition shape inferred from defineField(...) (the runtime helper)
11
+ * We keep it generic as "any" shape but with the important properties present
12
+ */
13
+ type FieldDef = {
14
+ readonly name: string;
15
+ readonly type: string;
16
+ readonly fields?: readonly any[]; // only present when type === 'blocks'
17
+ };
18
+
19
+ /**
20
+ * Block definition shape inferred from defineBlock(...) (the runtime helper)
21
+ */
22
+ type BlockDef = {
23
+ readonly name: string;
24
+ readonly type: string;
25
+ readonly fields?: readonly FieldDef[];
26
+ };
27
+
28
+ /**
29
+ * Primitive field node (non-'blocks'): DBBase + kept fields but no children
30
+ */
31
+ type PrimitiveFieldNode<TField extends FieldDef> =
32
+ DBBase & {
33
+ readonly name: TField["name"];
34
+ readonly type: TField["type"];
35
+ // no children (leaf), no nested fields
36
+ readonly children?: [];
37
+ readonly fields?: {}; // empty object for leaf
38
+ };
39
+
40
+ /**
41
+ * For 'blocks' typed field, we need:
42
+ * - the block node representing the 'blocks' wrapper (has DBBase props)
43
+ * - its 'children' are an array of BlockNodes corresponding to nested block defs supplied in the field's 'fields' array
44
+ * - its 'fields' property is the mapping of its own child-field names (can be empty)
45
+ */
46
+ type BlocksFieldNode<
47
+ TField extends FieldDef,
48
+ TFieldDefs extends readonly BlockDef[]
49
+ > = DBBase & {
50
+ readonly name: TField["name"]; // e.g. "blocks" or "images"
51
+ readonly type: "blocks"; // literally 'blocks'
52
+ readonly children: BlockNodeFromBlockDefs<TFieldDefs>[]; // children are instances of the nested blocks
53
+ readonly fields: FieldsFromFieldDefs<TFieldDefs[number]["fields"]>; // the blocks-wrapper's own fields mapping (if any)
54
+ };
55
+
56
+ /**
57
+ * Build the 'fields' object for a set of FieldDef[].
58
+ * Maps each field name -> either PrimitiveFieldNode or BlocksFieldNode recursively.
59
+ */
60
+ type FieldsFromFieldDefs<TDefs> =
61
+ // If no fields
62
+ TDefs extends readonly any[]
63
+ ? {
64
+ // For each field F in TDefs, map F['name'] -> node type
65
+ [F in ArrayElement<TDefs> as F extends { name: infer N extends string } ? N : never]:
66
+ F extends { type: "blocks"; fields: readonly BlockDef[] }
67
+ ? BlocksFieldNode<F, F["fields"]>
68
+ : PrimitiveFieldNode<F>;
69
+ }
70
+ : {};
71
+
72
+ /**
73
+ * A Block node type for a particular BlockDef.
74
+ * - fields: mapping derived from the block's declared fields
75
+ * - children: by default [], because in our final shape all immediate children are placed under 'fields' of the parent.
76
+ * BUT for nodes that are themselves 'blocks' wrappers (i.e. appear as a Block instance of a nested block def),
77
+ * their 'children' will contain actual child blocks (these are handled via BlocksFieldNode above).
78
+ */
79
+ export type BlockNode<T extends BlockDef> = DBBase & {
80
+ readonly name: T["name"];
81
+ readonly type: T["type"];
82
+ readonly fields: FieldsFromFieldDefs<T["fields"]>;
83
+ // for regular block nodes, children will usually be [] (top-level parent's children moved into fields)
84
+ readonly children: [];
85
+ };
86
+
87
+ /**
88
+ * Construct BlockNode unions for a set of block defs (used when blocks field has multiple block subdefs)
89
+ */
90
+ type BlockNodeFromBlockDefs<TDefs extends readonly BlockDef[]> =
91
+ ArrayElement<TDefs> extends infer B ? (B extends BlockDef ? BlockNode<B> : never) : never;
92
+
93
+ /**
94
+ * The top-level forest return type when you pass a structure: it's an array of BlockNode of any top-level BlockDef
95
+ */
96
+ export type BlockTreeFromStructure<TStructure extends readonly BlockDef[]> =
97
+ BlockNodeFromBlockDefs<TStructure>;
package/readme.md ADDED
@@ -0,0 +1,205 @@
1
+ # Alstar Studio
2
+
3
+ Alstar Studio is a **fullstack framework** for building CMS-driven applications with **native Node.js** and **Hono**.
4
+
5
+
6
+ ## Installation
7
+
8
+ Create a new project:
9
+
10
+ ```sh
11
+ pnpm create @alstar
12
+ ```
13
+
14
+ Follow the CLI prompts to set up a starter project in your chosen folder.
15
+
16
+
17
+ ## Development
18
+
19
+ Start the dev server:
20
+
21
+ ```sh
22
+ pnpm run dev
23
+ ```
24
+
25
+ This runs a **Hono server**.
26
+
27
+ The core app is created via `createStudio(structure)`, which returns the Hono app. This makes it possible to extend the server with plugins or custom settings:
28
+
29
+ ```ts
30
+ const app = await createStudio(structure)
31
+ // app.use(...) custom middleware
32
+ ```
33
+
34
+
35
+ ## Routing
36
+
37
+ Pages are defined in the `/pages` directory.
38
+
39
+ * Each `.ts` file becomes a route.
40
+ * Dynamic routes are created with square brackets, e.g. `/pages/[slug].ts`.
41
+
42
+
43
+ ## CMS
44
+
45
+ Access the CMS at:
46
+
47
+ ```
48
+ /studio
49
+ ```
50
+
51
+ ### Defining Content Structure
52
+
53
+ Pass a `Structure` object to `createStudio(structure)` to define the schema.
54
+
55
+ Use the helpers:
56
+
57
+ ```ts
58
+ import {
59
+ defineBlock,
60
+ defineField,
61
+ defineStructure,
62
+ defineBlockField
63
+ } from '@alstar/studio'
64
+ ```
65
+
66
+ ### Example: Schema Definition
67
+
68
+ ```ts
69
+ const titleField = defineField({
70
+ label: 'Title',
71
+ type: 'text' | 'image' | 'markdown' | 'slug',
72
+ description: 'Page title'
73
+ })
74
+
75
+ const pageBuilder = defineBlockField({
76
+ label: 'Sections',
77
+ type: 'blocks',
78
+ children: {
79
+ hero: defineBlock({
80
+ label: 'Hero',
81
+ type: 'hero',
82
+ fields: { /* fields */ },
83
+ }),
84
+ gallery: defineBlock({
85
+ label: 'Gallery',
86
+ type: 'gallery',
87
+ fields: { /* fields */ },
88
+ }),
89
+ },
90
+ })
91
+
92
+ const entryBlock = defineBlock({
93
+ label: 'Entry',
94
+ type: 'entry',
95
+ fields: {
96
+ title: titleField,
97
+ builder: pageBuilder
98
+ }
99
+ })
100
+
101
+ export default defineStructure({
102
+ entry: entryBlock
103
+ })
104
+ ```
105
+
106
+ ### Concepts
107
+
108
+ * **Blocks** contain **fields**.
109
+ * **Block fields** (`type: 'blocks'`) can nest multiple block types under `children`.
110
+ * This enables **page builders** and reusable structures.
111
+
112
+ All content is stored in a **SQLite database** (`studio.db`) and can be queried in the templates with the `query` module.
113
+
114
+
115
+ ## Frontend
116
+
117
+ The framework encourages **server-side rendering** with Hono’s HTML helper (re-exported by the `@alstar/studio` package):
118
+
119
+ ```ts
120
+ import { defineEntry, html } from '@alstar/studio'
121
+
122
+ export default defineEntry((c) => {
123
+ const slug = c.req.param('slug')
124
+
125
+ return html`
126
+ <h1>Hello World</h1>
127
+ <p>This page is: ${slug}</p>
128
+ `
129
+ })
130
+ ```
131
+
132
+ ### Interactivity
133
+
134
+ Even though the framework allows for having any library and tool for creating client-side behavior, it's recommended to use lightweight libraries such as:
135
+
136
+ * [Datastar](https://data-star.dev/) (used internally by the Studio)
137
+ * [Alpine.js](https://alpinejs.dev/)
138
+
139
+ ## Quickstart Example Project
140
+
141
+ This example shows how to define a simple **page schema** and render it on the frontend.
142
+
143
+ ### 1. Define the CMS Schema (`./index.ts`)
144
+
145
+ ```ts
146
+ import { createStudio, defineBlock, defineField, defineStructure } from '@alstar/studio'
147
+
148
+ const page = defineBlock({
149
+ label: 'Page',
150
+ type: 'page',
151
+ fields: {
152
+ title: defineField({
153
+ label: 'Title',
154
+ type: 'text',
155
+ }),
156
+ slug: defineField({
157
+ label: 'Slug',
158
+ type: 'slug',
159
+ }),
160
+ body: defineField({
161
+ label: 'Body',
162
+ type: 'markdown',
163
+ }),
164
+ },
165
+ })
166
+
167
+ const structure = defineStructure({
168
+ page,
169
+ })
170
+
171
+ await createStudio(structure)
172
+ ```
173
+
174
+ ### 2. Create a Frontend Route (`/pages/[slug].ts`)
175
+
176
+ ```ts
177
+ import { defineEntry, html, query } from '@alstar/studio'
178
+
179
+ export default defineEntry(c) => {
180
+ const slug = c.req.param('slug')
181
+ const page = query.root({ type: 'slug', value: slug })
182
+
183
+ if (!page) return c.notFound()
184
+
185
+ return html`
186
+ <main>
187
+ <h1>${page.fields.title.value}</h1>
188
+ <article>${page.fields.body.value}</article>
189
+ </main>
190
+ `
191
+ }
192
+ ```
193
+
194
+ ### 3. Run the Project
195
+
196
+ ```sh
197
+ pnpm run dev
198
+ ```
199
+
200
+ Visit:
201
+
202
+ * **CMS admin**: `http://localhost:3000/studio`
203
+ * **Frontend page**: `http://localhost:3000/my-first-page`
204
+
205
+ Create a new page in the CMS, set its slug field to `my-first-page`, and the frontend will render it automatically.
package/schema.sql ADDED
@@ -0,0 +1,18 @@
1
+ CREATE TABLE blocks (
2
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
3
+ created_at DATE DEFAULT (datetime('now')),
4
+ updated_at DATE DEFAULT (datetime('now')),
5
+
6
+ name TEXT not null,
7
+ label TEXT not null,
8
+ type TEXT not null,
9
+ sort_order INTEGER not null default 0,
10
+ value TEXT,
11
+ options JSON,
12
+ status TEXT default 'enabled',
13
+ parent_id INTEGER,
14
+ _depth INTEGER,
15
+ foreign key (parent_id) references blocks (id)
16
+
17
+ );
18
+ CREATE TABLE sqlite_sequence(name,seq);
package/schemas.ts CHANGED
@@ -1,55 +1,14 @@
1
1
  import { sql } from './utils/sql.ts'
2
2
 
3
- // export const entriesTable = {
4
- // tableName: 'entries',
5
- // columns: sql`
6
- // title TEXT not null, -- Title of the page
7
- // slug TEXT not null unique, -- URL slug for the page
8
- // meta_description TEXT -- Optional meta description for SEO
9
- // `,
10
- // }
11
-
12
- // export const fieldTable = {
13
- // tableName: 'fields',
14
- // columns: sql`
15
- // name TEXT not null, -- Name of the field (e.g., "content", "header", "image")
16
- // type TEXT not null, -- Field type (e.g., "text", "image", "video")
17
- // label TEXT not null, -- Field label (e.g., "Text", "Image", "Video")
18
- // options TEXT -- Additional options or settings (can be a JSON string if needed)
19
- // `,
20
- // }
21
-
22
- // export const entriesFieldsTable = {
23
- // tableName: 'entry_fields',
24
- // columns: sql`
25
- // entry_id INTEGER not null, -- Foreign key to pages
26
- // field_id INTEGER not null, -- Foreign key to fields
27
- // position INTEGER, -- Optional: order of the field on the page
28
- // content TEXT, -- Content of the field (e.g., text, image URL, etc.)
29
- // foreign key (entry_id) references entries (id),
30
- // foreign key (field_id) references fields (id)
31
- // `,
32
- // }
33
-
34
- // export const entryTypeTable = {
35
- // tableName: 'entry_types',
36
- // columns: sql`
37
- // name TEXT not null, -- Name of the field (e.g., "content", "header", "image")
38
- // type TEXT not null, -- Field type (e.g., "text", "image", "video")
39
- // label TEXT not null, -- Field label (e.g., "Text", "Image", "Video")
40
- // options TEXT -- Additional options or settings (can be a JSON string if needed)
41
- // `,
42
- // }
3
+ // -- API keys
4
+ export const usersTable = {
5
+ tableName: 'users',
6
+ columns: sql`
7
+ email TEXT not null,
8
+ hash TEXT
9
+ `,
10
+ }
43
11
 
44
- // export const entryEntryTypeTable = {
45
- // tableName: 'entry_entry_types',
46
- // columns: sql`
47
- // entry_id INTEGER not null, -- Foreign key to pages
48
- // entry_type_id INTEGER not null, -- Foreign key to fields
49
- // foreign key (entry_id) references entries (id),
50
- // foreign key (entry_type_id) references entry_types (id)
51
- // `,
52
- // }
53
12
 
54
13
  // -- Blocks
55
14
  export const blocksTable = {
@@ -58,10 +17,22 @@ export const blocksTable = {
58
17
  name TEXT not null,
59
18
  label TEXT not null,
60
19
  type TEXT not null,
61
- sort_order INTEGER not null default 0,
62
20
  value TEXT,
63
21
  options JSON,
64
- parent_block_id INTEGER,
65
- foreign key (parent_block_id) references blocks (id)
22
+ status TEXT default 'enabled',
23
+ sort_order INTEGER not null default 0,
24
+ -- _depth INTEGER,
25
+ parent_id INTEGER,
26
+ foreign key (parent_id) references blocks (id)
27
+ `,
28
+ }
29
+
30
+ // -- API keys
31
+ export const apiKeysTable = {
32
+ tableName: 'api_keys',
33
+ columns: sql`
34
+ name TEXT not null,
35
+ value TEXT,
36
+ hint TEXT
66
37
  `,
67
38
  }
package/types.ts CHANGED
@@ -1,14 +1,142 @@
1
- import { type HonoOptions } from "hono/hono-base"
2
- import { type BlankEnv } from "hono/types"
1
+ import { type HttpBindings } from '@hono/node-server'
2
+ import { type Context } from 'hono'
3
+ import { type HonoOptions } from 'hono/hono-base'
4
+ import { type BlankInput, type BlankEnv } from 'hono/types'
5
+ import {
6
+ BlockFieldInstance,
7
+ BlockInstance,
8
+ FieldInstance,
9
+ } from './utils/define.ts'
10
+
11
+ // DeepReadonly utility type
12
+ export type DeepReadonly<T> =
13
+ T extends (...args: any[]) => any // functions stay as-is
14
+ ? T
15
+ : T extends any[] // arrays/tuples
16
+ ? { [K in keyof T]: DeepReadonly<T[K]> }
17
+ : T extends object // objects
18
+ ? { [K in keyof T]: DeepReadonly<T[K]> }
19
+ : T; // primitives
20
+
21
+ export type PrimitiveField = {
22
+ name: string
23
+ label: string
24
+ type: 'text' | 'slug' | 'markdown' | 'image'
25
+ }
26
+
27
+ export type BlockField = {
28
+ name: string
29
+ label: string
30
+ type: 'blocks'
31
+ children: Record<string, Field | Block>
32
+ }
33
+
34
+ export type Field = PrimitiveField | BlockField
3
35
 
4
36
  export type Block = {
5
37
  name: string
6
38
  label: string
7
39
  type: string
8
- fields?: Block[]
40
+ fields: Record<string, Field | Block>
9
41
  }
10
42
 
11
- export type Structure = Block[]
43
+ export type Structure = Record<string, BlockDefStructure>
44
+ // export type Structure = Record<string, BlockDefStructure>
45
+
46
+ // --- Field & block definitions ---
47
+ type FieldType = 'text' | 'slug' | 'markdown' | 'image'
48
+
49
+ interface BaseField {
50
+ label: string
51
+ type: FieldType
52
+ description?: string
53
+ }
54
+
55
+ interface TextField extends BaseField {
56
+ type: 'text' | 'slug' | 'markdown'
57
+ }
58
+
59
+ interface TextFieldStructure extends TextField {
60
+ instanceOf: typeof FieldInstance
61
+ }
62
+
63
+ interface ImageField extends BaseField {
64
+ type: 'image'
65
+ }
66
+
67
+ interface ImageFieldStructure extends ImageField {
68
+ instanceOf: typeof FieldInstance
69
+ }
70
+
71
+ export interface BlocksFieldDef {
72
+ label: string
73
+ type: 'blocks'
74
+ description?: string
75
+ children: Record<string, BlockDefStructure | FieldDefStructure>
76
+ }
77
+
78
+ export interface BlocksFieldDefStructure extends BlocksFieldDef {
79
+ instanceOf: typeof BlockFieldInstance
80
+ }
81
+
82
+ export type FieldDef = TextField | ImageField
83
+ export type FieldDefStructure = TextFieldStructure | ImageFieldStructure
84
+
85
+ export interface BlockDef {
86
+ label: string
87
+ type: string
88
+ fields: Record<string, FieldDefStructure | BlocksFieldDefStructure>
89
+ description?: string
90
+ }
91
+
92
+ export interface BlockDefStructure extends BlockDef {
93
+ instanceOf: typeof BlockInstance
94
+ }
95
+
96
+ // type DBDefaults = {
97
+ // id: number
98
+ // created_at: string
99
+ // updated_at: string
100
+ // name: string
101
+ // label: string
102
+ // // type: string
103
+ // sort_order: number
104
+ // value: string
105
+ // options: string | null
106
+ // status: 'enabled' | 'disabled'
107
+ // parent_id: number | null
108
+ // depth: number
109
+ // }
110
+
111
+ type BaseDBResult = {
112
+ id: number
113
+ created_at: string
114
+ updated_at: string
115
+ name: string
116
+ label: string
117
+ sort_order: number
118
+ value: string | null
119
+ options: any
120
+ status: 'enabled' | 'disabled'
121
+ parent_id: number | null
122
+ depth: number
123
+ }
124
+
125
+ export type DBPrimitiveFieldResult = BaseDBResult & {
126
+ type: FieldDef
127
+ }
128
+
129
+ export type DBBlockFieldResult = BaseDBResult & {
130
+ type: 'blocks'
131
+ children: DBBlockResult[]
132
+ }
133
+
134
+ export type DBBlockResult = BaseDBResult & {
135
+ type: string
136
+ fields: Record<string, DBFieldResult>
137
+ }
138
+
139
+ export type DBFieldResult = DBPrimitiveFieldResult & DBBlockFieldResult
12
140
 
13
141
  export type DBBlock = Block & {
14
142
  id: number
@@ -16,10 +144,21 @@ export type DBBlock = Block & {
16
144
  updated_at: string
17
145
  value: string | null
18
146
  sort_order: number | null
19
- parent_block_id: number | null
147
+ parent_id: number | null
20
148
  options: number | null
21
149
  }
22
150
 
151
+ export type BlockStatus = 'enabled' | 'disabled'
152
+
23
153
  export type StudioConfig = {
154
+ siteName?: string
24
155
  honoConfig?: HonoOptions<BlankEnv>
156
+ port?: number
157
+ structure: Structure
25
158
  }
159
+
160
+ export type RequestContext = Context<
161
+ { Bindings: HttpBindings },
162
+ string,
163
+ BlankInput
164
+ >
package/utils/auth.ts ADDED
@@ -0,0 +1,54 @@
1
+ import { db } from '@alstar/db'
2
+ import { type MiddlewareHandler } from 'hono'
3
+ import { getCookie, setCookie } from 'hono/cookie'
4
+ import { HTTPException } from 'hono/http-exception'
5
+ import { sql } from './sql.ts'
6
+
7
+ const middleware: MiddlewareHandler = async (c, next) => {
8
+ const url = new URL(c.req.url)
9
+ const user = db.database
10
+ .prepare(sql`
11
+ select
12
+ email
13
+ from
14
+ users
15
+ `)
16
+ .get()
17
+
18
+ const cookie = getCookie(c, 'login')
19
+
20
+ if (
21
+ !user &&
22
+ url.pathname !== '/studio/register' &&
23
+ url.pathname !== '/studio/api/auth/register'
24
+ ) {
25
+ return c.redirect('/studio/register')
26
+ }
27
+
28
+ if (
29
+ user &&
30
+ !cookie &&
31
+ url.pathname !== '/studio/login' &&
32
+ url.pathname !== '/studio/api/auth/login'
33
+ ) {
34
+ return c.redirect('/studio/login')
35
+ }
36
+
37
+ // console.log(cookie)
38
+
39
+ // deleteCookie(c, 'cookie_name')
40
+
41
+ // const allCookies = getCookie(c)
42
+
43
+ await next()
44
+
45
+ // const authorized = false
46
+
47
+ // if(!authorized) {
48
+ // throw new HTTPException(401, { message: 'Custom error message' })
49
+ // } else {
50
+ // await next()
51
+ // }
52
+ }
53
+
54
+ export default middleware
@@ -6,7 +6,7 @@ type Block = {
6
6
  sort_order: number
7
7
  value: string | null
8
8
  options: any // JSON-parsed if necessary
9
- parent_block_id: number | null
9
+ parent_id: number | null
10
10
  depth: number
11
11
  // ... you can add other fields if needed
12
12
  }
@@ -26,13 +26,13 @@ export function buildBlockTree(blocks: Block[]): BlockWithChildren {
26
26
  for (const block of blocks) {
27
27
  const current = blockMap.get(block.id)!
28
28
 
29
- if (block.parent_block_id != null) {
30
- const parent = blockMap.get(block.parent_block_id)
29
+ if (block.parent_id != null) {
30
+ const parent = blockMap.get(block.parent_id)
31
31
  if (parent) {
32
32
  parent.fields.push(current)
33
33
  } else {
34
34
  console.warn(
35
- `Parent with id ${block.parent_block_id} not found for block ${block.id}`,
35
+ `Parent with id ${block.parent_id} not found for block ${block.id}`,
36
36
  )
37
37
  }
38
38
  } else {
@@ -0,0 +1,9 @@
1
+ import crypto from 'node:crypto'
2
+
3
+ export const createHash = (str: string) => {
4
+ const hash = crypto.createHash('sha256')
5
+
6
+ hash.update(str)
7
+
8
+ return hash.digest().toString('base64')
9
+ }