mirror of
https://github.com/nicotsx/ironmount.git
synced 2025-12-10 12:10:51 +01:00
feat(settings): change password
This commit is contained in:
@@ -3,6 +3,8 @@ import { validator } from "hono-openapi";
|
||||
import { Hono } from "hono";
|
||||
import { deleteCookie, getCookie, setCookie } from "hono/cookie";
|
||||
import {
|
||||
changePasswordBodySchema,
|
||||
changePasswordDto,
|
||||
getMeDto,
|
||||
getStatusDto,
|
||||
loginBodySchema,
|
||||
@@ -10,6 +12,7 @@ import {
|
||||
logoutDto,
|
||||
registerBodySchema,
|
||||
registerDto,
|
||||
type ChangePasswordDto,
|
||||
type GetMeDto,
|
||||
type GetStatusDto,
|
||||
type LoginDto,
|
||||
@@ -100,4 +103,27 @@ export const authController = new Hono()
|
||||
.get("/status", getStatusDto, async (c) => {
|
||||
const hasUsers = await authService.hasUsers();
|
||||
return c.json<GetStatusDto>({ hasUsers });
|
||||
})
|
||||
.post("/change-password", changePasswordDto, validator("json", changePasswordBodySchema), async (c) => {
|
||||
const sessionId = getCookie(c, COOKIE_NAME);
|
||||
|
||||
if (!sessionId) {
|
||||
return c.json<ChangePasswordDto>({ success: false, message: "Not authenticated" }, 401);
|
||||
}
|
||||
|
||||
const session = await authService.verifySession(sessionId);
|
||||
|
||||
if (!session) {
|
||||
deleteCookie(c, COOKIE_NAME, COOKIE_OPTIONS);
|
||||
return c.json<ChangePasswordDto>({ success: false, message: "Not authenticated" }, 401);
|
||||
}
|
||||
|
||||
const body = c.req.valid("json");
|
||||
|
||||
try {
|
||||
await authService.changePassword(session.user.id, body.currentPassword, body.newPassword);
|
||||
return c.json<ChangePasswordDto>({ success: true, message: "Password changed successfully" });
|
||||
} catch (error) {
|
||||
return c.json<ChangePasswordDto>({ success: false, message: toMessage(error) }, 400);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -34,9 +34,6 @@ export const loginDto = describeRoute({
|
||||
},
|
||||
},
|
||||
},
|
||||
401: {
|
||||
description: "Invalid credentials",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -55,9 +52,6 @@ export const registerDto = describeRoute({
|
||||
},
|
||||
},
|
||||
},
|
||||
400: {
|
||||
description: "Invalid request or username already exists",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -125,5 +119,34 @@ export const getStatusDto = describeRoute({
|
||||
|
||||
export type GetStatusDto = typeof statusResponseSchema.infer;
|
||||
|
||||
export const changePasswordBodySchema = type({
|
||||
currentPassword: "string>0",
|
||||
newPassword: "string>7",
|
||||
});
|
||||
|
||||
const changePasswordResponseSchema = type({
|
||||
success: "boolean",
|
||||
message: "string",
|
||||
});
|
||||
|
||||
export const changePasswordDto = describeRoute({
|
||||
description: "Change current user password",
|
||||
operationId: "changePassword",
|
||||
tags: ["Auth"],
|
||||
responses: {
|
||||
200: {
|
||||
description: "Password changed successfully",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: resolver(changePasswordResponseSchema),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export type ChangePasswordDto = typeof changePasswordResponseSchema.infer;
|
||||
|
||||
export type LoginBody = typeof loginBodySchema.infer;
|
||||
export type RegisterBody = typeof registerBodySchema.infer;
|
||||
export type ChangePasswordBody = typeof changePasswordBodySchema.infer;
|
||||
|
||||
@@ -134,6 +134,33 @@ export class AuthService {
|
||||
const [user] = await db.select({ id: usersTable.id }).from(usersTable).limit(1);
|
||||
return !!user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change password for a user
|
||||
*/
|
||||
async changePassword(userId: number, currentPassword: string, newPassword: string) {
|
||||
const [user] = await db.select().from(usersTable).where(eq(usersTable.id, userId));
|
||||
|
||||
if (!user) {
|
||||
throw new Error("User not found");
|
||||
}
|
||||
|
||||
const isValid = await Bun.password.verify(currentPassword, user.passwordHash);
|
||||
|
||||
if (!isValid) {
|
||||
throw new Error("Current password is incorrect");
|
||||
}
|
||||
|
||||
const newPasswordHash = await Bun.password.hash(newPassword, {
|
||||
algorithm: "argon2id",
|
||||
memoryCost: 19456,
|
||||
timeCost: 2,
|
||||
});
|
||||
|
||||
await db.update(usersTable).set({ passwordHash: newPasswordHash }).where(eq(usersTable.id, userId));
|
||||
|
||||
logger.info(`Password changed for user: ${user.username}`);
|
||||
}
|
||||
}
|
||||
|
||||
export const authService = new AuthService();
|
||||
|
||||
@@ -334,6 +334,7 @@ const forget = async (config: RepositoryConfig, options: RetentionPolicy, extra:
|
||||
args.push("--prune");
|
||||
args.push("--json");
|
||||
|
||||
await $`restic unlock --repo ${repoUrl}`.env(env).nothrow();
|
||||
const res = await $`restic ${args}`.env(env).nothrow();
|
||||
|
||||
if (res.exitCode !== 0) {
|
||||
|
||||
Reference in New Issue
Block a user