Template
1
0

feat: modular domain driven boilerplate

This commit is contained in:
2025-09-22 01:29:55 +02:00
parent 2433f59d1a
commit 9be3230c84
160 changed files with 2468 additions and 1525 deletions

View File

@@ -1,7 +0,0 @@
import { ConflictError } from "@platform/relay";
export class AccountEmailClaimedError extends ConflictError {
constructor(email: string) {
super(`Email '${email}' is already claimed by another account.`);
}
}

View File

@@ -1,5 +0,0 @@
import z from "zod";
export const RoleSchema = z.union([z.literal("user"), z.literal("admin")]);
export type Role = z.infer<typeof RoleSchema>;

View File

@@ -1,30 +0,0 @@
import { AccountSchema } from "@platform/models/account.ts";
import { NameSchema } from "@platform/models/value-objects/name.ts";
import { ForbiddenError, NotFoundError, route, UnauthorizedError } from "@platform/relay";
import z from "zod";
import { AccountEmailClaimedError } from "./errors.ts";
export const create = route
.post("/api/v1/accounts")
.body(
z.object({
name: NameSchema,
email: z.email(),
}),
)
.errors([AccountEmailClaimedError])
.response(z.uuid());
export const getById = route
.get("/api/v1/accounts/:id")
.params({
id: z.string(),
})
.errors([UnauthorizedError, ForbiddenError, NotFoundError])
.response(AccountSchema);
export const routes = {
create,
getById,
};

View File

@@ -1,33 +0,0 @@
import z from "zod";
const EmailStrategySchema = z.object({
type: z.literal("email"),
value: z.string(),
});
const PasswordStrategySchema = z.object({
type: z.literal("password"),
alias: z.string(),
password: z.string(),
});
const PasskeyStrategySchema = z.object({
type: z.literal("passkey"),
credId: z.string(),
credPublicKey: z.string(),
webauthnUserId: z.string(),
counter: z.number(),
backupEligible: z.boolean(),
backupStatus: z.boolean(),
transports: z.string(),
createdAt: z.date(),
lastUsed: z.date(),
});
export const StrategySchema = z.discriminatedUnion("type", [
EmailStrategySchema,
PasswordStrategySchema,
PasskeyStrategySchema,
]);
export type Strategy = z.infer<typeof StrategySchema>;

View File

@@ -0,0 +1,17 @@
import z from "zod";
import { AuditUserSchema, AuditUserType } from "./user.ts";
export const AuditActorSchema = z.object({
user: AuditUserSchema,
});
export const auditors = {
system: AuditActorSchema.parse({
user: {
typeId: AuditUserType.System,
},
}),
};
export type AuditActor = z.infer<typeof AuditActorSchema>;

View File

@@ -0,0 +1,17 @@
import z from "zod";
export enum AuditUserType {
Unknown = 0,
Identity = 1,
System = 2,
Service = 3,
Other = 99,
}
export const AuditUserSchema = z.object({
typeId: z.enum(AuditUserType).describe("The account type identifier."),
uid: z
.string()
.optional()
.describe("The unique user identifier. For example, the Windows user SID, ActiveDirectory DN or AWS user ARN."),
});

View File

@@ -1,40 +0,0 @@
import { AccountSchema } from "@platform/models/account.ts";
import { route, UnauthorizedError } from "@platform/relay";
import z from "zod";
export * from "./errors.ts";
export * from "./strategies.ts";
export const email = route.post("/api/v1/auth/email").body(
z.object({
base: z.url(),
email: z.email(),
}),
);
export const password = route.post("/api/v1/auth/password").body(
z.object({
alias: z.string(),
password: z.string(),
}),
);
export const code = route
.get("/api/v1/auth/code/:accountId/code/:codeId/:value")
.params({
accountId: z.string(),
codeId: z.string(),
value: z.string(),
})
.query({
next: z.string().optional(),
});
export const session = route.get("/api/v1/auth/session").response(AccountSchema).errors([UnauthorizedError]);
export const routes = {
email,
password,
code,
session,
};

View File

@@ -6,6 +6,6 @@
"dependencies": {
"@platform/models": "workspace:*",
"@platform/relay": "workspace:*",
"zod": "4"
"zod": "4.1.11"
}
}