feat: integrate feedback module

This commit is contained in:
2026-02-08 21:48:55 +01:00
parent 453a603392
commit 7ec826dae3
48 changed files with 4413 additions and 1168 deletions

79
app/api/feedback/route.ts Normal file
View File

@@ -0,0 +1,79 @@
import { NextRequest, NextResponse } from 'next/server';
import { createDirectus, rest, authentication, staticToken, createItem, readItems } from '@directus/sdk';
import { config } from '@/lib/config';
async function getAuthenticatedClient() {
const { url, token: rawToken } = config.infraCMS;
const effectiveUrl = url;
const token = rawToken?.trim();
if (!token) {
throw new Error('INFRA_DIRECTUS_TOKEN is not configured');
}
const client = createDirectus(effectiveUrl)
.with(staticToken(token))
.with(rest());
return client;
}
export async function GET() {
try {
const client = await getAuthenticatedClient();
const items = await client.request(readItems('visual_feedback', {
fields: ['*'],
sort: ['-date_created'],
}));
return NextResponse.json(items);
} catch (error: any) {
const errMsg = error.errors?.[0]?.message || error.message || 'Unknown Directus Error';
console.error('Error fetching feedback:', {
msg: errMsg,
url: config.infraCMS.url,
status: error.response?.status,
errors: error.errors
});
return NextResponse.json({ error: errMsg }, { status: 500 });
}
}
export async function POST(req: NextRequest) {
try {
const client = await getAuthenticatedClient();
const body = await req.json();
const { action, ...data } = body;
if (action === 'reply') {
const reply = await client.request(createItem('visual_feedback_comments', {
feedback_id: data.feedbackId,
user_name: data.userName,
text: data.text,
}));
return NextResponse.json(reply);
}
const feedback = await client.request(createItem('visual_feedback', {
project: 'klz-cables',
url: data.url,
selector: data.selector,
x: data.x,
y: data.y,
type: data.type,
text: data.text,
user_name: data.userName,
user_identity: data.userIdentity,
}));
return NextResponse.json(feedback);
} catch (error: any) {
const errMsg = error.errors?.[0]?.message || error.message || 'Unknown Directus Error';
console.error('Error saving feedback:', {
msg: errMsg,
url: config.infraCMS.url,
status: error.response?.status,
errors: error.errors
});
return NextResponse.json({ error: errMsg }, { status: 500 });
}
}

43
app/api/whoami/route.ts Normal file
View File

@@ -0,0 +1,43 @@
import { NextRequest, NextResponse } from 'next/server';
import { envSchema, getRawEnv } from '@/lib/env';
export async function GET(req: NextRequest) {
const env = envSchema.parse(getRawEnv());
const gatekeeperUrl = env.GATEKEEPER_URL;
const host = req.headers.get('host') || '';
const { searchParams } = new URL(req.url);
const hasBypassParam = searchParams.get('gatekeeper_bypass') === 'true';
const isLocal = host.includes('localhost') || host.includes('127.0.0.1') || host.includes('klz.localhost');
const isBypassEnabled = hasBypassParam || env.GATEKEEPER_BYPASS_ENABLED || (env.NODE_ENV === 'development' && isLocal);
// If bypass is enabled or we are in local development, use "Dev-Admin" identity.
if (isBypassEnabled) {
return NextResponse.json({
authenticated: true,
identity: 'Dev-Admin',
isDevFallback: true
});
}
try {
// We forward the cookie header to gatekeeper so it can identify the session
const response = await fetch(`${gatekeeperUrl}/api/whoami`, {
headers: {
cookie: req.headers.get('cookie') || '',
},
cache: 'no-store',
});
if (!response.ok) {
return NextResponse.json({ authenticated: false, identity: 'Guest' });
}
const data = await response.json();
return NextResponse.json(data);
} catch (error: any) {
console.error('Error proxying to gatekeeper:', error);
return NextResponse.json({ authenticated: false, identity: 'Guest (Auth Error)' });
}
}