@buildpad/cli 0.1.4

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.
Files changed (44) hide show
  1. package/README.md +557 -0
  2. package/dist/chunk-J4KKVECI.js +365 -0
  3. package/dist/chunk-J4KKVECI.js.map +1 -0
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +2582 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/outdated-JMAYAZ7W.js +110 -0
  8. package/dist/outdated-JMAYAZ7W.js.map +1 -0
  9. package/dist/templates/api/auth-callback-route.ts +36 -0
  10. package/dist/templates/api/auth-headers.ts +72 -0
  11. package/dist/templates/api/auth-login-route.ts +63 -0
  12. package/dist/templates/api/auth-logout-route.ts +41 -0
  13. package/dist/templates/api/auth-user-route.ts +71 -0
  14. package/dist/templates/api/collections-route.ts +54 -0
  15. package/dist/templates/api/fields-route.ts +44 -0
  16. package/dist/templates/api/files-id-route.ts +116 -0
  17. package/dist/templates/api/files-route.ts +83 -0
  18. package/dist/templates/api/items-id-route.ts +120 -0
  19. package/dist/templates/api/items-route.ts +88 -0
  20. package/dist/templates/api/login-page.tsx +142 -0
  21. package/dist/templates/api/permissions-me-route.ts +72 -0
  22. package/dist/templates/api/relations-route.ts +46 -0
  23. package/dist/templates/app/content/[collection]/[id]/page.tsx +35 -0
  24. package/dist/templates/app/content/[collection]/page.tsx +65 -0
  25. package/dist/templates/app/content/layout.tsx +64 -0
  26. package/dist/templates/app/content/page.tsx +66 -0
  27. package/dist/templates/app/design-tokens.css +183 -0
  28. package/dist/templates/app/globals.css +58 -0
  29. package/dist/templates/app/layout.tsx +49 -0
  30. package/dist/templates/app/page.tsx +23 -0
  31. package/dist/templates/components/ColorSchemeToggle.tsx +35 -0
  32. package/dist/templates/lib/common-utils.ts +156 -0
  33. package/dist/templates/lib/hooks/index.ts +98 -0
  34. package/dist/templates/lib/services/index.ts +31 -0
  35. package/dist/templates/lib/theme.ts +241 -0
  36. package/dist/templates/lib/types/index.ts +10 -0
  37. package/dist/templates/lib/utils-index.ts +32 -0
  38. package/dist/templates/lib/utils.ts +14 -0
  39. package/dist/templates/lib/vform/index.ts +24 -0
  40. package/dist/templates/middleware/middleware.ts +29 -0
  41. package/dist/templates/supabase/client.ts +25 -0
  42. package/dist/templates/supabase/middleware.ts +66 -0
  43. package/dist/templates/supabase/server.ts +45 -0
  44. package/package.json +61 -0
@@ -0,0 +1,110 @@
1
+ import {
2
+ getRegistry,
3
+ loadConfig
4
+ } from "./chunk-J4KKVECI.js";
5
+
6
+ // src/commands/outdated.ts
7
+ import chalk from "chalk";
8
+ import ora from "ora";
9
+ async function getRegistry2() {
10
+ try {
11
+ return await getRegistry();
12
+ } catch (err) {
13
+ console.error(chalk.red("Failed to load registry:", err.message));
14
+ process.exit(1);
15
+ }
16
+ }
17
+ async function outdated(options) {
18
+ const { cwd, json = false } = options;
19
+ const config = await loadConfig(cwd);
20
+ if (!config) {
21
+ if (json) {
22
+ console.log(JSON.stringify({ error: "buildpad.json not found" }));
23
+ } else {
24
+ console.log(chalk.red('\n\u2717 buildpad.json not found. Run "npx buildpad init" first.\n'));
25
+ }
26
+ process.exit(1);
27
+ }
28
+ const spinner = json ? null : ora("Checking for updates...").start();
29
+ try {
30
+ const registry = await getRegistry2();
31
+ const result = {
32
+ outdated: [],
33
+ upToDate: [],
34
+ unknown: [],
35
+ registryVersion: registry.version,
36
+ installedRegistryVersion: config.registryVersion
37
+ };
38
+ for (const componentName of config.installedComponents) {
39
+ const versionInfo = config.componentVersions?.[componentName];
40
+ if (!versionInfo) {
41
+ result.unknown.push(componentName);
42
+ } else if (versionInfo.version !== registry.version) {
43
+ result.outdated.push({
44
+ name: componentName,
45
+ installedVersion: versionInfo.version,
46
+ latestVersion: registry.version,
47
+ installedAt: versionInfo.installedAt
48
+ });
49
+ } else {
50
+ result.upToDate.push(componentName);
51
+ }
52
+ }
53
+ for (const libName of config.installedLib) {
54
+ const versionInfo = config.componentVersions?.[`lib/${libName}`];
55
+ if (!versionInfo) {
56
+ result.unknown.push(`lib/${libName}`);
57
+ }
58
+ }
59
+ spinner?.stop();
60
+ if (json) {
61
+ console.log(JSON.stringify(result, null, 2));
62
+ return;
63
+ }
64
+ console.log(chalk.bold("\n\u{1F4E6} Component Update Status\n"));
65
+ console.log(chalk.dim(`Registry version: ${registry.version}`));
66
+ if (config.registryVersion) {
67
+ console.log(chalk.dim(`Installed at registry version: ${config.registryVersion}
68
+ `));
69
+ } else {
70
+ console.log(chalk.dim(`Installed at registry version: unknown
71
+ `));
72
+ }
73
+ if (result.outdated.length > 0) {
74
+ console.log(chalk.yellow(`
75
+ \u26A0 ${result.outdated.length} component(s) have updates available:
76
+ `));
77
+ for (const comp of result.outdated) {
78
+ const installedDate = new Date(comp.installedAt).toLocaleDateString();
79
+ console.log(chalk.yellow(` ${comp.name}`));
80
+ console.log(chalk.dim(` ${comp.installedVersion} \u2192 ${comp.latestVersion} (installed ${installedDate})`));
81
+ }
82
+ console.log(chalk.dim("\n Update with:"));
83
+ console.log(chalk.cyan(` npx buildpad add ${result.outdated.map((c) => c.name).join(" ")} --overwrite
84
+ `));
85
+ }
86
+ if (result.unknown.length > 0) {
87
+ console.log(chalk.dim(`
88
+ ${result.unknown.length} component(s) without version info (installed before tracking):`));
89
+ result.unknown.forEach((name) => console.log(chalk.dim(` - ${name}`)));
90
+ console.log(chalk.dim("\n Reinstall with --overwrite to enable version tracking."));
91
+ }
92
+ if (result.outdated.length === 0 && result.unknown.length === 0) {
93
+ console.log(chalk.green("\u2713 All components are up to date!\n"));
94
+ }
95
+ console.log(chalk.dim(`
96
+ Total: ${config.installedComponents.length} components, ${config.installedLib.length} lib modules`));
97
+ console.log(chalk.dim(` Up to date: ${result.upToDate.length}`));
98
+ console.log(chalk.dim(` Outdated: ${result.outdated.length}`));
99
+ console.log(chalk.dim(` Unknown: ${result.unknown.length}
100
+ `));
101
+ } catch (error) {
102
+ spinner?.fail("Failed to check for updates");
103
+ console.error(chalk.red(error));
104
+ process.exit(1);
105
+ }
106
+ }
107
+ export {
108
+ outdated
109
+ };
110
+ //# sourceMappingURL=outdated-JMAYAZ7W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/outdated.ts"],"sourcesContent":["/**\n * Buildpad CLI - Outdated Command\n * \n * Checks for component updates by comparing installed versions\n * to the current registry version.\n */\n\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { loadConfig } from './init.js';\nimport {\n getRegistry as fetchRegistry,\n type Registry,\n} from '../resolver.js';\n\n/**\n * Load registry (local or remote via resolver)\n */\nasync function getRegistry(): Promise<Registry> {\n try {\n return await fetchRegistry();\n } catch (err: any) {\n console.error(chalk.red('Failed to load registry:', err.message));\n process.exit(1);\n }\n}\n\n/**\n * Main outdated command\n */\nexport async function outdated(options: {\n cwd: string;\n json?: boolean;\n}) {\n const { cwd, json = false } = options;\n \n // Load config\n const config = await loadConfig(cwd);\n if (!config) {\n if (json) {\n console.log(JSON.stringify({ error: 'buildpad.json not found' }));\n } else {\n console.log(chalk.red('\\n✗ buildpad.json not found. Run \"npx buildpad init\" first.\\n'));\n }\n process.exit(1);\n }\n \n const spinner = json ? null : ora('Checking for updates...').start();\n \n try {\n const registry = await getRegistry();\n const result: OutdatedResult = {\n outdated: [],\n upToDate: [],\n unknown: [],\n registryVersion: registry.version,\n installedRegistryVersion: config.registryVersion,\n };\n \n // Check each installed component\n for (const componentName of config.installedComponents) {\n const versionInfo = config.componentVersions?.[componentName];\n \n if (!versionInfo) {\n // No version info - installed before version tracking\n result.unknown.push(componentName);\n } else if (versionInfo.version !== registry.version) {\n // Outdated\n result.outdated.push({\n name: componentName,\n installedVersion: versionInfo.version,\n latestVersion: registry.version,\n installedAt: versionInfo.installedAt,\n });\n } else {\n // Up to date\n result.upToDate.push(componentName);\n }\n }\n \n // Check lib modules too\n for (const libName of config.installedLib) {\n const versionInfo = config.componentVersions?.[`lib/${libName}`];\n \n if (!versionInfo) {\n result.unknown.push(`lib/${libName}`);\n }\n }\n \n spinner?.stop();\n \n if (json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n \n // Display results\n console.log(chalk.bold('\\n📦 Component Update Status\\n'));\n console.log(chalk.dim(`Registry version: ${registry.version}`));\n if (config.registryVersion) {\n console.log(chalk.dim(`Installed at registry version: ${config.registryVersion}\\n`));\n } else {\n console.log(chalk.dim(`Installed at registry version: unknown\\n`));\n }\n \n if (result.outdated.length > 0) {\n console.log(chalk.yellow(`\\n⚠ ${result.outdated.length} component(s) have updates available:\\n`));\n \n for (const comp of result.outdated) {\n const installedDate = new Date(comp.installedAt).toLocaleDateString();\n console.log(chalk.yellow(` ${comp.name}`));\n console.log(chalk.dim(` ${comp.installedVersion} → ${comp.latestVersion} (installed ${installedDate})`));\n }\n \n console.log(chalk.dim('\\n Update with:'));\n console.log(chalk.cyan(` npx buildpad add ${result.outdated.map(c => c.name).join(' ')} --overwrite\\n`));\n }\n \n if (result.unknown.length > 0) {\n console.log(chalk.dim(`\\n ${result.unknown.length} component(s) without version info (installed before tracking):`));\n result.unknown.forEach(name => console.log(chalk.dim(` - ${name}`)));\n console.log(chalk.dim('\\n Reinstall with --overwrite to enable version tracking.'));\n }\n \n if (result.outdated.length === 0 && result.unknown.length === 0) {\n console.log(chalk.green('✓ All components are up to date!\\n'));\n }\n \n // Summary\n console.log(chalk.dim(`\\nTotal: ${config.installedComponents.length} components, ${config.installedLib.length} lib modules`));\n console.log(chalk.dim(` Up to date: ${result.upToDate.length}`));\n console.log(chalk.dim(` Outdated: ${result.outdated.length}`));\n console.log(chalk.dim(` Unknown: ${result.unknown.length}\\n`));\n \n } catch (error) {\n spinner?.fail('Failed to check for updates');\n console.error(chalk.red(error));\n process.exit(1);\n }\n}\n"],"mappings":"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","names":["getRegistry"]}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Auth Callback Route
3
+ *
4
+ * Handles OAuth callback and email confirmation redirects.
5
+ * Exchanges the auth code for a session server-side.
6
+ *
7
+ * @buildpad/origin: api-routes/auth-callback
8
+ * @buildpad/version: 1.0.0
9
+ */
10
+
11
+ import { NextRequest, NextResponse } from 'next/server';
12
+ import { createClient } from '@/lib/supabase/server';
13
+
14
+ /**
15
+ * GET /api/auth/callback
16
+ *
17
+ * Handles the OAuth redirect callback.
18
+ * Exchanges the code for a session and redirects to the app.
19
+ */
20
+ export async function GET(request: NextRequest) {
21
+ const { searchParams, origin } = request.nextUrl;
22
+ const code = searchParams.get('code');
23
+ const next = searchParams.get('next') ?? '/';
24
+
25
+ if (code) {
26
+ const supabase = await createClient();
27
+ const { error } = await supabase.auth.exchangeCodeForSession(code);
28
+
29
+ if (!error) {
30
+ return NextResponse.redirect(`${origin}${next}`);
31
+ }
32
+ }
33
+
34
+ // Auth code exchange failed — redirect to login with error
35
+ return NextResponse.redirect(`${origin}/login?error=auth_callback_failed`);
36
+ }
@@ -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 Buildpad CLI.
6
+ *
7
+ * @buildpad/origin: api-routes/auth-headers
8
+ * @buildpad/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
+ * @buildpad/origin: api-routes/auth-login
10
+ * @buildpad/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
+ * @buildpad/origin: api-routes/auth-logout
8
+ * @buildpad/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
+ * @buildpad/origin: api-routes/auth-user
8
+ * @buildpad/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,54 @@
1
+ /**
2
+ * Collections API Route
3
+ *
4
+ * Proxies collection metadata requests to the DaaS backend.
5
+ * Required for ContentNavigation and useCollections hook.
6
+ *
7
+ * @buildpad/origin: api-routes/collections
8
+ * @buildpad/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/collections
16
+ * List all collections the current user has access to
17
+ */
18
+ export async function GET(request: NextRequest) {
19
+ try {
20
+ const headers = await getAuthHeaders();
21
+ const daasUrl = getDaasUrl();
22
+
23
+ const searchParams = request.nextUrl.searchParams.toString();
24
+ const url = `${daasUrl}/api/collections${searchParams ? `?${searchParams}` : ''}`;
25
+
26
+ const response = await fetch(url, {
27
+ headers,
28
+ cache: 'no-store',
29
+ });
30
+
31
+ if (!response.ok) {
32
+ const error = await response.json().catch(() => ({
33
+ errors: [{ message: 'Request failed' }],
34
+ }));
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('Collections API error:', error);
42
+ return NextResponse.json(
43
+ {
44
+ errors: [
45
+ {
46
+ message:
47
+ error instanceof Error ? error.message : 'Internal server error',
48
+ },
49
+ ],
50
+ },
51
+ { status: 500 }
52
+ );
53
+ }
54
+ }
@@ -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
+ * @buildpad/origin: api-routes/fields
8
+ * @buildpad/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
+ * @buildpad/origin: api-routes/files-id
7
+ * @buildpad/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
+ }