create-mantiq 0.5.16 → 0.5.17
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/src/index.ts +4 -0
- package/stubs/api-only/routes/api.ts.stub +9 -5
- package/stubs/manifest.json +16 -0
- package/stubs/react/src/App.tsx.stub +3 -1
- package/stubs/react/src/lib/api.ts.stub +12 -9
- package/stubs/shared/app/Http/Controllers/ApiAuthController.ts.stub +12 -18
- package/stubs/shared/app/Http/Controllers/AuthController.ts.stub +11 -18
- package/stubs/shared/app/Http/Controllers/PageController.ts.stub +3 -3
- package/stubs/shared/app/Http/Controllers/UserController.ts.stub +15 -21
- package/stubs/shared/app/Http/Requests/LoginRequest.ts.stub +10 -0
- package/stubs/shared/app/Http/Requests/RegisterRequest.ts.stub +11 -0
- package/stubs/shared/app/Http/Requests/StoreUserRequest.ts.stub +11 -0
- package/stubs/shared/app/Http/Requests/UpdateUserRequest.ts.stub +11 -0
- package/stubs/shared/routes/api.ts.stub +5 -3
- package/stubs/shared/routes/web.ts.stub +5 -3
- package/stubs/svelte/src/App.svelte.stub +1 -1
- package/stubs/svelte/src/lib/api.ts.stub +12 -9
- package/stubs/svelte/src/main.ts.stub +3 -1
- package/stubs/vue/src/App.vue.stub +2 -1
- package/stubs/vue/src/lib/api.ts.stub +12 -9
- package/stubs/vue/src/main.ts.stub +3 -1
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -188,6 +188,10 @@ if (kit) {
|
|
|
188
188
|
{ stub: 'api-only/routes/api.ts.stub', target: 'routes/api.ts' },
|
|
189
189
|
{ stub: 'shared/app/Http/Controllers/ApiAuthController.ts.stub', target: 'app/Http/Controllers/ApiAuthController.ts' },
|
|
190
190
|
{ stub: 'shared/app/Http/Controllers/UserController.ts.stub', target: 'app/Http/Controllers/UserController.ts' },
|
|
191
|
+
{ stub: 'shared/app/Http/Requests/RegisterRequest.ts.stub', target: 'app/Http/Requests/RegisterRequest.ts' },
|
|
192
|
+
{ stub: 'shared/app/Http/Requests/LoginRequest.ts.stub', target: 'app/Http/Requests/LoginRequest.ts' },
|
|
193
|
+
{ stub: 'shared/app/Http/Requests/StoreUserRequest.ts.stub', target: 'app/Http/Requests/StoreUserRequest.ts' },
|
|
194
|
+
{ stub: 'shared/app/Http/Requests/UpdateUserRequest.ts.stub', target: 'app/Http/Requests/UpdateUserRequest.ts' },
|
|
191
195
|
{ stub: 'shared/database/seeders/DatabaseSeeder.ts.stub', target: 'database/seeders/DatabaseSeeder.ts' },
|
|
192
196
|
{ stub: 'shared/database/factories/UserFactory.ts.stub', target: 'database/factories/UserFactory.ts' },
|
|
193
197
|
]
|
|
@@ -2,19 +2,23 @@ import type { Router } from '@mantiq/core'
|
|
|
2
2
|
import { json } from '@mantiq/core'
|
|
3
3
|
import { ApiAuthController } from '../app/Http/Controllers/ApiAuthController.ts'
|
|
4
4
|
import { UserController } from '../app/Http/Controllers/UserController.ts'
|
|
5
|
+
import { RegisterRequest } from '../app/Http/Requests/RegisterRequest.ts'
|
|
6
|
+
import { LoginRequest } from '../app/Http/Requests/LoginRequest.ts'
|
|
7
|
+
import { StoreUserRequest } from '../app/Http/Requests/StoreUserRequest.ts'
|
|
8
|
+
import { UpdateUserRequest } from '../app/Http/Requests/UpdateUserRequest.ts'
|
|
5
9
|
|
|
6
10
|
export default function (router: Router) {
|
|
7
11
|
router.get('/ping', () => json({ status: 'ok', timestamp: new Date().toISOString() }))
|
|
8
12
|
|
|
9
|
-
// Token auth —
|
|
10
|
-
router.post('/register', [ApiAuthController, 'register'])
|
|
11
|
-
router.post('/login', [ApiAuthController, 'login'])
|
|
13
|
+
// Token auth — FormRequest auto-validates, controller receives validated data
|
|
14
|
+
router.post('/register', [ApiAuthController, 'register', RegisterRequest])
|
|
15
|
+
router.post('/login', [ApiAuthController, 'login', LoginRequest])
|
|
12
16
|
router.post('/logout', [ApiAuthController, 'logout']).middleware('auth:api')
|
|
13
17
|
router.get('/user', [ApiAuthController, 'user']).middleware('auth:api')
|
|
14
18
|
|
|
15
19
|
// Users CRUD (protected)
|
|
16
20
|
router.get('/users', [UserController, 'index']).middleware('auth:api')
|
|
17
|
-
router.post('/users', [UserController, 'store']).middleware('auth:api')
|
|
18
|
-
router.put('/users/:id', [UserController, 'update']).middleware('auth:api')
|
|
21
|
+
router.post('/users', [UserController, 'store', StoreUserRequest]).middleware('auth:api')
|
|
22
|
+
router.put('/users/:id', [UserController, 'update', UpdateUserRequest]).middleware('auth:api')
|
|
19
23
|
router.delete('/users/:id', [UserController, 'destroy']).middleware('auth:api')
|
|
20
24
|
}
|
package/stubs/manifest.json
CHANGED
|
@@ -1399,6 +1399,22 @@
|
|
|
1399
1399
|
"stub": "app/Http/Controllers/UserController.ts.stub",
|
|
1400
1400
|
"target": "app/Http/Controllers/UserController.ts"
|
|
1401
1401
|
},
|
|
1402
|
+
{
|
|
1403
|
+
"stub": "app/Http/Requests/RegisterRequest.ts.stub",
|
|
1404
|
+
"target": "app/Http/Requests/RegisterRequest.ts"
|
|
1405
|
+
},
|
|
1406
|
+
{
|
|
1407
|
+
"stub": "app/Http/Requests/LoginRequest.ts.stub",
|
|
1408
|
+
"target": "app/Http/Requests/LoginRequest.ts"
|
|
1409
|
+
},
|
|
1410
|
+
{
|
|
1411
|
+
"stub": "app/Http/Requests/StoreUserRequest.ts.stub",
|
|
1412
|
+
"target": "app/Http/Requests/StoreUserRequest.ts"
|
|
1413
|
+
},
|
|
1414
|
+
{
|
|
1415
|
+
"stub": "app/Http/Requests/UpdateUserRequest.ts.stub",
|
|
1416
|
+
"target": "app/Http/Requests/UpdateUserRequest.ts"
|
|
1417
|
+
},
|
|
1402
1418
|
{
|
|
1403
1419
|
"stub": "routes/web.ts.stub",
|
|
1404
1420
|
"target": "routes/web.ts"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { useState, useCallback, useEffect } from 'react'
|
|
2
2
|
|
|
3
|
+
declare global { interface Window { __MANTIQ_DATA__?: Record<string, any> } }
|
|
4
|
+
|
|
3
5
|
interface MantiqAppProps {
|
|
4
6
|
pages: Record<string, React.ComponentType<any>>
|
|
5
7
|
initialData?: Record<string, any>
|
|
@@ -15,7 +17,7 @@ function initTheme() {
|
|
|
15
17
|
initTheme()
|
|
16
18
|
|
|
17
19
|
export function MantiqApp({ pages, initialData }: MantiqAppProps) {
|
|
18
|
-
const windowData = typeof window !== 'undefined' ?
|
|
20
|
+
const windowData = typeof window !== 'undefined' ? window.__MANTIQ_DATA__ : {}
|
|
19
21
|
const initial = initialData ?? windowData
|
|
20
22
|
const [page, setPage] = useState<string>(initial._page ?? 'Login')
|
|
21
23
|
const [data, setData] = useState<Record<string, any>>(initial)
|
|
@@ -3,7 +3,9 @@ function getCookie(name: string): string | null {
|
|
|
3
3
|
return match ? decodeURIComponent(match[1]!) : null
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
type ApiResult<T> = { ok: true; status: number; data: T } | { ok: false; status: number; data: null }
|
|
7
|
+
|
|
8
|
+
export async function api<T = any>(url: string, opts: RequestInit = {}): Promise<ApiResult<T>> {
|
|
7
9
|
const headers: Record<string, string> = { Accept: 'application/json', ...(opts.headers as Record<string, string>) }
|
|
8
10
|
|
|
9
11
|
// Attach XSRF token for CSRF protection on mutating requests
|
|
@@ -17,22 +19,23 @@ export async function api<T = any>(url: string, opts: RequestInit = {}): Promise
|
|
|
17
19
|
// Session expired — redirect to login
|
|
18
20
|
if (res.status === 401 || res.status === 419) {
|
|
19
21
|
window.location.href = '/login'
|
|
20
|
-
return { ok: false, status: res.status, data: null
|
|
22
|
+
return { ok: false, status: res.status, data: null }
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
const ct = res.headers.get('content-type') ?? ''
|
|
24
26
|
const data = ct.includes('json') ? await res.json() : null
|
|
25
|
-
return { ok:
|
|
27
|
+
if (!res.ok) return { ok: false, status: res.status, data: null }
|
|
28
|
+
return { ok: true, status: res.status, data }
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
export function post(url: string, body: object) {
|
|
29
|
-
return api(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
31
|
+
export function post<T = any>(url: string, body: object) {
|
|
32
|
+
return api<T>(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
export function put(url: string, body: object) {
|
|
33
|
-
return api(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
35
|
+
export function put<T = any>(url: string, body: object) {
|
|
36
|
+
return api<T>(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
34
37
|
}
|
|
35
38
|
|
|
36
|
-
export function del(url: string) {
|
|
37
|
-
return api(url, { method: 'DELETE' })
|
|
39
|
+
export function del<T = any>(url: string) {
|
|
40
|
+
return api<T>(url, { method: 'DELETE' })
|
|
38
41
|
}
|
|
@@ -8,33 +8,27 @@ import { User } from '../../Models/User.ts'
|
|
|
8
8
|
* Issues bearer tokens instead of session cookies.
|
|
9
9
|
*/
|
|
10
10
|
export class ApiAuthController {
|
|
11
|
-
async register(request: MantiqRequest): Promise<Response> {
|
|
12
|
-
|
|
13
|
-
if (!body.name || !body.email || !body.password) abort(422, 'Name, email and password are required.')
|
|
14
|
-
if (body.password!.length < 6) abort(422, 'Password must be at least 6 characters.')
|
|
15
|
-
if (await User.where('email', body.email).first()) abort(422, 'A user with this email already exists.')
|
|
11
|
+
async register(request: MantiqRequest, data: Record<string, any>): Promise<Response> {
|
|
12
|
+
if (await User.where('email', data.email).first()) abort(422, 'A user with this email already exists.')
|
|
16
13
|
|
|
17
14
|
const user = await User.create({
|
|
18
|
-
name:
|
|
19
|
-
email:
|
|
20
|
-
password: await hash(
|
|
15
|
+
name: data.name,
|
|
16
|
+
email: data.email,
|
|
17
|
+
password: await hash(data.password),
|
|
21
18
|
})
|
|
22
19
|
|
|
23
|
-
const { plainTextToken } = await
|
|
20
|
+
const { plainTextToken } = await user.createToken(data.device_name ?? 'api')
|
|
24
21
|
|
|
25
22
|
return json({ message: 'Registered.', user: user.toObject(), token: plainTextToken }, 201)
|
|
26
23
|
}
|
|
27
24
|
|
|
28
|
-
async login(request: MantiqRequest): Promise<Response> {
|
|
29
|
-
const
|
|
30
|
-
if (!
|
|
31
|
-
|
|
32
|
-
const user = await User.where('email', body.email).first()
|
|
33
|
-
if (!user || !(await hashCheck(body.password!, user.getAuthPassword()))) {
|
|
25
|
+
async login(request: MantiqRequest, data: Record<string, any>): Promise<Response> {
|
|
26
|
+
const user = await User.where('email', data.email).first()
|
|
27
|
+
if (!user || !(await hashCheck(data.password, user.getAuthPassword()))) {
|
|
34
28
|
abort(401, 'Invalid credentials.')
|
|
35
29
|
}
|
|
36
30
|
|
|
37
|
-
const { plainTextToken } = await
|
|
31
|
+
const { plainTextToken } = await user!.createToken(data.device_name ?? 'api')
|
|
38
32
|
|
|
39
33
|
return json({ message: 'Logged in.', user: user!.toObject(), token: plainTextToken })
|
|
40
34
|
}
|
|
@@ -45,7 +39,7 @@ export class ApiAuthController {
|
|
|
45
39
|
const user = await manager.guard('api').user()
|
|
46
40
|
|
|
47
41
|
if (user) {
|
|
48
|
-
const token =
|
|
42
|
+
const token = user.currentAccessToken?.()
|
|
49
43
|
if (token) await token.delete()
|
|
50
44
|
}
|
|
51
45
|
|
|
@@ -58,6 +52,6 @@ export class ApiAuthController {
|
|
|
58
52
|
const user = await manager.guard('api').user()
|
|
59
53
|
if (!user) abort(401, 'Unauthenticated.')
|
|
60
54
|
|
|
61
|
-
return json({ user:
|
|
55
|
+
return json({ user: user!.toObject() })
|
|
62
56
|
}
|
|
63
57
|
}
|
|
@@ -1,44 +1,37 @@
|
|
|
1
1
|
import type { MantiqRequest } from '@mantiq/core'
|
|
2
|
-
import { json, hash,
|
|
2
|
+
import { json, hash, abort } from '@mantiq/core'
|
|
3
3
|
import { auth } from '@mantiq/auth'
|
|
4
4
|
import { User } from '../../Models/User.ts'
|
|
5
5
|
|
|
6
6
|
export class AuthController {
|
|
7
|
-
async register(request: MantiqRequest): Promise<Response> {
|
|
8
|
-
|
|
9
|
-
if (!body.name || !body.email || !body.password) abort(422, 'Name, email and password are required.')
|
|
10
|
-
if (body.password!.length < 6) abort(422, 'Password must be at least 6 characters.')
|
|
11
|
-
|
|
12
|
-
if (await User.where('email', body.email).first()) abort(422, 'A user with this email already exists.')
|
|
7
|
+
async register(request: MantiqRequest, data: Record<string, any>): Promise<Response> {
|
|
8
|
+
if (await User.where('email', data.email).first()) abort(422, 'A user with this email already exists.')
|
|
13
9
|
|
|
14
10
|
const user = await User.create({
|
|
15
|
-
name:
|
|
16
|
-
email:
|
|
17
|
-
password: await hash(
|
|
11
|
+
name: data.name,
|
|
12
|
+
email: data.email,
|
|
13
|
+
password: await hash(data.password),
|
|
18
14
|
})
|
|
19
15
|
|
|
20
16
|
const manager = auth()
|
|
21
17
|
manager.setRequest(request)
|
|
22
|
-
await manager.login(user
|
|
18
|
+
await manager.login(user)
|
|
23
19
|
|
|
24
20
|
return json({ message: 'Registered.', user: user.toObject() }, 201)
|
|
25
21
|
}
|
|
26
22
|
|
|
27
|
-
async login(request: MantiqRequest): Promise<Response> {
|
|
28
|
-
const body = await request.input() as { email?: string; password?: string; remember?: boolean }
|
|
29
|
-
if (!body.email || !body.password) abort(422, 'Email and password are required.')
|
|
30
|
-
|
|
23
|
+
async login(request: MantiqRequest, data: Record<string, any>): Promise<Response> {
|
|
31
24
|
const manager = auth()
|
|
32
25
|
manager.setRequest(request)
|
|
33
26
|
|
|
34
27
|
const success = await manager.attempt(
|
|
35
|
-
{ email:
|
|
36
|
-
|
|
28
|
+
{ email: data.email, password: data.password },
|
|
29
|
+
data.remember ?? false,
|
|
37
30
|
)
|
|
38
31
|
if (!success) abort(401, 'Invalid credentials.')
|
|
39
32
|
|
|
40
33
|
const user = await manager.user()
|
|
41
|
-
return json({ message: 'Logged in.', user })
|
|
34
|
+
return json({ message: 'Logged in.', user: user?.toObject() })
|
|
42
35
|
}
|
|
43
36
|
|
|
44
37
|
async logout(request: MantiqRequest): Promise<Response> {
|
|
@@ -10,9 +10,9 @@ async function getUser(request: MantiqRequest) {
|
|
|
10
10
|
const user = await manager.user()
|
|
11
11
|
if (!user) return null
|
|
12
12
|
return {
|
|
13
|
-
id:
|
|
14
|
-
name:
|
|
15
|
-
email:
|
|
13
|
+
id: user.getAttribute('id') ?? user.getAuthIdentifier(),
|
|
14
|
+
name: user.getAttribute('name') ?? '',
|
|
15
|
+
email: user.getAttribute('email') ?? '',
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -11,47 +11,41 @@ export class UserController {
|
|
|
11
11
|
const sortDir = request.query('dir') === 'asc' ? 'asc' : 'desc'
|
|
12
12
|
|
|
13
13
|
const baseQuery = () => search
|
|
14
|
-
? User.
|
|
14
|
+
? User.where('name', 'LIKE', `%${search}%`).orWhere('email', 'LIKE', `%${search}%`)
|
|
15
15
|
: User.query()
|
|
16
16
|
|
|
17
|
-
const total = await
|
|
18
|
-
const filteredTotal = await
|
|
19
|
-
const users = await
|
|
17
|
+
const total = await User.count()
|
|
18
|
+
const filteredTotal = await baseQuery().count()
|
|
19
|
+
const users = await baseQuery()
|
|
20
20
|
.orderBy(sortBy, sortDir)
|
|
21
21
|
.limit(perPage)
|
|
22
22
|
.offset((page - 1) * perPage)
|
|
23
|
-
.get()
|
|
23
|
+
.get()
|
|
24
24
|
|
|
25
25
|
return json({
|
|
26
|
-
data: users.map(
|
|
26
|
+
data: users.map(u => u.toObject()),
|
|
27
27
|
meta: { total, filtered_total: filteredTotal, page, per_page: perPage, last_page: Math.ceil(filteredTotal / perPage) },
|
|
28
28
|
})
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
async store(request: MantiqRequest): Promise<Response> {
|
|
32
|
-
|
|
33
|
-
if (!body?.name || !body?.email || !body?.password) abort(422, 'Name, email and password are required.')
|
|
34
|
-
|
|
35
|
-
const existing = await User.where('email', body.email).first()
|
|
36
|
-
if (existing) abort(422, 'Email already exists.')
|
|
31
|
+
async store(request: MantiqRequest, data: Record<string, any>): Promise<Response> {
|
|
32
|
+
if (await User.where('email', data.email).first()) abort(422, 'Email already exists.')
|
|
37
33
|
|
|
38
34
|
const user = await User.create({
|
|
39
|
-
name:
|
|
40
|
-
email:
|
|
41
|
-
password: await hash(
|
|
35
|
+
name: data.name,
|
|
36
|
+
email: data.email,
|
|
37
|
+
password: await hash(data.password),
|
|
42
38
|
})
|
|
43
39
|
|
|
44
40
|
return json({ data: user.toObject() }, 201)
|
|
45
41
|
}
|
|
46
42
|
|
|
47
|
-
async update(request: MantiqRequest): Promise<Response> {
|
|
43
|
+
async update(request: MantiqRequest, data: Record<string, any>): Promise<Response> {
|
|
48
44
|
const user = await User.find(Number(request.param('id')))
|
|
49
45
|
if (!user) abort(404, 'User not found.')
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
if (body.email) user.setAttribute('email', body.email)
|
|
54
|
-
if (body.password) user.setAttribute('password', await hash(body.password))
|
|
46
|
+
if (data.name) user.setAttribute('name', data.name)
|
|
47
|
+
if (data.email) user.setAttribute('email', data.email)
|
|
48
|
+
if (data.password) user.setAttribute('password', await hash(data.password))
|
|
55
49
|
|
|
56
50
|
await user.save()
|
|
57
51
|
return json({ data: user.toObject() })
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { Router } from '@mantiq/core'
|
|
2
2
|
import { json } from '@mantiq/core'
|
|
3
3
|
import { UserController } from '../app/Http/Controllers/UserController.ts'
|
|
4
|
+
import { StoreUserRequest } from '../app/Http/Requests/StoreUserRequest.ts'
|
|
5
|
+
import { UpdateUserRequest } from '../app/Http/Requests/UpdateUserRequest.ts'
|
|
4
6
|
|
|
5
7
|
export default function (router: Router) {
|
|
6
8
|
// Public
|
|
7
9
|
router.get('/ping', () => json({ status: 'ok', timestamp: new Date().toISOString() }))
|
|
8
10
|
|
|
9
|
-
// Protected —
|
|
11
|
+
// Protected — FormRequest auto-validates before controller runs
|
|
10
12
|
router.get('/users', [UserController, 'index']).middleware('auth')
|
|
11
|
-
router.post('/users', [UserController, 'store']).middleware('auth')
|
|
12
|
-
router.put('/users/:id', [UserController, 'update']).middleware('auth')
|
|
13
|
+
router.post('/users', [UserController, 'store', StoreUserRequest]).middleware('auth')
|
|
14
|
+
router.put('/users/:id', [UserController, 'update', UpdateUserRequest]).middleware('auth')
|
|
13
15
|
router.delete('/users/:id', [UserController, 'destroy']).middleware('auth')
|
|
14
16
|
}
|
|
@@ -2,6 +2,8 @@ import type { Router } from '@mantiq/core'
|
|
|
2
2
|
import { HomeController } from '../app/Http/Controllers/HomeController.ts'
|
|
3
3
|
import { PageController } from '../app/Http/Controllers/PageController.ts'
|
|
4
4
|
import { AuthController } from '../app/Http/Controllers/AuthController.ts'
|
|
5
|
+
import { RegisterRequest } from '../app/Http/Requests/RegisterRequest.ts'
|
|
6
|
+
import { LoginRequest } from '../app/Http/Requests/LoginRequest.ts'
|
|
5
7
|
|
|
6
8
|
export default function (router: Router) {
|
|
7
9
|
router.get('/', [HomeController, 'index'])
|
|
@@ -16,8 +18,8 @@ export default function (router: Router) {
|
|
|
16
18
|
router.get('/account/security', [PageController, 'security']).middleware('auth')
|
|
17
19
|
router.get('/account/preferences', [PageController, 'preferences']).middleware('auth')
|
|
18
20
|
|
|
19
|
-
// Auth actions
|
|
20
|
-
router.post('/login', [AuthController, 'login'])
|
|
21
|
-
router.post('/register', [AuthController, 'register'])
|
|
21
|
+
// Auth actions — FormRequest auto-validates before controller runs
|
|
22
|
+
router.post('/login', [AuthController, 'login', LoginRequest])
|
|
23
|
+
router.post('/register', [AuthController, 'register', RegisterRequest])
|
|
22
24
|
router.post('/logout', [AuthController, 'logout']).middleware('auth')
|
|
23
25
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
initialData?: Record<string, any>
|
|
10
10
|
} = $props()
|
|
11
11
|
|
|
12
|
-
const windowData = typeof window !== 'undefined' ? (window as any).__MANTIQ_DATA__ : {}
|
|
12
|
+
const windowData = typeof window !== 'undefined' ? (window as Record<string, any>).__MANTIQ_DATA__ ?? {} : {}
|
|
13
13
|
const bootstrapData = (() => initialData ?? windowData)()
|
|
14
14
|
|
|
15
15
|
let currentPage = $state<string>(bootstrapData._page ?? 'Login')
|
|
@@ -3,7 +3,9 @@ function getCookie(name: string): string | null {
|
|
|
3
3
|
return match ? decodeURIComponent(match[1]!) : null
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
type ApiResult<T> = { ok: true; status: number; data: T } | { ok: false; status: number; data: null }
|
|
7
|
+
|
|
8
|
+
export async function api<T = any>(url: string, opts: RequestInit = {}): Promise<ApiResult<T>> {
|
|
7
9
|
const headers: Record<string, string> = { Accept: 'application/json', ...(opts.headers as Record<string, string>) }
|
|
8
10
|
|
|
9
11
|
// Attach XSRF token for CSRF protection on mutating requests
|
|
@@ -17,22 +19,23 @@ export async function api<T = any>(url: string, opts: RequestInit = {}): Promise
|
|
|
17
19
|
// Session expired — redirect to login
|
|
18
20
|
if (res.status === 401 || res.status === 419) {
|
|
19
21
|
window.location.href = '/login'
|
|
20
|
-
return { ok: false, status: res.status, data: null
|
|
22
|
+
return { ok: false, status: res.status, data: null }
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
const ct = res.headers.get('content-type') ?? ''
|
|
24
26
|
const data = ct.includes('json') ? await res.json() : null
|
|
25
|
-
return { ok:
|
|
27
|
+
if (!res.ok) return { ok: false, status: res.status, data: null }
|
|
28
|
+
return { ok: true, status: res.status, data }
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
export function post(url: string, body: object) {
|
|
29
|
-
return api(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
31
|
+
export function post<T = any>(url: string, body: object) {
|
|
32
|
+
return api<T>(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
export function put(url: string, body: object) {
|
|
33
|
-
return api(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
35
|
+
export function put<T = any>(url: string, body: object) {
|
|
36
|
+
return api<T>(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
34
37
|
}
|
|
35
38
|
|
|
36
|
-
export function del(url: string) {
|
|
37
|
-
return api(url, { method: 'DELETE' })
|
|
39
|
+
export function del<T = any>(url: string) {
|
|
40
|
+
return api<T>(url, { method: 'DELETE' })
|
|
38
41
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
declare global { interface Window { __MANTIQ_DATA__?: Record<string, any> } }
|
|
2
|
+
|
|
1
3
|
import './style.css'
|
|
2
4
|
import { mount } from 'svelte'
|
|
3
5
|
import App from './App.svelte'
|
|
4
6
|
import { pages } from './pages.ts'
|
|
5
7
|
|
|
6
8
|
const target = document.getElementById('app')!
|
|
7
|
-
const data =
|
|
9
|
+
const data = window.__MANTIQ_DATA__ ?? {}
|
|
8
10
|
|
|
9
11
|
// Clear SSR content and mount fresh — avoids hydration mismatches
|
|
10
12
|
// with shadcn-svelte sidebar components
|
|
@@ -6,7 +6,8 @@ const props = defineProps<{
|
|
|
6
6
|
initialData?: Record<string, any>
|
|
7
7
|
}>()
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
declare global { interface Window { __MANTIQ_DATA__?: Record<string, any> } }
|
|
10
|
+
const windowData = typeof window !== 'undefined' ? window.__MANTIQ_DATA__ ?? {} : {}
|
|
10
11
|
const initial = props.initialData ?? windowData
|
|
11
12
|
|
|
12
13
|
const currentPage = ref<string>(initial._page ?? 'Login')
|
|
@@ -3,7 +3,9 @@ function getCookie(name: string): string | null {
|
|
|
3
3
|
return match ? decodeURIComponent(match[1]!) : null
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
type ApiResult<T> = { ok: true; status: number; data: T } | { ok: false; status: number; data: null }
|
|
7
|
+
|
|
8
|
+
export async function api<T = any>(url: string, opts: RequestInit = {}): Promise<ApiResult<T>> {
|
|
7
9
|
const headers: Record<string, string> = { Accept: 'application/json', ...(opts.headers as Record<string, string>) }
|
|
8
10
|
|
|
9
11
|
// Attach XSRF token for CSRF protection on mutating requests
|
|
@@ -17,22 +19,23 @@ export async function api<T = any>(url: string, opts: RequestInit = {}): Promise
|
|
|
17
19
|
// Session expired — redirect to login
|
|
18
20
|
if (res.status === 401 || res.status === 419) {
|
|
19
21
|
window.location.href = '/login'
|
|
20
|
-
return { ok: false, status: res.status, data: null
|
|
22
|
+
return { ok: false, status: res.status, data: null }
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
const ct = res.headers.get('content-type') ?? ''
|
|
24
26
|
const data = ct.includes('json') ? await res.json() : null
|
|
25
|
-
return { ok:
|
|
27
|
+
if (!res.ok) return { ok: false, status: res.status, data: null }
|
|
28
|
+
return { ok: true, status: res.status, data }
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
export function post(url: string, body: object) {
|
|
29
|
-
return api(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
31
|
+
export function post<T = any>(url: string, body: object) {
|
|
32
|
+
return api<T>(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
export function put(url: string, body: object) {
|
|
33
|
-
return api(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
35
|
+
export function put<T = any>(url: string, body: object) {
|
|
36
|
+
return api<T>(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
34
37
|
}
|
|
35
38
|
|
|
36
|
-
export function del(url: string) {
|
|
37
|
-
return api(url, { method: 'DELETE' })
|
|
39
|
+
export function del<T = any>(url: string) {
|
|
40
|
+
return api<T>(url, { method: 'DELETE' })
|
|
38
41
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
declare global { interface Window { __MANTIQ_DATA__?: Record<string, any> } }
|
|
2
|
+
|
|
1
3
|
import './style.css'
|
|
2
4
|
import { createApp, createSSRApp } from 'vue'
|
|
3
5
|
import App from './App.vue'
|
|
4
6
|
import { pages } from './pages.ts'
|
|
5
7
|
|
|
6
8
|
const root = document.getElementById('app')!
|
|
7
|
-
const data =
|
|
9
|
+
const data = window.__MANTIQ_DATA__ ?? {}
|
|
8
10
|
|
|
9
11
|
const app = root.innerHTML.trim()
|
|
10
12
|
? createSSRApp(App, { pages, initialData: data })
|