create-mantiq 0.5.8 → 0.5.10
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/package.json +1 -1
- package/stubs/react/src/lib/api.ts.stub +15 -4
- package/stubs/react/vite.config.ts.stub +46 -1
- package/stubs/shared/routes/web.ts.stub +7 -0
- package/stubs/svelte/src/lib/api.ts.stub +15 -4
- package/stubs/svelte/vite.config.ts.stub +38 -1
- package/stubs/vue/src/lib/api.ts.stub +15 -4
- package/stubs/vue/vite.config.ts.stub +38 -1
package/package.json
CHANGED
|
@@ -3,13 +3,24 @@ function getCookie(name: string): string | null {
|
|
|
3
3
|
return match ? decodeURIComponent(match[1]!) : null
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
/** Ensure a session + XSRF-TOKEN cookie exists before mutating requests. */
|
|
7
|
+
let csrfReady: Promise<void> | null = null
|
|
8
|
+
async function ensureCsrf(): Promise<void> {
|
|
9
|
+
if (getCookie('XSRF-TOKEN')) return
|
|
10
|
+
if (!csrfReady) {
|
|
11
|
+
csrfReady = fetch('/csrf-cookie', { credentials: 'same-origin' }).then(() => { csrfReady = null })
|
|
12
|
+
}
|
|
13
|
+
await csrfReady
|
|
14
|
+
}
|
|
15
|
+
|
|
6
16
|
export async function api<T = any>(url: string, opts: RequestInit = {}): Promise<{ ok: boolean; status: number; data: T }> {
|
|
7
17
|
const headers: Record<string, string> = { Accept: 'application/json', ...(opts.headers as Record<string, string>) }
|
|
8
18
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
19
|
+
// For mutating requests, ensure CSRF cookie exists then attach it
|
|
20
|
+
if (opts.method && !['GET', 'HEAD', 'OPTIONS'].includes(opts.method)) {
|
|
21
|
+
await ensureCsrf()
|
|
22
|
+
const xsrf = getCookie('XSRF-TOKEN')
|
|
23
|
+
if (xsrf) headers['X-XSRF-TOKEN'] = xsrf
|
|
13
24
|
}
|
|
14
25
|
|
|
15
26
|
const res = await fetch(url, { ...opts, credentials: 'same-origin', headers })
|
|
@@ -4,7 +4,52 @@ import tailwindcss from '@tailwindcss/vite'
|
|
|
4
4
|
import path from 'path'
|
|
5
5
|
|
|
6
6
|
export default defineConfig({
|
|
7
|
-
plugins: [
|
|
7
|
+
plugins: [
|
|
8
|
+
react(),
|
|
9
|
+
tailwindcss(),
|
|
10
|
+
// Proxy non-asset requests to the Mantiq backend in dev mode
|
|
11
|
+
// so session cookies and CSRF tokens work on the same origin
|
|
12
|
+
{
|
|
13
|
+
name: 'mantiq-proxy',
|
|
14
|
+
configureServer(server) {
|
|
15
|
+
server.middlewares.use((req, res, next) => {
|
|
16
|
+
// Let Vite handle its own files: HMR, source files, node_modules
|
|
17
|
+
const viteOwned = req.url?.startsWith('/@') ||
|
|
18
|
+
req.url?.startsWith('/src/') ||
|
|
19
|
+
req.url?.startsWith('/node_modules/') ||
|
|
20
|
+
req.url?.includes('?import') ||
|
|
21
|
+
req.url?.includes('?t=')
|
|
22
|
+
|
|
23
|
+
if (viteOwned) return next()
|
|
24
|
+
|
|
25
|
+
// For HTML page navigations (SPA), let Vite serve index.html
|
|
26
|
+
const accept = req.headers.accept ?? ''
|
|
27
|
+
if (req.method === 'GET' && accept.includes('text/html')) return next()
|
|
28
|
+
|
|
29
|
+
// Everything else (POST, API calls, etc.) → proxy to backend
|
|
30
|
+
const backend = `http://localhost:${process.env.APP_PORT ?? 3000}`
|
|
31
|
+
import('node:http').then(({ request }) => {
|
|
32
|
+
const proxyReq = request(
|
|
33
|
+
`${backend}${req.url}`,
|
|
34
|
+
{
|
|
35
|
+
method: req.method,
|
|
36
|
+
headers: req.headers,
|
|
37
|
+
},
|
|
38
|
+
(proxyRes) => {
|
|
39
|
+
res.writeHead(proxyRes.statusCode ?? 500, proxyRes.headers)
|
|
40
|
+
proxyRes.pipe(res)
|
|
41
|
+
},
|
|
42
|
+
)
|
|
43
|
+
proxyReq.on('error', () => {
|
|
44
|
+
res.writeHead(502)
|
|
45
|
+
res.end('Backend not running. Start with: bun run index.ts')
|
|
46
|
+
})
|
|
47
|
+
req.pipe(proxyReq)
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
],
|
|
8
53
|
publicDir: false,
|
|
9
54
|
resolve: {
|
|
10
55
|
alias: {
|
|
@@ -18,6 +18,13 @@ export default function (router: Router) {
|
|
|
18
18
|
router.get('/account/security', [PageController, 'security']).middleware('auth')
|
|
19
19
|
router.get('/account/preferences', [PageController, 'preferences']).middleware('auth')
|
|
20
20
|
|
|
21
|
+
// CSRF cookie — SPA calls this on mount to establish session + XSRF token
|
|
22
|
+
router.get('/csrf-cookie', (request: any) => {
|
|
23
|
+
// Session middleware already started the session and CSRF middleware
|
|
24
|
+
// will attach the XSRF-TOKEN cookie on the response. Just return 204.
|
|
25
|
+
return new Response(null, { status: 204 })
|
|
26
|
+
})
|
|
27
|
+
|
|
21
28
|
// Auth actions
|
|
22
29
|
router.post('/login', [AuthController, 'login'])
|
|
23
30
|
router.post('/register', [AuthController, 'register'])
|
|
@@ -3,13 +3,24 @@ function getCookie(name: string): string | null {
|
|
|
3
3
|
return match ? decodeURIComponent(match[1]!) : null
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
/** Ensure a session + XSRF-TOKEN cookie exists before mutating requests. */
|
|
7
|
+
let csrfReady: Promise<void> | null = null
|
|
8
|
+
async function ensureCsrf(): Promise<void> {
|
|
9
|
+
if (getCookie('XSRF-TOKEN')) return
|
|
10
|
+
if (!csrfReady) {
|
|
11
|
+
csrfReady = fetch('/csrf-cookie', { credentials: 'same-origin' }).then(() => { csrfReady = null })
|
|
12
|
+
}
|
|
13
|
+
await csrfReady
|
|
14
|
+
}
|
|
15
|
+
|
|
6
16
|
export async function api<T = any>(url: string, opts: RequestInit = {}): Promise<{ ok: boolean; status: number; data: T }> {
|
|
7
17
|
const headers: Record<string, string> = { Accept: 'application/json', ...(opts.headers as Record<string, string>) }
|
|
8
18
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
19
|
+
// For mutating requests, ensure CSRF cookie exists then attach it
|
|
20
|
+
if (opts.method && !['GET', 'HEAD', 'OPTIONS'].includes(opts.method)) {
|
|
21
|
+
await ensureCsrf()
|
|
22
|
+
const xsrf = getCookie('XSRF-TOKEN')
|
|
23
|
+
if (xsrf) headers['X-XSRF-TOKEN'] = xsrf
|
|
13
24
|
}
|
|
14
25
|
|
|
15
26
|
const res = await fetch(url, { ...opts, credentials: 'same-origin', headers })
|
|
@@ -4,7 +4,44 @@ import tailwindcss from '@tailwindcss/vite'
|
|
|
4
4
|
import path from 'path'
|
|
5
5
|
|
|
6
6
|
export default defineConfig({
|
|
7
|
-
plugins: [
|
|
7
|
+
plugins: [
|
|
8
|
+
svelte(),
|
|
9
|
+
tailwindcss(),
|
|
10
|
+
{
|
|
11
|
+
name: 'mantiq-proxy',
|
|
12
|
+
configureServer(server) {
|
|
13
|
+
server.middlewares.use((req, res, next) => {
|
|
14
|
+
const viteOwned = req.url?.startsWith('/@') ||
|
|
15
|
+
req.url?.startsWith('/src/') ||
|
|
16
|
+
req.url?.startsWith('/node_modules/') ||
|
|
17
|
+
req.url?.includes('?import') ||
|
|
18
|
+
req.url?.includes('?t=')
|
|
19
|
+
|
|
20
|
+
if (viteOwned) return next()
|
|
21
|
+
|
|
22
|
+
const accept = req.headers.accept ?? ''
|
|
23
|
+
if (req.method === 'GET' && accept.includes('text/html')) return next()
|
|
24
|
+
|
|
25
|
+
const backend = `http://localhost:${process.env.APP_PORT ?? 3000}`
|
|
26
|
+
import('node:http').then(({ request }) => {
|
|
27
|
+
const proxyReq = request(
|
|
28
|
+
`${backend}${req.url}`,
|
|
29
|
+
{ method: req.method, headers: req.headers },
|
|
30
|
+
(proxyRes) => {
|
|
31
|
+
res.writeHead(proxyRes.statusCode ?? 500, proxyRes.headers)
|
|
32
|
+
proxyRes.pipe(res)
|
|
33
|
+
},
|
|
34
|
+
)
|
|
35
|
+
proxyReq.on('error', () => {
|
|
36
|
+
res.writeHead(502)
|
|
37
|
+
res.end('Backend not running. Start with: bun run index.ts')
|
|
38
|
+
})
|
|
39
|
+
req.pipe(proxyReq)
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
],
|
|
8
45
|
publicDir: false,
|
|
9
46
|
resolve: {
|
|
10
47
|
alias: {
|
|
@@ -3,13 +3,24 @@ function getCookie(name: string): string | null {
|
|
|
3
3
|
return match ? decodeURIComponent(match[1]!) : null
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
/** Ensure a session + XSRF-TOKEN cookie exists before mutating requests. */
|
|
7
|
+
let csrfReady: Promise<void> | null = null
|
|
8
|
+
async function ensureCsrf(): Promise<void> {
|
|
9
|
+
if (getCookie('XSRF-TOKEN')) return
|
|
10
|
+
if (!csrfReady) {
|
|
11
|
+
csrfReady = fetch('/csrf-cookie', { credentials: 'same-origin' }).then(() => { csrfReady = null })
|
|
12
|
+
}
|
|
13
|
+
await csrfReady
|
|
14
|
+
}
|
|
15
|
+
|
|
6
16
|
export async function api<T = any>(url: string, opts: RequestInit = {}): Promise<{ ok: boolean; status: number; data: T }> {
|
|
7
17
|
const headers: Record<string, string> = { Accept: 'application/json', ...(opts.headers as Record<string, string>) }
|
|
8
18
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
19
|
+
// For mutating requests, ensure CSRF cookie exists then attach it
|
|
20
|
+
if (opts.method && !['GET', 'HEAD', 'OPTIONS'].includes(opts.method)) {
|
|
21
|
+
await ensureCsrf()
|
|
22
|
+
const xsrf = getCookie('XSRF-TOKEN')
|
|
23
|
+
if (xsrf) headers['X-XSRF-TOKEN'] = xsrf
|
|
13
24
|
}
|
|
14
25
|
|
|
15
26
|
const res = await fetch(url, { ...opts, credentials: 'same-origin', headers })
|
|
@@ -4,7 +4,44 @@ import tailwindcss from '@tailwindcss/vite'
|
|
|
4
4
|
import path from 'path'
|
|
5
5
|
|
|
6
6
|
export default defineConfig({
|
|
7
|
-
plugins: [
|
|
7
|
+
plugins: [
|
|
8
|
+
vue(),
|
|
9
|
+
tailwindcss(),
|
|
10
|
+
{
|
|
11
|
+
name: 'mantiq-proxy',
|
|
12
|
+
configureServer(server) {
|
|
13
|
+
server.middlewares.use((req, res, next) => {
|
|
14
|
+
const viteOwned = req.url?.startsWith('/@') ||
|
|
15
|
+
req.url?.startsWith('/src/') ||
|
|
16
|
+
req.url?.startsWith('/node_modules/') ||
|
|
17
|
+
req.url?.includes('?import') ||
|
|
18
|
+
req.url?.includes('?t=')
|
|
19
|
+
|
|
20
|
+
if (viteOwned) return next()
|
|
21
|
+
|
|
22
|
+
const accept = req.headers.accept ?? ''
|
|
23
|
+
if (req.method === 'GET' && accept.includes('text/html')) return next()
|
|
24
|
+
|
|
25
|
+
const backend = `http://localhost:${process.env.APP_PORT ?? 3000}`
|
|
26
|
+
import('node:http').then(({ request }) => {
|
|
27
|
+
const proxyReq = request(
|
|
28
|
+
`${backend}${req.url}`,
|
|
29
|
+
{ method: req.method, headers: req.headers },
|
|
30
|
+
(proxyRes) => {
|
|
31
|
+
res.writeHead(proxyRes.statusCode ?? 500, proxyRes.headers)
|
|
32
|
+
proxyRes.pipe(res)
|
|
33
|
+
},
|
|
34
|
+
)
|
|
35
|
+
proxyReq.on('error', () => {
|
|
36
|
+
res.writeHead(502)
|
|
37
|
+
res.end('Backend not running. Start with: bun run index.ts')
|
|
38
|
+
})
|
|
39
|
+
req.pipe(proxyReq)
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
],
|
|
8
45
|
publicDir: false,
|
|
9
46
|
resolve: {
|
|
10
47
|
alias: {
|