199 lines
4.4 KiB
TypeScript
199 lines
4.4 KiB
TypeScript
import { zValidator } from "@hono/zod-validator";
|
|
import { and, eq, isNull, or } from "drizzle-orm";
|
|
import { Hono } from "hono";
|
|
import { deleteCookie } from "hono/cookie";
|
|
import { HTTPException } from "hono/http-exception";
|
|
import { z } from "zod";
|
|
import db from "../../drizzle";
|
|
import { users } from "../../drizzle/schema/users";
|
|
import { checkPassword } from "../../utils/passwordUtils";
|
|
import {
|
|
generateAccessToken,
|
|
generateRefreshToken,
|
|
verifyRefreshToken,
|
|
} from "../../utils/authUtils";
|
|
import { rolesSchema } from "../../drizzle/schema/roles";
|
|
import { rolesToUsers } from "../../drizzle/schema/rolesToUsers";
|
|
import HonoEnv from "../../types/HonoEnv";
|
|
import { permissionsToUsers } from "../../drizzle/schema/permissionsToUsers";
|
|
import { permissionsToRoles } from "../../drizzle/schema/permissionsToRoles";
|
|
import { permissionsSchema } from "../../drizzle/schema/permissions";
|
|
import { SpecificPermissionCode } from "../../data/permissions";
|
|
import authInfo from "../../middlewares/authInfo";
|
|
import { unauthorized } from "../../errors/DashboardError";
|
|
import appEnv from "../../appEnv";
|
|
|
|
const authRoutes = new Hono<HonoEnv>()
|
|
.post(
|
|
"/login",
|
|
zValidator(
|
|
"form",
|
|
z.object({
|
|
username: z.string(),
|
|
password: z.string(),
|
|
___jwt: z.string().default("false"),
|
|
})
|
|
),
|
|
async (c) => {
|
|
const formData = c.req.valid("form");
|
|
|
|
const user = await db
|
|
.select()
|
|
.from(users)
|
|
.where(
|
|
and(
|
|
isNull(users.deletedAt),
|
|
eq(users.isEnabled, true),
|
|
or(
|
|
eq(users.username, formData.username),
|
|
eq(users.email, formData.username)
|
|
)
|
|
)
|
|
)
|
|
.leftJoin(
|
|
permissionsToUsers,
|
|
eq(permissionsToUsers.userId, users.id)
|
|
)
|
|
.leftJoin(rolesToUsers, eq(rolesToUsers.userId, users.id))
|
|
.leftJoin(rolesSchema, eq(rolesToUsers.roleId, rolesSchema.id))
|
|
.leftJoin(
|
|
permissionsToRoles,
|
|
eq(permissionsToRoles.roleId, rolesSchema.id)
|
|
)
|
|
.leftJoin(
|
|
permissionsSchema,
|
|
or(
|
|
eq(
|
|
permissionsSchema.id,
|
|
permissionsToUsers.permissionId
|
|
),
|
|
eq(
|
|
permissionsSchema.id,
|
|
permissionsToRoles.permissionId
|
|
)
|
|
)
|
|
);
|
|
|
|
if (!user.length) {
|
|
throw new HTTPException(400, {
|
|
message: "Invalid username or password",
|
|
});
|
|
}
|
|
|
|
const isSuccess = await checkPassword(
|
|
formData.password,
|
|
user[0].users.password
|
|
);
|
|
|
|
if (!isSuccess) {
|
|
throw new HTTPException(400, {
|
|
message: "Invalid username or password",
|
|
});
|
|
}
|
|
|
|
const accessToken = await generateAccessToken({
|
|
uid: user[0].users.id,
|
|
});
|
|
|
|
const refreshToken = await generateRefreshToken({
|
|
uid: user[0].users.id,
|
|
});
|
|
|
|
const cookieSecret = appEnv.COOKIE_SECRET;
|
|
|
|
if (!cookieSecret)
|
|
throw new HTTPException(500, {
|
|
message: "The 'COOKIE_SECRET' env var is not set",
|
|
});
|
|
|
|
// await setSignedCookie(
|
|
// c,
|
|
// "access_token",
|
|
// accessToken,
|
|
// cookieSecret,
|
|
// {
|
|
// secure: true,
|
|
// httpOnly: true,
|
|
// prefix: "secure",
|
|
// expires:
|
|
// }
|
|
// );
|
|
|
|
const permissions = new Set<SpecificPermissionCode>();
|
|
user.forEach((user) => {
|
|
if (user.permissions?.code) {
|
|
permissions.add(
|
|
user.permissions.code as SpecificPermissionCode
|
|
);
|
|
}
|
|
});
|
|
|
|
return c.json({
|
|
accessToken,
|
|
refreshToken,
|
|
user: {
|
|
id: user[0].users.id,
|
|
name: user[0].users.name,
|
|
role: user[0].roles?.code,
|
|
permissions: Array.from(permissions),
|
|
},
|
|
});
|
|
}
|
|
)
|
|
.post(
|
|
"/refresh-token",
|
|
zValidator(
|
|
"json",
|
|
z.object({
|
|
refreshToken: z.string(),
|
|
})
|
|
),
|
|
async (c) => {
|
|
const { refreshToken } = c.req.valid("json");
|
|
|
|
const decoded = await verifyRefreshToken(refreshToken);
|
|
|
|
if (!decoded) {
|
|
throw new HTTPException(401, {
|
|
message: "Invalid refresh token",
|
|
});
|
|
}
|
|
|
|
const accessToken = await generateAccessToken({
|
|
uid: decoded.uid,
|
|
});
|
|
|
|
return c.json({
|
|
accessToken,
|
|
});
|
|
}
|
|
)
|
|
.get("/my-profile", authInfo, async (c) => {
|
|
const currentUser = c.var.currentUser;
|
|
|
|
if (!currentUser) {
|
|
throw unauthorized();
|
|
}
|
|
|
|
return c.json({ ...currentUser, id: c.var.uid! });
|
|
})
|
|
.get("/logout", (c) => {
|
|
const uid = c.var.uid;
|
|
|
|
if (!uid) {
|
|
return c.notFound();
|
|
}
|
|
|
|
deleteCookie(c, "access_token", {
|
|
secure: true,
|
|
httpOnly: true,
|
|
prefix: "secure",
|
|
});
|
|
|
|
return c.json({
|
|
message: "Logged out successfully",
|
|
});
|
|
});
|
|
|
|
export default authRoutes;
|