fix: Move more directories

This commit is contained in:
Wanjohi
2025-09-06 16:50:44 +03:00
parent 1c1c73910b
commit 9818165a90
248 changed files with 9 additions and 9566 deletions

View File

@@ -0,0 +1,186 @@
import { z } from "zod";
import { Common } from "../common";
import { createEvent } from "../event";
import { Polar } from "../polar/index";
import { createID, fn } from "../utils";
import { userTable } from "./user.sql";
import { Examples } from "../examples";
import { and, eq, isNull, asc } from "drizzle-orm";
import { ErrorCodes, VisibleError } from "../error";
import { createTransaction, useTransaction } from "../drizzle/transaction";
export namespace User {
export const Info = z
.object({
id: z.string().openapi({
description: Common.IdDescription,
example: Examples.User.id,
}),
name: z.string().regex(/^[a-zA-Z ]{1,32}$/, "Use a friendly name.").openapi({
description: "The name of this account",
example: Examples.User.name
}),
polarCustomerID: z.string().nullable().openapi({
description: "Associated Polar.sh customer identifier",
example: Examples.User.polarCustomerID,
}),
avatarUrl: z.string().url().nullable().openapi({
description: "The url to the profile picture",
example: Examples.User.avatarUrl
}),
email: z.string().openapi({
description: "Primary email address for user notifications and authentication",
example: Examples.User.email,
}),
lastLogin: z.date().openapi({
description: "Timestamp of user's most recent authentication",
example: Examples.User.lastLogin
})
})
.openapi({
ref: "User",
description: "User account entity with core identification and authentication details",
example: Examples.User,
});
export type Info = z.infer<typeof Info>;
export class UserExistsError extends VisibleError {
constructor(username: string) {
super(
"already_exists",
ErrorCodes.Validation.ALREADY_EXISTS,
`A user with this email ${username} already exists`
);
}
}
export const Events = {
Created: createEvent(
"user.created",
z.object({
userID: Info.shape.id,
}),
),
};
export const create = fn(
Info
.omit({
lastLogin: true,
polarCustomerID: true,
}).partial({
avatarUrl: true,
id: true
}),
async (input) => {
const userID = createID("user")
const customer = await Polar.fromUserEmail(input.email)
const id = input.id ?? userID;
await createTransaction(async (tx) => {
const result = await tx
.insert(userTable)
.values({
id,
avatarUrl: input.avatarUrl,
email: input.email,
name: input.name,
polarCustomerID: customer?.id,
lastLogin: Common.utc()
})
.onConflictDoNothing({
target: [userTable.email]
})
if (result.count === 0) {
throw new UserExistsError(input.email)
}
})
return id;
})
export const fromEmail = fn(
Info.shape.email.min(1),
async (email) =>
useTransaction(async (tx) =>
tx
.select()
.from(userTable)
.where(
and(
eq(userTable.email, email),
isNull(userTable.timeDeleted)
)
)
.orderBy(asc(userTable.timeCreated))
.execute()
.then(rows => rows.map(serialize).at(0))
)
)
export const fromID = fn(
Info.shape.id.min(1),
(id) =>
useTransaction(async (tx) =>
tx
.select()
.from(userTable)
.where(
and(
eq(userTable.id, id),
isNull(userTable.timeDeleted)
)
)
.orderBy(asc(userTable.timeCreated))
.execute()
.then(rows => rows.map(serialize).at(0))
),
)
export const remove = fn(
Info.shape.id.min(1),
(id) =>
useTransaction(async (tx) => {
await tx
.update(userTable)
.set({
timeDeleted: Common.utc(),
})
.where(and(eq(userTable.id, id)))
.execute();
return id;
}),
);
export const acknowledgeLogin = fn(
Info.shape.id,
(id) =>
useTransaction(async (tx) =>
tx
.update(userTable)
.set({
lastLogin: Common.utc(),
})
.where(and(eq(userTable.id, id)))
.execute()
),
)
export function serialize(
input: typeof userTable.$inferSelect
): z.infer<typeof Info> {
return {
id: input.id,
name: input.name,
email: input.email,
avatarUrl: input.avatarUrl,
lastLogin: input.lastLogin,
polarCustomerID: input.polarCustomerID,
}
}
}

View File

@@ -0,0 +1,18 @@
import { id, timestamps, utc } from "../drizzle/types";
import { pgTable, text, unique, varchar } from "drizzle-orm/pg-core";
export const userTable = pgTable(
"users",
{
...id,
...timestamps,
email: varchar("email", { length: 255 }).notNull(),
avatarUrl: text("avatar_url"),
lastLogin: utc("last_login").notNull(),
name: varchar("name", { length: 255 }).notNull(),
polarCustomerID: varchar("polar_customer_id", { length: 255 }),
},
(user) => [
unique("idx_user_email").on(user.email),
]
);