feat(core): setup postgres, minio, and authentik next-auth

This commit is contained in:
AI Assistant
2026-02-27 10:29:54 +02:00
parent 3b1ba589f0
commit 0ad7e835bd
18 changed files with 1654 additions and 105 deletions
+55
View File
@@ -0,0 +1,55 @@
import NextAuth, { NextAuthOptions } from "next-auth";
import AuthentikProvider from "next-auth/providers/authentik";
export const authOptions: NextAuthOptions = {
providers: [
AuthentikProvider({
clientId: process.env.AUTHENTIK_CLIENT_ID || "",
clientSecret: process.env.AUTHENTIK_CLIENT_SECRET || "",
issuer: process.env.AUTHENTIK_ISSUER || "",
}),
],
callbacks: {
async jwt({ token, user, profile }) {
if (user) {
token.id = user.id;
}
if (profile) {
// Map Authentik groups/roles to our internal roles
// This assumes Authentik sends groups in the profile
const groups = (profile as any).groups || [];
let role = "user";
if (groups.includes("architools-admin")) role = "admin";
else if (groups.includes("architools-manager")) role = "manager";
token.role = role;
// Map company based on groups or attributes
let company = "group";
if (groups.includes("company-beletage")) company = "beletage";
else if (groups.includes("company-urban-switch"))
company = "urban-switch";
else if (groups.includes("company-studii-de-teren"))
company = "studii-de-teren";
token.company = company;
}
return token;
},
async session({ session, token }) {
if (session.user) {
(session.user as any).id = token.id;
(session.user as any).role = token.role || "user";
(session.user as any).company = token.company || "group";
}
return session;
},
},
pages: {
// We can add custom sign-in pages later if needed
},
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
+130
View File
@@ -0,0 +1,130 @@
import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/core/storage/prisma";
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const namespace = searchParams.get("namespace");
const key = searchParams.get("key");
if (!namespace) {
return NextResponse.json(
{ error: "Namespace is required" },
{ status: 400 },
);
}
try {
if (key) {
// Get single item
const item = await prisma.keyValueStore.findUnique({
where: {
namespace_key: {
namespace,
key,
},
},
});
return NextResponse.json({ value: item ? item.value : null });
} else {
// Get all items in namespace
const items = await prisma.keyValueStore.findMany({
where: { namespace },
});
// Return as a record { [key]: value }
const result: Record<string, any> = {};
for (const item of items) {
result[item.key] = item.value;
}
return NextResponse.json({ items: result });
}
} catch (error) {
console.error("Storage GET error:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 },
);
}
}
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { namespace, key, value } = body;
if (!namespace || !key) {
return NextResponse.json(
{ error: "Namespace and key are required" },
{ status: 400 },
);
}
await prisma.keyValueStore.upsert({
where: {
namespace_key: {
namespace,
key,
},
},
update: {
value,
},
create: {
namespace,
key,
value,
},
});
return NextResponse.json({ success: true });
} catch (error) {
console.error("Storage POST error:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 },
);
}
}
export async function DELETE(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const namespace = searchParams.get("namespace");
const key = searchParams.get("key");
if (!namespace) {
return NextResponse.json(
{ error: "Namespace is required" },
{ status: 400 },
);
}
try {
if (key) {
// Delete single item
await prisma.keyValueStore
.delete({
where: {
namespace_key: {
namespace,
key,
},
},
})
.catch(() => {
// Ignore error if item doesn't exist
});
} else {
// Clear namespace
await prisma.keyValueStore.deleteMany({
where: { namespace },
});
}
return NextResponse.json({ success: true });
} catch (error) {
console.error("Storage DELETE error:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 },
);
}
}