feat: banning
This commit is contained in:
parent
bc1687e40a
commit
d9f2836fb9
11 changed files with 576 additions and 21 deletions
45
website/src/routes/api/admin/users/+server.ts
Normal file
45
website/src/routes/api/admin/users/+server.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { auth } from '$lib/auth';
|
||||
import { error, json } from '@sveltejs/kit';
|
||||
import { db } from '$lib/server/db';
|
||||
import { user } from '$lib/server/db/schema';
|
||||
import { desc, eq } from 'drizzle-orm';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
export const GET: RequestHandler = async ({ request }) => {
|
||||
const session = await auth.api.getSession({ headers: request.headers });
|
||||
|
||||
if (!session?.user) {
|
||||
throw error(401, 'Not authenticated');
|
||||
}
|
||||
|
||||
const [currentUser] = await db
|
||||
.select({ isAdmin: user.isAdmin })
|
||||
.from(user)
|
||||
.where(eq(user.id, Number(session.user.id)))
|
||||
.limit(1);
|
||||
|
||||
if (!currentUser?.isAdmin) {
|
||||
throw error(403, 'Admin access required');
|
||||
}
|
||||
|
||||
try {
|
||||
const users = await db
|
||||
.select({
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
isAdmin: user.isAdmin,
|
||||
isBanned: user.isBanned,
|
||||
banReason: user.banReason,
|
||||
createdAt: user.createdAt
|
||||
})
|
||||
.from(user)
|
||||
.orderBy(desc(user.createdAt));
|
||||
|
||||
return json(users);
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch users:', e);
|
||||
throw error(500, 'Internal server error');
|
||||
}
|
||||
};
|
||||
74
website/src/routes/api/admin/users/ban/+server.ts
Normal file
74
website/src/routes/api/admin/users/ban/+server.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import { auth } from '$lib/auth';
|
||||
import { error, json } from '@sveltejs/kit';
|
||||
import { db } from '$lib/server/db';
|
||||
import { user, session } from '$lib/server/db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
export const POST: RequestHandler = async ({ request }) => {
|
||||
const authSession = await auth.api.getSession({ headers: request.headers });
|
||||
|
||||
if (!authSession?.user) {
|
||||
throw error(401, 'Not authenticated');
|
||||
}
|
||||
|
||||
const [currentUser] = await db
|
||||
.select({ isAdmin: user.isAdmin })
|
||||
.from(user)
|
||||
.where(eq(user.id, Number(authSession.user.id)))
|
||||
.limit(1);
|
||||
|
||||
if (!currentUser?.isAdmin) {
|
||||
throw error(403, 'Admin access required');
|
||||
}
|
||||
|
||||
const { username, reason } = await request.json();
|
||||
|
||||
if (!username?.trim() || !reason?.trim()) {
|
||||
throw error(400, 'Username and reason are required');
|
||||
}
|
||||
|
||||
try {
|
||||
const [targetUser] = await db
|
||||
.select({ id: user.id, username: user.username, isAdmin: user.isAdmin })
|
||||
.from(user)
|
||||
.where(eq(user.username, username.trim()))
|
||||
.limit(1);
|
||||
|
||||
if (!targetUser) {
|
||||
throw error(404, 'User not found');
|
||||
}
|
||||
|
||||
if (targetUser.isAdmin) {
|
||||
throw error(400, 'Cannot ban admin users');
|
||||
}
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(user)
|
||||
.set({
|
||||
isBanned: true,
|
||||
banReason: reason.trim(),
|
||||
updatedAt: new Date()
|
||||
})
|
||||
.where(eq(user.id, targetUser.id));
|
||||
|
||||
await tx
|
||||
.delete(session)
|
||||
.where(eq(session.userId, targetUser.id));
|
||||
});
|
||||
|
||||
try {
|
||||
const { clearUserCache } = await import('$lib/../hooks.server.js');
|
||||
clearUserCache(targetUser.id.toString());
|
||||
} catch (e) {
|
||||
console.warn('Failed to clear user cache:', e);
|
||||
}
|
||||
|
||||
return json({ success: true });
|
||||
} catch (e: any) {
|
||||
if (e.status) throw e;
|
||||
console.error('Failed to ban user:', e);
|
||||
throw error(500, 'Internal server error');
|
||||
}
|
||||
};
|
||||
47
website/src/routes/api/admin/users/banned-list/+server.ts
Normal file
47
website/src/routes/api/admin/users/banned-list/+server.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { auth } from '$lib/auth';
|
||||
import { error, json } from '@sveltejs/kit';
|
||||
import { db } from '$lib/server/db';
|
||||
import { user } from '$lib/server/db/schema';
|
||||
import { desc, eq, and, not, like } from 'drizzle-orm';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
export const GET: RequestHandler = async ({ request }) => {
|
||||
const session = await auth.api.getSession({ headers: request.headers });
|
||||
|
||||
if (!session?.user) {
|
||||
throw error(401, 'Not authenticated');
|
||||
}
|
||||
|
||||
const [currentUser] = await db
|
||||
.select({ isAdmin: user.isAdmin })
|
||||
.from(user)
|
||||
.where(eq(user.id, Number(session.user.id)))
|
||||
.limit(1);
|
||||
|
||||
if (!currentUser?.isAdmin) {
|
||||
throw error(403, 'Admin access required');
|
||||
}
|
||||
|
||||
try {
|
||||
const bannedUsers = await db
|
||||
.select({
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
username: user.username,
|
||||
banReason: user.banReason
|
||||
})
|
||||
.from(user)
|
||||
.where(
|
||||
and(
|
||||
eq(user.isBanned, true),
|
||||
not(like(user.banReason, '%Account deletion requested%'))
|
||||
)
|
||||
)
|
||||
.orderBy(desc(user.updatedAt));
|
||||
|
||||
return json(bannedUsers);
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch banned users:', e);
|
||||
throw error(500, 'Internal server error');
|
||||
}
|
||||
};
|
||||
46
website/src/routes/api/admin/users/unban/+server.ts
Normal file
46
website/src/routes/api/admin/users/unban/+server.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { auth } from '$lib/auth';
|
||||
import { error, json } from '@sveltejs/kit';
|
||||
import { db } from '$lib/server/db';
|
||||
import { user } from '$lib/server/db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
export const POST: RequestHandler = async ({ request }) => {
|
||||
const authSession = await auth.api.getSession({ headers: request.headers });
|
||||
|
||||
if (!authSession?.user) {
|
||||
throw error(401, 'Not authenticated');
|
||||
}
|
||||
|
||||
const [currentUser] = await db
|
||||
.select({ isAdmin: user.isAdmin })
|
||||
.from(user)
|
||||
.where(eq(user.id, Number(authSession.user.id)))
|
||||
.limit(1);
|
||||
|
||||
if (!currentUser?.isAdmin) {
|
||||
throw error(403, 'Admin access required');
|
||||
}
|
||||
|
||||
const { userId } = await request.json();
|
||||
|
||||
if (!userId) {
|
||||
throw error(400, 'User ID is required');
|
||||
}
|
||||
|
||||
try {
|
||||
await db
|
||||
.update(user)
|
||||
.set({
|
||||
isBanned: false,
|
||||
banReason: null,
|
||||
updatedAt: new Date()
|
||||
})
|
||||
.where(eq(user.id, userId));
|
||||
|
||||
return json({ success: true });
|
||||
} catch (e) {
|
||||
console.error('Failed to unban user:', e);
|
||||
throw error(500, 'Internal server error');
|
||||
}
|
||||
};
|
||||
Reference in a new issue