vasp-cli 0.3.1 → 0.9.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/vasp +80 -31
- package/package.json +2 -2
- package/starters/minimal.vasp +1 -1
- package/starters/recipe.vasp +70 -0
- package/starters/todo-auth-ssr.vasp +33 -20
- package/starters/todo.vasp +15 -8
- package/templates/shared/.gitignore.hbs +1 -0
- package/templates/shared/README.md.hbs +53 -0
- package/templates/shared/auth/client/Login.vue.hbs +1 -1
- package/templates/shared/auth/client/Register.vue.hbs +1 -1
- package/templates/shared/auth/server/index.hbs +4 -8
- package/templates/shared/auth/server/middleware.hbs +33 -15
- package/templates/shared/auth/server/plugin.hbs +7 -0
- package/templates/shared/auth/server/providers/github.hbs +1 -1
- package/templates/shared/auth/server/providers/google.hbs +1 -1
- package/templates/shared/auth/server/providers/usernameAndPassword.hbs +3 -6
- package/templates/shared/bunfig.toml.hbs +3 -0
- package/templates/shared/drizzle/schema.hbs +39 -9
- package/templates/shared/jobs/_job.hbs +12 -2
- package/templates/shared/package.json.hbs +14 -4
- package/templates/shared/server/db/client.hbs +19 -1
- package/templates/shared/server/db/seed.hbs +16 -0
- package/templates/shared/server/index.hbs +48 -0
- package/templates/shared/server/middleware/errorHandler.hbs +75 -0
- package/templates/shared/server/middleware/logger.hbs +74 -0
- package/templates/{templates/shared → shared}/server/middleware/rateLimit.hbs +2 -2
- package/templates/shared/server/routes/_vasp.hbs +37 -0
- package/templates/shared/server/routes/actions/_action.hbs +5 -1
- package/templates/shared/server/routes/api/_api.hbs +24 -0
- package/templates/shared/server/routes/crud/_crud.hbs +103 -11
- package/templates/shared/server/routes/queries/_query.hbs +5 -1
- package/templates/shared/server/routes/realtime/_channel.hbs +58 -10
- package/templates/shared/shared/types.hbs +58 -0
- package/templates/shared/shared/validation.hbs +20 -0
- package/templates/shared/tests/actions/_action.test.js.hbs +7 -0
- package/templates/shared/tests/actions/_action.test.ts.hbs +7 -0
- package/templates/shared/tests/auth/login.test.js.hbs +7 -0
- package/templates/shared/tests/auth/login.test.ts.hbs +7 -0
- package/templates/shared/tests/crud/_entity.test.js.hbs +7 -0
- package/templates/shared/tests/crud/_entity.test.ts.hbs +7 -0
- package/templates/shared/tests/queries/_query.test.js.hbs +7 -0
- package/templates/shared/tests/queries/_query.test.ts.hbs +7 -0
- package/templates/shared/tests/setup.js.hbs +5 -0
- package/templates/shared/tests/setup.ts.hbs +5 -0
- package/templates/shared/tests/vitest.config.js.hbs +8 -0
- package/templates/shared/tests/vitest.config.ts.hbs +8 -0
- package/templates/shared/tsconfig.json.hbs +2 -1
- package/templates/spa/js/src/App.vue.hbs +9 -1
- package/templates/spa/js/src/components/VaspErrorBoundary.vue.hbs +33 -0
- package/templates/spa/js/src/components/VaspNotifications.vue.hbs +60 -0
- package/templates/spa/js/src/vasp/auth.js.hbs +31 -15
- package/templates/spa/js/src/vasp/client/actions.js.hbs +7 -1
- package/templates/spa/js/src/vasp/client/crud.js.hbs +94 -5
- package/templates/spa/js/src/vasp/useVaspNotifications.js.hbs +35 -0
- package/templates/spa/js/vite.config.js.hbs +1 -0
- package/templates/spa/ts/src/App.vue.hbs +9 -1
- package/templates/spa/ts/src/components/VaspErrorBoundary.vue.hbs +33 -0
- package/templates/spa/ts/src/components/VaspNotifications.vue.hbs +60 -0
- package/templates/spa/ts/src/vasp/auth.ts.hbs +31 -15
- package/templates/spa/ts/src/vasp/client/actions.ts.hbs +7 -1
- package/templates/spa/ts/src/vasp/client/crud.ts.hbs +96 -10
- package/templates/spa/ts/src/vasp/client/types.ts.hbs +14 -28
- package/templates/spa/ts/src/vasp/useVaspNotifications.ts.hbs +41 -0
- package/templates/spa/ts/vite.config.ts.hbs +1 -0
- package/templates/ssr/js/error.vue.hbs +23 -0
- package/templates/ssr/js/nuxt.config.js.hbs +1 -0
- package/templates/ssr/js/plugins/vasp.client.js.hbs +11 -1
- package/templates/ssr/ts/error.vue.hbs +26 -0
- package/templates/ssr/ts/nuxt.config.ts.hbs +1 -0
- package/templates/ssr/ts/plugins/vasp.client.ts.hbs +11 -1
- package/templates/starters/minimal.vasp +15 -0
- package/templates/starters/recipe.vasp +70 -0
- package/templates/starters/todo-auth-ssr.vasp +65 -0
- package/templates/starters/todo.vasp +42 -0
- package/templates/templates/shared/.gitignore.hbs +0 -8
- package/templates/templates/shared/auth/client/Login.vue.hbs +0 -46
- package/templates/templates/shared/auth/client/Register.vue.hbs +0 -42
- package/templates/templates/shared/auth/server/index.hbs +0 -51
- package/templates/templates/shared/auth/server/middleware.hbs +0 -33
- package/templates/templates/shared/auth/server/providers/github.hbs +0 -48
- package/templates/templates/shared/auth/server/providers/google.hbs +0 -53
- package/templates/templates/shared/auth/server/providers/usernameAndPassword.hbs +0 -69
- package/templates/templates/shared/bunfig.toml.hbs +0 -2
- package/templates/templates/shared/drizzle/schema.hbs +0 -48
- package/templates/templates/shared/jobs/_job.hbs +0 -34
- package/templates/templates/shared/jobs/boss.hbs +0 -15
- package/templates/templates/shared/package.json.hbs +0 -35
- package/templates/templates/shared/server/db/client.hbs +0 -12
- package/templates/templates/shared/server/index.hbs +0 -60
- package/templates/templates/shared/server/routes/actions/_action.hbs +0 -20
- package/templates/templates/shared/server/routes/crud/_crud.hbs +0 -86
- package/templates/templates/shared/server/routes/jobs/_schedule.hbs +0 -12
- package/templates/templates/shared/server/routes/queries/_query.hbs +0 -20
- package/templates/templates/shared/server/routes/realtime/_channel.hbs +0 -78
- package/templates/templates/shared/server/routes/realtime/index.hbs +0 -9
- package/templates/templates/shared/tsconfig.json.hbs +0 -21
- package/templates/templates/spa/js/index.html.hbs +0 -12
- package/templates/templates/spa/js/src/App.vue.hbs +0 -3
- package/templates/templates/spa/js/src/main.js.hbs +0 -9
- package/templates/templates/spa/js/src/router/index.js.hbs +0 -41
- package/templates/templates/spa/js/src/vasp/auth.js.hbs +0 -45
- package/templates/templates/spa/js/src/vasp/client/actions.js.hbs +0 -15
- package/templates/templates/spa/js/src/vasp/client/crud.js.hbs +0 -30
- package/templates/templates/spa/js/src/vasp/client/index.js.hbs +0 -16
- package/templates/templates/spa/js/src/vasp/client/queries.js.hbs +0 -15
- package/templates/templates/spa/js/src/vasp/client/realtime.js.hbs +0 -51
- package/templates/templates/spa/js/src/vasp/plugin.js.hbs +0 -11
- package/templates/templates/spa/js/vite.config.js.hbs +0 -26
- package/templates/templates/spa/ts/index.html.hbs +0 -12
- package/templates/templates/spa/ts/src/App.vue.hbs +0 -3
- package/templates/templates/spa/ts/src/main.ts.hbs +0 -9
- package/templates/templates/spa/ts/src/router/index.ts.hbs +0 -41
- package/templates/templates/spa/ts/src/vasp/auth.ts.hbs +0 -53
- package/templates/templates/spa/ts/src/vasp/client/actions.ts.hbs +0 -19
- package/templates/templates/spa/ts/src/vasp/client/crud.ts.hbs +0 -37
- package/templates/templates/spa/ts/src/vasp/client/index.ts.hbs +0 -17
- package/templates/templates/spa/ts/src/vasp/client/queries.ts.hbs +0 -19
- package/templates/templates/spa/ts/src/vasp/client/realtime.ts.hbs +0 -56
- package/templates/templates/spa/ts/src/vasp/client/types.ts.hbs +0 -33
- package/templates/templates/spa/ts/src/vasp/plugin.ts.hbs +0 -12
- package/templates/templates/spa/ts/vite.config.ts.hbs +0 -26
- package/templates/templates/ssr/js/_page.vue.hbs +0 -10
- package/templates/templates/ssr/js/app.vue.hbs +0 -3
- package/templates/templates/ssr/js/composables/useAuth.js.hbs +0 -52
- package/templates/templates/ssr/js/composables/useVasp.js.hbs +0 -6
- package/templates/templates/ssr/js/middleware/auth.js.hbs +0 -8
- package/templates/templates/ssr/js/nuxt.config.js.hbs +0 -15
- package/templates/templates/ssr/js/plugins/vasp.client.js.hbs +0 -27
- package/templates/templates/ssr/js/plugins/vasp.server.js.hbs +0 -33
- package/templates/templates/ssr/ts/_page.vue.hbs +0 -10
- package/templates/templates/ssr/ts/app.vue.hbs +0 -3
- package/templates/templates/ssr/ts/composables/useAuth.ts.hbs +0 -56
- package/templates/templates/ssr/ts/composables/useVasp.ts.hbs +0 -10
- package/templates/templates/ssr/ts/middleware/auth.ts.hbs +0 -8
- package/templates/templates/ssr/ts/nuxt.config.ts.hbs +0 -19
- package/templates/templates/ssr/ts/plugins/vasp.client.ts.hbs +0 -27
- package/templates/templates/ssr/ts/plugins/vasp.server.ts.hbs +0 -33
- /package/templates/{templates/shared/.env.example.hbs → shared/.env.hbs} +0 -0
- /package/templates/{templates/shared → shared}/drizzle/drizzle.config.hbs +0 -0
- /package/templates/{templates/shared → shared}/server/middleware/csrf.hbs +0 -0
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { Elysia } from 'elysia'
|
|
2
|
-
{{#if ../hasAuth}}
|
|
3
|
-
import jwt from '@elysiajs/jwt'
|
|
4
|
-
{{/if}}
|
|
5
|
-
|
|
6
|
-
// Room-based subscriber map for '{{name}}' channel
|
|
7
|
-
// Each room key maps to a set of connected WebSocket clients
|
|
8
|
-
const rooms = new Map()
|
|
9
|
-
|
|
10
|
-
function getRoom(roomId) {
|
|
11
|
-
if (!rooms.has(roomId)) rooms.set(roomId, new Set())
|
|
12
|
-
return rooms.get(roomId)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Publish a realtime event to all subscribers in a specific room of '{{name}}'.
|
|
17
|
-
* Called automatically by CRUD mutation handlers.
|
|
18
|
-
*/
|
|
19
|
-
export function publish{{pascalCase name}}(event, data, roomId = 'default') {
|
|
20
|
-
const room = rooms.get(roomId)
|
|
21
|
-
if (!room) return
|
|
22
|
-
const message = JSON.stringify({ channel: '{{camelCase name}}', room: roomId, event, data })
|
|
23
|
-
for (const ws of room) {
|
|
24
|
-
try { ws.send(message) } catch { room.delete(ws) }
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Broadcast to all rooms of '{{name}}'.
|
|
30
|
-
*/
|
|
31
|
-
export function broadcast{{pascalCase name}}(event, data) {
|
|
32
|
-
const message = JSON.stringify({ channel: '{{camelCase name}}', event, data })
|
|
33
|
-
for (const [, room] of rooms) {
|
|
34
|
-
for (const ws of room) {
|
|
35
|
-
try { ws.send(message) } catch { room.delete(ws) }
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export const {{camelCase name}}Channel = new Elysia()
|
|
41
|
-
{{#if ../hasAuth}}
|
|
42
|
-
.use(jwt({ name: 'jwt', secret: process.env.JWT_SECRET || 'vasp-dev-secret' }))
|
|
43
|
-
{{/if}}
|
|
44
|
-
.ws('/ws/{{camelCase name}}', {
|
|
45
|
-
{{#if ../hasAuth}}
|
|
46
|
-
async beforeHandle({ jwt: jwtPlugin, request }) {
|
|
47
|
-
const url = new URL(request.url)
|
|
48
|
-
const token = url.searchParams.get('token')
|
|
49
|
-
if (!token) return new Response('Unauthorized', { status: 401 })
|
|
50
|
-
const payload = await jwtPlugin.verify(token)
|
|
51
|
-
if (!payload) return new Response('Unauthorized', { status: 401 })
|
|
52
|
-
},
|
|
53
|
-
{{/if}}
|
|
54
|
-
open(ws) {
|
|
55
|
-
const room = ws.data?.query?.room ?? 'default'
|
|
56
|
-
getRoom(room).add(ws)
|
|
57
|
-
ws.data._room = room
|
|
58
|
-
},
|
|
59
|
-
message(ws, msg) {
|
|
60
|
-
// Client can switch rooms via { action: 'join', room: 'roomId' }
|
|
61
|
-
try {
|
|
62
|
-
const parsed = typeof msg === 'string' ? JSON.parse(msg) : msg
|
|
63
|
-
if (parsed.action === 'join' && parsed.room) {
|
|
64
|
-
const oldRoom = ws.data._room
|
|
65
|
-
if (oldRoom) rooms.get(oldRoom)?.delete(ws)
|
|
66
|
-
ws.data._room = parsed.room
|
|
67
|
-
getRoom(parsed.room).add(ws)
|
|
68
|
-
ws.send(JSON.stringify({ ack: 'joined', room: parsed.room }))
|
|
69
|
-
return
|
|
70
|
-
}
|
|
71
|
-
} catch { /* ignore parse errors */ }
|
|
72
|
-
ws.send(JSON.stringify({ ack: msg }))
|
|
73
|
-
},
|
|
74
|
-
close(ws) {
|
|
75
|
-
const room = ws.data?._room
|
|
76
|
-
if (room) rooms.get(room)?.delete(ws)
|
|
77
|
-
},
|
|
78
|
-
})
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ESNext",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
7
|
-
"strict": true,
|
|
8
|
-
"noUnusedLocals": true,
|
|
9
|
-
"noUnusedParameters": true,
|
|
10
|
-
"noFallthroughCasesInSwitch": true,
|
|
11
|
-
"esModuleInterop": true,
|
|
12
|
-
"skipLibCheck": true,
|
|
13
|
-
"jsx": "preserve",
|
|
14
|
-
"paths": {
|
|
15
|
-
"@src/*": ["./src/*"],
|
|
16
|
-
"@vasp-framework/client": ["./src/vasp/client/index.ts"]
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"include": ["src/**/*.ts", "src/**/*.vue", "server/**/*.ts", "drizzle/**/*.ts"],
|
|
20
|
-
"exclude": ["node_modules", "dist", ".vasp-gen"]
|
|
21
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>{{appTitle}}</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<div id="app"></div>
|
|
10
|
-
<script type="module" src="/src/main.js"></script>
|
|
11
|
-
</body>
|
|
12
|
-
</html>
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { createRouter, createWebHistory } from 'vue-router'
|
|
2
|
-
{{#if hasAuth}}
|
|
3
|
-
import { useAuth } from '../vasp/auth.js'
|
|
4
|
-
{{/if}}
|
|
5
|
-
|
|
6
|
-
const routes = [
|
|
7
|
-
{{#each routes}}
|
|
8
|
-
{
|
|
9
|
-
path: '{{path}}',
|
|
10
|
-
component: () => import('{{lookup ../pagesMap to}}'),
|
|
11
|
-
},
|
|
12
|
-
{{/each}}
|
|
13
|
-
{{#if hasAuth}}
|
|
14
|
-
{
|
|
15
|
-
path: '/login',
|
|
16
|
-
component: () => import('../pages/Login.vue'),
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
path: '/register',
|
|
20
|
-
component: () => import('../pages/Register.vue'),
|
|
21
|
-
},
|
|
22
|
-
{{/if}}
|
|
23
|
-
]
|
|
24
|
-
|
|
25
|
-
const router = createRouter({
|
|
26
|
-
history: createWebHistory(),
|
|
27
|
-
routes,
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
{{#if hasAuth}}
|
|
31
|
-
router.beforeEach(async (to) => {
|
|
32
|
-
const { user, checkAuth } = useAuth()
|
|
33
|
-
await checkAuth()
|
|
34
|
-
const publicPaths = ['/login', '/register']
|
|
35
|
-
if (!user.value && !publicPaths.includes(to.path)) {
|
|
36
|
-
return '/login'
|
|
37
|
-
}
|
|
38
|
-
})
|
|
39
|
-
{{/if}}
|
|
40
|
-
|
|
41
|
-
export default router
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { ref } from 'vue'
|
|
2
|
-
import { $fetch } from 'ofetch'
|
|
3
|
-
|
|
4
|
-
const user = ref(null)
|
|
5
|
-
let checked = false
|
|
6
|
-
|
|
7
|
-
const API = import.meta.env.VITE_API_URL || '/api'
|
|
8
|
-
|
|
9
|
-
export function useAuth() {
|
|
10
|
-
async function checkAuth() {
|
|
11
|
-
if (checked) return
|
|
12
|
-
checked = true
|
|
13
|
-
try {
|
|
14
|
-
user.value = await $fetch(`${API}/auth/me`, { credentials: 'include' })
|
|
15
|
-
} catch {
|
|
16
|
-
user.value = null
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async function login(username, password) {
|
|
21
|
-
user.value = await $fetch(`${API}/auth/login`, {
|
|
22
|
-
method: 'POST',
|
|
23
|
-
body: { username, password },
|
|
24
|
-
credentials: 'include',
|
|
25
|
-
})
|
|
26
|
-
return user.value
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async function register(username, password, email) {
|
|
30
|
-
user.value = await $fetch(`${API}/auth/register`, {
|
|
31
|
-
method: 'POST',
|
|
32
|
-
body: { username, password, email },
|
|
33
|
-
credentials: 'include',
|
|
34
|
-
})
|
|
35
|
-
return user.value
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function logout() {
|
|
39
|
-
await $fetch(`${API}/auth/logout`, { method: 'POST', credentials: 'include' })
|
|
40
|
-
user.value = null
|
|
41
|
-
checked = false
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return { user, checkAuth, login, register, logout }
|
|
45
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// Auto-generated by Vasp — do not edit
|
|
2
|
-
import { useVasp } from '@vasp-framework/runtime'
|
|
3
|
-
|
|
4
|
-
{{#each actions}}
|
|
5
|
-
/**
|
|
6
|
-
* Call the '{{name}}' action on the Vasp backend.
|
|
7
|
-
* @param {unknown} args - Arguments to pass to the action function
|
|
8
|
-
* @returns {Promise<unknown>}
|
|
9
|
-
*/
|
|
10
|
-
export async function {{camelCase name}}(args) {
|
|
11
|
-
const { $vasp } = useVasp()
|
|
12
|
-
return $vasp.action('{{camelCase name}}', args)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
{{/each}}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// Auto-generated by Vasp — do not edit
|
|
2
|
-
import { $fetch } from 'ofetch'
|
|
3
|
-
|
|
4
|
-
const API = import.meta.env.VITE_API_URL || '/api'
|
|
5
|
-
|
|
6
|
-
{{#each cruds}}
|
|
7
|
-
/**
|
|
8
|
-
* CRUD helpers for the '{{entity}}' entity.
|
|
9
|
-
*/
|
|
10
|
-
export function use{{pascalCase entity}}Crud() {
|
|
11
|
-
const base = `${API}/crud/{{camelCase entity}}`
|
|
12
|
-
|
|
13
|
-
return {
|
|
14
|
-
{{#if (includes operations "list")}}
|
|
15
|
-
list: () => $fetch(base, { credentials: 'include' }),
|
|
16
|
-
{{/if}}
|
|
17
|
-
{{#if (includes operations "create")}}
|
|
18
|
-
create: (data) => $fetch(base, { method: 'POST', body: data, credentials: 'include' }),
|
|
19
|
-
{{/if}}
|
|
20
|
-
get: (id) => $fetch(`${base}/${id}`, { credentials: 'include' }),
|
|
21
|
-
{{#if (includes operations "update")}}
|
|
22
|
-
update: (id, data) => $fetch(`${base}/${id}`, { method: 'PUT', body: data, credentials: 'include' }),
|
|
23
|
-
{{/if}}
|
|
24
|
-
{{#if (includes operations "delete")}}
|
|
25
|
-
remove: (id) => $fetch(`${base}/${id}`, { method: 'DELETE', credentials: 'include' }),
|
|
26
|
-
{{/if}}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
{{/each}}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// Auto-generated by Vasp — do not edit directly
|
|
2
|
-
// Re-run `vasp build` or restart `vasp start` to regenerate
|
|
3
|
-
|
|
4
|
-
export { useVasp } from '@vasp-framework/runtime'
|
|
5
|
-
{{#each queries}}
|
|
6
|
-
export { {{camelCase name}} } from './queries.js'
|
|
7
|
-
{{/each}}
|
|
8
|
-
{{#each actions}}
|
|
9
|
-
export { {{camelCase name}} } from './actions.js'
|
|
10
|
-
{{/each}}
|
|
11
|
-
{{#each cruds}}
|
|
12
|
-
export { use{{pascalCase entity}}Crud } from './crud.js'
|
|
13
|
-
{{/each}}
|
|
14
|
-
{{#if hasRealtime}}
|
|
15
|
-
export { useRealtime } from './realtime.js'
|
|
16
|
-
{{/if}}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// Auto-generated by Vasp — do not edit
|
|
2
|
-
import { useVasp } from '@vasp-framework/runtime'
|
|
3
|
-
|
|
4
|
-
{{#each queries}}
|
|
5
|
-
/**
|
|
6
|
-
* Call the '{{name}}' query on the Vasp backend.
|
|
7
|
-
* @param {unknown} [args] - Arguments to pass to the query function
|
|
8
|
-
* @returns {Promise<unknown>}
|
|
9
|
-
*/
|
|
10
|
-
export async function {{camelCase name}}(args) {
|
|
11
|
-
const { $vasp } = useVasp()
|
|
12
|
-
return $vasp.query('{{camelCase name}}', args)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
{{/each}}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
// Auto-generated by Vasp — do not edit
|
|
2
|
-
import { ref, onUnmounted } from 'vue'
|
|
3
|
-
|
|
4
|
-
const WS_BASE = import.meta.env.VITE_WS_URL || `ws://${location.host}`
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* useRealtime — subscribe to a Vasp realtime channel.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* const { on } = useRealtime('todoChannel')
|
|
11
|
-
* on('created', (todo) => todos.value.push(todo))
|
|
12
|
-
*/
|
|
13
|
-
export function useRealtime(channelName) {
|
|
14
|
-
const connected = ref(false)
|
|
15
|
-
const handlers = new Map()
|
|
16
|
-
let ws = null
|
|
17
|
-
let reconnectTimer = null
|
|
18
|
-
|
|
19
|
-
function connect() {
|
|
20
|
-
ws = new WebSocket(`${WS_BASE}/ws/${channelName}`)
|
|
21
|
-
|
|
22
|
-
ws.onopen = () => { connected.value = true }
|
|
23
|
-
ws.onclose = () => {
|
|
24
|
-
connected.value = false
|
|
25
|
-
reconnectTimer = setTimeout(connect, 3000) // auto-reconnect
|
|
26
|
-
}
|
|
27
|
-
ws.onerror = () => ws.close()
|
|
28
|
-
ws.onmessage = ({ data }) => {
|
|
29
|
-
try {
|
|
30
|
-
const msg = JSON.parse(data)
|
|
31
|
-
if (msg.channel === channelName && handlers.has(msg.event)) {
|
|
32
|
-
handlers.get(msg.event)(msg.data)
|
|
33
|
-
}
|
|
34
|
-
} catch {}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function on(event, handler) {
|
|
39
|
-
handlers.set(event, handler)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function disconnect() {
|
|
43
|
-
clearTimeout(reconnectTimer)
|
|
44
|
-
ws?.close()
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
connect()
|
|
48
|
-
onUnmounted(disconnect)
|
|
49
|
-
|
|
50
|
-
return { connected, on, disconnect }
|
|
51
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { createVaspClient } from '@vasp-framework/runtime'
|
|
2
|
-
|
|
3
|
-
export const vaspPlugin = {
|
|
4
|
-
install(app) {
|
|
5
|
-
const client = createVaspClient({
|
|
6
|
-
baseURL: import.meta.env.VITE_API_URL || '/api',
|
|
7
|
-
})
|
|
8
|
-
app.provide('$vasp', client)
|
|
9
|
-
app.config.globalProperties.$vasp = client
|
|
10
|
-
},
|
|
11
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vite'
|
|
2
|
-
import vue from '@vitejs/plugin-vue'
|
|
3
|
-
import { fileURLToPath, URL } from 'node:url'
|
|
4
|
-
|
|
5
|
-
export default defineConfig({
|
|
6
|
-
plugins: [vue()],
|
|
7
|
-
resolve: {
|
|
8
|
-
alias: {
|
|
9
|
-
'@src': fileURLToPath(new URL('./src', import.meta.url)),
|
|
10
|
-
'@vasp-framework/client': fileURLToPath(new URL('./src/vasp/client', import.meta.url)),
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
server: {
|
|
14
|
-
port: {{frontendPort}},
|
|
15
|
-
proxy: {
|
|
16
|
-
'/api': {
|
|
17
|
-
target: 'http://localhost:{{backendPort}}',
|
|
18
|
-
changeOrigin: true,
|
|
19
|
-
},
|
|
20
|
-
'/ws': {
|
|
21
|
-
target: 'ws://localhost:{{backendPort}}',
|
|
22
|
-
ws: true,
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
})
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>{{appTitle}}</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<div id="app"></div>
|
|
10
|
-
<script type="module" src="/src/main.ts"></script>
|
|
11
|
-
</body>
|
|
12
|
-
</html>
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
|
|
2
|
-
{{#if hasAuth}}
|
|
3
|
-
import { useAuth } from '../vasp/auth.js'
|
|
4
|
-
{{/if}}
|
|
5
|
-
|
|
6
|
-
const routes: RouteRecordRaw[] = [
|
|
7
|
-
{{#each routes}}
|
|
8
|
-
{
|
|
9
|
-
path: '{{path}}',
|
|
10
|
-
component: () => import('{{lookup ../pagesMap to}}'),
|
|
11
|
-
},
|
|
12
|
-
{{/each}}
|
|
13
|
-
{{#if hasAuth}}
|
|
14
|
-
{
|
|
15
|
-
path: '/login',
|
|
16
|
-
component: () => import('../pages/Login.vue'),
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
path: '/register',
|
|
20
|
-
component: () => import('../pages/Register.vue'),
|
|
21
|
-
},
|
|
22
|
-
{{/if}}
|
|
23
|
-
]
|
|
24
|
-
|
|
25
|
-
const router = createRouter({
|
|
26
|
-
history: createWebHistory(),
|
|
27
|
-
routes,
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
{{#if hasAuth}}
|
|
31
|
-
router.beforeEach(async (to) => {
|
|
32
|
-
const { user, checkAuth } = useAuth()
|
|
33
|
-
await checkAuth()
|
|
34
|
-
const publicPaths = ['/login', '/register']
|
|
35
|
-
if (!user.value && !publicPaths.includes(to.path)) {
|
|
36
|
-
return '/login'
|
|
37
|
-
}
|
|
38
|
-
})
|
|
39
|
-
{{/if}}
|
|
40
|
-
|
|
41
|
-
export default router
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { ref, type Ref } from 'vue'
|
|
2
|
-
import { $fetch } from 'ofetch'
|
|
3
|
-
|
|
4
|
-
export interface AuthUser {
|
|
5
|
-
id: number
|
|
6
|
-
username: string
|
|
7
|
-
email: string | null
|
|
8
|
-
createdAt: Date
|
|
9
|
-
updatedAt: Date
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const user: Ref<AuthUser | null> = ref(null)
|
|
13
|
-
let checked = false
|
|
14
|
-
|
|
15
|
-
const API = import.meta.env.VITE_API_URL || '/api'
|
|
16
|
-
|
|
17
|
-
export function useAuth() {
|
|
18
|
-
async function checkAuth(): Promise<void> {
|
|
19
|
-
if (checked) return
|
|
20
|
-
checked = true
|
|
21
|
-
try {
|
|
22
|
-
user.value = await $fetch<AuthUser>(`${API}/auth/me`, { credentials: 'include' })
|
|
23
|
-
} catch {
|
|
24
|
-
user.value = null
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async function login(username: string, password: string): Promise<AuthUser> {
|
|
29
|
-
user.value = await $fetch<AuthUser>(`${API}/auth/login`, {
|
|
30
|
-
method: 'POST',
|
|
31
|
-
body: { username, password },
|
|
32
|
-
credentials: 'include',
|
|
33
|
-
})
|
|
34
|
-
return user.value!
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async function register(username: string, password: string, email?: string): Promise<AuthUser> {
|
|
38
|
-
user.value = await $fetch<AuthUser>(`${API}/auth/register`, {
|
|
39
|
-
method: 'POST',
|
|
40
|
-
body: { username, password, email },
|
|
41
|
-
credentials: 'include',
|
|
42
|
-
})
|
|
43
|
-
return user.value!
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function logout(): Promise<void> {
|
|
47
|
-
await $fetch(`${API}/auth/logout`, { method: 'POST', credentials: 'include' })
|
|
48
|
-
user.value = null
|
|
49
|
-
checked = false
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return { user, checkAuth, login, register, logout }
|
|
53
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// Auto-generated by Vasp — do not edit
|
|
2
|
-
import { useVasp } from '@vasp-framework/runtime'
|
|
3
|
-
import type {
|
|
4
|
-
{{#each actions}}
|
|
5
|
-
{{pascalCase name}}Args,
|
|
6
|
-
{{pascalCase name}}Return,
|
|
7
|
-
{{/each}}
|
|
8
|
-
} from './types.js'
|
|
9
|
-
|
|
10
|
-
{{#each actions}}
|
|
11
|
-
/**
|
|
12
|
-
* Call the '{{name}}' action on the Vasp backend.
|
|
13
|
-
*/
|
|
14
|
-
export async function {{camelCase name}}(args: {{pascalCase name}}Args): Promise<{{pascalCase name}}Return> {
|
|
15
|
-
const { $vasp } = useVasp()
|
|
16
|
-
return $vasp.action('{{camelCase name}}', args) as Promise<{{pascalCase name}}Return>
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
{{/each}}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// Auto-generated by Vasp — do not edit
|
|
2
|
-
import { $fetch } from 'ofetch'
|
|
3
|
-
import type {
|
|
4
|
-
{{#each cruds}}
|
|
5
|
-
{{pascalCase entity}},
|
|
6
|
-
New{{pascalCase entity}},
|
|
7
|
-
{{/each}}
|
|
8
|
-
} from './types.js'
|
|
9
|
-
|
|
10
|
-
const API = import.meta.env.VITE_API_URL || '/api'
|
|
11
|
-
|
|
12
|
-
{{#each cruds}}
|
|
13
|
-
export function use{{pascalCase entity}}Crud() {
|
|
14
|
-
const base = `${API}/crud/{{camelCase entity}}`
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
{{#if (includes operations "list")}}
|
|
18
|
-
list: (): Promise<{{pascalCase entity}}[]> => $fetch(base, { credentials: 'include' }),
|
|
19
|
-
{{/if}}
|
|
20
|
-
{{#if (includes operations "create")}}
|
|
21
|
-
create: (data: New{{pascalCase entity}}): Promise<{{pascalCase entity}}> =>
|
|
22
|
-
$fetch(base, { method: 'POST', body: data, credentials: 'include' }),
|
|
23
|
-
{{/if}}
|
|
24
|
-
get: (id: number): Promise<{{pascalCase entity}}> =>
|
|
25
|
-
$fetch(`${base}/${id}`, { credentials: 'include' }),
|
|
26
|
-
{{#if (includes operations "update")}}
|
|
27
|
-
update: (id: number, data: Partial<New{{pascalCase entity}}>): Promise<{{pascalCase entity}}> =>
|
|
28
|
-
$fetch(`${base}/${id}`, { method: 'PUT', body: data, credentials: 'include' }),
|
|
29
|
-
{{/if}}
|
|
30
|
-
{{#if (includes operations "delete")}}
|
|
31
|
-
remove: (id: number): Promise<{ ok: boolean }> =>
|
|
32
|
-
$fetch(`${base}/${id}`, { method: 'DELETE', credentials: 'include' }),
|
|
33
|
-
{{/if}}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
{{/each}}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// Auto-generated by Vasp — do not edit directly
|
|
2
|
-
// Re-run `vasp build` or restart `vasp start` to regenerate
|
|
3
|
-
|
|
4
|
-
export { useVasp } from '@vasp-framework/runtime'
|
|
5
|
-
{{#each queries}}
|
|
6
|
-
export { {{camelCase name}} } from './queries.js'
|
|
7
|
-
{{/each}}
|
|
8
|
-
{{#each actions}}
|
|
9
|
-
export { {{camelCase name}} } from './actions.js'
|
|
10
|
-
{{/each}}
|
|
11
|
-
{{#each cruds}}
|
|
12
|
-
export { use{{pascalCase entity}}Crud } from './crud.js'
|
|
13
|
-
{{/each}}
|
|
14
|
-
{{#if hasRealtime}}
|
|
15
|
-
export { useRealtime } from './realtime.js'
|
|
16
|
-
{{/if}}
|
|
17
|
-
export type * from './types.js'
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// Auto-generated by Vasp — do not edit
|
|
2
|
-
import { useVasp } from '@vasp-framework/runtime'
|
|
3
|
-
import type {
|
|
4
|
-
{{#each queries}}
|
|
5
|
-
{{pascalCase name}}Args,
|
|
6
|
-
{{pascalCase name}}Return,
|
|
7
|
-
{{/each}}
|
|
8
|
-
} from './types.js'
|
|
9
|
-
|
|
10
|
-
{{#each queries}}
|
|
11
|
-
/**
|
|
12
|
-
* Call the '{{name}}' query on the Vasp backend.
|
|
13
|
-
*/
|
|
14
|
-
export async function {{camelCase name}}(args?: {{pascalCase name}}Args): Promise<{{pascalCase name}}Return> {
|
|
15
|
-
const { $vasp } = useVasp()
|
|
16
|
-
return $vasp.query('{{camelCase name}}', args) as Promise<{{pascalCase name}}Return>
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
{{/each}}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
// Auto-generated by Vasp — do not edit
|
|
2
|
-
import { ref, onUnmounted, type Ref } from 'vue'
|
|
3
|
-
|
|
4
|
-
const WS_BASE = import.meta.env.VITE_WS_URL || `ws://${location.host}`
|
|
5
|
-
|
|
6
|
-
export interface RealtimeMessage {
|
|
7
|
-
channel: string
|
|
8
|
-
event: string
|
|
9
|
-
data: unknown
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface UseRealtimeResult {
|
|
13
|
-
connected: Ref<boolean>
|
|
14
|
-
on: (event: string, handler: (data: unknown) => void) => void
|
|
15
|
-
disconnect: () => void
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function useRealtime(channelName: string): UseRealtimeResult {
|
|
19
|
-
const connected = ref(false)
|
|
20
|
-
const handlers = new Map<string, (data: unknown) => void>()
|
|
21
|
-
let ws: WebSocket | null = null
|
|
22
|
-
let reconnectTimer: ReturnType<typeof setTimeout> | null = null
|
|
23
|
-
|
|
24
|
-
function connect() {
|
|
25
|
-
ws = new WebSocket(`${WS_BASE}/ws/${channelName}`)
|
|
26
|
-
|
|
27
|
-
ws.onopen = () => { connected.value = true }
|
|
28
|
-
ws.onclose = () => {
|
|
29
|
-
connected.value = false
|
|
30
|
-
reconnectTimer = setTimeout(connect, 3000)
|
|
31
|
-
}
|
|
32
|
-
ws.onerror = () => ws?.close()
|
|
33
|
-
ws.onmessage = ({ data }: MessageEvent<string>) => {
|
|
34
|
-
try {
|
|
35
|
-
const msg = JSON.parse(data) as RealtimeMessage
|
|
36
|
-
if (msg.channel === channelName) {
|
|
37
|
-
handlers.get(msg.event)?.(msg.data)
|
|
38
|
-
}
|
|
39
|
-
} catch {}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function on(event: string, handler: (data: unknown) => void) {
|
|
44
|
-
handlers.set(event, handler)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function disconnect() {
|
|
48
|
-
if (reconnectTimer) clearTimeout(reconnectTimer)
|
|
49
|
-
ws?.close()
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
connect()
|
|
53
|
-
onUnmounted(disconnect)
|
|
54
|
-
|
|
55
|
-
return { connected, on, disconnect }
|
|
56
|
-
}
|