aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/server/repositories
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/repositories')
-rw-r--r--src/server/repositories/index.ts3
-rw-r--r--src/server/repositories/refresh-token.ts35
-rw-r--r--src/server/repositories/types.ts46
-rw-r--r--src/server/repositories/user.ts57
4 files changed, 141 insertions, 0 deletions
diff --git a/src/server/repositories/index.ts b/src/server/repositories/index.ts
new file mode 100644
index 0000000..04b1f35
--- /dev/null
+++ b/src/server/repositories/index.ts
@@ -0,0 +1,3 @@
+export { refreshTokenRepository } from "./refresh-token.js";
+export * from "./types.js";
+export { userRepository } from "./user.js";
diff --git a/src/server/repositories/refresh-token.ts b/src/server/repositories/refresh-token.ts
new file mode 100644
index 0000000..e92a744
--- /dev/null
+++ b/src/server/repositories/refresh-token.ts
@@ -0,0 +1,35 @@
+import { and, eq, gt } from "drizzle-orm";
+import { db, refreshTokens } from "../db/index.js";
+import type { RefreshTokenRepository } from "./types.js";
+
+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/src/server/repositories/types.ts b/src/server/repositories/types.ts
new file mode 100644
index 0000000..1ab4bdc
--- /dev/null
+++ b/src/server/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/src/server/repositories/user.ts b/src/server/repositories/user.ts
new file mode 100644
index 0000000..e571409
--- /dev/null
+++ b/src/server/repositories/user.ts
@@ -0,0 +1,57 @@
+import { eq } from "drizzle-orm";
+import { db, users } from "../db/index.js";
+import type { UserPublic, UserRepository } from "./types.js";
+
+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,
+ });
+ if (!newUser) {
+ throw new Error("Failed to create user");
+ }
+ 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;
+ },
+};