Document Directus production contract and repair auth
This commit is contained in:
parent
0a7ee5ff35
commit
92ce8bce97
32 changed files with 24821 additions and 65 deletions
|
|
@ -1,13 +1,12 @@
|
|||
// app/api/auth/register/route.ts
|
||||
import { NextResponse } from "next/server";
|
||||
import { rateLimit, requestIp } from "@/lib/rate-limit";
|
||||
import {
|
||||
createDirectusUser,
|
||||
directusUserExists,
|
||||
loginDirectus,
|
||||
} from "@/lib/directus";
|
||||
|
||||
const DIRECTUS = (process.env.DIRECTUS_URL || process.env.NEXT_PUBLIC_API_BASE_URL || "").replace(/\/$/, "");
|
||||
const SERVICE_TOKEN =
|
||||
process.env.DIRECTUS_SERVICE_TOKEN ||
|
||||
process.env.DIRECTUS_ADMIN_TOKEN ||
|
||||
process.env.DIRECTUS_TOKEN_ADMIN_REGISTER || "";
|
||||
const DEFAULT_ROLE = process.env.DIRECTUS_DEFAULT_ROLE || undefined;
|
||||
const SECURE = process.env.NODE_ENV === "production";
|
||||
|
||||
function bad(message: string, status = 400) {
|
||||
|
|
@ -15,16 +14,18 @@ function bad(message: string, status = 400) {
|
|||
}
|
||||
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
|
||||
async function directusLogin(email: string, password: string) {
|
||||
const r = await fetch(`${DIRECTUS}/auth/login`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
||||
body: JSON.stringify({ email, password }),
|
||||
cache: "no-store",
|
||||
function isConflict(error: any): boolean {
|
||||
const code = error?.detail?.errors?.[0]?.extensions?.code;
|
||||
const message = error?.detail?.errors?.[0]?.message || error?.message || "";
|
||||
return error?.status === 409 || code === "RECORD_NOT_UNIQUE" || /unique|already exists/i.test(message);
|
||||
}
|
||||
|
||||
function logDirectusFailure(stage: string, error: any) {
|
||||
console.error(`[auth/register] Directus ${stage} failed`, {
|
||||
status: error?.status,
|
||||
code: error?.detail?.errors?.[0]?.extensions?.code,
|
||||
message: error?.detail?.errors?.[0]?.message || error?.message,
|
||||
});
|
||||
const j = await r.json().catch(() => ({}));
|
||||
if (!r.ok) throw new Error(j?.errors?.[0]?.message || j?.message || `Login failed (${r.status})`);
|
||||
return j?.data || j;
|
||||
}
|
||||
|
||||
export async function POST(req: Request) {
|
||||
|
|
@ -32,9 +33,6 @@ export async function POST(req: Request) {
|
|||
if (!rateLimit(`register:${requestIp(req)}`, 5, 60 * 60_000)) {
|
||||
return bad("Too many registration attempts. Please try again later.", 429);
|
||||
}
|
||||
if (!DIRECTUS) return bad("Missing DIRECTUS_URL/NEXT_PUBLIC_API_BASE_URL", 500);
|
||||
if (!SERVICE_TOKEN) return bad("Missing DIRECTUS_SERVICE_TOKEN / admin token", 500);
|
||||
|
||||
const body = await req.json().catch(() => ({} as any));
|
||||
const email = String(body?.email ?? "").trim().toLowerCase();
|
||||
const username = String(body?.username ?? "").trim();
|
||||
|
|
@ -46,53 +44,35 @@ export async function POST(req: Request) {
|
|||
if (password.length < 8) return bad("Password must be at least 8 characters");
|
||||
if (password !== confirm) return bad("Passwords do not match");
|
||||
|
||||
// Optional pre-check to return a friendly 409 instead of a generic Directus error
|
||||
const existsRes = await fetch(
|
||||
`${DIRECTUS}/users?filter[_or][0][email][_eq]=${encodeURIComponent(email)}` +
|
||||
`&filter[_or][1][username][_eq]=${encodeURIComponent(username)}` +
|
||||
`&fields=id,email,username&limit=1`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${SERVICE_TOKEN}`,
|
||||
Accept: "application/json",
|
||||
},
|
||||
cache: "no-store",
|
||||
}
|
||||
);
|
||||
const existsJson = await existsRes.json().catch(() => ({}));
|
||||
if (Array.isArray(existsJson?.data) && existsJson.data.length > 0) {
|
||||
try {
|
||||
if (await directusUserExists(email, username)) {
|
||||
return bad("Email or username already in use", 409);
|
||||
}
|
||||
|
||||
// Create user with sane defaults
|
||||
const createPayload: any = {
|
||||
email,
|
||||
username,
|
||||
password,
|
||||
};
|
||||
if (DEFAULT_ROLE) createPayload.role = DEFAULT_ROLE;
|
||||
|
||||
const createRes = await fetch(`${DIRECTUS}/users`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${SERVICE_TOKEN}`,
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(createPayload),
|
||||
cache: "no-store",
|
||||
});
|
||||
|
||||
const cj = await createRes.json().catch(() => ({}));
|
||||
if (!createRes.ok) {
|
||||
const msg = cj?.errors?.[0]?.message || cj?.message || `User create failed (${createRes.status})`;
|
||||
return bad(msg, createRes.status || 500);
|
||||
} catch (error: any) {
|
||||
logDirectusFailure("duplicate lookup", error);
|
||||
return bad("Sign-up is temporarily unavailable.", 503);
|
||||
}
|
||||
|
||||
// Auto-login (email-based; directus expects "email" even though it's an identifier)
|
||||
const tokens = await directusLogin(email, password);
|
||||
let user: { id: string };
|
||||
try {
|
||||
// Resolves the production Users role through the Registration Bot
|
||||
// policy, then creates an active local-auth account in that role.
|
||||
user = await createDirectusUser({ email, username, password });
|
||||
} catch (error: any) {
|
||||
if (isConflict(error)) return bad("Email or username already in use", 409);
|
||||
logDirectusFailure("user creation", error);
|
||||
return bad("Sign-up is temporarily unavailable.", 503);
|
||||
}
|
||||
|
||||
const res = NextResponse.json({ ok: true, id: cj?.data?.id || null }, { status: 201 });
|
||||
let tokens: any;
|
||||
try {
|
||||
tokens = await loginDirectus(email, password);
|
||||
} catch (error: any) {
|
||||
logDirectusFailure("post-registration login", error);
|
||||
return bad("Your account was created, but automatic sign-in failed. Please sign in.", 502);
|
||||
}
|
||||
|
||||
const res = NextResponse.json({ ok: true, id: user.id }, { status: 201 });
|
||||
if (tokens?.access_token) {
|
||||
res.cookies.set("ma_at", tokens.access_token, {
|
||||
path: "/",
|
||||
|
|
|
|||
|
|
@ -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" },
|
||||
|
|
|
|||
34
directus/README.md
Normal file
34
directus/README.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# Directus production reference
|
||||
|
||||
This directory documents how the Next.js application integrates with the production Directus instance. It is evidence for development and review; it is not an automated migration source and must not be applied to production without a separate, reviewed change process.
|
||||
|
||||
## Evidence and authority
|
||||
|
||||
- `schema.yaml` is the fresh schema snapshot collected from production Directus 12.1.1 on 2026-07-10. It is the authoritative schema evidence in this directory for that collection time.
|
||||
- `reference/` contains immutable, sanitized source evidence collected from the same deployment: deployment inventory, Compose and Nginx summaries, environment-variable names, roles, policies, access assignments, permissions, settings metadata, flows, operations, user-field metadata, and system-table layouts.
|
||||
- `../app/schema.yaml` is an older Directus 11.12.0 snapshot. It remains useful as historical application evidence, but it is not the current production schema. It must remain in place alongside the current snapshot.
|
||||
- The Markdown files directly under `directus/` are human-readable interpretations of the application source and raw evidence. When prose conflicts with raw evidence, re-check the evidence and update the prose.
|
||||
- `env.example` is a names-and-placeholders contract. It contains no production values.
|
||||
|
||||
The exports intentionally exclude users, user-specific access assignments, credentials, sessions, uploaded data, and flow option values. Consequently, a static credential's actual user/role binding and the detailed behavior inside flows cannot be proven from this material.
|
||||
|
||||
## Documents
|
||||
|
||||
- `deployment.md`: production topology and explicitly configured versus unobserved settings.
|
||||
- `auth-contract.md`: application-to-Directus authentication and account operations.
|
||||
- `permissions.md`: role, policy, field, filter, validation, and preset matrix.
|
||||
- `flows.md`: flow and operation graphs, with unresolved behavior called out.
|
||||
- `schema-differences.md`: structural comparison with `../app/schema.yaml`.
|
||||
- `integration-testing.md`: proposed disposable integration-test environment.
|
||||
|
||||
## Refresh procedure
|
||||
|
||||
1. Collect from the running production instance without changing it.
|
||||
2. Sanitize the result before it enters the repository. Exclude secret values, credentials, user rows, email addresses, sessions, cookies, uploaded files, and sensitive flow options.
|
||||
3. Store a new schema snapshot under `snapshots/`, using a date or version, for example `snapshots/schema-directus-12.1.1-2026-07-10.yaml`. Do not overwrite `schema.yaml` or `../app/schema.yaml` as part of a historical refresh.
|
||||
4. Store supporting sanitized exports in a correspondingly dated reference location, or clearly record which snapshot they describe.
|
||||
5. Record the Directus version, database vendor, collection time, and checksums.
|
||||
6. Compare the new snapshot to the preceding production snapshot and update the human-readable documents only from observed differences.
|
||||
7. Run a sensitive-data scan and `git diff --check`, then review the complete diff before committing.
|
||||
|
||||
The current collection process is described only by its outputs; the exact export commands are not present. That part of the refresh procedure is therefore **unresolved** and should be documented when a repeatable sanitized collection script is added.
|
||||
37
directus/auth-contract.md
Normal file
37
directus/auth-contract.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Authentication and account contract
|
||||
|
||||
Status labels mean:
|
||||
|
||||
- **Verified**: directly supported by application source plus the sanitized production exports.
|
||||
- **Inferred**: the source intends the behavior, but a runtime binding, omitted value, or live test is missing.
|
||||
- **Unresolved**: the required implementation or evidence is absent.
|
||||
|
||||
The app uses a Directus access token as a user bearer, normally stored in the HTTP-only `ma_at` cookie. It stores a refresh token in HTTP-only `ma_rt` and a five-minute recent-authentication marker in HTTP-only `ma_ra`. In production these cookies are `Secure`, `SameSite=Lax`, and path `/` where set by the current auth routes.
|
||||
|
||||
## Operation matrix
|
||||
|
||||
| Operation | Application route/module | Directus endpoint | Credential and required permission | Success and important failures | Status |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Signup | `POST /api/auth/register`; `app/app/api/auth/register/route.ts` | `GET /users` duplicate lookup; `GET /roles` Users-role lookup; `POST /users`; then `POST /auth/login` | The canonical static server credential is `DIRECTUS_TOKEN_ADMIN_REGISTER`, with legacy service/admin names as fallbacks. It must represent the Registration Bot role/policy or an equivalent identity. Registration policy grants the required role read and user read/create permissions. | Validates all fields, email shape, password length >=8, and confirmation. Resolves the `Users` role, creates an active account with email/username/password, and performs email-based auto-login. Successful login sets `ma_at` and possibly `ma_rt`. Permission/configuration failures are logged server-side and returned as generic temporary-unavailable responses. | Application flow, build, mocked request contract, and policy are **verified**; production static credential-to-role binding still requires deployment verification because users/values were omitted. |
|
||||
| Duplicate-user check | Same route | `GET /users?filter[...]&fields=id&limit=1` | Registration policy requires `directus_users.read` for `id`, which is exported. | A returned row or create-time uniqueness conflict produces the same generic email-or-username 409. A failed lookup stops signup with a generic 503 instead of continuing with uncertain permissions. | **Verified** source and mocked request behavior; production credential capability requires a live test. |
|
||||
| Email login | `POST /api/auth/login`; `app/app/api/auth/login/route.ts` | `POST /auth/login` | No bearer is supplied; local email/password credentials are sent to Directus. No collection permission is used for the auth endpoint itself. | On success sets one-hour `ma_at`, optional 30-day `ma_rt`, and optionally five-minute `ma_ra` for a reauth request. Authentication failures are deliberately collapsed to a generic 401. App IP limiter allows 20 attempts/minute per process. | **Verified** implementation; live Directus local-auth behavior is **inferred** pending integration tests. |
|
||||
| Username login | Same login route; `emailForUsername()` in `app/lib/directus.ts` | Static-token `GET /users` lookup, then `POST /auth/login` with resolved email | The same registration credential used by signup must read `directus_users.username,email`; Registration policy exports those fields. | Missing username gives generic 401. Lookup errors give 503. Resolved email follows email-login behavior. The shared helper accepts `DIRECTUS_URL` with `NEXT_PUBLIC_API_BASE_URL` fallback. | Source, build, required policy, and mocked request contract are **verified**; credential binding and end-to-end production success require a deployment test. |
|
||||
| Current user | `GET /api/account`, `GET /api/me`, proxy `/api/dx/users/me`, middleware; relevant modules under `app/app/api/` and `app/middleware.ts` | `GET /users/me` with varying field lists | Current user's access token. Users Self Access policy permits own-row read of `id,username,first_name,last_name,email,password,avatar,location,title,description,tags,language,tfa_secret,email_notifications`; Users policy additionally permits public-like `id,username,first_name,avatar,location`. | Returns requested profile fields if field permissions allow. Middleware checks `id` at most once per minute and redirects to reauth on failure. `/api/me` requests `display_name`, which is not present in the exported custom user-field metadata or allowed-field lists, so that field's behavior is unresolved. | Own-row permissions and route calls are **verified**. |
|
||||
| Token refresh | Token storage occurs in register/login and `app/lib/auth-cookies.ts` | Expected Directus endpoint would be `/auth/refresh`, but no call exists | `ma_rt` is stored, but no application route consumes it. | There is no implemented refresh behavior. Expired/invalid `ma_at` causes middleware to clear `ma_at`/`ma_v` and redirect to sign-in. | **Verified missing implementation**. |
|
||||
| Logout | `POST` or `GET /api/auth/logout`; `app/app/api/auth/logout/route.ts` | None | No Directus call and no credential required by the handler. | Expires only `ma_at`. It does not revoke the Directus session and does not clear `ma_rt`, `ma_ra`, or `ma_v`. | **Verified**. |
|
||||
| Password change | `POST`/`PATCH /api/account/password`; `app/app/api/account/password/route.ts` | `GET /users/me`; optionally static-token `GET /users`; `POST /auth/login`; `PATCH /users/me` | Current user token needs own-row read and `directus_users.update` for `password`; Users Self Access grants both. Username verification additionally needs Registration read permission. | Requires current/new values and an eight-character new password. Verifies the current password through local login, then patches only `password`. External-provider rejection depends on reading `provider`, but `provider` is not in the Users Self Access read fields, so this guard may not receive the field. Successful change does not rotate or clear existing app tokens. | Main path and permissions **verified**; provider guard and session effects **unresolved**. |
|
||||
| Profile changes | `PATCH /api/account` and `PATCH /api/account/profile`; UI primarily uses the latter | `PATCH /users/me` | Current user token. Users Self Access update permits `first_name,last_name,email,avatar,location,password`; it does not permit `username`. | First/last name and location update directly. Email requires `ma_ra=1`; the marker is cleared after a successful sensitive update. UI always includes email in its payload, so its normal save path requests reauthentication even when email is unchanged. Username is classified sensitive but is never added to the patch payload and is not update-permitted. | **Verified**. |
|
||||
| Avatar change | `POST /api/account/avatar`; `app/app/api/account/avatar/route.ts` | `POST /files`, then `PATCH /users/me` | Current user token. Users policy permits `directus_files.create` on all fields; Users Self Access permits own `avatar` update. | Requires a configured avatar folder, <=10 MB JPEG/PNG/WebP/GIF, and recognized signature. Uploads file then links its ID. If linking fails, the uploaded file is not cleaned up. | **Verified** except folder value and folder-specific enforcement, which are **unresolved**. |
|
||||
| Reauthentication | `POST /api/auth/reconfirm`; `app/app/api/auth/reconfirm/route.ts`, or login with `reauth=true` | Optional static-token `GET /users`, then `POST /auth/login` | Local credentials; username form also requires Registration `directus_users.read`. | Successful reconfirm replaces `ma_at` and sets `ma_ra` for five minutes. It does not set/replace `ma_rt`. Login's reauth path sets `ma_ra` alongside normal token cookies. Failures are generic 401. | **Verified** implementation; end-to-end username path remains unresolved. |
|
||||
|
||||
## Permission identity model
|
||||
|
||||
The export verifies these role-to-policy assignments:
|
||||
|
||||
- Registration Bot role -> Registration policy.
|
||||
- Users role -> Users Self Access policy and Users policy, in that sort order.
|
||||
- Administrator role -> Administrator policy.
|
||||
- Supporter Bot role -> Supporter policy.
|
||||
- Public policy has a role-less access assignment.
|
||||
|
||||
Because user rows and user-specific access assignments were intentionally omitted, the evidence cannot prove which identity owns any configured static credential. A deployment test must verify each credential's effective policy rather than relying on its environment-variable name.
|
||||
54
directus/deployment.md
Normal file
54
directus/deployment.md
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Production deployment
|
||||
|
||||
Unless marked **inferred** or **unresolved**, facts below are verified by `reference/deployment-inventory.md`, `reference/docker-compose.sanitized.yml`, `reference/environment-variable-names.txt`, `reference/nginx-summary.txt`, or `reference/exports/settings.json`.
|
||||
|
||||
## Runtime and topology
|
||||
|
||||
| Item | Production observation |
|
||||
| --- | --- |
|
||||
| Directus version | 12.1.1 |
|
||||
| Container image | `directus/directus:latest` |
|
||||
| Compose service and container | `directus` |
|
||||
| Restart policy | `unless-stopped` |
|
||||
| Database client | MySQL; host, database, and credential values were intentionally omitted |
|
||||
| Public URL | `https://forms.lasereverything.net` |
|
||||
| Container port | Directus listens on 8055; Compose binds it to host loopback `127.0.0.1:8056` |
|
||||
| Networks | Private `directus_net` bridge with IPv4/IPv6 subnets, plus external `backend_net` |
|
||||
| Upload mount | Host `./uploads` to `/directus/uploads`, read/write |
|
||||
| Extension mount | Host `./extensions` to `/directus/extensions`, read/write |
|
||||
|
||||
Production currently uses the mutable `directus/directus:latest` image tag. This document records that fact and does not change it. The running version was separately observed as 12.1.1, so a future container recreation could select a different image unless deployment controls outside the supplied evidence pin it.
|
||||
|
||||
The extensions directory is mounted, but its contents were not exported. Whether custom extensions are installed or active is **unresolved**.
|
||||
|
||||
## Reverse proxy
|
||||
|
||||
Nginx terminates TLS for `forms.lasereverything.net` and proxies to host loopback port 8056. It forwards the original host, client IP, forwarding chain, scheme, and WebSocket upgrade headers. Proxy read and send timeouts are 300 seconds. The request body limit is 100 MB. TLS 1.2 and 1.3 are enabled and HSTS is emitted for one year with subdomains.
|
||||
|
||||
The sanitized Nginx summary shows reflected-origin CORS response headers with credentials enabled and permits the common API methods and request headers. Directus-specific CORS environment variables are not explicitly set. The exact Nginx origin allow-list or surrounding condition that governs the reflected origin was not included, so whether arbitrary origins are accepted is **unresolved**.
|
||||
|
||||
## Mail
|
||||
|
||||
Directus is explicitly configured for SMTP transport. The observed categories are sender, SMTP host, port, secure mode, username, and password. Port 465 and secure mode `true` are verified; sender, host, and credential values were omitted. Delivery behavior was not tested.
|
||||
|
||||
The Next.js application separately references its own SMTP variable names for supporter-claim mail. Those names are documented in `env.example`; whether they point to the same SMTP service is **unresolved**.
|
||||
|
||||
## Authentication and security configuration
|
||||
|
||||
| Area | Explicit production evidence | Default or unresolved behavior |
|
||||
| --- | --- | --- |
|
||||
| Bootstrap administrator | `ADMIN_EMAIL` and `ADMIN_PASSWORD` names are present | Values and whether bootstrap variables still affect an initialized database are intentionally unknown |
|
||||
| Signing/encryption material | `KEY` and `SECRET` names are present | Values omitted |
|
||||
| Public registration | No registration environment variables are explicit; settings export has `public_registration=0` | Public `/users/register` is disabled; the app instead uses a privileged `/users` create flow |
|
||||
| Registration verification | Settings export has `public_registration_verify_email=0` and no public registration role/filter | Inactive while public registration is disabled |
|
||||
| Auth providers | `AUTH_PROVIDERS` is not explicitly set | Available/default providers were not exported; application code assumes local email/password auth |
|
||||
| Login-attempt setting | Column layout reports a database default of 25 | The live `auth_login_attempts` value was not exported; the app also applies an in-memory 20-per-minute IP limit |
|
||||
| Password policy | Column exists and defaults to null in the exported table layout | The live setting was not exported; the app enforces only an eight-character minimum on registration/change |
|
||||
| CORS | No Directus CORS environment variables are explicit | Nginx adds CORS headers; effective origin policy is unresolved as noted above |
|
||||
| Cookies | No Directus cookie environment variables are explicit | The app stores Directus tokens in its own `ma_at` and `ma_rt` cookies; effective Directus defaults are not proven here |
|
||||
| TFA | All exported policies have `enforce_tfa=0` | Per-user TFA state was intentionally not exported |
|
||||
| Policy IP restrictions | All exported policies have `ip_access=null` | No policy-level IP restriction is evidenced |
|
||||
| Admin/app access | Administrator policy has both; all other exported policies have neither | Verified from policy export |
|
||||
| MCP/AI/new 12.x settings | System-table columns exist | Live values were not included in the sanitized settings export and must not be inferred from column defaults |
|
||||
|
||||
Environment absence proves only that a setting was not explicitly supplied to the collected container. It does not prove behavior controlled by the database, an extension, another configuration source, or a Directus default that may vary by version.
|
||||
71
directus/env.example
Normal file
71
directus/env.example
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Names-and-placeholders contract only. Never place production values in this file.
|
||||
# Classification: public-build = embedded/exposed by Next.js; server = Next.js server only;
|
||||
# deployment = Directus container/bootstrap only.
|
||||
|
||||
# Shared Directus location
|
||||
NEXT_PUBLIC_API_BASE_URL=<public-directus-base-url> # Next.js; public-build
|
||||
NEXT_PUBLIC_ASSET_URL=<public-directus-assets-base-url> # Next.js; public-build
|
||||
DIRECTUS_URL=<server-directus-base-url> # Next.js; server
|
||||
|
||||
# Next.js privileged Directus identities
|
||||
DIRECTUS_TOKEN_ADMIN_REGISTER=<registration-static-credential> # Next.js; server
|
||||
DIRECTUS_SERVICE_TOKEN=<legacy-service-credential-if-still-supported> # Next.js; server
|
||||
DIRECTUS_ADMIN_TOKEN=<legacy-admin-credential-if-still-supported> # Next.js; server
|
||||
DIRECTUS_TOKEN_ADMIN_SUPPORTER=<supporter-static-credential> # Next.js and backfill script; server
|
||||
DIRECTUS_TOKEN_SUBMIT=<settings-submission-credential> # Next.js; server
|
||||
|
||||
# Next.js Directus role, collection, folder, and upload configuration
|
||||
DIRECTUS_ROLE_MEMBER_NAME=<registered-user-role-name> # Next.js helper; server
|
||||
DIRECTUS_PROJECTS_COLLECTION=<projects-collection-name> # Next.js helper; server
|
||||
DIRECTUS_AVATAR_FOLDER_ID=<avatar-folder-id> # Next.js; server
|
||||
DX_FOLDER_FIBER_PHOTOS=<folder-id> # Next.js; server
|
||||
DX_FOLDER_FIBER_SCREENS=<folder-id> # Next.js; server
|
||||
DX_FOLDER_GALVO_PHOTOS=<folder-id> # Next.js; server
|
||||
DX_FOLDER_GALVO_SCREENS=<folder-id> # Next.js; server
|
||||
DX_FOLDER_GANTRY_PHOTOS=<folder-id> # Next.js; server
|
||||
DX_FOLDER_GANTRY_SCREENS=<folder-id> # Next.js; server
|
||||
DX_FOLDER_UV_PHOTOS=<folder-id> # Next.js; server
|
||||
DX_FOLDER_UV_SCREENS=<folder-id> # Next.js; server
|
||||
FILE_MAX_MB=<positive-number> # Next.js project uploads; server
|
||||
SETTINGS_IMAGE_MAX_MB=<positive-number> # Next.js settings uploads; server
|
||||
|
||||
# Directus container identity and cryptographic configuration
|
||||
KEY=<directus-key> # Directus; deployment
|
||||
SECRET=<directus-secret> # Directus; deployment
|
||||
PUBLIC_URL=<public-directus-base-url> # Directus; deployment
|
||||
ADMIN_EMAIL=<bootstrap-admin-address> # Directus bootstrap; deployment
|
||||
ADMIN_PASSWORD=<bootstrap-admin-credential> # Directus bootstrap; deployment
|
||||
|
||||
# Directus database
|
||||
DB_CLIENT=mysql # Directus; deployment
|
||||
DB_HOST=<database-host> # Directus; deployment
|
||||
DB_PORT=<database-port> # Directus; deployment
|
||||
DB_DATABASE=<database-name> # Directus; deployment
|
||||
DB_USER=<database-user> # Directus; deployment
|
||||
DB_PASSWORD=<database-credential> # Directus; deployment
|
||||
DB_FILENAME=<unused-for-mysql-or-database-file> # Directus environment name observed; deployment
|
||||
|
||||
# Directus mail
|
||||
EMAIL_TRANSPORT=smtp # Directus; deployment
|
||||
EMAIL_FROM=<sender-address> # Directus and Next.js supporter mail; deployment/server
|
||||
EMAIL_SMTP_HOST=<smtp-host> # Directus; deployment
|
||||
EMAIL_SMTP_PORT=465 # Directus; deployment
|
||||
EMAIL_SMTP_SECURE=true # Directus; deployment
|
||||
EMAIL_SMTP_USER=<smtp-user> # Directus; deployment
|
||||
EMAIL_SMTP_PASSWORD=<smtp-credential> # Directus; deployment
|
||||
|
||||
# Next.js supporter mail uses a separate naming convention
|
||||
SMTP_HOST=<smtp-host> # Next.js; server
|
||||
SMTP_PORT=<smtp-port> # Next.js; server
|
||||
SMTP_SECURE=<true-or-false> # Next.js; server
|
||||
SMTP_USER=<smtp-user> # Next.js; server
|
||||
SMTP_PASS=<smtp-credential> # Next.js; server
|
||||
|
||||
# Runtime classification
|
||||
NODE_ENV=<production-or-development> # Directus and Next.js; deployment/server
|
||||
|
||||
# The production Directus environment also contains runtime-provided NODE_VERSION,
|
||||
# NPM_CONFIG_UPDATE_NOTIFIER, PATH, and YARN_VERSION. They are not application
|
||||
# integration configuration and therefore have no configurable placeholders here.
|
||||
# AUTH_PROVIDERS, Directus CORS/cookie variables, and public-registration variables
|
||||
# were explicitly reported as not set and are intentionally not implied here.
|
||||
47
directus/flows.md
Normal file
47
directus/flows.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Directus flows
|
||||
|
||||
Two active event-triggered flows were exported. Both run with accountability `all`. The export deliberately replaces every operation's options with only `options_keys`; code, filters, collections, queries, keys, error messages, status codes, and event scopes are not available. The graphs below are therefore structural, not behavioral proofs.
|
||||
|
||||
## `unique_key_claims`
|
||||
|
||||
Description: “A flow for setting a unique key for each claim to prevent duplicates.”
|
||||
|
||||
```text
|
||||
Run Script (exec_5dli5)
|
||||
resolve -> Read Data (item_read_e4myo)
|
||||
resolve -> Condition (condition_s458i)
|
||||
resolve -> Throw Error (throw_error_22tpm)
|
||||
reject -> end
|
||||
reject -> end
|
||||
reject -> end
|
||||
```
|
||||
|
||||
- Trigger: event.
|
||||
- Flow option keys: `type`, `scope`, `collections`, `return`.
|
||||
- Script option key: `code`.
|
||||
- Read option keys: `permissions`, `emitEvents`, `collection`, `query`.
|
||||
- Condition option key: `filter`.
|
||||
- Error option keys: `status`, `message`, `code`.
|
||||
|
||||
**Unresolved:** the triggering collection/event, how the unique key is calculated, what data is queried, the condition's polarity, error response, and whether the graph handles races atomically. The description suggests duplicate prevention, but omitted options prevent verification.
|
||||
|
||||
## `claim_auto_assign`
|
||||
|
||||
Description: “Assigns a claimant to owner and rejects other pending claims.”
|
||||
|
||||
```text
|
||||
Condition (condition_dl7cj)
|
||||
resolve -> Read Data (item_read_oyges) -> end
|
||||
reject -> end
|
||||
```
|
||||
|
||||
- Trigger: event.
|
||||
- Flow option keys: `type`, `scope`, `collections`.
|
||||
- Condition option key: `filter`.
|
||||
- Read option keys: `permissions`, `emitEvents`, `collection`, `key`.
|
||||
|
||||
**Unresolved:** the triggering collection/event, condition, item key, read collection, and any mutation that performs the assignment or rejection. The exported graph contains only a condition followed by an item-read operation; no update operation is visible. The description's assignment behavior therefore cannot be reconciled with the sanitized graph without the omitted options or additional extension/hook behavior.
|
||||
|
||||
## Refreshing flow documentation
|
||||
|
||||
A future sanitized export should retain non-sensitive structural option values such as event type, collection name, and graph routing when safe, while continuing to remove credentials, destination addresses, headers, code containing secrets, and personal data. Each retained value must be reviewed rather than copied wholesale.
|
||||
61
directus/integration-testing.md
Normal file
61
directus/integration-testing.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# Proposed disposable Directus integration testing
|
||||
|
||||
No tests described here have been implemented or run. This is a future design that must never target production.
|
||||
|
||||
## Environment
|
||||
|
||||
Use a dedicated Compose project with:
|
||||
|
||||
- A version-pinned Directus image matching production (currently 12.1.1), not the production `latest` deployment reference.
|
||||
- A disposable MySQL database matching the production database family.
|
||||
- Ephemeral uploads and a test-only extensions mount populated only when production extensions have been reviewed and are needed.
|
||||
- Random per-run signing secrets and test-only static credentials.
|
||||
- A loopback-only port and an unmistakable test public URL.
|
||||
- Mail captured by a local sink; no external delivery.
|
||||
- No production database, network, credentials, host mounts, or URLs.
|
||||
|
||||
Provision schema and access configuration from reviewed test fixtures derived from `schema.yaml` and the sanitized role/policy exports. Do not apply the production snapshot automatically until a reviewed fixture process accounts for system collections and version compatibility.
|
||||
|
||||
## Test identities
|
||||
|
||||
Create only synthetic records:
|
||||
|
||||
- Registration Bot with Registration policy and a test-only static credential.
|
||||
- Supporter Bot with Supporter policy if cross-feature checks need it.
|
||||
- A normal Users-role account with both Users policies.
|
||||
- An administrator used only for fixture setup/teardown, never by application requests.
|
||||
|
||||
Have the test harness assert each identity's effective permissions before auth scenarios. This catches a valid credential attached to the wrong role/policy.
|
||||
|
||||
## Required scenarios
|
||||
|
||||
| Area | Cases and assertions |
|
||||
| --- | --- |
|
||||
| Registration | Valid account creates an active, login-capable Users-role account; app cookies have expected flags; omitted/default role behavior is explicit; whitespace in passwords is preserved. |
|
||||
| Duplicate accounts | Duplicate email and duplicate username return the same non-enumerating conflict; mixed case behavior is recorded; failed pre-check cannot turn into a permission disclosure. |
|
||||
| Email login | Correct credentials succeed; incorrect password and unknown account have indistinguishable responses; rate-limit behavior is isolated per test process. |
|
||||
| Username login | Registration credential resolves username using only permitted fields; correct credentials succeed; unknown username is generic; missing/mis-scoped credential fails clearly without exposing Directus internals. |
|
||||
| Current user | `/users/me` and application wrappers return only expected fields; another user's private fields cannot be queried; middleware validation accepts a valid token and rejects expired/revoked tokens. |
|
||||
| Refresh | Once implemented, valid refresh rotates/returns expected tokens and cookies; expired/revoked/reused tokens fail; the endpoint never accepts an access token in place of refresh. Until then, assert/document that no application refresh endpoint exists. |
|
||||
| Logout | Once strengthened, clears all auth state and invokes Directus revocation as designed. Until then, characterize the current local-only `ma_at` clearing and retained refresh cookie as a known failing contract. |
|
||||
| Profile changes | Own first/last name and location succeed; email requires recent reauth; username update is denied; other-user updates are denied; the recent-auth marker expires and is single-use. |
|
||||
| Password changes | Correct current password succeeds; incorrect current password fails; minimum length is enforced; username identifier path works; external-provider behavior is tested if enabled; existing token/session behavior is explicitly asserted. |
|
||||
| Avatar changes | Allowed image succeeds into the test folder and links to self; bad type/signature/size fails; other-user linking and unauthorized folder behavior fail; partial upload cleanup behavior is characterized. |
|
||||
| Least privilege | Registration identity can read only required role/user fields and create only permitted user fields; cannot update/delete users, read unrelated collections, administer settings, or access the Data Studio. Test whether exported sensitive read fields can be narrowed. |
|
||||
|
||||
## Permission-focused negative tests
|
||||
|
||||
- Remove Registration `directus_users.read`: username resolution and duplicate checks should fail predictably while the effect on create is separately asserted.
|
||||
- Remove Registration `directus_users.create`: signup must fail without leaking permission internals.
|
||||
- Attach the static credential to the wrong role to prove deployment validation catches it.
|
||||
- Attempt to override role, status, policy, ID, and authentication data through the public app payload; the server must ignore or reject every override.
|
||||
- Verify Users updates are limited to `id=$CURRENT_USER` and permitted fields.
|
||||
- Exercise the exported `user_rigs` `%CURRENT_USER` update filter and record actual Directus 12.1.1 behavior.
|
||||
|
||||
## Harness and lifecycle
|
||||
|
||||
Run tests from outside the Next.js process against its HTTP interface, with a smaller Directus-level suite for permission diagnostics. Wait for both MySQL and Directus health, seed fixtures idempotently, run serial auth tests where rate limiting or cookie state matters, then destroy containers and volumes.
|
||||
|
||||
Log method, path template, status, Directus error code, and synthetic fixture ID only. Redact credentials and cookie values at the logging boundary. Never snapshot response bodies containing private profile fields.
|
||||
|
||||
CI should fail if the configured Directus version differs from the fixture version, a required permission assertion changes, a sensitive environment name lacks a test placeholder, or teardown leaves the disposable project running.
|
||||
90
directus/permissions.md
Normal file
90
directus/permissions.md
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# Roles, policies, and permissions
|
||||
|
||||
This is a readable rendering of `reference/exports/roles.json`, `policies.json`, `access.json`, and `permissions.json`. `C/R/U/D` mean create/read/update/delete. An absent action means no permission row was exported; it does not establish permissions inherited from an omitted user-specific policy.
|
||||
|
||||
## Role and policy assignments
|
||||
|
||||
| Role/access subject | Assigned policy | Admin access | Data Studio app access | Enforced TFA | IP restriction |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Administrator | Administrator | Yes | Yes | No | None |
|
||||
| Registration Bot | Registration | No | No | No | None |
|
||||
| Supporter Bot | Supporter | No | No | No | None |
|
||||
| Users | Users Self Access (sort 1), Users (sort 2) | No | No | No | None |
|
||||
| Public/role-less | Public | No | No | No | None |
|
||||
|
||||
No user-specific access assignments appear in the sanitized export because user data was intentionally omitted. The Administrator policy's collection permissions are implicit in `admin_access`; no individual rows were exported for it.
|
||||
|
||||
## High-level matrix
|
||||
|
||||
| Collection group | Public | Users | Users Self Access | Registration | Supporter |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Public catalog/content collections listed below | R | R | - | - | - |
|
||||
| `directus_fields` | R: `id,collection,field` | R: metadata fields listed below | - | - | - |
|
||||
| `directus_files` | R all fields | C/R all fields | - | - | - |
|
||||
| `directus_users` | - | R public profile fields | own-row R/U | R/C broad registration fields | - |
|
||||
| Projects and settings collections | R | C/R/U/D | - | - | - |
|
||||
| `projects_files` | R | C/R | - | - | - |
|
||||
| `user_claims` | - | C/R/U/D with presets and own/pending filters | - | - | - |
|
||||
| `user_rigs` | - | C/R/U/D with owner controls | - | - | - |
|
||||
| `user_rig_type` | - | C/R | - | - | - |
|
||||
| `user_memberships` | - | - | - | - | C/R/U all fields |
|
||||
| `directus_roles` | - | - | - | R selected role fields | - |
|
||||
|
||||
## Public policy
|
||||
|
||||
Unrestricted read, all fields, is exported for:
|
||||
|
||||
`bg_cat`, `bg_entries`, `bg_sub_cat`, `directus_files`, `hazard_danger`, `hazard_severity`, `hazard_source`, `hazard_tags`, `laser_focus_lens`, `laser_focus_lens_config`, `laser_focus_lens_diameter`, `laser_scan_lens`, `laser_scan_lens_apt`, `laser_scan_lens_config`, `laser_scan_lens_exp`, `laser_software`, `laser_source`, `material`, `material_cat`, `material_coating`, `material_coating_hazard_tags`, `material_color`, `material_hazard_tags`, `material_opacity`, `material_status`, `projects`, `projects_files`, `settings_co2gal`, `settings_co2gan`, `settings_fiber`, and `settings_uv`.
|
||||
|
||||
Public can also read `directus_fields`, restricted to `field,collection,id`. There are no exported public create, update, or delete permissions.
|
||||
|
||||
## Users policy
|
||||
|
||||
Users have unrestricted, all-field read for every public content collection above. Additional or expanded permissions are:
|
||||
|
||||
| Collection | Actions | Row filter | Fields/presets |
|
||||
| --- | --- | --- | --- |
|
||||
| `directus_fields` | R | None | `id,collection,field,special,interface,options,readonly,hidden,required,sort,width,group,note` |
|
||||
| `directus_files` | C/R | None | All fields; no folder/owner filter or preset exported |
|
||||
| `directus_users` | R | None | `username,first_name,avatar,location,id` |
|
||||
| `projects` | C/R/U/D | U/D require `owner=$CURRENT_USER`; C/R unrestricted | All fields; C presets `owner` and `uploader` to `$CURRENT_USER` |
|
||||
| `projects_files` | C/R | None | All fields; no ownership filter exported |
|
||||
| `settings_co2gal` | C/R/U/D | U/D require `owner=$CURRENT_USER`; C/R unrestricted | All fields; C presets owner/uploader to current user |
|
||||
| `settings_co2gan` | C/R/U/D | Same as above | Same as above |
|
||||
| `settings_fiber` | C/R/U/D | Same as above | Same as above |
|
||||
| `settings_uv` | C/R/U/D | U/D require `owner=$CURRENT_USER`; C/R unrestricted | C/R/U all fields and owner/uploader presets on C; D has `fields=null` in the export |
|
||||
| `user_claims` | C/R/U/D | R: claimant is current user. U/D: claimant is current user and status is `pending`. C unrestricted. | C fields omit `claimant` and `status` but preset them to current user/`pending`; R selected fields; U only `note`; D `fields=null` |
|
||||
| `user_rigs` | C/R/U/D | R/D require owner current user. U filter contains `owner _eq "%CURRENT_USER"` exactly as exported. C unrestricted. | All fields; C presets owner to `$CURRENT_USER` |
|
||||
| `user_rig_type` | C/R | None | All fields |
|
||||
|
||||
The `%CURRENT_USER` update filter on `user_rigs` differs from the `$CURRENT_USER` token used everywhere else. This is a **verified exported value** and is likely ineffective, but its Directus runtime interpretation has not been tested.
|
||||
|
||||
No validation rules are present on any exported Users permission row.
|
||||
|
||||
## Users Self Access policy
|
||||
|
||||
Both rows require `id=$CURRENT_USER`:
|
||||
|
||||
- Read fields: `username,last_name,first_name,email,password,avatar,location,title,description,tags,language,tfa_secret,email_notifications,id`.
|
||||
- Update fields: `first_name,last_name,email,password,avatar,location`.
|
||||
|
||||
There are no presets or validation rules. Username is readable but not updateable. The exported read list includes sensitive field names such as `password` and `tfa_secret`; Directus may suppress stored secret values independently, but that behavior is **not proven by this export** and should be tested.
|
||||
|
||||
## Registration policy
|
||||
|
||||
- `directus_users.create`: unrestricted rows; fields `email,username,password,role,status,auth_data,first_name,last_name,avatar,id`.
|
||||
- `directus_users.read`: unrestricted rows; a broad field list including `username,email,id,password,auth_data,token,policies,role,status,tfa_secret` and UI/theme fields.
|
||||
- `directus_roles.read`: unrestricted rows; fields `id,name,icon,description,parent,children`.
|
||||
|
||||
No validation rules or presets are exported. Least privilege therefore depends on the static credential being held only by the server and the application strictly controlling submitted fields. The application registration route currently uses email, username, password, and optionally role; username-to-email login resolution uses read access.
|
||||
|
||||
## Supporter policy
|
||||
|
||||
`user_memberships` has unrestricted create, read, and update permissions on all fields. No delete row, row filters, validation rules, or presets are exported. The policy is assigned to Supporter Bot.
|
||||
|
||||
## Validation and preset summary
|
||||
|
||||
- Every exported `validation` value is null.
|
||||
- Presets exist only for Users creates on projects/settings (`owner`/`uploader`), user claims (`claimant`/`pending`), and user rigs (`owner`).
|
||||
- Row filters exist only for Users updates/deletes and selected reads described above, plus own-row filters in Users Self Access.
|
||||
- Null permissions mean no row filter in these exports; null fields on delete rows are preserved as observed rather than interpreted as all/no fields.
|
||||
29
directus/reference/deployment-inventory.md
Normal file
29
directus/reference/deployment-inventory.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Directus Deployment Inventory
|
||||
|
||||
Generated from the running production container.
|
||||
|
||||
- Directus version: 12.1.1
|
||||
- Container image: directus/directus:latest
|
||||
- Public URL: https://forms.lasereverything.net
|
||||
- Host binding: 127.0.0.1:8056
|
||||
- Database client: mysql
|
||||
- Database service and database names: intentionally omitted
|
||||
- Email transport: smtp
|
||||
- SMTP port: 465
|
||||
- SMTP secure mode: true
|
||||
- Explicit AUTH_PROVIDERS variable: not set
|
||||
- Explicit CORS variables: not set
|
||||
- Explicit cookie variables: not set
|
||||
- Explicit registration environment variables: not set
|
||||
|
||||
## Persistent mounts
|
||||
|
||||
- `/directus/extensions` is mounted from the host, mode `rw`
|
||||
- `/directus/uploads` is mounted from the host, mode `rw`
|
||||
|
||||
## Notes
|
||||
|
||||
- Secret values are intentionally excluded.
|
||||
- Registration behavior also depends on fields stored in the Directus settings table.
|
||||
- Role, policy, and permission exports are stored separately.
|
||||
- Flow option values are excluded because they may contain credentials, headers, addresses, or other sensitive configuration.
|
||||
28
directus/reference/docker-compose.sanitized.yml
Normal file
28
directus/reference/docker-compose.sanitized.yml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
services:
|
||||
directus:
|
||||
image: directus/directus:latest
|
||||
container_name: directus
|
||||
ports:
|
||||
- "127.0.0.1:8056:8055"
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- ./uploads:/directus/uploads
|
||||
- ./extensions:/directus/extensions
|
||||
networks:
|
||||
- default
|
||||
- backend_net
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: directus_net
|
||||
driver: bridge
|
||||
enable_ipv6: true
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 10.40.0.0/16
|
||||
- subnet: fd00:40::/64
|
||||
backend_net:
|
||||
external: true
|
||||
|
||||
24
directus/reference/environment-variable-names.txt
Normal file
24
directus/reference/environment-variable-names.txt
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
ADMIN_EMAIL
|
||||
ADMIN_PASSWORD
|
||||
DB_CLIENT
|
||||
DB_DATABASE
|
||||
DB_FILENAME
|
||||
DB_HOST
|
||||
DB_PASSWORD
|
||||
DB_PORT
|
||||
DB_USER
|
||||
EMAIL_FROM
|
||||
EMAIL_SMTP_HOST
|
||||
EMAIL_SMTP_PASSWORD
|
||||
EMAIL_SMTP_PORT
|
||||
EMAIL_SMTP_SECURE
|
||||
EMAIL_SMTP_USER
|
||||
EMAIL_TRANSPORT
|
||||
KEY
|
||||
NODE_ENV
|
||||
NODE_VERSION
|
||||
NPM_CONFIG_UPDATE_NOTIFIER
|
||||
PATH
|
||||
PUBLIC_URL
|
||||
SECRET
|
||||
YARN_VERSION
|
||||
44
directus/reference/exports/access.json
Normal file
44
directus/reference/exports/access.json
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[
|
||||
{
|
||||
"id": "45b5000f-f495-4940-935a-d4bf2e409844",
|
||||
"role": "a6d3c757-a96b-4f0c-b96b-a6109d60a7ba",
|
||||
"policy": "93fc7a9e-16de-45d4-89b1-9fa129e81442",
|
||||
"sort": null,
|
||||
"user": null
|
||||
},
|
||||
{
|
||||
"id": "2ddbc9da-e0f5-4dff-a952-c3d7586ab706",
|
||||
"role": "296a28bc-60ab-4251-8bef-27f6dfb67948",
|
||||
"policy": "80b3902c-06e6-480c-9d29-d2a46552f60e",
|
||||
"sort": 1,
|
||||
"user": null
|
||||
},
|
||||
{
|
||||
"id": "767277dd-3b08-4d72-9bde-437af91e1d88",
|
||||
"role": "a2a695be-88f9-4948-b2a4-e0e22b01b32e",
|
||||
"policy": "2e4acc56-7f7e-4a10-9775-1c0dd9f47ded",
|
||||
"sort": 1,
|
||||
"user": null
|
||||
},
|
||||
{
|
||||
"id": "abfa5434-1ace-4e1f-9a93-7d84aabb0a95",
|
||||
"role": "00e518e3-75b3-4c6f-b57d-51a4f1af4781",
|
||||
"policy": "494e10a0-03ec-40b9-a7a5-c3a1e2b95001",
|
||||
"sort": 1,
|
||||
"user": null
|
||||
},
|
||||
{
|
||||
"id": "c1480ea2-ca4d-46c9-8034-09b76d0a41cf",
|
||||
"role": null,
|
||||
"policy": "abf8a154-5b1c-4a46-ac9c-7300570f4f17",
|
||||
"sort": 1,
|
||||
"user": null
|
||||
},
|
||||
{
|
||||
"id": "cf8e40bd-3fbe-4df5-a845-01802a3e7e1c",
|
||||
"role": "296a28bc-60ab-4251-8bef-27f6dfb67948",
|
||||
"policy": "536f020c-4f05-497d-8a6c-54e13f62c694",
|
||||
"sort": 2,
|
||||
"user": null
|
||||
}
|
||||
]
|
||||
19
directus/reference/exports/directus-user-fields.json
Normal file
19
directus/reference/exports/directus-user-fields.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
[
|
||||
{
|
||||
"field": "username",
|
||||
"special": null,
|
||||
"interface": "input",
|
||||
"options": null,
|
||||
"display": "raw",
|
||||
"display_options": null,
|
||||
"readonly": 0,
|
||||
"hidden": 0,
|
||||
"required": 1,
|
||||
"sort": 1,
|
||||
"width": "full",
|
||||
"note": null,
|
||||
"conditions": null,
|
||||
"validation": null,
|
||||
"validation_message": null
|
||||
}
|
||||
]
|
||||
35
directus/reference/exports/flows.json
Normal file
35
directus/reference/exports/flows.json
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
[
|
||||
{
|
||||
"id": "fdc2d675-e365-4431-9cd0-27e571c89e04",
|
||||
"name": "claim_auto_assign",
|
||||
"icon": "bolt",
|
||||
"color": null,
|
||||
"description": "Assigns a claimant to owner and rejects other pending claims.",
|
||||
"status": "active",
|
||||
"trigger": "event",
|
||||
"accountability": "all",
|
||||
"options_keys": [
|
||||
"type",
|
||||
"scope",
|
||||
"collections"
|
||||
],
|
||||
"root_operation": "e19723a6-e75c-4bc8-92aa-be06bb0528af"
|
||||
},
|
||||
{
|
||||
"id": "99cc3ea5-3ab7-431f-a4f6-ce4e7677b092",
|
||||
"name": "unique_key_claims",
|
||||
"icon": "bolt",
|
||||
"color": null,
|
||||
"description": "A flow for setting a unique key for each claim to prevent duplicates.",
|
||||
"status": "active",
|
||||
"trigger": "event",
|
||||
"accountability": "all",
|
||||
"options_keys": [
|
||||
"type",
|
||||
"scope",
|
||||
"collections",
|
||||
"return"
|
||||
],
|
||||
"root_operation": "2245466f-8178-4e5c-b058-819eb421de47"
|
||||
}
|
||||
]
|
||||
94
directus/reference/exports/operations.json
Normal file
94
directus/reference/exports/operations.json
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
[
|
||||
{
|
||||
"id": "03c6582f-800d-4e94-9fea-f77a9ea5e3aa",
|
||||
"name": "Condition",
|
||||
"key": "condition_s458i",
|
||||
"type": "condition",
|
||||
"position_x": 55,
|
||||
"position_y": 1,
|
||||
"resolve": "d4266993-ceae-452a-9b7d-664c11bf1013",
|
||||
"reject": null,
|
||||
"flow": "99cc3ea5-3ab7-431f-a4f6-ce4e7677b092",
|
||||
"options_keys": [
|
||||
"filter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cb7101fc-e2c5-4cfc-b1d6-486ac70d286f",
|
||||
"name": "Read Data",
|
||||
"key": "item_read_e4myo",
|
||||
"type": "item-read",
|
||||
"position_x": 37,
|
||||
"position_y": 1,
|
||||
"resolve": "03c6582f-800d-4e94-9fea-f77a9ea5e3aa",
|
||||
"reject": null,
|
||||
"flow": "99cc3ea5-3ab7-431f-a4f6-ce4e7677b092",
|
||||
"options_keys": [
|
||||
"permissions",
|
||||
"emitEvents",
|
||||
"collection",
|
||||
"query"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "2245466f-8178-4e5c-b058-819eb421de47",
|
||||
"name": "Run Script",
|
||||
"key": "exec_5dli5",
|
||||
"type": "exec",
|
||||
"position_x": 19,
|
||||
"position_y": 1,
|
||||
"resolve": "cb7101fc-e2c5-4cfc-b1d6-486ac70d286f",
|
||||
"reject": null,
|
||||
"flow": "99cc3ea5-3ab7-431f-a4f6-ce4e7677b092",
|
||||
"options_keys": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "d4266993-ceae-452a-9b7d-664c11bf1013",
|
||||
"name": "Throw Error",
|
||||
"key": "throw_error_22tpm",
|
||||
"type": "throw-error",
|
||||
"position_x": 73,
|
||||
"position_y": 1,
|
||||
"resolve": null,
|
||||
"reject": null,
|
||||
"flow": "99cc3ea5-3ab7-431f-a4f6-ce4e7677b092",
|
||||
"options_keys": [
|
||||
"status",
|
||||
"message",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "e19723a6-e75c-4bc8-92aa-be06bb0528af",
|
||||
"name": "Condition",
|
||||
"key": "condition_dl7cj",
|
||||
"type": "condition",
|
||||
"position_x": 19,
|
||||
"position_y": 1,
|
||||
"resolve": "ebd0e644-1be0-4200-89c4-844b06c7d788",
|
||||
"reject": null,
|
||||
"flow": "fdc2d675-e365-4431-9cd0-27e571c89e04",
|
||||
"options_keys": [
|
||||
"filter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ebd0e644-1be0-4200-89c4-844b06c7d788",
|
||||
"name": "Read Data",
|
||||
"key": "item_read_oyges",
|
||||
"type": "item-read",
|
||||
"position_x": 37,
|
||||
"position_y": 1,
|
||||
"resolve": null,
|
||||
"reject": null,
|
||||
"flow": "fdc2d675-e365-4431-9cd0-27e571c89e04",
|
||||
"options_keys": [
|
||||
"permissions",
|
||||
"emitEvents",
|
||||
"collection",
|
||||
"key"
|
||||
]
|
||||
}
|
||||
]
|
||||
1176
directus/reference/exports/permissions.json
Normal file
1176
directus/reference/exports/permissions.json
Normal file
File diff suppressed because it is too large
Load diff
62
directus/reference/exports/policies.json
Normal file
62
directus/reference/exports/policies.json
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
[
|
||||
{
|
||||
"id": "abf8a154-5b1c-4a46-ac9c-7300570f4f17",
|
||||
"name": "$t:public_label",
|
||||
"icon": "public",
|
||||
"description": "$t:public_description",
|
||||
"admin_access": 0,
|
||||
"app_access": 0,
|
||||
"enforce_tfa": 0,
|
||||
"ip_access": null
|
||||
},
|
||||
{
|
||||
"id": "93fc7a9e-16de-45d4-89b1-9fa129e81442",
|
||||
"name": "Administrator",
|
||||
"icon": "verified",
|
||||
"description": "$t:admin_description",
|
||||
"admin_access": 1,
|
||||
"app_access": 1,
|
||||
"enforce_tfa": 0,
|
||||
"ip_access": null
|
||||
},
|
||||
{
|
||||
"id": "494e10a0-03ec-40b9-a7a5-c3a1e2b95001",
|
||||
"name": "Registration",
|
||||
"icon": "robot",
|
||||
"description": "Registration Bot",
|
||||
"admin_access": 0,
|
||||
"app_access": 0,
|
||||
"enforce_tfa": 0,
|
||||
"ip_access": null
|
||||
},
|
||||
{
|
||||
"id": "2e4acc56-7f7e-4a10-9775-1c0dd9f47ded",
|
||||
"name": "Supporter",
|
||||
"icon": "robot_2",
|
||||
"description": "Supporter Bot",
|
||||
"admin_access": 0,
|
||||
"app_access": 0,
|
||||
"enforce_tfa": 0,
|
||||
"ip_access": null
|
||||
},
|
||||
{
|
||||
"id": "536f020c-4f05-497d-8a6c-54e13f62c694",
|
||||
"name": "Users",
|
||||
"icon": "badge",
|
||||
"description": "Registered Users",
|
||||
"admin_access": 0,
|
||||
"app_access": 0,
|
||||
"enforce_tfa": 0,
|
||||
"ip_access": null
|
||||
},
|
||||
{
|
||||
"id": "80b3902c-06e6-480c-9d29-d2a46552f60e",
|
||||
"name": "Users Self Access",
|
||||
"icon": "badge",
|
||||
"description": "Registered Users Self Access",
|
||||
"admin_access": 0,
|
||||
"app_access": 0,
|
||||
"enforce_tfa": 0,
|
||||
"ip_access": null
|
||||
}
|
||||
]
|
||||
30
directus/reference/exports/roles.json
Normal file
30
directus/reference/exports/roles.json
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
[
|
||||
{
|
||||
"id": "a6d3c757-a96b-4f0c-b96b-a6109d60a7ba",
|
||||
"name": "Administrator",
|
||||
"icon": "verified",
|
||||
"description": "$t:admin_description",
|
||||
"parent": null
|
||||
},
|
||||
{
|
||||
"id": "00e518e3-75b3-4c6f-b57d-51a4f1af4781",
|
||||
"name": "Registration Bot",
|
||||
"icon": "robot",
|
||||
"description": "Registers accounts.",
|
||||
"parent": null
|
||||
},
|
||||
{
|
||||
"id": "a2a695be-88f9-4948-b2a4-e0e22b01b32e",
|
||||
"name": "Supporter Bot",
|
||||
"icon": "supervised_user_circle",
|
||||
"description": "Registers supporters.",
|
||||
"parent": null
|
||||
},
|
||||
{
|
||||
"id": "296a28bc-60ab-4251-8bef-27f6dfb67948",
|
||||
"name": "Users",
|
||||
"icon": "accessibility_new",
|
||||
"description": "Registered users.",
|
||||
"parent": null
|
||||
}
|
||||
]
|
||||
11
directus/reference/exports/settings.json
Normal file
11
directus/reference/exports/settings.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[
|
||||
{
|
||||
"project_name": "Directus",
|
||||
"project_url": null,
|
||||
"default_language": "en-US",
|
||||
"public_registration": 0,
|
||||
"public_registration_verify_email": 0,
|
||||
"public_registration_role": null,
|
||||
"public_registration_email_filter": null
|
||||
}
|
||||
]
|
||||
33
directus/reference/nginx-summary.txt
Normal file
33
directus/reference/nginx-summary.txt
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
3: listen 80;
|
||||
4: server_name forms.lasereverything.net;
|
||||
5: client_max_body_size 100M;
|
||||
8: location /.well-known/acme-challenge/ {
|
||||
13: location / {
|
||||
20: listen 443 ssl;
|
||||
22: server_name forms.lasereverything.net;
|
||||
23: client_max_body_size 100M;
|
||||
28: ssl_protocols TLSv1.2 TLSv1.3;
|
||||
31: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
33: location / {
|
||||
34: proxy_pass http://127.0.0.1:8056;
|
||||
37: proxy_set_header Host $host;
|
||||
38: proxy_set_header X-Real-IP $remote_addr;
|
||||
39: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
40: proxy_set_header X-Forwarded-Proto $scheme;
|
||||
43: proxy_set_header Upgrade $http_upgrade;
|
||||
44: proxy_set_header Connection "upgrade";
|
||||
46: proxy_read_timeout 300;
|
||||
48: proxy_send_timeout 300;
|
||||
57: add_header Access-Control-Allow-Origin "$http_origin" always;
|
||||
58: add_header Access-Control-Allow-Credentials "true" always;
|
||||
59: add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With" always;
|
||||
60: add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||||
61: add_header Vary "Origin" always;
|
||||
66: add_header Access-Control-Max-Age 1728000 always;
|
||||
67: add_header Content-Type "text/plain; charset=utf-8" always;
|
||||
68: add_header Content-Length 0 always;
|
||||
71: add_header Access-Control-Allow-Origin "$http_origin" always;
|
||||
72: add_header Access-Control-Allow-Credentials "true" always;
|
||||
73: add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With" always;
|
||||
74: add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||||
75: add_header Vary "Origin" always;
|
||||
8
directus/reference/schema-comparison.txt
Normal file
8
directus/reference/schema-comparison.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
Live schema SHA-256:
|
||||
709c5f5fa6c239406085e8c7e5959d2d096d36ad1d8676727a0ca473536498e6 /root/directus-codex-context-20260710-102253/schema.yaml
|
||||
|
||||
Existing repository schema SHA-256:
|
||||
8e21cd555b297e729a39f387eadbf4d32621c986516c9d3cc896b01d02efdf8b /srv/codex-work/lasereverything.net.db/app/schema.yaml
|
||||
|
||||
Comparison:
|
||||
The live and repository schema files differ.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
id char(36) NO PRI NULL
|
||||
role char(36) YES MUL NULL
|
||||
user char(36) YES MUL NULL
|
||||
policy char(36) NO MUL NULL
|
||||
sort int(11) YES NULL
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
id int(10) unsigned NO PRI NULL auto_increment
|
||||
collection varchar(64) NO MUL NULL
|
||||
field varchar(64) NO NULL
|
||||
special varchar(64) YES NULL
|
||||
interface varchar(64) YES NULL
|
||||
options longtext YES NULL
|
||||
display varchar(64) YES NULL
|
||||
display_options longtext YES NULL
|
||||
readonly tinyint(1) NO 0
|
||||
hidden tinyint(1) NO 0
|
||||
sort int(10) unsigned YES NULL
|
||||
width varchar(30) YES full
|
||||
translations longtext YES NULL
|
||||
note text YES NULL
|
||||
conditions longtext YES NULL
|
||||
required tinyint(1) YES 0
|
||||
group varchar(64) YES NULL
|
||||
validation longtext YES NULL
|
||||
validation_message text YES NULL
|
||||
searchable tinyint(1) NO 1
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
id char(36) NO PRI NULL
|
||||
name varchar(255) NO NULL
|
||||
icon varchar(64) YES NULL
|
||||
color varchar(255) YES NULL
|
||||
description text YES NULL
|
||||
status varchar(255) NO active
|
||||
trigger varchar(255) YES NULL
|
||||
accountability varchar(255) YES all
|
||||
options longtext YES NULL
|
||||
operation char(36) YES UNI NULL
|
||||
date_created timestamp YES current_timestamp()
|
||||
user_created char(36) YES MUL NULL
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
id char(36) NO PRI NULL
|
||||
name varchar(255) YES NULL
|
||||
key varchar(255) NO NULL
|
||||
type varchar(255) NO NULL
|
||||
position_x int(11) NO NULL
|
||||
position_y int(11) NO NULL
|
||||
options longtext YES NULL
|
||||
resolve char(36) YES UNI NULL
|
||||
reject char(36) YES UNI NULL
|
||||
flow char(36) NO MUL NULL
|
||||
date_created timestamp YES current_timestamp()
|
||||
user_created char(36) YES MUL NULL
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
id int(10) unsigned NO PRI NULL auto_increment
|
||||
collection varchar(64) NO MUL NULL
|
||||
action varchar(10) NO NULL
|
||||
permissions longtext YES NULL
|
||||
validation longtext YES NULL
|
||||
presets longtext YES NULL
|
||||
fields text YES NULL
|
||||
policy char(36) NO MUL NULL
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
id char(36) NO PRI NULL
|
||||
name varchar(100) NO NULL
|
||||
icon varchar(64) NO badge
|
||||
description text YES NULL
|
||||
ip_access text YES NULL
|
||||
enforce_tfa tinyint(1) NO 0
|
||||
admin_access tinyint(1) NO 0
|
||||
app_access tinyint(1) NO 0
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
id char(36) NO PRI NULL
|
||||
name varchar(100) NO NULL
|
||||
icon varchar(64) NO supervised_user_circle
|
||||
description text YES NULL
|
||||
parent char(36) YES MUL NULL
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
id int(10) unsigned NO PRI NULL auto_increment
|
||||
project_name varchar(100) NO Directus
|
||||
project_url varchar(255) YES NULL
|
||||
project_color varchar(255) NO #6644FF
|
||||
project_logo char(36) YES MUL NULL
|
||||
public_foreground char(36) YES MUL NULL
|
||||
public_background char(36) YES MUL NULL
|
||||
public_note text YES NULL
|
||||
auth_login_attempts int(10) unsigned YES 25
|
||||
auth_password_policy varchar(100) YES NULL
|
||||
storage_asset_transform varchar(7) YES all
|
||||
storage_asset_presets longtext YES NULL
|
||||
custom_css text YES NULL
|
||||
storage_default_folder char(36) YES MUL NULL
|
||||
basemaps longtext YES NULL
|
||||
mapbox_key varchar(255) YES NULL
|
||||
module_bar longtext YES NULL
|
||||
project_descriptor varchar(100) YES NULL
|
||||
default_language varchar(255) NO en-US
|
||||
custom_aspect_ratios longtext YES NULL
|
||||
public_favicon char(36) YES MUL NULL
|
||||
default_appearance varchar(255) NO auto
|
||||
default_theme_light varchar(255) YES NULL
|
||||
theme_light_overrides longtext YES NULL
|
||||
default_theme_dark varchar(255) YES NULL
|
||||
theme_dark_overrides longtext YES NULL
|
||||
report_error_url varchar(255) YES NULL
|
||||
report_bug_url varchar(255) YES NULL
|
||||
report_feature_url varchar(255) YES NULL
|
||||
public_registration tinyint(1) NO 0
|
||||
public_registration_verify_email tinyint(1) NO 1
|
||||
public_registration_role char(36) YES MUL NULL
|
||||
public_registration_email_filter longtext YES NULL
|
||||
visual_editor_urls longtext YES NULL
|
||||
project_id char(36) YES NULL
|
||||
mcp_enabled tinyint(1) NO 0
|
||||
mcp_allow_deletes tinyint(1) NO 0
|
||||
mcp_prompts_collection varchar(255) YES NULL
|
||||
mcp_system_prompt_enabled tinyint(1) NO 1
|
||||
mcp_system_prompt text YES NULL
|
||||
project_owner varchar(255) YES NULL
|
||||
project_usage varchar(255) YES NULL
|
||||
org_name varchar(255) YES NULL
|
||||
product_updates tinyint(1) YES NULL
|
||||
project_status varchar(255) YES NULL
|
||||
ai_openai_api_key text YES NULL
|
||||
ai_anthropic_api_key text YES NULL
|
||||
ai_system_prompt text YES NULL
|
||||
ai_google_api_key text YES NULL
|
||||
ai_openai_compatible_api_key text YES NULL
|
||||
ai_openai_compatible_base_url text YES NULL
|
||||
ai_openai_compatible_name text YES NULL
|
||||
ai_openai_compatible_models longtext YES NULL
|
||||
ai_openai_compatible_headers longtext YES NULL
|
||||
ai_openai_allowed_models longtext YES NULL
|
||||
ai_anthropic_allowed_models longtext YES NULL
|
||||
ai_google_allowed_models longtext YES NULL
|
||||
collaborative_editing_enabled tinyint(1) NO 0
|
||||
ai_translation_default_model text YES NULL
|
||||
ai_translation_glossary longtext YES NULL
|
||||
ai_translation_style_guide text YES NULL
|
||||
license_key varchar(255) YES NULL
|
||||
license_token text YES NULL
|
||||
mcp_oauth_enabled tinyint(1) NO 0
|
||||
mcp_oauth_dcr_enabled tinyint(1) NO 0
|
||||
mcp_oauth_cimd_enabled tinyint(1) NO 0
|
||||
|
55
directus/schema-differences.md
Normal file
55
directus/schema-differences.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# Production versus older schema snapshot
|
||||
|
||||
Compared files:
|
||||
|
||||
- Current production evidence: `directus/schema.yaml`, Directus 12.1.1, MySQL.
|
||||
- Older repository snapshot: `app/schema.yaml`, Directus 11.12.0, MySQL.
|
||||
|
||||
The supplied hashes differ. A structural YAML comparison found 36 versus 34 collection entries, 394 versus 379 field entries, and 138 versus 136 relation entries. Neither snapshot was modified.
|
||||
|
||||
## Meaningful additions
|
||||
|
||||
### `directus_users` collection metadata
|
||||
|
||||
The current snapshot includes `directus_users` as a collection with display template `{{ username }} ({{ email }})`. The older snapshot contains the custom `directus_users.username` field and relations to the system collection but omits the collection entry itself.
|
||||
|
||||
The username field remains a unique nullable `varchar(255)` at the database layer while Directus metadata marks it required, visible, editable, and searchable. The older snapshot has the same database and required-field characteristics; current metadata additionally contains the Directus 12 `searchable` property.
|
||||
|
||||
### `user_memberships`
|
||||
|
||||
Production adds the `user_memberships` collection with 15 fields:
|
||||
|
||||
- Integer auto-increment primary key: `id`.
|
||||
- Provider/account data: `provider`, `external_user_id`, `email`, `username`, `status`, `tier`.
|
||||
- Timing: `started_at`, `renews_at`, `last_event_at`.
|
||||
- Claim/support data: `claim_token`, `claim_expires_at`, `raw`.
|
||||
- User relations: `app_user`, `claim_user_id`.
|
||||
|
||||
`app_user` and `claim_user_id` are nullable indexed M2O foreign keys to `directus_users.id`; both use `SET NULL` on delete and `RESTRICT` on update. The schema does not mark `claim_token` unique. Provider choices are Ko-Fi, Patreon, and LMA/Mighty; status choices are active, canceled, refunded, and one-time. These are metadata choices, not database constraints.
|
||||
|
||||
### Settings raster metadata
|
||||
|
||||
The `settings_co2gal.raster_settings` repeater gained three nested metadata fields: `angle` (integer), `auto` (boolean), and `increment` (integer). These are changes inside the JSON/repeater field definition, not new top-level database columns.
|
||||
|
||||
### Display metadata
|
||||
|
||||
- `settings_co2gal.owner` changed from no display renderer to related-values with template `{{username}}`.
|
||||
- `bg_entries.scores` changed its display formatting template from `{{ cat }}` to `{{ body }}`.
|
||||
|
||||
## Version-generated metadata differences
|
||||
|
||||
Most raw differences are serialization changes rather than business-schema changes:
|
||||
|
||||
- All 34 pre-existing collection entries gained `meta.autosave_revision_interval: null` and `meta.status: active`.
|
||||
- All 379 pre-existing field entries gained `meta.searchable: true`.
|
||||
|
||||
These additions are consistent across the snapshots and correspond to newer snapshot metadata emitted by Directus 12. They do not by themselves show database-column changes.
|
||||
|
||||
## Unchanged structural areas
|
||||
|
||||
- No older collection, field, or relation key was removed.
|
||||
- All 136 relations present in the older snapshot compare equal structurally.
|
||||
- The two added relations are the new `user_memberships` links described above.
|
||||
- Database vendor remains MySQL and snapshot format version remains 1.
|
||||
|
||||
This comparison is structural and does not compare data, permissions, policies, flows, settings-table values, indexes not represented by the snapshot, extension behavior, or migration history.
|
||||
22571
directus/schema.yaml
Normal file
22571
directus/schema.yaml
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue