First Commit. Duplicate bunext-sqlite preset
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { SiteData } from "@/src/data/site-data";
|
||||
|
||||
/**
|
||||
* Global company letterhead defaults.
|
||||
* These prefill sender blocks on new letter/email templates
|
||||
* and appear in the branded footer of generated PDFs.
|
||||
* Per-template fields override these.
|
||||
*
|
||||
* TODO: Replace the placeholder contact details below with the
|
||||
* official company registered address, phone and RC number.
|
||||
*/
|
||||
export const CompanyLetterheadDefaults = {
|
||||
name: SiteData["SiteName"],
|
||||
address_lines: [
|
||||
"Your Company Limited",
|
||||
"No. 1 Example Street, Example City",
|
||||
],
|
||||
phone: "+000 000 000 0000",
|
||||
email: "[email protected]",
|
||||
website: SiteData["SiteURL"],
|
||||
rc_number: "",
|
||||
bank_name: "",
|
||||
ngn_acc_number: "",
|
||||
} as const;
|
||||
|
||||
export type CompanyLetterhead = {
|
||||
name?: string;
|
||||
address_lines?: readonly string[];
|
||||
phone?: string;
|
||||
email?: string;
|
||||
website?: string;
|
||||
rc_number?: string;
|
||||
bank_name?: string;
|
||||
ngn_acc_number?: string;
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
export const EmailStatuses = [
|
||||
{
|
||||
title: "Draft",
|
||||
value: "draft",
|
||||
},
|
||||
{
|
||||
title: "Sent",
|
||||
value: "sent",
|
||||
},
|
||||
] as const;
|
||||
|
||||
export type EmailStatus = (typeof EmailStatuses)[number]["value"];
|
||||
|
||||
export const EmailAttachmentTypes = [
|
||||
{
|
||||
title: "Letter",
|
||||
value: "letter",
|
||||
},
|
||||
{
|
||||
title: "Invoice",
|
||||
value: "invoice",
|
||||
},
|
||||
{
|
||||
title: "Quote",
|
||||
value: "quote",
|
||||
},
|
||||
{
|
||||
title: "File",
|
||||
value: "file",
|
||||
},
|
||||
] as const;
|
||||
|
||||
export type EmailAttachmentType = (typeof EmailAttachmentTypes)[number]["value"];
|
||||
|
||||
export const InvoiceStatuses = [
|
||||
{
|
||||
title: "Draft",
|
||||
value: "draft",
|
||||
},
|
||||
{
|
||||
title: "Issued",
|
||||
value: "issued",
|
||||
},
|
||||
{
|
||||
title: "Paid",
|
||||
value: "paid",
|
||||
},
|
||||
{
|
||||
title: "Cancelled",
|
||||
value: "cancelled",
|
||||
},
|
||||
] as const;
|
||||
|
||||
export type InvoiceStatus = (typeof InvoiceStatuses)[number]["value"];
|
||||
|
||||
export const QuoteStatuses = [
|
||||
{
|
||||
title: "Draft",
|
||||
value: "draft",
|
||||
},
|
||||
{
|
||||
title: "Sent",
|
||||
value: "sent",
|
||||
},
|
||||
{
|
||||
title: "Accepted",
|
||||
value: "accepted",
|
||||
},
|
||||
{
|
||||
title: "Declined",
|
||||
value: "declined",
|
||||
},
|
||||
{
|
||||
title: "Expired",
|
||||
value: "expired",
|
||||
},
|
||||
] as const;
|
||||
|
||||
export type QuoteStatus = (typeof QuoteStatuses)[number]["value"];
|
||||
@@ -0,0 +1,22 @@
|
||||
export const LogCategories = [
|
||||
{
|
||||
title: "Auth",
|
||||
value: "auth",
|
||||
},
|
||||
{
|
||||
title: "WebSocket",
|
||||
value: "websocket",
|
||||
},
|
||||
{
|
||||
title: "Cron",
|
||||
value: "cron",
|
||||
},
|
||||
{
|
||||
title: "System",
|
||||
value: "system",
|
||||
},
|
||||
{
|
||||
title: "Error",
|
||||
value: "error",
|
||||
},
|
||||
] as const;
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { TwuiHeaderLink } from "@moduletrace/twui/elements/HeaderNav";
|
||||
|
||||
export const MainLinks: TwuiHeaderLink[] = [
|
||||
{
|
||||
title: "Features",
|
||||
url: "/#features",
|
||||
},
|
||||
{
|
||||
title: "About",
|
||||
url: "/about",
|
||||
},
|
||||
{
|
||||
title: "Contact",
|
||||
url: "/contact",
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,29 @@
|
||||
export const MediaTypes = [
|
||||
{
|
||||
title: "Image",
|
||||
value: "image",
|
||||
},
|
||||
{
|
||||
title: "Video",
|
||||
value: "video",
|
||||
},
|
||||
{
|
||||
title: "Document",
|
||||
value: "document",
|
||||
},
|
||||
] as const;
|
||||
|
||||
export const MediaParadigms = [
|
||||
{
|
||||
title: "User Profile Image",
|
||||
value: "user-profile-image",
|
||||
},
|
||||
{
|
||||
title: "Generic",
|
||||
value: "generic",
|
||||
},
|
||||
{
|
||||
title: "Event",
|
||||
value: "event",
|
||||
},
|
||||
] as const;
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { TableType } from "@/src/types";
|
||||
|
||||
/**
|
||||
* Tables holding secrets (password hashes, pending login codes). They are
|
||||
* never reachable through the admin CRUD API, not even by super admins.
|
||||
*/
|
||||
export const PrivateTables: TableType[] = ["user_passwords", "sso_login_codes"];
|
||||
@@ -0,0 +1,75 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import {
|
||||
RolesAdminCantUpdate,
|
||||
SuperAdminOnlyEditableUserTypes,
|
||||
UneditableUserTypes,
|
||||
UserTypes,
|
||||
UserTypeAssignmentMatrix,
|
||||
UserTypeDeletionMatrix,
|
||||
} from "./user-types-dict";
|
||||
|
||||
describe("UserTypes dict", () => {
|
||||
test("has unique values", () => {
|
||||
const values = UserTypes.map((ut) => ut.value);
|
||||
expect(new Set(values).size).toBe(values.length);
|
||||
});
|
||||
|
||||
test("contains required core types", () => {
|
||||
const values = new Set(UserTypes.map((ut) => ut.value));
|
||||
const required = [
|
||||
"super_admin",
|
||||
"admin",
|
||||
"revoked",
|
||||
"read_only",
|
||||
] as const;
|
||||
for (const type of required) {
|
||||
expect(values.has(type)).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
test("super_admin is uneditable and excluded from admin updates", () => {
|
||||
expect(UneditableUserTypes).toContain("super_admin");
|
||||
expect(RolesAdminCantUpdate).toContain("super_admin");
|
||||
expect(SuperAdminOnlyEditableUserTypes).toContain("revoked");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Assignment matrices", () => {
|
||||
test("cover every user type", () => {
|
||||
for (const ut of UserTypes) {
|
||||
expect(UserTypeAssignmentMatrix[ut.value]).toBeDefined();
|
||||
expect(UserTypeDeletionMatrix[ut.value]).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
test("super_admin is only assignable by pass-through actors", () => {
|
||||
expect(
|
||||
UserTypeAssignmentMatrix["super_admin"].allowed_actors,
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
test("elevated types are pass-through only", () => {
|
||||
const elevated = [
|
||||
"admin",
|
||||
"president",
|
||||
"vice_president",
|
||||
"secretary",
|
||||
] as const;
|
||||
for (const type of elevated) {
|
||||
const entry = UserTypeAssignmentMatrix[type];
|
||||
expect(Array.isArray(entry.allowed_actors)).toBe(true);
|
||||
expect((entry.allowed_actors as string[]).length).toBe(0);
|
||||
}
|
||||
});
|
||||
|
||||
test("admin can manage auditor but not another admin", () => {
|
||||
expect(
|
||||
UserTypeAssignmentMatrix["auditor"].allowed_actors,
|
||||
).toContain("admin");
|
||||
|
||||
expect(
|
||||
(UserTypeAssignmentMatrix["admin"].allowed_actors as string[])
|
||||
.length,
|
||||
).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,134 @@
|
||||
import type { UserType, UserTypeAssignmentMatrixType } from "../types";
|
||||
|
||||
export const UserTypes = [
|
||||
{
|
||||
value: "super_admin",
|
||||
},
|
||||
{
|
||||
value: "admin",
|
||||
},
|
||||
{
|
||||
value: "auditor",
|
||||
},
|
||||
{
|
||||
value: "president",
|
||||
},
|
||||
{
|
||||
value: "vice_president",
|
||||
},
|
||||
{
|
||||
value: "secretary",
|
||||
},
|
||||
{
|
||||
value: "revoked",
|
||||
},
|
||||
{
|
||||
value: "read_only",
|
||||
},
|
||||
] as const;
|
||||
|
||||
export const SuperAdminOnlyEditableUserTypes: UserType[] = ["revoked"];
|
||||
|
||||
export const UneditableUserTypes: UserType[] = ["super_admin"];
|
||||
|
||||
export const RolesAdminCantUpdate: UserType[] = [
|
||||
"super_admin",
|
||||
"admin",
|
||||
"revoked",
|
||||
];
|
||||
|
||||
export const UserManagementUserTypes: UserType[] = ["admin"];
|
||||
|
||||
/**
|
||||
* Declarative ACL for granting user types.
|
||||
*
|
||||
* Matrix forms:
|
||||
* - `allowed_actors` as an array lists actor types allowed to perform the action.
|
||||
* - `allowed_actors` as a per-actor matrix keys actors to rules:
|
||||
* - `allowed_targets`: the target user must hold at least one of these types.
|
||||
* - `excluded_targets`: the target user must not hold any of these types.
|
||||
* - Entry-level `excluded_targets` blocks the action when the target user
|
||||
* holds any listed type.
|
||||
* - Empty/missing `allowed_actors` means only pass-through actors
|
||||
* (`super_admin`) may perform the action.
|
||||
* - `same_as` clones the rules of another actor type.
|
||||
*/
|
||||
export const UserTypeAssignmentMatrix: UserTypeAssignmentMatrixType = {
|
||||
super_admin: {},
|
||||
admin: {
|
||||
allowed_actors: [],
|
||||
},
|
||||
president: {
|
||||
allowed_actors: [],
|
||||
},
|
||||
vice_president: {
|
||||
allowed_actors: [],
|
||||
},
|
||||
secretary: {
|
||||
allowed_actors: [],
|
||||
},
|
||||
auditor: {
|
||||
allowed_actors: ["admin"],
|
||||
},
|
||||
read_only: {
|
||||
allowed_actors: {
|
||||
admin: {
|
||||
excluded_targets: ["super_admin"],
|
||||
},
|
||||
},
|
||||
},
|
||||
revoked: {
|
||||
allowed_actors: {
|
||||
admin: {
|
||||
allowed_targets: ["read_only"],
|
||||
excluded_targets: [
|
||||
"super_admin",
|
||||
"admin",
|
||||
"president",
|
||||
"vice_president",
|
||||
"secretary",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const UserTypeDeletionMatrix: UserTypeAssignmentMatrixType = {
|
||||
super_admin: {},
|
||||
admin: {
|
||||
allowed_actors: [],
|
||||
},
|
||||
president: {
|
||||
allowed_actors: [],
|
||||
},
|
||||
vice_president: {
|
||||
allowed_actors: [],
|
||||
},
|
||||
secretary: {
|
||||
allowed_actors: [],
|
||||
},
|
||||
auditor: {
|
||||
allowed_actors: ["admin"],
|
||||
},
|
||||
read_only: {
|
||||
allowed_actors: {
|
||||
admin: {
|
||||
excluded_targets: ["super_admin"],
|
||||
},
|
||||
},
|
||||
},
|
||||
revoked: {
|
||||
allowed_actors: {
|
||||
admin: {
|
||||
allowed_targets: ["read_only"],
|
||||
excluded_targets: [
|
||||
"super_admin",
|
||||
"admin",
|
||||
"president",
|
||||
"vice_president",
|
||||
"secretary",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
export const WebsocketRooms = [
|
||||
{
|
||||
title: "Public",
|
||||
value: "public",
|
||||
description: `Public Room for All`,
|
||||
},
|
||||
] as const;
|
||||
Reference in New Issue
Block a user