@alstar/studio 0.0.0-beta.10 → 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.
- package/api/api-key.ts +40 -44
- package/api/auth.ts +66 -0
- package/api/backup.ts +30 -28
- package/api/block.ts +109 -110
- package/api/index.ts +17 -10
- package/api/mcp.ts +39 -42
- package/components/AdminPanel.ts +17 -4
- package/components/Backup.ts +5 -2
- package/components/BlockFieldRenderer.ts +6 -2
- package/components/Entries.ts +2 -2
- package/components/FieldRenderer.ts +1 -1
- package/components/Settings.ts +21 -18
- package/components/SiteLayout.ts +8 -5
- package/components/Users.ts +46 -0
- package/components/fields/Markdown.ts +1 -1
- package/components/fields/Slug.ts +113 -0
- package/components/fields/Text.ts +1 -1
- package/components/fields/index.ts +4 -1
- package/components/icons.ts +36 -0
- package/index.ts +40 -12
- package/package.json +6 -3
- package/pages/entry/[id].ts +1 -3
- package/pages/error.ts +14 -0
- package/pages/index.ts +1 -1
- package/pages/login.ts +21 -0
- package/pages/register.ts +33 -0
- package/pages/settings.ts +1 -3
- package/public/studio/css/settings.css +4 -0
- package/public/studio/js/markdown-editor.js +1 -1
- package/public/studio/js/sortable-list.js +1 -1
- package/public/studio/main.css +5 -0
- package/public/studio/main.js +12 -0
- package/queries/block-2.ts +339 -0
- package/queries/getBlockTrees-2.ts +71 -0
- package/queries/index.ts +1 -1
- package/readme.md +2 -2
- package/schema.sql +18 -0
- package/schemas.ts +11 -1
- package/types.ts +11 -0
- package/utils/auth.ts +54 -0
- package/utils/create-hash.ts +9 -0
- package/utils/define.ts +1 -3
- package/utils/startup-log.ts +15 -6
package/api/api-key.ts
CHANGED
|
@@ -8,66 +8,62 @@ import crypto from 'node:crypto'
|
|
|
8
8
|
import { stripNewlines } from '../utils/strip-newlines.ts'
|
|
9
9
|
import { sql } from '../utils/sql.ts'
|
|
10
10
|
import Settings from '../components/Settings.ts'
|
|
11
|
+
import { createHash } from '../utils/create-hash.ts'
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
const app = new Hono<{ Bindings: HttpBindings }>()
|
|
13
|
+
const app = new Hono<{ Bindings: HttpBindings }>()
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
app.post('/api-key', async (c) => {
|
|
16
|
+
return streamSSE(c, async (stream) => {
|
|
17
|
+
const formData = await c.req.formData()
|
|
18
|
+
const data = Object.fromEntries(formData.entries())
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
if (!data) return
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
const hash = crypto.createHash('sha256')
|
|
22
|
+
const apiKey = crypto.randomUUID()
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
const hash = createHash(apiKey)
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
const xs = (length: number) => '*'.repeat(length)
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
hint: `${apiKey.substring(0, 8)}-${xs(4)}-${xs(4)}-${xs(4)}-${xs(12)}`,
|
|
35
|
-
})
|
|
28
|
+
db.insertInto('api_keys', {
|
|
29
|
+
name: data.name?.toString(),
|
|
30
|
+
value: hash,
|
|
31
|
+
hint: `${apiKey.substring(0, 8)}-${xs(4)}-${xs(4)}-${xs(4)}-${xs(12)}`,
|
|
32
|
+
})
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
await stream.writeSSE({
|
|
35
|
+
event: 'datastar-patch-signals',
|
|
36
|
+
data: `signals { apiKey: '${apiKey}', name: '' }`,
|
|
37
|
+
})
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
})
|
|
39
|
+
await stream.writeSSE({
|
|
40
|
+
event: 'datastar-patch-elements',
|
|
41
|
+
data: `elements ${stripNewlines(Settings())}`,
|
|
46
42
|
})
|
|
47
43
|
})
|
|
44
|
+
})
|
|
48
45
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
app.delete('/api-key', async (c) => {
|
|
47
|
+
return streamSSE(c, async (stream) => {
|
|
48
|
+
const formData = await c.req.formData()
|
|
52
49
|
|
|
53
|
-
|
|
50
|
+
const value = formData.get('value')?.toString()
|
|
54
51
|
|
|
55
|
-
|
|
52
|
+
if (!value) return
|
|
56
53
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
db.database
|
|
55
|
+
.prepare(sql`
|
|
56
|
+
delete from api_keys
|
|
57
|
+
where
|
|
58
|
+
value = ?
|
|
59
|
+
`)
|
|
60
|
+
.run(value)
|
|
64
61
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
})
|
|
62
|
+
await stream.writeSSE({
|
|
63
|
+
event: 'datastar-patch-elements',
|
|
64
|
+
data: `elements ${stripNewlines(Settings())}`,
|
|
69
65
|
})
|
|
70
66
|
})
|
|
67
|
+
})
|
|
71
68
|
|
|
72
|
-
|
|
73
|
-
}
|
|
69
|
+
export const apiKeyRoutes = app
|
package/api/auth.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type HttpBindings } from '@hono/node-server'
|
|
2
|
+
import { Hono } from 'hono'
|
|
3
|
+
import { createHash } from '../utils/create-hash.ts'
|
|
4
|
+
import { db } from '@alstar/db'
|
|
5
|
+
import { streamSSE } from 'hono/streaming'
|
|
6
|
+
import { sql } from '../utils/sql.ts'
|
|
7
|
+
import { setCookie } from 'hono/cookie'
|
|
8
|
+
|
|
9
|
+
const app = new Hono<{ Bindings: HttpBindings }>()
|
|
10
|
+
|
|
11
|
+
app.post('/register', async (c) => {
|
|
12
|
+
return streamSSE(c, async (stream) => {
|
|
13
|
+
const formData = await c.req.formData()
|
|
14
|
+
const data = Object.fromEntries(formData.entries())
|
|
15
|
+
|
|
16
|
+
if (!data || !data.email || !data.password) return
|
|
17
|
+
|
|
18
|
+
const hash = createHash(data.password.toString())
|
|
19
|
+
|
|
20
|
+
db.insertInto('users', {
|
|
21
|
+
email: data.email?.toString(),
|
|
22
|
+
hash: hash,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
await stream.writeSSE({
|
|
26
|
+
event: 'datastar-patch-signals',
|
|
27
|
+
data: `signals { status: 200, message: 'User "${data.email}" created successfully' }`,
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
app.post('/login', async (c) => {
|
|
33
|
+
const formData = await c.req.formData()
|
|
34
|
+
const data = Object.fromEntries(formData.entries())
|
|
35
|
+
|
|
36
|
+
if (!data || !data.email || !data.password) return
|
|
37
|
+
|
|
38
|
+
const user = db.database
|
|
39
|
+
.prepare(sql`
|
|
40
|
+
select
|
|
41
|
+
*
|
|
42
|
+
from
|
|
43
|
+
users
|
|
44
|
+
where
|
|
45
|
+
email = ?
|
|
46
|
+
`)
|
|
47
|
+
.get(data.email.toString())
|
|
48
|
+
|
|
49
|
+
if (!user) {
|
|
50
|
+
return c.json({ status: 404, message: 'No user with that email' })
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const passwordHash = createHash(data.password.toString())
|
|
54
|
+
|
|
55
|
+
if (passwordHash !== user.hash) {
|
|
56
|
+
return c.json({ status: 401, message: 'Wrong password' })
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
setCookie(c, 'login', 'yes')
|
|
60
|
+
|
|
61
|
+
return c.redirect('/studio')
|
|
62
|
+
|
|
63
|
+
// return c.json({ status: 200, message: 'Logged in!' })
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
export const authRoutes = app
|
package/api/backup.ts
CHANGED
|
@@ -1,38 +1,40 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fsp from 'node:fs/promises'
|
|
2
|
+
import { backup } from 'node:sqlite'
|
|
3
|
+
|
|
2
4
|
import { Hono } from 'hono'
|
|
5
|
+
import { type HttpBindings } from '@hono/node-server'
|
|
3
6
|
import { streamSSE } from 'hono/streaming'
|
|
4
|
-
import { DatabaseSync } from 'node:sqlite'
|
|
5
|
-
|
|
6
|
-
import { stripNewlines } from '../utils/strip-newlines.ts'
|
|
7
7
|
import { db } from '@alstar/db'
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const app = new Hono<{ Bindings: HttpBindings }>()
|
|
10
|
+
|
|
11
|
+
app.post('/backup', async (c) => {
|
|
12
|
+
const date = new Date()
|
|
13
|
+
const name = `./backups/backup-${date.toISOString()}.db`
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// rate: 1, // Copy one page at a time.
|
|
15
|
-
// progress: ({ totalPages, remainingPages }) => {
|
|
16
|
-
// console.log('Backup in progress', { totalPages, remainingPages })
|
|
17
|
-
// },
|
|
18
|
-
// })
|
|
15
|
+
try {
|
|
16
|
+
fsp.mkdir('./backups', { recursive: true })
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
await backup(db.database, name)
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
console.log('Backup')
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
return streamSSE(c, async (stream) => {
|
|
23
|
+
await stream.writeSSE({
|
|
24
|
+
event: 'datastar-patch-signals',
|
|
25
|
+
data: `signals { status: 200, message: '${name} created' }`,
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.log(error)
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
return streamSSE(c, async (stream) => {
|
|
32
|
+
await stream.writeSSE({
|
|
33
|
+
event: 'datastar-patch-signals',
|
|
34
|
+
data: `signals { status: 500, message: '${name} failed' }`,
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
})
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
}
|
|
40
|
+
export const backupRoutes = app
|
package/api/block.ts
CHANGED
|
@@ -13,92 +13,64 @@ import {
|
|
|
13
13
|
} from '../queries/block-with-children.ts'
|
|
14
14
|
import AdminPanel from '../components/AdminPanel.ts'
|
|
15
15
|
import { query } from '../queries/index.ts'
|
|
16
|
+
import { studioStructure } from '../index.ts'
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
const app = new Hono<{ Bindings: HttpBindings }>()
|
|
18
|
+
const app = new Hono<{ Bindings: HttpBindings }>()
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
app.post('/block', async (c) => {
|
|
21
|
+
return streamSSE(c, async (stream) => {
|
|
22
|
+
const formData = await c.req.formData()
|
|
23
|
+
const data = Object.fromEntries(formData.entries())
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
const row = studioStructure[data.name?.toString()]
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
if (!row) return
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
db.insertInto('blocks', {
|
|
30
|
+
name: data.name?.toString(),
|
|
31
|
+
label: row.label,
|
|
32
|
+
type: row.type,
|
|
33
|
+
})
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
})
|
|
35
|
+
await stream.writeSSE({
|
|
36
|
+
event: 'datastar-patch-elements',
|
|
37
|
+
data: `elements ${stripNewlines(AdminPanel())}`,
|
|
39
38
|
})
|
|
40
39
|
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
app.post('/new-block', async (c) => {
|
|
43
|
+
return streamSSE(c, async (stream) => {
|
|
44
|
+
const formData = await c.req.formData()
|
|
45
|
+
const data = Object.fromEntries(formData)
|
|
46
|
+
|
|
47
|
+
db.insertInto('blocks', {
|
|
48
|
+
type: data.type.toString(),
|
|
49
|
+
name: data.name.toString(),
|
|
50
|
+
label: data.label.toString(),
|
|
51
|
+
parent_id: data.parent_id.toString(),
|
|
52
|
+
sort_order: data.sort_order.toString(),
|
|
53
|
+
})
|
|
41
54
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const data = Object.fromEntries(formData)
|
|
46
|
-
|
|
47
|
-
db.insertInto('blocks', {
|
|
48
|
-
type: data.type.toString(),
|
|
49
|
-
name: data.name.toString(),
|
|
50
|
-
label: data.label.toString(),
|
|
51
|
-
parent_id: data.parent_id.toString(),
|
|
52
|
-
sort_order: data.sort_order.toString(),
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
await stream.writeSSE({
|
|
56
|
-
event: 'datastar-patch-elements',
|
|
57
|
-
data: `elements ${stripNewlines(Entry({ entryId: parseInt(data.entry_id.toString()) }))}`,
|
|
58
|
-
})
|
|
55
|
+
await stream.writeSSE({
|
|
56
|
+
event: 'datastar-patch-elements',
|
|
57
|
+
data: `elements ${stripNewlines(Entry({ entryId: parseInt(data.entry_id.toString()) }))}`,
|
|
59
58
|
})
|
|
60
59
|
})
|
|
60
|
+
})
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const id = formData.get('id')?.toString()
|
|
67
|
-
const value = formData.get('value')?.toString()
|
|
68
|
-
const name = formData.get('name')?.toString()
|
|
69
|
-
const entryId = formData.get('entryId')?.toString()
|
|
70
|
-
const parentId = formData.get('parentId')?.toString()
|
|
71
|
-
// const sortOrder = formData.get('sort_order')?.toString()
|
|
62
|
+
app.patch('/block', async (c) => {
|
|
63
|
+
return streamSSE(c, async (stream) => {
|
|
64
|
+
const formData = await c.req.formData()
|
|
72
65
|
|
|
73
|
-
|
|
66
|
+
const id = formData.get('id')?.toString()
|
|
67
|
+
const value = formData.get('value')?.toString()
|
|
68
|
+
const name = formData.get('name')?.toString()
|
|
69
|
+
const entryId = formData.get('entryId')?.toString()
|
|
70
|
+
const parentId = formData.get('parentId')?.toString()
|
|
71
|
+
// const sortOrder = formData.get('sort_order')?.toString()
|
|
74
72
|
|
|
75
|
-
|
|
76
|
-
update blocks
|
|
77
|
-
set
|
|
78
|
-
value = ?
|
|
79
|
-
where
|
|
80
|
-
id = ?;
|
|
81
|
-
`)
|
|
82
|
-
|
|
83
|
-
transaction.run(value, id)
|
|
84
|
-
|
|
85
|
-
if (entryId === parentId && name?.toString() === 'title') {
|
|
86
|
-
const rootBlock = query.block({
|
|
87
|
-
id: parentId?.toString() || null,
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
if (rootBlock.type !== 'single') {
|
|
91
|
-
await stream.writeSSE({
|
|
92
|
-
event: 'datastar-patch-elements',
|
|
93
|
-
data: `elements <a href="/admin/entry/${entryId}" id="block_link_${entryId}">${value}</a>`,
|
|
94
|
-
})
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
})
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
app.patch('/value', async (c) => {
|
|
101
|
-
const body = await c.req.json()
|
|
73
|
+
if (!id || !value) return
|
|
102
74
|
|
|
103
75
|
const transaction = db.database.prepare(sql`
|
|
104
76
|
update blocks
|
|
@@ -108,56 +80,83 @@ export default (structure: Structure) => {
|
|
|
108
80
|
id = ?;
|
|
109
81
|
`)
|
|
110
82
|
|
|
111
|
-
transaction.run(
|
|
83
|
+
transaction.run(value, id)
|
|
84
|
+
|
|
85
|
+
if (entryId === parentId && name?.toString() === 'title') {
|
|
86
|
+
const rootBlock = query.block({
|
|
87
|
+
id: parentId?.toString() || null,
|
|
88
|
+
})
|
|
112
89
|
|
|
113
|
-
|
|
90
|
+
if (rootBlock.type !== 'single') {
|
|
91
|
+
await stream.writeSSE({
|
|
92
|
+
event: 'datastar-patch-elements',
|
|
93
|
+
data: `elements <a href="/studio/entry/${entryId}" id="block_link_${entryId}">${value}</a>`,
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
}
|
|
114
97
|
})
|
|
98
|
+
})
|
|
115
99
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const formData = await c.req.formData()
|
|
100
|
+
app.patch('/value', async (c) => {
|
|
101
|
+
const body = await c.req.json()
|
|
119
102
|
|
|
120
|
-
|
|
121
|
-
|
|
103
|
+
const transaction = db.database.prepare(sql`
|
|
104
|
+
update blocks
|
|
105
|
+
set
|
|
106
|
+
value = ?
|
|
107
|
+
where
|
|
108
|
+
id = ?;
|
|
109
|
+
`)
|
|
122
110
|
|
|
123
|
-
|
|
111
|
+
transaction.run(body.value, body.id)
|
|
124
112
|
|
|
125
|
-
|
|
113
|
+
return c.json({ status: 200, message: 'success' })
|
|
114
|
+
})
|
|
126
115
|
|
|
127
|
-
|
|
116
|
+
app.delete('/block', async (c) => {
|
|
117
|
+
return streamSSE(c, async (stream) => {
|
|
118
|
+
const formData = await c.req.formData()
|
|
128
119
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
120
|
+
const id = formData.get('id')?.toString()
|
|
121
|
+
const entryId = formData.get('entry_id')?.toString()
|
|
122
|
+
|
|
123
|
+
if (!id) return
|
|
124
|
+
|
|
125
|
+
const transaction = db.database.prepare(deleteBlockWithChildren)
|
|
126
|
+
|
|
127
|
+
transaction.all(id)
|
|
128
|
+
|
|
129
|
+
if (entryId) {
|
|
130
|
+
await stream.writeSSE({
|
|
131
|
+
event: 'datastar-patch-elements',
|
|
132
|
+
data: `elements ${stripNewlines(Entry({ entryId: parseInt(entryId.toString()) }))}`,
|
|
133
|
+
})
|
|
134
|
+
} else {
|
|
135
|
+
await stream.writeSSE({
|
|
136
|
+
event: 'datastar-patch-elements',
|
|
137
|
+
data: `elements ${stripNewlines(AdminPanel())}`,
|
|
138
|
+
})
|
|
139
|
+
}
|
|
141
140
|
})
|
|
141
|
+
})
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
app.post('/sort-order', async (c) => {
|
|
144
|
+
const id = c.req.query('id')
|
|
145
|
+
const sortOrder = c.req.query('sort-order')
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
if (!id || !sortOrder) return
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
149
|
+
const transaction = db.database.prepare(sql`
|
|
150
|
+
update blocks
|
|
151
|
+
set
|
|
152
|
+
sort_order = ?
|
|
153
|
+
where
|
|
154
|
+
id = ?
|
|
155
|
+
`)
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
transaction.run(sortOrder, id)
|
|
158
158
|
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
return c.json({ status: 200, message: 'success' })
|
|
160
|
+
})
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
}
|
|
162
|
+
export const blockRoutes = app
|
package/api/index.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
import apiKey from './api-key.ts'
|
|
3
|
-
import type { Structure } from '../types.ts'
|
|
4
|
-
import backup from './backup.ts'
|
|
1
|
+
import { Hono } from 'hono'
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import { blockRoutes } from './block.ts'
|
|
4
|
+
import { apiKeyRoutes } from './api-key.ts'
|
|
5
|
+
import { backupRoutes } from './backup.ts'
|
|
6
|
+
import { authRoutes } from './auth.ts'
|
|
7
|
+
import { fieldRoutes } from '../components/fields/index.ts'
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
app.route('/', backup())
|
|
9
|
+
const routes = new Hono()
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
routes.route('/', blockRoutes)
|
|
12
|
+
routes.route('/', apiKeyRoutes)
|
|
13
|
+
routes.route('/', backupRoutes)
|
|
14
|
+
routes.route('/auth', authRoutes)
|
|
15
|
+
|
|
16
|
+
fieldRoutes.forEach((fieldRoute) => {
|
|
17
|
+
routes.route('/field', fieldRoute)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
export const apiRoutes = routes
|
package/api/mcp.ts
CHANGED
|
@@ -1,53 +1,50 @@
|
|
|
1
1
|
import { type HttpBindings } from '@hono/node-server'
|
|
2
|
-
import { ServerSentEventGenerator } from '@starfederation/datastar-sdk'
|
|
3
2
|
import { Hono } from 'hono'
|
|
4
3
|
import { sql } from '../utils/sql.ts'
|
|
5
4
|
import { db } from '@alstar/db'
|
|
6
5
|
import { bearerAuth } from 'hono/bearer-auth'
|
|
7
6
|
import crypto from 'node:crypto'
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
message: 'Response from MCP server!',
|
|
42
|
-
})
|
|
8
|
+
const app = new Hono<{ Bindings: HttpBindings }>()
|
|
9
|
+
|
|
10
|
+
app.use(
|
|
11
|
+
'/*',
|
|
12
|
+
bearerAuth({
|
|
13
|
+
verifyToken: async (token, c) => {
|
|
14
|
+
const hash = crypto.createHash('sha256')
|
|
15
|
+
|
|
16
|
+
hash.update(token)
|
|
17
|
+
|
|
18
|
+
const digest = hash.digest().toString('base64')
|
|
19
|
+
|
|
20
|
+
const exists = db.database
|
|
21
|
+
.prepare(sql`
|
|
22
|
+
select
|
|
23
|
+
value
|
|
24
|
+
from
|
|
25
|
+
api_keys
|
|
26
|
+
where
|
|
27
|
+
value = ?
|
|
28
|
+
`)
|
|
29
|
+
.get(digest)
|
|
30
|
+
|
|
31
|
+
return !!exists
|
|
32
|
+
},
|
|
33
|
+
}),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
app.get('/entry', async (c) => {
|
|
37
|
+
return c.json({
|
|
38
|
+
status: 'success',
|
|
39
|
+
message: 'Response from MCP server!',
|
|
43
40
|
})
|
|
41
|
+
})
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
})
|
|
43
|
+
app.post('/entry', async (c) => {
|
|
44
|
+
return c.json({
|
|
45
|
+
status: 'success',
|
|
46
|
+
message: 'New entry created!',
|
|
50
47
|
})
|
|
48
|
+
})
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
}
|
|
50
|
+
export const mcpRoutes = app
|