@microbuild/cli 0.1.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/README.md +555 -0
- package/dist/chunk-6YA3DSAE.js +362 -0
- package/dist/chunk-6YA3DSAE.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2279 -0
- package/dist/index.js.map +1 -0
- package/dist/outdated-TV5ERBNC.js +110 -0
- package/dist/outdated-TV5ERBNC.js.map +1 -0
- package/dist/templates/api/auth-callback-route.ts +36 -0
- package/dist/templates/api/auth-headers.ts +72 -0
- package/dist/templates/api/auth-login-route.ts +63 -0
- package/dist/templates/api/auth-logout-route.ts +41 -0
- package/dist/templates/api/auth-user-route.ts +71 -0
- package/dist/templates/api/fields-route.ts +44 -0
- package/dist/templates/api/files-id-route.ts +116 -0
- package/dist/templates/api/files-route.ts +83 -0
- package/dist/templates/api/items-id-route.ts +120 -0
- package/dist/templates/api/items-route.ts +88 -0
- package/dist/templates/api/login-page.tsx +142 -0
- package/dist/templates/api/relations-route.ts +46 -0
- package/dist/templates/app/design-tokens.css +183 -0
- package/dist/templates/app/globals.css +58 -0
- package/dist/templates/app/layout.tsx +49 -0
- package/dist/templates/app/page.tsx +23 -0
- package/dist/templates/components/ColorSchemeToggle.tsx +35 -0
- package/dist/templates/lib/common-utils.ts +156 -0
- package/dist/templates/lib/hooks/index.ts +98 -0
- package/dist/templates/lib/services/index.ts +26 -0
- package/dist/templates/lib/theme.ts +241 -0
- package/dist/templates/lib/types/index.ts +10 -0
- package/dist/templates/lib/utils-index.ts +32 -0
- package/dist/templates/lib/utils.ts +14 -0
- package/dist/templates/lib/vform/index.ts +24 -0
- package/dist/templates/middleware/middleware.ts +29 -0
- package/dist/templates/supabase/client.ts +25 -0
- package/dist/templates/supabase/middleware.ts +66 -0
- package/dist/templates/supabase/server.ts +45 -0
- package/package.json +61 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Auth Headers Helper
|
|
3
|
+
*
|
|
4
|
+
* Forwards authentication tokens from Supabase session to DaaS backend.
|
|
5
|
+
* This file is copied to your project by the Microbuild CLI.
|
|
6
|
+
*
|
|
7
|
+
* @microbuild/origin: api-routes/auth-headers
|
|
8
|
+
* @microbuild/version: 1.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createClient } from '@/lib/supabase/server';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get authentication headers for DaaS API requests
|
|
15
|
+
* Extracts the access token from the current Supabase session
|
|
16
|
+
*/
|
|
17
|
+
export async function getAuthHeaders(): Promise<HeadersInit> {
|
|
18
|
+
const supabase = await createClient();
|
|
19
|
+
const { data: { session } } = await supabase.auth.getSession();
|
|
20
|
+
|
|
21
|
+
const headers: HeadersInit = {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
if (session?.access_token) {
|
|
26
|
+
headers['Authorization'] = `Bearer ${session.access_token}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return headers;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get the DaaS backend URL from environment
|
|
34
|
+
*/
|
|
35
|
+
export function getDaasUrl(): string {
|
|
36
|
+
const url = process.env.NEXT_PUBLIC_MICROBUILD_DAAS_URL;
|
|
37
|
+
if (!url) {
|
|
38
|
+
throw new Error('NEXT_PUBLIC_MICROBUILD_DAAS_URL is not configured in .env.local');
|
|
39
|
+
}
|
|
40
|
+
return url;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Make an authenticated request to the DaaS backend
|
|
45
|
+
*/
|
|
46
|
+
export async function daasRequest<T = unknown>(
|
|
47
|
+
path: string,
|
|
48
|
+
options: RequestInit = {}
|
|
49
|
+
): Promise<{ data: T | null; error: Error | null }> {
|
|
50
|
+
try {
|
|
51
|
+
const baseUrl = getDaasUrl();
|
|
52
|
+
const headers = await getAuthHeaders();
|
|
53
|
+
|
|
54
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
55
|
+
...options,
|
|
56
|
+
headers: {
|
|
57
|
+
...headers,
|
|
58
|
+
...options.headers,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
const errorData = await response.json().catch(() => ({}));
|
|
64
|
+
throw new Error(errorData.errors?.[0]?.message || `Request failed: ${response.status}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const data = await response.json();
|
|
68
|
+
return { data: data.data ?? data, error: null };
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return { data: null, error: error as Error };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Login API Route (Proxy)
|
|
3
|
+
*
|
|
4
|
+
* Proxies login requests to the DaaS backend.
|
|
5
|
+
* This ensures no CORS issues because the browser only talks to the same-origin Next.js server.
|
|
6
|
+
*
|
|
7
|
+
* Pattern: Browser → Next.js API Route → DaaS Backend → Supabase Auth
|
|
8
|
+
*
|
|
9
|
+
* @microbuild/origin: api-routes/auth-login
|
|
10
|
+
* @microbuild/version: 1.0.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
14
|
+
import { createClient } from '@/lib/supabase/server';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* POST /api/auth/login
|
|
18
|
+
*
|
|
19
|
+
* Authenticates user with email/password via Supabase Auth.
|
|
20
|
+
* The session cookie is set server-side, avoiding CORS issues.
|
|
21
|
+
*/
|
|
22
|
+
export async function POST(request: NextRequest) {
|
|
23
|
+
try {
|
|
24
|
+
const { email, password } = await request.json();
|
|
25
|
+
|
|
26
|
+
if (!email || !password) {
|
|
27
|
+
return NextResponse.json(
|
|
28
|
+
{ errors: [{ message: 'Email and password are required' }] },
|
|
29
|
+
{ status: 400 }
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const supabase = await createClient();
|
|
34
|
+
|
|
35
|
+
const { data, error } = await supabase.auth.signInWithPassword({
|
|
36
|
+
email,
|
|
37
|
+
password,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (error) {
|
|
41
|
+
return NextResponse.json(
|
|
42
|
+
{ errors: [{ message: error.message }] },
|
|
43
|
+
{ status: 401 }
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return NextResponse.json({
|
|
48
|
+
data: {
|
|
49
|
+
user: data.user,
|
|
50
|
+
session: {
|
|
51
|
+
access_token: data.session?.access_token,
|
|
52
|
+
expires_at: data.session?.expires_at,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error('Login error:', error);
|
|
58
|
+
return NextResponse.json(
|
|
59
|
+
{ errors: [{ message: error instanceof Error ? error.message : 'Login failed' }] },
|
|
60
|
+
{ status: 500 }
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Logout API Route (Proxy)
|
|
3
|
+
*
|
|
4
|
+
* Proxies logout requests through the Next.js server.
|
|
5
|
+
* Clears the Supabase session cookie server-side.
|
|
6
|
+
*
|
|
7
|
+
* @microbuild/origin: api-routes/auth-logout
|
|
8
|
+
* @microbuild/version: 1.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { NextResponse } from 'next/server';
|
|
12
|
+
import { createClient } from '@/lib/supabase/server';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* POST /api/auth/logout
|
|
16
|
+
*
|
|
17
|
+
* Signs out the current user and clears session cookies.
|
|
18
|
+
*/
|
|
19
|
+
export async function POST() {
|
|
20
|
+
try {
|
|
21
|
+
const supabase = await createClient();
|
|
22
|
+
|
|
23
|
+
const { error } = await supabase.auth.signOut();
|
|
24
|
+
|
|
25
|
+
if (error) {
|
|
26
|
+
console.error('Logout error:', error);
|
|
27
|
+
return NextResponse.json(
|
|
28
|
+
{ errors: [{ message: error.message }] },
|
|
29
|
+
{ status: 500 }
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return NextResponse.json({ data: { message: 'Logged out successfully' } });
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error('Unexpected logout error:', error);
|
|
36
|
+
return NextResponse.json(
|
|
37
|
+
{ errors: [{ message: 'Failed to logout' }] },
|
|
38
|
+
{ status: 500 }
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth User API Route (Proxy)
|
|
3
|
+
*
|
|
4
|
+
* Returns the currently authenticated user's information.
|
|
5
|
+
* Proxies through the Next.js server to avoid CORS issues.
|
|
6
|
+
*
|
|
7
|
+
* @microbuild/origin: api-routes/auth-user
|
|
8
|
+
* @microbuild/version: 1.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { NextResponse } from 'next/server';
|
|
12
|
+
import { createClient } from '@/lib/supabase/server';
|
|
13
|
+
import { getAuthHeaders, getDaasUrl } from '@/lib/api/auth-headers';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* GET /api/auth/user
|
|
17
|
+
*
|
|
18
|
+
* Returns current user info. Tries DaaS backend first (for full user profile
|
|
19
|
+
* with roles/permissions), falls back to Supabase Auth user.
|
|
20
|
+
*/
|
|
21
|
+
export async function GET() {
|
|
22
|
+
try {
|
|
23
|
+
const supabase = await createClient();
|
|
24
|
+
const { data: { user }, error: authError } = await supabase.auth.getUser();
|
|
25
|
+
|
|
26
|
+
if (authError || !user) {
|
|
27
|
+
return NextResponse.json(
|
|
28
|
+
{ errors: [{ message: 'Authentication required' }] },
|
|
29
|
+
{ status: 401 }
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Try to get enhanced user profile from DaaS backend
|
|
34
|
+
try {
|
|
35
|
+
const headers = await getAuthHeaders();
|
|
36
|
+
const daasUrl = getDaasUrl();
|
|
37
|
+
|
|
38
|
+
const response = await fetch(`${daasUrl}/api/users/me`, {
|
|
39
|
+
headers,
|
|
40
|
+
cache: 'no-store',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (response.ok) {
|
|
44
|
+
const data = await response.json();
|
|
45
|
+
return NextResponse.json({ data: data.data || data });
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
// DaaS not available, fall back to Supabase user
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Fallback: return basic Supabase user info
|
|
52
|
+
return NextResponse.json({
|
|
53
|
+
data: {
|
|
54
|
+
id: user.id,
|
|
55
|
+
email: user.email,
|
|
56
|
+
first_name: user.user_metadata?.first_name || null,
|
|
57
|
+
last_name: user.user_metadata?.last_name || null,
|
|
58
|
+
avatar: user.user_metadata?.avatar || null,
|
|
59
|
+
status: 'active',
|
|
60
|
+
role: null,
|
|
61
|
+
admin_access: false,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('Auth user error:', error);
|
|
66
|
+
return NextResponse.json(
|
|
67
|
+
{ errors: [{ message: 'Failed to get user info' }] },
|
|
68
|
+
{ status: 500 }
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fields API Route
|
|
3
|
+
*
|
|
4
|
+
* Proxies field schema requests to the DaaS backend.
|
|
5
|
+
* Required for CollectionForm, VForm, and dynamic field rendering.
|
|
6
|
+
*
|
|
7
|
+
* @microbuild/origin: api-routes/fields
|
|
8
|
+
* @microbuild/version: 1.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
12
|
+
import { getAuthHeaders, getDaasUrl } from '@/lib/api/auth-headers';
|
|
13
|
+
|
|
14
|
+
type RouteParams = { params: Promise<{ collection: string }> };
|
|
15
|
+
|
|
16
|
+
export async function GET(
|
|
17
|
+
request: NextRequest,
|
|
18
|
+
{ params }: RouteParams
|
|
19
|
+
) {
|
|
20
|
+
try {
|
|
21
|
+
const { collection } = await params;
|
|
22
|
+
const headers = await getAuthHeaders();
|
|
23
|
+
const daasUrl = getDaasUrl();
|
|
24
|
+
|
|
25
|
+
const response = await fetch(`${daasUrl}/api/fields/${collection}`, {
|
|
26
|
+
headers,
|
|
27
|
+
cache: 'no-store',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
const error = await response.json().catch(() => ({ errors: [{ message: 'Request failed' }] }));
|
|
32
|
+
return NextResponse.json(error, { status: response.status });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const data = await response.json();
|
|
36
|
+
return NextResponse.json(data);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error('Fields API error:', error);
|
|
39
|
+
return NextResponse.json(
|
|
40
|
+
{ errors: [{ message: error instanceof Error ? error.message : 'Internal server error' }] },
|
|
41
|
+
{ status: 500 }
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single File API Route
|
|
3
|
+
*
|
|
4
|
+
* Proxies single file operations to the DaaS backend.
|
|
5
|
+
*
|
|
6
|
+
* @microbuild/origin: api-routes/files-id
|
|
7
|
+
* @microbuild/version: 1.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
11
|
+
import { getAuthHeaders, getDaasUrl } from '@/lib/api/auth-headers';
|
|
12
|
+
|
|
13
|
+
type RouteParams = { params: Promise<{ id: string }> };
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* GET /api/files/[id]
|
|
17
|
+
* Get file metadata by ID
|
|
18
|
+
*/
|
|
19
|
+
export async function GET(
|
|
20
|
+
request: NextRequest,
|
|
21
|
+
{ params }: RouteParams
|
|
22
|
+
) {
|
|
23
|
+
try {
|
|
24
|
+
const { id } = await params;
|
|
25
|
+
const headers = await getAuthHeaders();
|
|
26
|
+
const daasUrl = getDaasUrl();
|
|
27
|
+
|
|
28
|
+
const response = await fetch(`${daasUrl}/api/files/${id}`, {
|
|
29
|
+
headers,
|
|
30
|
+
cache: 'no-store',
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
const error = await response.json().catch(() => ({ errors: [{ message: 'Request failed' }] }));
|
|
35
|
+
return NextResponse.json(error, { status: response.status });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const data = await response.json();
|
|
39
|
+
return NextResponse.json(data);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('Files GET by ID error:', error);
|
|
42
|
+
return NextResponse.json(
|
|
43
|
+
{ errors: [{ message: error instanceof Error ? error.message : 'Internal server error' }] },
|
|
44
|
+
{ status: 500 }
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* PATCH /api/files/[id]
|
|
51
|
+
* Update file metadata
|
|
52
|
+
*/
|
|
53
|
+
export async function PATCH(
|
|
54
|
+
request: NextRequest,
|
|
55
|
+
{ params }: RouteParams
|
|
56
|
+
) {
|
|
57
|
+
try {
|
|
58
|
+
const { id } = await params;
|
|
59
|
+
const headers = await getAuthHeaders();
|
|
60
|
+
const daasUrl = getDaasUrl();
|
|
61
|
+
const body = await request.json();
|
|
62
|
+
|
|
63
|
+
const response = await fetch(`${daasUrl}/api/files/${id}`, {
|
|
64
|
+
method: 'PATCH',
|
|
65
|
+
headers,
|
|
66
|
+
body: JSON.stringify(body),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
const error = await response.json().catch(() => ({ errors: [{ message: 'Request failed' }] }));
|
|
71
|
+
return NextResponse.json(error, { status: response.status });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const data = await response.json();
|
|
75
|
+
return NextResponse.json(data);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('Files PATCH error:', error);
|
|
78
|
+
return NextResponse.json(
|
|
79
|
+
{ errors: [{ message: error instanceof Error ? error.message : 'Internal server error' }] },
|
|
80
|
+
{ status: 500 }
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* DELETE /api/files/[id]
|
|
87
|
+
* Delete a file
|
|
88
|
+
*/
|
|
89
|
+
export async function DELETE(
|
|
90
|
+
request: NextRequest,
|
|
91
|
+
{ params }: RouteParams
|
|
92
|
+
) {
|
|
93
|
+
try {
|
|
94
|
+
const { id } = await params;
|
|
95
|
+
const headers = await getAuthHeaders();
|
|
96
|
+
const daasUrl = getDaasUrl();
|
|
97
|
+
|
|
98
|
+
const response = await fetch(`${daasUrl}/api/files/${id}`, {
|
|
99
|
+
method: 'DELETE',
|
|
100
|
+
headers,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
const error = await response.json().catch(() => ({ errors: [{ message: 'Request failed' }] }));
|
|
105
|
+
return NextResponse.json(error, { status: response.status });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return NextResponse.json({ data: null });
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error('Files DELETE error:', error);
|
|
111
|
+
return NextResponse.json(
|
|
112
|
+
{ errors: [{ message: error instanceof Error ? error.message : 'Internal server error' }] },
|
|
113
|
+
{ status: 500 }
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Files API Route
|
|
3
|
+
*
|
|
4
|
+
* Proxies file operations to the DaaS backend.
|
|
5
|
+
* Required for file upload components.
|
|
6
|
+
*
|
|
7
|
+
* @microbuild/origin: api-routes/files
|
|
8
|
+
* @microbuild/version: 1.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
12
|
+
import { getAuthHeaders, getDaasUrl } from '@/lib/api/auth-headers';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* GET /api/files
|
|
16
|
+
* List files with optional filtering
|
|
17
|
+
*/
|
|
18
|
+
export async function GET(request: NextRequest) {
|
|
19
|
+
try {
|
|
20
|
+
const headers = await getAuthHeaders();
|
|
21
|
+
const daasUrl = getDaasUrl();
|
|
22
|
+
|
|
23
|
+
// Forward query parameters
|
|
24
|
+
const searchParams = request.nextUrl.searchParams.toString();
|
|
25
|
+
const url = `${daasUrl}/api/files${searchParams ? `?${searchParams}` : ''}`;
|
|
26
|
+
|
|
27
|
+
const response = await fetch(url, {
|
|
28
|
+
headers,
|
|
29
|
+
cache: 'no-store',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
const error = await response.json().catch(() => ({ errors: [{ message: 'Request failed' }] }));
|
|
34
|
+
return NextResponse.json(error, { status: response.status });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const data = await response.json();
|
|
38
|
+
return NextResponse.json(data);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('Files GET error:', error);
|
|
41
|
+
return NextResponse.json(
|
|
42
|
+
{ errors: [{ message: error instanceof Error ? error.message : 'Internal server error' }] },
|
|
43
|
+
{ status: 500 }
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* POST /api/files
|
|
50
|
+
* Upload a file
|
|
51
|
+
*/
|
|
52
|
+
export async function POST(request: NextRequest) {
|
|
53
|
+
try {
|
|
54
|
+
const headers = await getAuthHeaders();
|
|
55
|
+
const daasUrl = getDaasUrl();
|
|
56
|
+
|
|
57
|
+
// Get the form data from the request
|
|
58
|
+
const formData = await request.formData();
|
|
59
|
+
|
|
60
|
+
// Remove Content-Type header to let fetch set it with boundary for multipart
|
|
61
|
+
const { 'Content-Type': _, ...restHeaders } = headers as Record<string, string>;
|
|
62
|
+
|
|
63
|
+
const response = await fetch(`${daasUrl}/api/files`, {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers: restHeaders,
|
|
66
|
+
body: formData,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
const error = await response.json().catch(() => ({ errors: [{ message: 'Upload failed' }] }));
|
|
71
|
+
return NextResponse.json(error, { status: response.status });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const data = await response.json();
|
|
75
|
+
return NextResponse.json(data);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('Files POST error:', error);
|
|
78
|
+
return NextResponse.json(
|
|
79
|
+
{ errors: [{ message: error instanceof Error ? error.message : 'Internal server error' }] },
|
|
80
|
+
{ status: 500 }
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Items Single Item API Route
|
|
3
|
+
*
|
|
4
|
+
* Proxies single item CRUD operations to the DaaS backend.
|
|
5
|
+
*
|
|
6
|
+
* @microbuild/origin: api-routes/items-id
|
|
7
|
+
* @microbuild/version: 1.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
11
|
+
import { getAuthHeaders, getDaasUrl } from '@/lib/api/auth-headers';
|
|
12
|
+
|
|
13
|
+
type RouteParams = { params: Promise<{ collection: string; id: string }> };
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* GET /api/items/[collection]/[id]
|
|
17
|
+
* Get a single item by ID
|
|
18
|
+
*/
|
|
19
|
+
export async function GET(
|
|
20
|
+
request: NextRequest,
|
|
21
|
+
{ params }: RouteParams
|
|
22
|
+
) {
|
|
23
|
+
try {
|
|
24
|
+
const { collection, id } = await params;
|
|
25
|
+
const headers = await getAuthHeaders();
|
|
26
|
+
const daasUrl = getDaasUrl();
|
|
27
|
+
|
|
28
|
+
// Forward query parameters (e.g., fields)
|
|
29
|
+
const searchParams = request.nextUrl.searchParams.toString();
|
|
30
|
+
const url = `${daasUrl}/api/items/${collection}/${id}${searchParams ? `?${searchParams}` : ''}`;
|
|
31
|
+
|
|
32
|
+
const response = await fetch(url, {
|
|
33
|
+
headers,
|
|
34
|
+
cache: 'no-store',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
const error = await response.json().catch(() => ({ errors: [{ message: 'Request failed' }] }));
|
|
39
|
+
return NextResponse.json(error, { status: response.status });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const data = await response.json();
|
|
43
|
+
return NextResponse.json(data);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('Items GET by ID error:', error);
|
|
46
|
+
return NextResponse.json(
|
|
47
|
+
{ errors: [{ message: error instanceof Error ? error.message : 'Internal server error' }] },
|
|
48
|
+
{ status: 500 }
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* PATCH /api/items/[collection]/[id]
|
|
55
|
+
* Update an existing item
|
|
56
|
+
*/
|
|
57
|
+
export async function PATCH(
|
|
58
|
+
request: NextRequest,
|
|
59
|
+
{ params }: RouteParams
|
|
60
|
+
) {
|
|
61
|
+
try {
|
|
62
|
+
const { collection, id } = await params;
|
|
63
|
+
const headers = await getAuthHeaders();
|
|
64
|
+
const daasUrl = getDaasUrl();
|
|
65
|
+
const body = await request.json();
|
|
66
|
+
|
|
67
|
+
const response = await fetch(`${daasUrl}/api/items/${collection}/${id}`, {
|
|
68
|
+
method: 'PATCH',
|
|
69
|
+
headers,
|
|
70
|
+
body: JSON.stringify(body),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
const error = await response.json().catch(() => ({ errors: [{ message: 'Request failed' }] }));
|
|
75
|
+
return NextResponse.json(error, { status: response.status });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const data = await response.json();
|
|
79
|
+
return NextResponse.json(data);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error('Items PATCH error:', error);
|
|
82
|
+
return NextResponse.json(
|
|
83
|
+
{ errors: [{ message: error instanceof Error ? error.message : 'Internal server error' }] },
|
|
84
|
+
{ status: 500 }
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* DELETE /api/items/[collection]/[id]
|
|
91
|
+
* Delete an item
|
|
92
|
+
*/
|
|
93
|
+
export async function DELETE(
|
|
94
|
+
request: NextRequest,
|
|
95
|
+
{ params }: RouteParams
|
|
96
|
+
) {
|
|
97
|
+
try {
|
|
98
|
+
const { collection, id } = await params;
|
|
99
|
+
const headers = await getAuthHeaders();
|
|
100
|
+
const daasUrl = getDaasUrl();
|
|
101
|
+
|
|
102
|
+
const response = await fetch(`${daasUrl}/api/items/${collection}/${id}`, {
|
|
103
|
+
method: 'DELETE',
|
|
104
|
+
headers,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
const error = await response.json().catch(() => ({ errors: [{ message: 'Request failed' }] }));
|
|
109
|
+
return NextResponse.json(error, { status: response.status });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return NextResponse.json({ data: null });
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error('Items DELETE error:', error);
|
|
115
|
+
return NextResponse.json(
|
|
116
|
+
{ errors: [{ message: error instanceof Error ? error.message : 'Internal server error' }] },
|
|
117
|
+
{ status: 500 }
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Items Collection API Route
|
|
3
|
+
*
|
|
4
|
+
* Proxies CRUD operations for collection items to the DaaS backend.
|
|
5
|
+
* Supports Directus-compatible query parameters.
|
|
6
|
+
*
|
|
7
|
+
* @microbuild/origin: api-routes/items
|
|
8
|
+
* @microbuild/version: 1.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
12
|
+
import { getAuthHeaders, getDaasUrl } from '@/lib/api/auth-headers';
|
|
13
|
+
|
|
14
|
+
type RouteParams = { params: Promise<{ collection: string }> };
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* GET /api/items/[collection]
|
|
18
|
+
* List items with optional filtering, sorting, and pagination
|
|
19
|
+
*/
|
|
20
|
+
export async function GET(
|
|
21
|
+
request: NextRequest,
|
|
22
|
+
{ params }: RouteParams
|
|
23
|
+
) {
|
|
24
|
+
try {
|
|
25
|
+
const { collection } = await params;
|
|
26
|
+
const headers = await getAuthHeaders();
|
|
27
|
+
const daasUrl = getDaasUrl();
|
|
28
|
+
|
|
29
|
+
// Forward all query parameters
|
|
30
|
+
const searchParams = request.nextUrl.searchParams.toString();
|
|
31
|
+
const url = `${daasUrl}/api/items/${collection}${searchParams ? `?${searchParams}` : ''}`;
|
|
32
|
+
|
|
33
|
+
const response = await fetch(url, {
|
|
34
|
+
headers,
|
|
35
|
+
cache: 'no-store',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
const error = await response.json().catch(() => ({ errors: [{ message: 'Request failed' }] }));
|
|
40
|
+
return NextResponse.json(error, { status: response.status });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const data = await response.json();
|
|
44
|
+
return NextResponse.json(data);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error('Items GET error:', error);
|
|
47
|
+
return NextResponse.json(
|
|
48
|
+
{ errors: [{ message: error instanceof Error ? error.message : 'Internal server error' }] },
|
|
49
|
+
{ status: 500 }
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* POST /api/items/[collection]
|
|
56
|
+
* Create a new item
|
|
57
|
+
*/
|
|
58
|
+
export async function POST(
|
|
59
|
+
request: NextRequest,
|
|
60
|
+
{ params }: RouteParams
|
|
61
|
+
) {
|
|
62
|
+
try {
|
|
63
|
+
const { collection } = await params;
|
|
64
|
+
const headers = await getAuthHeaders();
|
|
65
|
+
const daasUrl = getDaasUrl();
|
|
66
|
+
const body = await request.json();
|
|
67
|
+
|
|
68
|
+
const response = await fetch(`${daasUrl}/api/items/${collection}`, {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
headers,
|
|
71
|
+
body: JSON.stringify(body),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
const error = await response.json().catch(() => ({ errors: [{ message: 'Request failed' }] }));
|
|
76
|
+
return NextResponse.json(error, { status: response.status });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const data = await response.json();
|
|
80
|
+
return NextResponse.json(data);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error('Items POST error:', error);
|
|
83
|
+
return NextResponse.json(
|
|
84
|
+
{ errors: [{ message: error instanceof Error ? error.message : 'Internal server error' }] },
|
|
85
|
+
{ status: 500 }
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|