@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.
- package/README.md +557 -0
- package/dist/chunk-J4KKVECI.js +365 -0
- package/dist/chunk-J4KKVECI.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2582 -0
- package/dist/index.js.map +1 -0
- package/dist/outdated-JMAYAZ7W.js +110 -0
- package/dist/outdated-JMAYAZ7W.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/collections-route.ts +54 -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/permissions-me-route.ts +72 -0
- package/dist/templates/api/relations-route.ts +46 -0
- package/dist/templates/app/content/[collection]/[id]/page.tsx +35 -0
- package/dist/templates/app/content/[collection]/page.tsx +65 -0
- package/dist/templates/app/content/layout.tsx +64 -0
- package/dist/templates/app/content/page.tsx +66 -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 +31 -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,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
|
+
}
|