-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added a not found page (404) and admin routes
- Loading branch information
Showing
15 changed files
with
371 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { drizzle } from "drizzle-orm/postgres-js"; | ||
import { z } from "zod"; | ||
import * as schema from "./schema.js"; | ||
import dotenv from "dotenv"; | ||
import postgres from "postgres"; | ||
|
||
dotenv.config(); | ||
|
||
const env = z | ||
.object({ | ||
DB_CONNECTION_URI: z.string().url(), | ||
ADMIN_EMAILS: z | ||
.string() | ||
.transform((value) => { | ||
return value.split(",").filter((maybeEmail) => { | ||
const result = z.string().email().safeParse(maybeEmail); | ||
|
||
if (!result.success) { | ||
console.warn("Skipping invalid email:", maybeEmail); | ||
return false; | ||
} | ||
|
||
return true; | ||
}); | ||
}) | ||
.optional(), | ||
}) | ||
.parse(process.env); | ||
|
||
const sql = postgres(env.DB_CONNECTION_URI, { max: 1 }); | ||
const db = drizzle(sql, { schema }); | ||
|
||
|
||
|
||
if (env.ADMIN_EMAILS) { | ||
await Promise.all( | ||
env.ADMIN_EMAILS.map(async (email) => { | ||
try { | ||
await db.insert(schema.adminsTable).values({ email }); | ||
|
||
console.log("Created admin:", email); | ||
} catch (error) { | ||
if (error instanceof postgres.PostgresError && error.code === "23505") { | ||
console.warn("Admin already exists:", email); | ||
return; | ||
} | ||
console.error(error); | ||
} | ||
}), | ||
); | ||
} | ||
|
||
await sql.end({ timeout: 1 }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { Request, Response, Router } from "express"; | ||
import { db } from "@/db.js"; | ||
|
||
import { AdminService } from "./admin.service.js"; | ||
import { checkAdmin } from "@/middlewares/admin.middlewares.js"; | ||
|
||
export class AdminController { | ||
public readonly router = Router(); | ||
|
||
// Constructor initialization of routes | ||
constructor( | ||
private readonly adminService: AdminService, | ||
) { | ||
this.router.get( | ||
"/admin/users", | ||
checkAdmin, | ||
this.getAllUsers.bind(this) | ||
); | ||
|
||
} | ||
|
||
// Get all users with their details | ||
|
||
public async getAllUsers(req: Request, res: Response) { | ||
try { | ||
const users = await this.adminService.getAllUsers(db); | ||
|
||
return res.status(200).send({ users }); | ||
} catch (error) { | ||
console.error(error); | ||
res.status(500).send({ errors: [{ message: "Internal server error" }] }); | ||
} | ||
} | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { dateToUTCTimestamp } from "@/utils.js"; | ||
import { adminsTable, usersTable } from "@db/schema.js"; | ||
import { User } from "@shared/index.js"; | ||
import { z, ZodType } from "zod"; | ||
|
||
|
||
export const userSchema = z.object({ | ||
id: z.string(), | ||
username: z.string(), | ||
email: z.string(), | ||
emailVerified: z.boolean(), | ||
avatarKey: z.string().nullable(), | ||
createdAt: z.number(), | ||
role: z.string(), | ||
score: z.number(), | ||
level: z.number(), | ||
currency: z.number(), | ||
isAdmin: z.boolean(), | ||
}) satisfies ZodType<User>; | ||
|
||
export class AdminService { | ||
constructor() {} | ||
|
||
async isAdminEmail(db: any, email: string): Promise<boolean> { | ||
const admin = await db | ||
.select(adminsTable) | ||
.where({ email }) | ||
.single(); | ||
return !!admin; | ||
} | ||
|
||
async getAllUsers(db: any): Promise<User[]> { | ||
const user = await db | ||
.select(usersTable) | ||
.all(); | ||
return user.map((u: any) => userSchema.parse({ | ||
id: u.id, | ||
username: u.username, | ||
email: u.email, | ||
emailVerified: u.emailVerified, | ||
avatarKey: u.avatarKey, | ||
createdAt: dateToUTCTimestamp(u.createdAt), | ||
role: u.role, | ||
score: u.score, | ||
level: u.level, | ||
currency: u.currency, | ||
isAdmin: u.isAdmin, | ||
} satisfies User)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import type { NextFunction, Request, Response } from "express"; | ||
import { AdminService } from "@/admin/admin.service.js"; | ||
import { db } from "@/db.js"; | ||
|
||
|
||
// Middleware to check if the user is an admin. | ||
export async function checkAdmin(req: Request, res: Response, next: NextFunction) { | ||
if (!req.session.user) { | ||
return res.status(401).send({ errors: [{ message: "Unauthorized" }] }); | ||
} | ||
|
||
const adminService = new AdminService(); | ||
const isAdmin = await adminService.isAdminEmail(db, req.session.user.email); | ||
|
||
if (!isAdmin) { | ||
return res.status(403).send({ errors: [{ message: "Forbidden" }] }); | ||
} | ||
|
||
next(); | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.