diff options
Diffstat (limited to 'pkgs/server/src/repositories')
| -rw-r--r-- | pkgs/server/src/repositories/index.ts | 3 | ||||
| -rw-r--r-- | pkgs/server/src/repositories/refresh-token.ts | 35 | ||||
| -rw-r--r-- | pkgs/server/src/repositories/types.ts | 46 | ||||
| -rw-r--r-- | pkgs/server/src/repositories/user.ts | 55 |
4 files changed, 139 insertions, 0 deletions
diff --git a/pkgs/server/src/repositories/index.ts b/pkgs/server/src/repositories/index.ts new file mode 100644 index 0000000..f1bcfb1 --- /dev/null +++ b/pkgs/server/src/repositories/index.ts @@ -0,0 +1,3 @@ +export { refreshTokenRepository } from "./refresh-token"; +export * from "./types"; +export { userRepository } from "./user"; diff --git a/pkgs/server/src/repositories/refresh-token.ts b/pkgs/server/src/repositories/refresh-token.ts new file mode 100644 index 0000000..82302df --- /dev/null +++ b/pkgs/server/src/repositories/refresh-token.ts @@ -0,0 +1,35 @@ +import { and, eq, gt } from "drizzle-orm"; +import { db, refreshTokens } from "../db"; +import type { RefreshTokenRepository } from "./types"; + +export const refreshTokenRepository: RefreshTokenRepository = { + async findValidToken(tokenHash) { + const [token] = await db + .select({ + id: refreshTokens.id, + userId: refreshTokens.userId, + expiresAt: refreshTokens.expiresAt, + }) + .from(refreshTokens) + .where( + and( + eq(refreshTokens.tokenHash, tokenHash), + gt(refreshTokens.expiresAt, new Date()), + ), + ) + .limit(1); + return token; + }, + + async create(data) { + await db.insert(refreshTokens).values({ + userId: data.userId, + tokenHash: data.tokenHash, + expiresAt: data.expiresAt, + }); + }, + + async deleteById(id) { + await db.delete(refreshTokens).where(eq(refreshTokens.id, id)); + }, +}; diff --git a/pkgs/server/src/repositories/types.ts b/pkgs/server/src/repositories/types.ts new file mode 100644 index 0000000..1ab4bdc --- /dev/null +++ b/pkgs/server/src/repositories/types.ts @@ -0,0 +1,46 @@ +/** + * Repository types for abstracting database operations + */ + +export interface User { + id: string; + username: string; + passwordHash: string; + createdAt: Date; + updatedAt: Date; +} + +export interface UserPublic { + id: string; + username: string; + createdAt: Date; +} + +export interface RefreshToken { + id: string; + userId: string; + tokenHash: string; + expiresAt: Date; + createdAt: Date; +} + +export interface UserRepository { + findByUsername( + username: string, + ): Promise<Pick<User, "id" | "username" | "passwordHash"> | undefined>; + existsByUsername(username: string): Promise<boolean>; + create(data: { username: string; passwordHash: string }): Promise<UserPublic>; + findById(id: string): Promise<Pick<User, "id" | "username"> | undefined>; +} + +export interface RefreshTokenRepository { + findValidToken( + tokenHash: string, + ): Promise<Pick<RefreshToken, "id" | "userId" | "expiresAt"> | undefined>; + create(data: { + userId: string; + tokenHash: string; + expiresAt: Date; + }): Promise<void>; + deleteById(id: string): Promise<void>; +} diff --git a/pkgs/server/src/repositories/user.ts b/pkgs/server/src/repositories/user.ts new file mode 100644 index 0000000..7917632 --- /dev/null +++ b/pkgs/server/src/repositories/user.ts @@ -0,0 +1,55 @@ +import { eq } from "drizzle-orm"; +import { db, users } from "../db"; +import type { UserPublic, UserRepository } from "./types"; + +export const userRepository: UserRepository = { + async findByUsername(username) { + const [user] = await db + .select({ + id: users.id, + username: users.username, + passwordHash: users.passwordHash, + }) + .from(users) + .where(eq(users.username, username)) + .limit(1); + return user; + }, + + async existsByUsername(username) { + const [user] = await db + .select({ id: users.id }) + .from(users) + .where(eq(users.username, username)) + .limit(1); + return user !== undefined; + }, + + async create(data): Promise<UserPublic> { + const [newUser] = await db + .insert(users) + .values({ + username: data.username, + passwordHash: data.passwordHash, + }) + .returning({ + id: users.id, + username: users.username, + createdAt: users.createdAt, + }); + // Insert with returning should always return the created row + return newUser!; + }, + + async findById(id) { + const [user] = await db + .select({ + id: users.id, + username: users.username, + }) + .from(users) + .where(eq(users.id, id)) + .limit(1); + return user; + }, +}; |
