Document Directus production contract and repair auth

This commit is contained in:
makearmy 2026-07-10 10:51:04 -04:00
parent 0a7ee5ff35
commit 92ce8bce97
32 changed files with 24821 additions and 65 deletions

View file

@ -4,14 +4,28 @@
// ⚠️ NOTE: Do NOT import `headers()` / `cookies()` from "next/headers" here.
// Next 15's types can make those async and break build-time type checks in shared libs.
const BASE = (process.env.DIRECTUS_URL || "").replace(/\/$/, "");
const TOKEN_ADMIN_REGISTER = process.env.DIRECTUS_TOKEN_ADMIN_REGISTER || ""; // server-only
const BASE = (
process.env.DIRECTUS_URL ||
process.env.NEXT_PUBLIC_API_BASE_URL ||
""
).replace(/\/$/, "");
// DIRECTUS_TOKEN_ADMIN_REGISTER is the canonical credential for the production
// Registration Bot policy. Keep the older aliases as fallbacks so an existing
// deployment does not silently lose authentication during migration.
const TOKEN_ADMIN_REGISTER =
process.env.DIRECTUS_TOKEN_ADMIN_REGISTER ||
process.env.DIRECTUS_SERVICE_TOKEN ||
process.env.DIRECTUS_ADMIN_TOKEN ||
""; // server-only
const ROLE_MEMBER_NAME = process.env.DIRECTUS_ROLE_MEMBER_NAME || "Users";
const PROJECTS_COLLECTION = process.env.DIRECTUS_PROJECTS_COLLECTION || "projects";
if (!BASE) console.warn("[directus] Missing DIRECTUS_URL");
if (!BASE) console.warn("[directus] Missing DIRECTUS_URL / NEXT_PUBLIC_API_BASE_URL");
if (!TOKEN_ADMIN_REGISTER)
console.warn("[directus] Missing DIRECTUS_TOKEN_ADMIN_REGISTER (used for registration and username login)");
console.warn(
"[directus] Missing DIRECTUS_TOKEN_ADMIN_REGISTER (or legacy service-token alias) " +
"used for registration and username login"
);
export function bytesFromMB(mb: number) {
return Math.round(mb * 1024 * 1024);
@ -119,7 +133,8 @@ export async function dxDELETE<T = any>(path: string, bearer: string): Promise<T
// ─────────────────────────────────────────────────────────────
export async function directusAdminFetch<T = any>(path: string, init?: RequestInit): Promise<T> {
if (!TOKEN_ADMIN_REGISTER) throw new Error("Missing DIRECTUS_TOKEN_ADMIN_REGISTER");
if (!BASE) throw new Error("Missing Directus base URL");
if (!TOKEN_ADMIN_REGISTER) throw new Error("Missing Directus registration credential");
const res = await fetch(`${BASE}${path}`, {
...init,
headers: {
@ -231,13 +246,25 @@ export async function createDirectusUser(input: {
}
export async function emailForUsername(username: string): Promise<string | null> {
const q = `/users?filter[username][_eq]=${encodeURIComponent(username)}&fields=email&limit=1`;
const clean = username.trim();
if (!clean) return null;
const q = `/users?filter[username][_eq]=${encodeURIComponent(clean)}&fields=email&limit=1`;
const { data } = await directusAdminFetch<{ data: Array<{ email?: string }> }>(q);
const em = data?.[0]?.email;
return em ? String(em) : null;
}
export async function directusUserExists(email: string, username: string): Promise<boolean> {
const q =
`/users?filter[_or][0][email][_eq]=${encodeURIComponent(email)}` +
`&filter[_or][1][username][_eq]=${encodeURIComponent(username)}` +
`&fields=id&limit=1`;
const { data } = await directusAdminFetch<{ data: Array<{ id: string }> }>(q);
return Array.isArray(data) && data.length > 0;
}
export async function loginDirectus(email: string, password: string) {
if (!BASE) throw new Error("Missing Directus base URL");
const res = await fetch(`${BASE}/auth/login`, {
method: "POST",
headers: { Accept: "application/json", "Content-Type": "application/json" },