fixed members login and permissions issues

This commit is contained in:
2026-02-23 00:57:34 +00:00
parent ed0debfeac
commit 97da2e2736
42 changed files with 574 additions and 113 deletions
+2 -2
View File
@@ -3,14 +3,14 @@ import { officerdb, eq, Users } from 'officerdb';
import * as errors from '@@/custom-errors';
export const updateUserHandler: Handler = async function (ctx) {
const { name, avatar } = ctx.get('body');
const { name, username, avatar } = ctx.get('body');
const reqUser = ctx.get('user');
if (typeof name !== 'string') throw errors.BAD_REQUEST('Name is required');
await officerdb
.update(Users)
.set({ name, avatar: avatar ?? null })
.set({ name, username: typeof username === 'string' ? username : undefined, avatar: avatar ?? null })
.where(eq(Users.id, reqUser.id));
return ctx.json({ ok: true });
+109
View File
@@ -0,0 +1,109 @@
import { officerdb, eq, Users } from 'officerdb';
import { createRouter } from '@@/create-router';
import { sign } from '@@/jwt';
import { USER_ROLES } from 'definitions';
import { sendMail } from 'emailer';
import * as errors from '@@/custom-errors';
import { originMiddleware } from '@@/_middlewares';
import { updateUserHandler } from './update-user';
export const usersRouter = createRouter();
usersRouter.use(originMiddleware);
// List all users (Super Admin only)
usersRouter.get('/', async (ctx) => {
const user = ctx.get('user');
if (user.role !== 'Super Admin') throw errors.FORBIDDEN();
const users = await officerdb.query.Users.findMany();
const sanitized = users.map(({ password, ...rest }) => rest);
return ctx.json(sanitized);
});
// Self-update (any authenticated user)
usersRouter.put('/', updateUserHandler);
// Invite a new user (Super Admin only)
usersRouter.post('/invite', async (ctx) => {
const reqUser = ctx.get('user');
if (reqUser.role !== 'Super Admin') throw errors.FORBIDDEN();
const { email, role } = ctx.get('body');
if (!email || typeof email !== 'string' || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
throw errors.BAD_REQUEST('Invalid email address');
}
const validRoles = USER_ROLES.filter((r) => r !== 'Super Admin');
const assignedRole = (typeof role === 'string' && validRoles.includes(role as (typeof validRoles)[number]))
? (role as (typeof USER_ROLES)[number])
: ('Member' as const);
const existing = await officerdb.query.Users.findFirst({ where: eq(Users.email, email) });
if (existing) throw errors.CONFLICT('A user with this email already exists');
const insertedUsers = await officerdb.insert(Users).values({
email,
role: assignedRole,
status: 'Invited',
}).returning();
if (!insertedUsers || insertedUsers.length === 0) throw errors.INTERNAL_SERVER_ERROR('Failed to create user');
const dbUser = insertedUsers[0]!;
const origin = ctx.get('origin');
const verificationCode = await sign({ id: dbUser.id, email: dbUser.email }, '24h');
const url = `${origin}/auth/verify?verificationCode=${verificationCode}`;
await sendMail({
template: 'UserInvite',
subject: 'You have been invited to Officer',
to: email,
data: { invitedBy: reqUser.name ?? reqUser.email, url },
});
const { password, ...safeUser } = dbUser;
return ctx.json(safeUser);
});
// Resend invite (Super Admin only, status must be Invited)
usersRouter.post('/:id/resend-invite', async (ctx) => {
const reqUser = ctx.get('user');
if (reqUser.role !== 'Super Admin') throw errors.FORBIDDEN();
const id = Number(ctx.req.param('id'));
if (!id || isNaN(id)) throw errors.BAD_REQUEST('Invalid user ID');
const target = await officerdb.query.Users.findFirst({ where: eq(Users.id, id) });
if (!target) throw errors.NOT_FOUND('User not found');
if (target.status !== 'Invited') throw errors.BAD_REQUEST('User is not in Invited status');
const origin = ctx.get('origin');
const verificationCode = await sign({ id: target.id, email: target.email }, '24h');
const url = `${origin}/auth/verify?verificationCode=${verificationCode}`;
await sendMail({
template: 'UserInvite',
subject: 'You have been invited to Officer',
to: target.email,
data: { invitedBy: reqUser.name ?? reqUser.email, url },
});
return ctx.json({ ok: true });
});
// Delete a user (Super Admin only, cannot delete self)
usersRouter.delete('/:id', async (ctx) => {
const reqUser = ctx.get('user');
if (reqUser.role !== 'Super Admin') throw errors.FORBIDDEN();
const id = Number(ctx.req.param('id'));
if (!id || isNaN(id)) throw errors.BAD_REQUEST('Invalid user ID');
if (id === reqUser.id) throw errors.BAD_REQUEST('Cannot delete yourself');
const target = await officerdb.query.Users.findFirst({ where: eq(Users.id, id) });
if (!target) throw errors.NOT_FOUND('User not found');
await officerdb.delete(Users).where(eq(Users.id, id));
return ctx.json({ ok: true });
});