@korajs/cli 0.4.0 → 0.5.0
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/bin.cjs +1609 -643
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1086 -139
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-CMSX76KM.js → chunk-3WNFM3QB.js} +103 -96
- package/dist/chunk-3WNFM3QB.js.map +1 -0
- package/dist/{chunk-MVP5PDT4.js → chunk-PSRM56B7.js} +2 -2
- package/dist/{chunk-YGVO4POI.js → chunk-SOTZIWIF.js} +27 -15
- package/dist/chunk-SOTZIWIF.js.map +1 -0
- package/dist/create.cjs +54 -47
- package/dist/create.cjs.map +1 -1
- package/dist/create.js +2 -2
- package/dist/index.cjs +78 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +2 -2
- package/package.json +9 -3
- package/templates/react-basic/README.md.hbs +10 -1
- package/templates/react-basic/src/App.tsx +112 -126
- package/templates/react-basic/src/index.css +165 -159
- package/templates/react-basic/src/kora-worker.ts +0 -1
- package/templates/react-basic/src/main.tsx +12 -12
- package/templates/react-basic/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/react-basic/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-basic/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-basic/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-basic/src/schema.ts +6 -12
- package/templates/react-basic/src/vite-env.d.ts +17 -0
- package/templates/react-basic/{tsconfig.json → tsconfig.json.hbs} +1 -2
- package/templates/react-basic/vite.config.ts +57 -50
- package/templates/react-sync/.env.example +19 -0
- package/templates/react-sync/README.md.hbs +43 -1
- package/templates/react-sync/package.json.hbs +3 -0
- package/templates/react-sync/server.ts +90 -26
- package/templates/react-sync/src/App.tsx +132 -130
- package/templates/react-sync/src/auth.ts +25 -0
- package/templates/react-sync/src/index.css +212 -165
- package/templates/react-sync/src/kora-worker.ts +0 -1
- package/templates/react-sync/src/main.tsx +34 -18
- package/templates/react-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/react-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-sync/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-sync/src/schema.ts +6 -12
- package/templates/react-sync/src/vite-env.d.ts +17 -0
- package/templates/react-sync/{tsconfig.json → tsconfig.json.hbs} +1 -2
- package/templates/react-sync/vite.config.ts +67 -60
- package/templates/react-tailwind/README.md.hbs +10 -1
- package/templates/react-tailwind/src/App.tsx +186 -197
- package/templates/react-tailwind/src/index.css +3 -3
- package/templates/react-tailwind/src/kora-worker.ts +0 -1
- package/templates/react-tailwind/src/main.tsx +19 -12
- package/templates/react-tailwind/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/react-tailwind/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-tailwind/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-tailwind/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-tailwind/src/schema.ts +6 -12
- package/templates/react-tailwind/src/vite-env.d.ts +17 -0
- package/templates/react-tailwind/{tsconfig.json → tsconfig.json.hbs} +1 -2
- package/templates/react-tailwind/vite.config.ts +57 -50
- package/templates/react-tailwind-sync/.env.example +19 -0
- package/templates/react-tailwind-sync/README.md.hbs +43 -1
- package/templates/react-tailwind-sync/package.json.hbs +3 -0
- package/templates/react-tailwind-sync/server.ts +90 -26
- package/templates/react-tailwind-sync/src/App.tsx +246 -222
- package/templates/react-tailwind-sync/src/auth.ts +25 -0
- package/templates/react-tailwind-sync/src/index.css +3 -3
- package/templates/react-tailwind-sync/src/kora-worker.ts +0 -1
- package/templates/react-tailwind-sync/src/main.tsx +48 -18
- package/templates/react-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/react-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-tailwind-sync/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-tailwind-sync/src/schema.ts +6 -12
- package/templates/react-tailwind-sync/src/vite-env.d.ts +17 -0
- package/templates/react-tailwind-sync/{tsconfig.json → tsconfig.json.hbs} +1 -2
- package/templates/react-tailwind-sync/vite.config.ts +67 -60
- package/templates/tauri-react/.env.example +20 -0
- package/templates/tauri-react/README.md.hbs +36 -1
- package/templates/tauri-react/dev.ts +11 -11
- package/templates/tauri-react/package.json.hbs +2 -0
- package/templates/tauri-react/server.ts +86 -12
- package/templates/tauri-react/src/App.tsx +769 -544
- package/templates/tauri-react/src/AppShell.tsx +125 -83
- package/templates/tauri-react/src/SetupScreen.tsx +257 -208
- package/templates/tauri-react/src/auth.ts +45 -0
- package/templates/tauri-react/src/main.tsx +3 -3
- package/templates/tauri-react/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/tauri-react/src/modules/todos/todo.queries.ts +5 -0
- package/templates/tauri-react/src/modules/todos/todo.schema.ts +10 -0
- package/templates/tauri-react/src/modules/todos/useTodos.ts +31 -0
- package/templates/tauri-react/src/schema.ts +6 -12
- package/templates/tauri-react/src/sync-config.ts +47 -47
- package/templates/tauri-react/src/updater.ts +15 -15
- package/templates/tauri-react/src/vite-env.d.ts +17 -0
- package/templates/tauri-react/src-tauri/tauri.conf.json +1 -1
- package/templates/tauri-react/tsconfig.json.hbs +14 -0
- package/templates/tauri-react/vite.config.ts +9 -9
- package/dist/chunk-CMSX76KM.js.map +0 -1
- package/dist/chunk-YGVO4POI.js.map +0 -1
- package/templates/tauri-react/tsconfig.json +0 -15
- /package/dist/{chunk-MVP5PDT4.js.map → chunk-PSRM56B7.js.map} +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useCollection, useMutation, useQuery } from '@korajs/react'
|
|
2
|
+
import {
|
|
3
|
+
type CreateTodoInput,
|
|
4
|
+
type UpdateTodoStatusInput,
|
|
5
|
+
createTodo,
|
|
6
|
+
deleteTodo,
|
|
7
|
+
updateTodoStatus,
|
|
8
|
+
} from './todo.mutations'
|
|
9
|
+
import { orderedTodos } from './todo.queries'
|
|
10
|
+
|
|
11
|
+
export function useTodos() {
|
|
12
|
+
const todos = useCollection('todos')
|
|
13
|
+
const allTodos = useQuery(orderedTodos(todos))
|
|
14
|
+
const addTodo = useMutation((data: CreateTodoInput) => createTodo(todos, data))
|
|
15
|
+
const toggleTodo = useMutation((id: string, data: UpdateTodoStatusInput) =>
|
|
16
|
+
updateTodoStatus(todos, id, data),
|
|
17
|
+
)
|
|
18
|
+
const removeTodo = useMutation((id: string) => deleteTodo(todos, id))
|
|
19
|
+
|
|
20
|
+
const activeTodos = allTodos.filter((todo) => !todo.completed)
|
|
21
|
+
const completedTodos = allTodos.filter((todo) => !!todo.completed)
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
allTodos,
|
|
25
|
+
activeTodos,
|
|
26
|
+
completedTodos,
|
|
27
|
+
addTodo,
|
|
28
|
+
toggleTodo,
|
|
29
|
+
deleteTodo: removeTodo,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import { defineSchema
|
|
1
|
+
import { defineSchema } from 'korajs'
|
|
2
|
+
import { todos } from './modules/todos/todo.schema'
|
|
2
3
|
|
|
3
4
|
export default defineSchema({
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
title: t.string(),
|
|
9
|
-
completed: t.boolean().default(false),
|
|
10
|
-
createdAt: t.timestamp().auto(),
|
|
11
|
-
},
|
|
12
|
-
indexes: ['completed'],
|
|
13
|
-
},
|
|
14
|
-
},
|
|
5
|
+
version: 1,
|
|
6
|
+
collections: {
|
|
7
|
+
todos,
|
|
8
|
+
},
|
|
15
9
|
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface ImportMetaEnv {
|
|
2
|
+
readonly VITE_SYNC_URL?: string
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
interface ImportMeta {
|
|
6
|
+
readonly env: ImportMetaEnv
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
declare module '*?worker&url' {
|
|
10
|
+
const url: string
|
|
11
|
+
export default url
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare module '*?url' {
|
|
15
|
+
const url: string
|
|
16
|
+
export default url
|
|
17
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { existsSync, readdirSync
|
|
2
|
-
import {
|
|
1
|
+
import { copyFileSync, existsSync, readdirSync } from 'node:fs'
|
|
2
|
+
import { join, resolve } from 'node:path'
|
|
3
3
|
import tailwindcss from '@tailwindcss/vite'
|
|
4
4
|
import react from '@vitejs/plugin-react'
|
|
5
5
|
import type { Plugin } from 'vite'
|
|
@@ -11,23 +11,23 @@ import { defineConfig } from 'vite'
|
|
|
11
11
|
* cannot be used and SQLite WASM falls back to in-memory storage (no persistence).
|
|
12
12
|
*/
|
|
13
13
|
function crossOriginIsolation(): Plugin {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
14
|
+
return {
|
|
15
|
+
name: 'cross-origin-isolation',
|
|
16
|
+
configureServer(server) {
|
|
17
|
+
server.middlewares.use((_req, res, next) => {
|
|
18
|
+
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
|
|
19
|
+
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
|
|
20
|
+
next()
|
|
21
|
+
})
|
|
22
|
+
},
|
|
23
|
+
configurePreviewServer(server) {
|
|
24
|
+
server.middlewares.use((_req, res, next) => {
|
|
25
|
+
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
|
|
26
|
+
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
|
|
27
|
+
next()
|
|
28
|
+
})
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/**
|
|
@@ -37,50 +37,57 @@ function crossOriginIsolation(): Plugin {
|
|
|
37
37
|
* sqlite3 loads it dynamically) so the full OPFS VFS can initialize without errors
|
|
38
38
|
*/
|
|
39
39
|
function sqliteWasmHotfix(): Plugin {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
return {
|
|
41
|
+
name: 'sqlite-wasm-hotfix',
|
|
42
|
+
apply: 'build',
|
|
43
|
+
closeBundle() {
|
|
44
|
+
const assetsDir = resolve('dist', 'assets')
|
|
45
|
+
if (!existsSync(assetsDir)) return
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
// Copy hashed sqlite3.wasm to unhashed name
|
|
48
|
+
for (const file of readdirSync(assetsDir)) {
|
|
49
|
+
if (/^sqlite3-.+\.wasm$/.test(file)) {
|
|
50
|
+
copyFileSync(join(assetsDir, file), join(assetsDir, 'sqlite3.wasm'))
|
|
51
|
+
break
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
// Copy OPFS async proxy worker (dynamically loaded by sqlite3, not detected by Vite)
|
|
56
|
+
const proxyFile = resolve(
|
|
57
|
+
'node_modules',
|
|
58
|
+
'@sqlite.org',
|
|
59
|
+
'sqlite-wasm',
|
|
60
|
+
'sqlite-wasm',
|
|
61
|
+
'jswasm',
|
|
62
|
+
'sqlite3-opfs-async-proxy.js',
|
|
63
|
+
)
|
|
64
|
+
if (existsSync(proxyFile)) {
|
|
65
|
+
copyFileSync(proxyFile, join(assetsDir, 'sqlite3-opfs-async-proxy.js'))
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
}
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
export default defineConfig({
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
72
|
+
plugins: [react(), tailwindcss(), crossOriginIsolation(), sqliteWasmHotfix()],
|
|
73
|
+
worker: {
|
|
74
|
+
format: 'es',
|
|
75
|
+
},
|
|
76
|
+
optimizeDeps: {
|
|
77
|
+
exclude: ['@sqlite.org/sqlite-wasm', '@korajs/store'],
|
|
78
|
+
include: ['yjs'],
|
|
79
|
+
},
|
|
80
|
+
resolve: {
|
|
81
|
+
dedupe: ['yjs'],
|
|
82
|
+
},
|
|
83
|
+
server: {
|
|
84
|
+
allowedHosts: true,
|
|
85
|
+
proxy: {
|
|
86
|
+
'/kora-sync': {
|
|
87
|
+
target: 'ws://localhost:3001',
|
|
88
|
+
ws: true,
|
|
89
|
+
rewriteWsOrigin: true,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
86
93
|
})
|
|
@@ -5,3 +5,23 @@
|
|
|
5
5
|
|
|
6
6
|
# Sync server port
|
|
7
7
|
PORT=3001
|
|
8
|
+
|
|
9
|
+
# Server database
|
|
10
|
+
# Leave DATABASE_URL empty to use SQLite at KORA_SERVER_DB.
|
|
11
|
+
# Set DATABASE_URL for cloud Postgres deployments.
|
|
12
|
+
KORA_SERVER_DB=./kora-server.db
|
|
13
|
+
DATABASE_URL=
|
|
14
|
+
|
|
15
|
+
# Operational endpoints (/__kora, /__kora/status, /__kora/events, /__kora/metrics, backups)
|
|
16
|
+
# Set these in production. Use Authorization: Bearer <token> when calling protected endpoints.
|
|
17
|
+
KORA_ADMIN_TOKEN=
|
|
18
|
+
KORA_METRICS_TOKEN=
|
|
19
|
+
KORA_BACKUP_TOKEN=
|
|
20
|
+
|
|
21
|
+
# Optional sync path
|
|
22
|
+
KORA_SYNC_PATH=/kora-sync
|
|
23
|
+
|
|
24
|
+
# Auth
|
|
25
|
+
# Set this to enable built-in /auth/* routes and authenticated sync.
|
|
26
|
+
# Generate with: node -e "console.log(require('node:crypto').randomBytes(32).toString('hex'))"
|
|
27
|
+
KORA_AUTH_SECRET=
|
|
@@ -16,10 +16,17 @@ On first launch, you'll see a setup screen to enter your sync server URL. For lo
|
|
|
16
16
|
|
|
17
17
|
```
|
|
18
18
|
src/
|
|
19
|
-
schema.ts #
|
|
19
|
+
schema.ts # Schema entry point
|
|
20
|
+
auth.ts # Desktop auth client and OAuth callback helpers
|
|
20
21
|
main.tsx # Entry point
|
|
21
22
|
AppShell.tsx # Setup flow + Kora initialization
|
|
22
23
|
App.tsx # Main application UI
|
|
24
|
+
modules/
|
|
25
|
+
todos/
|
|
26
|
+
todo.schema.ts # Todo collection definition
|
|
27
|
+
todo.queries.ts # Reusable todo query builders
|
|
28
|
+
todo.mutations.ts # Todo mutation helpers
|
|
29
|
+
useTodos.ts # React hook connecting todos to the UI
|
|
23
30
|
SetupScreen.tsx # First-launch server configuration
|
|
24
31
|
sync-config.ts # Runtime sync URL storage
|
|
25
32
|
updater.ts # Auto-update checker
|
|
@@ -37,6 +44,9 @@ kora.config.ts # Kora configuration
|
|
|
37
44
|
release-desktop.yml # CI/CD for cross-platform builds
|
|
38
45
|
```
|
|
39
46
|
|
|
47
|
+
In a feature module, `schema` defines data shape, `queries` contain reads only, `mutations` contain
|
|
48
|
+
writes, and `useTodos.ts` is the React binding that exposes todo data and actions to components.
|
|
49
|
+
|
|
40
50
|
## Scripts
|
|
41
51
|
|
|
42
52
|
| Script | Description |
|
|
@@ -124,7 +134,32 @@ Users can also skip setup and use the app offline — they can connect later fro
|
|
|
124
134
|
| Variable | Default | Description |
|
|
125
135
|
|----------|---------|-------------|
|
|
126
136
|
| `VITE_SYNC_URL` | (none) | Pre-configure sync URL at build time |
|
|
137
|
+
| `VITE_AUTH_URL` | derived from sync URL | HTTP URL for `/auth/*` routes |
|
|
127
138
|
| `PORT` | `3001` | Sync server port |
|
|
139
|
+
| `KORA_SERVER_DB` | `./kora-server.db` | SQLite file used by the sync server when `DATABASE_URL` is empty |
|
|
140
|
+
| `KORA_AUTH_DB` | `./kora-auth.db` | SQLite file used by OAuth state and linked identities when `DATABASE_URL` is empty |
|
|
141
|
+
| `DATABASE_URL` | (none) | PostgreSQL connection string for production sync server deployments |
|
|
142
|
+
| `KORA_SYNC_PATH` | `/kora-sync` | WebSocket path exposed by the sync server |
|
|
143
|
+
| `KORA_AUTH_SECRET` | (none) | Enables built-in `/auth/*` routes and authenticated sync when set |
|
|
144
|
+
| `KORA_GOOGLE_CLIENT_ID` | (none) | Enables Google OAuth when set with `KORA_GOOGLE_REDIRECT_URI` |
|
|
145
|
+
| `KORA_GOOGLE_CLIENT_SECRET` | (none) | Google OAuth client secret for confidential web clients |
|
|
146
|
+
| `KORA_GOOGLE_REDIRECT_URI` | (none) | OAuth callback URL for loopback, custom scheme, or hosted handoff |
|
|
147
|
+
| `KORA_OAUTH_PKCE` | `true` when no client secret | Enables PKCE for public desktop/mobile OAuth clients |
|
|
148
|
+
| `KORA_ADMIN_TOKEN` | (none) | Protects `/__kora`, `/__kora/status`, and `/__kora/events` |
|
|
149
|
+
| `KORA_METRICS_TOKEN` | (none) | Protects `/__kora/metrics` |
|
|
150
|
+
| `KORA_BACKUP_TOKEN` | (none) | Protects server backup and restore endpoints |
|
|
151
|
+
|
|
152
|
+
## Authentication
|
|
153
|
+
|
|
154
|
+
Set `KORA_AUTH_SECRET` to enable built-in auth on the sync server. Generate a secret with:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
node -e "console.log(require('node:crypto').randomBytes(32).toString('hex'))"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The template creates a desktop auth client, wraps the app in `AuthProvider`, and wires sync with `createKoraAuthSync({ authClient, schema })`. Google OAuth is enabled on the server when `KORA_GOOGLE_CLIENT_ID` and `KORA_GOOGLE_REDIRECT_URI` are set.
|
|
161
|
+
|
|
162
|
+
For production desktop OAuth, use a loopback callback, custom URL scheme, or hosted handoff that returns `code` and `state` to the app, then complete the flow with `authClient.completeOAuthSignIn('google', { code, state })`. The generated `src/auth.ts` includes the callback completion helper. See the [Authentication Guide](https://ehoneahobed.github.io/kora/guide/authentication#desktop-and-tauri-apps).
|
|
128
163
|
|
|
129
164
|
## Code Signing
|
|
130
165
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type ChildProcess, spawn } from 'node:child_process'
|
|
2
2
|
|
|
3
3
|
// Starts both the sync server and the Tauri app with a single command.
|
|
4
4
|
// The sync server runs in the background so the desktop app can sync
|
|
@@ -7,10 +7,10 @@ import { spawn, type ChildProcess } from 'node:child_process'
|
|
|
7
7
|
const children: ChildProcess[] = []
|
|
8
8
|
|
|
9
9
|
function cleanup() {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
for (const child of children) {
|
|
11
|
+
child.kill()
|
|
12
|
+
}
|
|
13
|
+
process.exit()
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
process.on('SIGINT', cleanup)
|
|
@@ -18,20 +18,20 @@ process.on('SIGTERM', cleanup)
|
|
|
18
18
|
|
|
19
19
|
// Start the sync server
|
|
20
20
|
const syncServer = spawn('tsx', ['server.ts'], {
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
stdio: 'inherit',
|
|
22
|
+
shell: true,
|
|
23
23
|
})
|
|
24
24
|
children.push(syncServer)
|
|
25
25
|
|
|
26
26
|
// Start the Tauri app (manages Vite dev server + Rust build)
|
|
27
27
|
const tauriApp = spawn('tauri', ['dev'], {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
stdio: 'inherit',
|
|
29
|
+
shell: true,
|
|
30
30
|
})
|
|
31
31
|
children.push(tauriApp)
|
|
32
32
|
|
|
33
33
|
// If the Tauri app exits, stop everything
|
|
34
34
|
tauriApp.on('close', (code) => {
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
syncServer.kill()
|
|
36
|
+
process.exit(code ?? 0)
|
|
37
37
|
})
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"korajs": "{{koraVersion}}",
|
|
16
|
+
"@korajs/auth": "{{koraVersion}}",
|
|
16
17
|
"@korajs/core": "{{koraVersion}}",
|
|
17
18
|
"@korajs/react": "{{koraVersion}}",
|
|
18
19
|
"@korajs/tauri": "{{koraVersion}}",
|
|
@@ -30,6 +31,7 @@
|
|
|
30
31
|
"@types/react-dom": "^19.0.0",
|
|
31
32
|
"@vitejs/plugin-react": "^4.3.0",
|
|
32
33
|
"better-sqlite3": "^12.9.0",
|
|
34
|
+
"postgres": "^3.4.0",
|
|
33
35
|
"tsx": "^4.19.0",
|
|
34
36
|
"typescript": "^5.7.0",
|
|
35
37
|
"vite": "^6.0.0",
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type CreateKoraAuthServerOptions,
|
|
3
|
+
createKoraAuthServer,
|
|
4
|
+
createPostgresOAuthStores,
|
|
5
|
+
createSqliteOAuthStores,
|
|
6
|
+
googleProvider,
|
|
7
|
+
} from '@korajs/auth/server'
|
|
8
|
+
import {
|
|
9
|
+
createPostgresServerStore,
|
|
10
|
+
createProductionServer,
|
|
11
|
+
createSqliteServerStore,
|
|
12
|
+
} from '@korajs/server'
|
|
13
|
+
import schema from './src/schema'
|
|
2
14
|
|
|
3
15
|
// Sync server for your Kora desktop app.
|
|
4
16
|
// Started automatically by `pnpm dev`, or run standalone with `pnpm dev:server`.
|
|
@@ -8,16 +20,78 @@ import { createProductionServer, createSqliteServerStore } from '@korajs/server'
|
|
|
8
20
|
// For production, deploy this server and set VITE_SYNC_URL when building
|
|
9
21
|
// the desktop app: VITE_SYNC_URL=wss://your-server.com/kora-sync pnpm build
|
|
10
22
|
|
|
11
|
-
|
|
23
|
+
async function createStore() {
|
|
24
|
+
if (process.env.DATABASE_URL) {
|
|
25
|
+
return createPostgresServerStore({
|
|
26
|
+
connectionString: process.env.DATABASE_URL,
|
|
27
|
+
})
|
|
28
|
+
}
|
|
12
29
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
syncPath: '/kora-sync',
|
|
18
|
-
})
|
|
30
|
+
return createSqliteServerStore({
|
|
31
|
+
filename: process.env.KORA_SERVER_DB || './kora-server.db',
|
|
32
|
+
})
|
|
33
|
+
}
|
|
19
34
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
async function start() {
|
|
36
|
+
const store = await createStore()
|
|
37
|
+
await store.setSchema(schema)
|
|
38
|
+
|
|
39
|
+
const syncPath = process.env.KORA_SYNC_PATH || '/kora-sync'
|
|
40
|
+
const auth = await createAuth()
|
|
41
|
+
const server = createProductionServer({
|
|
42
|
+
store,
|
|
43
|
+
port: Number(process.env.PORT) || 3001,
|
|
44
|
+
syncPath,
|
|
45
|
+
httpRoutes: auth ? [{ path: '/auth', handle: auth.handleRequest }] : undefined,
|
|
46
|
+
syncOptions: auth ? { auth: auth.auth } : undefined,
|
|
47
|
+
operationalAuth: {
|
|
48
|
+
adminToken: process.env.KORA_ADMIN_TOKEN,
|
|
49
|
+
metricsToken: process.env.KORA_METRICS_TOKEN,
|
|
50
|
+
backupToken: process.env.KORA_BACKUP_TOKEN,
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const url = await server.start()
|
|
55
|
+
console.log(`Kora sync server running at ${url}`)
|
|
56
|
+
console.log(` Sync endpoint: ${url.replace('http', 'ws')}${syncPath}`)
|
|
57
|
+
if (auth) console.log(` Auth endpoint: ${url}/auth`)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
void start()
|
|
61
|
+
|
|
62
|
+
async function createAuth() {
|
|
63
|
+
if (!process.env.KORA_AUTH_SECRET) {
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const oauth = await createOAuthConfig()
|
|
68
|
+
return createKoraAuthServer({
|
|
69
|
+
jwtSecret: process.env.KORA_AUTH_SECRET,
|
|
70
|
+
...(oauth ? { oauth } : {}),
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function createOAuthConfig(): Promise<CreateKoraAuthServerOptions['oauth'] | undefined> {
|
|
75
|
+
if (!process.env.KORA_GOOGLE_CLIENT_ID || !process.env.KORA_GOOGLE_REDIRECT_URI) {
|
|
76
|
+
return undefined
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const oauthStores = process.env.DATABASE_URL
|
|
80
|
+
? await createPostgresOAuthStores({ connectionString: process.env.DATABASE_URL })
|
|
81
|
+
: await createSqliteOAuthStores({
|
|
82
|
+
filename: process.env.KORA_AUTH_DB || './kora-auth.db',
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
providers: [
|
|
87
|
+
googleProvider({
|
|
88
|
+
clientId: process.env.KORA_GOOGLE_CLIENT_ID,
|
|
89
|
+
clientSecret: process.env.KORA_GOOGLE_CLIENT_SECRET,
|
|
90
|
+
redirectUri: process.env.KORA_GOOGLE_REDIRECT_URI,
|
|
91
|
+
pkce: process.env.KORA_OAUTH_PKCE === 'true' || !process.env.KORA_GOOGLE_CLIENT_SECRET,
|
|
92
|
+
}),
|
|
93
|
+
],
|
|
94
|
+
stateStore: oauthStores.stateStore,
|
|
95
|
+
linkedIdentityStore: oauthStores.linkedIdentityStore,
|
|
96
|
+
}
|
|
97
|
+
}
|