revert fb8e77bb68
Local Build and Deploy / deploy (push) Successful in 1m11s

revert style: clean up code with prettier and eslint
This commit is contained in:
remco committed 2026-07-12 21:02:03 +02:00
1 parent 27de078f54
commit e85e4d74ea
378 files changed
+20710 -22428

No files matched your search

+213 -192
View File
@@ -1,31 +1,36 @@
"use server";
'use server'
import crypto from "node:crypto";
import { hash } from "bcryptjs";
import { z } from "zod";
import { Prisma } from "@/generated/prisma/client";
import { PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma";
import { adminAction } from "@/lib/safe-action";
import { ActionError, actionOk } from "@/lib/safe-action-shared";
import { logAudit } from "@/lib/services/audit";
import { rcon } from "@/lib/services/rcon";
import { notify } from "@/lib/services/webhook";
import { banUserSchema, createUserSchema, giveBadgeSchema, updateUserSchema } from "@/lib/validators/user";
import crypto from 'node:crypto'
import { hash } from 'bcryptjs'
import { z } from 'zod'
import { Prisma } from '@/generated/prisma/client'
import { PERMS } from '@/lib/permissions'
import { prisma } from '@/lib/prisma'
import { adminAction } from '@/lib/safe-action'
import { ActionError, actionOk } from '@/lib/safe-action-shared'
import { logAudit } from '@/lib/services/audit'
import { rcon } from '@/lib/services/rcon'
import { notify } from '@/lib/services/webhook'
import {
banUserSchema,
createUserSchema,
giveBadgeSchema,
updateUserSchema,
} from '@/lib/validators/user'
const DEFAULT_LOOK = "hr-115-42.hd-195-19.ch-3030-82.lg-275-1408.fa-1201.ca-1804-64";
const DEFAULT_LOOK = 'hr-115-42.hd-195-19.ch-3030-82.lg-275-1408.fa-1201.ca-1804-64'
export const createUser = adminAction(
{ permission: PERMS.USERS_EDIT, schema: createUserSchema },
async (ctx) => {
const { username, mail, password, rank, motto } = ctx.data;
const { username, mail, password, rank, motto } = ctx.data
if (rank >= ctx.session.user.rank && ctx.session.user.rank < 7) {
throw new ActionError("Cannot assign rank equal or higher than your own");
throw new ActionError('Cannot assign rank equal or higher than your own')
}
const hashedPassword = await hash(password, 12);
const now = Math.floor(Date.now() / 1000);
const hashedPassword = await hash(password, 12)
const now = Math.floor(Date.now() / 1000)
try {
const user = await prisma.$transaction(async (tx) => {
@@ -40,211 +45,221 @@ export const createUser = adminAction(
credits: 5000,
pixels: 5000,
accountCreated: now,
ipRegister: "0.0.0.0",
ipCurrent: "0.0.0.0",
ipRegister: '0.0.0.0',
ipCurrent: '0.0.0.0',
},
});
})
await tx.usersSettings.create({ data: { userId: created.id } });
await tx.usersSettings.create({ data: { userId: created.id } })
await tx.usersCurrency.createMany({
data: [
{ userId: created.id, type: 0, amount: 5000 },
{ userId: created.id, type: 5, amount: 5000 },
],
});
})
return created;
});
return created
})
logAudit({
userId: ctx.session.user.id,
action: "user_create",
target: "User",
action: 'user_create',
target: 'User',
targetId: user.id,
after: { username, mail, rank },
});
})
notify({
action: "user_edit",
action: 'user_edit',
actor: ctx.session.user.username,
target: username,
targetId: user.id,
details: "Account created by admin",
});
details: 'Account created by admin',
})
return actionOk({ id: user.id, username: user.username });
return actionOk({ id: user.id, username: user.username })
} catch (err) {
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === "P2002") {
const target = (err.meta?.target as string[]) ?? [];
if (target.includes("username")) throw new ActionError("Username already taken");
if (target.includes("mail")) throw new ActionError("Email already registered");
throw new ActionError("Username or email already in use");
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === 'P2002') {
const target = (err.meta?.target as string[]) ?? []
if (target.includes('username')) throw new ActionError('Username already taken')
if (target.includes('mail')) throw new ActionError('Email already registered')
throw new ActionError('Username or email already in use')
}
throw err;
throw err
}
},
);
)
const updateUserInput = updateUserSchema.extend({
id: z.coerce.number().int().positive(),
});
})
export const updateUser = adminAction(
{ permission: PERMS.USERS_EDIT, schema: updateUserInput },
async (ctx) => {
const { id, diamonds, duckets, ...userData } = ctx.data;
const { id, diamonds, duckets, ...userData } = ctx.data
const targetUser = await guardRank(id, ctx.session.user.rank);
const targetUser = await guardRank(id, ctx.session.user.rank)
if (userData.rank !== undefined && userData.rank >= ctx.session.user.rank && ctx.session.user.rank < 7) {
throw new ActionError("Cannot assign rank equal or higher than your own");
if (
userData.rank !== undefined &&
userData.rank >= ctx.session.user.rank &&
ctx.session.user.rank < 7
) {
throw new ActionError('Cannot assign rank equal or higher than your own')
}
await prisma.user.update({ where: { id }, data: userData });
await prisma.user.update({ where: { id }, data: userData })
if (diamonds !== undefined) {
await prisma.usersCurrency.upsert({
where: { userId_type: { userId: id, type: 5 } },
update: { amount: diamonds },
create: { userId: id, type: 5, amount: diamonds },
});
})
}
if (duckets !== undefined) {
await prisma.usersCurrency.upsert({
where: { userId_type: { userId: id, type: 0 } },
update: { amount: duckets },
create: { userId: id, type: 0, amount: duckets },
});
})
}
logAudit({
userId: ctx.session.user.id,
action: "user_edit",
target: "User",
action: 'user_edit',
target: 'User',
targetId: id,
before: { username: targetUser.username, mail: targetUser.mail, rank: targetUser.rank },
after: userData,
});
})
notify({
action: "user_edit",
action: 'user_edit',
actor: ctx.session.user.username,
target: targetUser.username,
targetId: id,
});
})
return actionOk();
return actionOk()
},
);
)
const banInput = banUserSchema.extend({});
const banInput = banUserSchema.extend({})
export const banUser = adminAction({ permission: PERMS.USERS_BAN, schema: banInput }, async (ctx) => {
const { userId, reason, duration, type, ip } = ctx.data;
export const banUser = adminAction(
{ permission: PERMS.USERS_BAN, schema: banInput },
async (ctx) => {
const { userId, reason, duration, type, ip } = ctx.data
const targetUser = await guardRank(userId, ctx.session.user.rank);
const targetUser = await guardRank(userId, ctx.session.user.rank)
const now = Math.floor(Date.now() / 1000);
const banExpire = duration > 0 ? now + duration * 3600 : 0;
const now = Math.floor(Date.now() / 1000)
const banExpire = duration > 0 ? now + duration * 3600 : 0
await prisma.ban.create({
data: {
userId,
userStaffId: ctx.session.user.id,
timestamp: now,
banExpire,
banReason: reason,
type: type || "account",
ip: ip || "",
machineId: "",
},
});
await prisma.ban.create({
data: {
userId,
userStaffId: ctx.session.user.id,
timestamp: now,
banExpire,
banReason: reason,
type: type || 'account',
ip: ip || '',
machineId: '',
},
})
await rcon.disconnectUser(userId);
await rcon.disconnectUser(userId)
logAudit({
userId: ctx.session.user.id,
action: "ban",
target: "User",
targetId: userId,
after: { reason, type, duration },
});
logAudit({
userId: ctx.session.user.id,
action: 'ban',
target: 'User',
targetId: userId,
after: { reason, type, duration },
})
notify({
action: "ban",
actor: ctx.session.user.username,
target: targetUser.username,
details: reason,
});
notify({
action: 'ban',
actor: ctx.session.user.username,
target: targetUser.username,
details: reason,
})
return actionOk();
});
return actionOk()
},
)
const unbanInput = z.object({ userId: z.coerce.number().int().positive() });
const unbanInput = z.object({ userId: z.coerce.number().int().positive() })
export const unbanUser = adminAction({ permission: PERMS.USERS_BAN, schema: unbanInput }, async (ctx) => {
const { userId } = ctx.data;
export const unbanUser = adminAction(
{ permission: PERMS.USERS_BAN, schema: unbanInput },
async (ctx) => {
const { userId } = ctx.data
const targetUser = await guardRank(userId, ctx.session.user.rank);
const targetUser = await guardRank(userId, ctx.session.user.rank)
await prisma.ban.deleteMany({ where: { userId } });
await prisma.ban.deleteMany({ where: { userId } })
logAudit({
userId: ctx.session.user.id,
action: "unban",
target: "User",
targetId: userId,
});
logAudit({
userId: ctx.session.user.id,
action: 'unban',
target: 'User',
targetId: userId,
})
notify({
action: "unban",
actor: ctx.session.user.username,
target: targetUser.username,
});
notify({
action: 'unban',
actor: ctx.session.user.username,
target: targetUser.username,
})
return actionOk();
});
return actionOk()
},
)
export const giveBadge = adminAction(
{ permission: PERMS.USERS_EDIT, schema: giveBadgeSchema },
async (ctx) => {
const { userId, badgeCode } = ctx.data;
const { userId, badgeCode } = ctx.data
await guardRank(userId, ctx.session.user.rank);
await guardRank(userId, ctx.session.user.rank)
const existing = await prisma.usersBadges.findFirst({ where: { userId, badgeCode } });
if (existing) throw new ActionError("Badge already assigned");
const existing = await prisma.usersBadges.findFirst({ where: { userId, badgeCode } })
if (existing) throw new ActionError('Badge already assigned')
await prisma.usersBadges.create({ data: { userId, badgeCode } });
await rcon.giveBadge(userId, badgeCode);
await prisma.usersBadges.create({ data: { userId, badgeCode } })
await rcon.giveBadge(userId, badgeCode)
return actionOk();
return actionOk()
},
);
)
// ── Remove Badge ────────────────────────────────────────────────────
const removeBadgeSchema = z.object({
userId: z.coerce.number().int().positive(),
badgeCode: z.string().min(1),
});
})
export const removeBadge = adminAction(
{ permission: PERMS.USERS_EDIT, schema: removeBadgeSchema },
async (ctx) => {
const { userId, badgeCode } = ctx.data;
const { userId, badgeCode } = ctx.data
await guardRank(userId, ctx.session.user.rank);
await guardRank(userId, ctx.session.user.rank)
const existing = await prisma.usersBadges.findFirst({ where: { userId, badgeCode } });
if (!existing) throw new ActionError("Badge not found");
const existing = await prisma.usersBadges.findFirst({ where: { userId, badgeCode } })
if (!existing) throw new ActionError('Badge not found')
await prisma.usersBadges.delete({ where: { id: existing.id } });
await rcon.removeBadge(userId, badgeCode);
await prisma.usersBadges.delete({ where: { id: existing.id } })
await rcon.removeBadge(userId, badgeCode)
return actionOk();
return actionOk()
},
);
)
// ── Rank guard helper ───────────────────────────────────────────────
@@ -252,163 +267,169 @@ async function guardRank(targetUserId: number, sessionRank: number) {
const target = await prisma.user.findUnique({
where: { id: targetUserId },
select: { username: true, rank: true, mail: true },
});
if (!target) throw new ActionError("User not found");
})
if (!target) throw new ActionError('User not found')
if (target.rank >= sessionRank && sessionRank < 7) {
throw new ActionError("Cannot modify user with equal or higher rank");
throw new ActionError('Cannot modify user with equal or higher rank')
}
return target;
return target
}
// ── Reset Password ──────────────────────────────────────────────────
const resetPasswordSchema = z.object({
userId: z.coerce.number().int().positive(),
});
})
export const resetPassword = adminAction(
{ permission: PERMS.USERS_RESET_PASSWORD, schema: resetPasswordSchema },
async (ctx) => {
const target = await guardRank(ctx.data.userId, ctx.session.user.rank);
const target = await guardRank(ctx.data.userId, ctx.session.user.rank)
const newPassword = crypto.randomBytes(12).toString("base64url").slice(0, 16);
const hashed = await hash(newPassword, 10);
const newPassword = crypto.randomBytes(12).toString('base64url').slice(0, 16)
const hashed = await hash(newPassword, 10)
await prisma.user.update({
where: { id: ctx.data.userId },
data: { password: hashed },
});
})
logAudit({
userId: ctx.session.user.id,
action: "reset_password",
target: "User",
action: 'reset_password',
target: 'User',
targetId: ctx.data.userId,
});
})
notify({
action: "user_edit",
action: 'user_edit',
actor: ctx.session.user.username,
target: target.username,
details: "Password reset",
});
details: 'Password reset',
})
return actionOk({ newPassword });
return actionOk({ newPassword })
},
);
)
// ── Disconnect User ─────────────────────────────────────────────────
const disconnectSchema = z.object({
userId: z.coerce.number().int().positive(),
});
})
export const disconnectUser = adminAction(
{ permission: PERMS.USERS_EDIT, schema: disconnectSchema },
async (ctx) => {
const target = await guardRank(ctx.data.userId, ctx.session.user.rank);
const success = await rcon.disconnectUser(ctx.data.userId);
if (!success) throw new ActionError("Failed to disconnect. Is the emulator running?");
const target = await guardRank(ctx.data.userId, ctx.session.user.rank)
const success = await rcon.disconnectUser(ctx.data.userId)
if (!success) throw new ActionError('Failed to disconnect. Is the emulator running?')
logAudit({
userId: ctx.session.user.id,
action: "user_disconnect",
target: "User",
action: 'user_disconnect',
target: 'User',
targetId: ctx.data.userId,
});
})
notify({
action: "disconnect",
action: 'disconnect',
actor: ctx.session.user.username,
target: target.username,
});
})
return actionOk();
return actionOk()
},
);
)
// ── Alert User (in-game message) ────────────────────────────────────
const alertUserSchema = z.object({
userId: z.coerce.number().int().positive(),
message: z.string().min(1).max(500),
});
})
export const alertUser = adminAction(
{ permission: PERMS.USERS_EDIT, schema: alertUserSchema },
async (ctx) => {
const success = await rcon.alertUser(ctx.data.userId, ctx.data.message);
if (!success) throw new ActionError("Failed to send alert. Is the emulator running?");
return actionOk();
const success = await rcon.alertUser(ctx.data.userId, ctx.data.message)
if (!success) throw new ActionError('Failed to send alert. Is the emulator running?')
return actionOk()
},
);
)
// ── Mute User ───────────────────────────────────────────────────────
const muteSchema = z.object({
userId: z.coerce.number().int().positive(),
duration: z.coerce.number().int().min(0).default(0),
});
})
export const muteUser = adminAction({ permission: PERMS.USERS_EDIT, schema: muteSchema }, async (ctx) => {
const _target = await guardRank(ctx.data.userId, ctx.session.user.rank);
const success = await rcon.muteUser(ctx.data.userId, ctx.data.duration);
if (!success) throw new ActionError("Failed to mute. Is the emulator running?");
export const muteUser = adminAction(
{ permission: PERMS.USERS_EDIT, schema: muteSchema },
async (ctx) => {
const _target = await guardRank(ctx.data.userId, ctx.session.user.rank)
const success = await rcon.muteUser(ctx.data.userId, ctx.data.duration)
if (!success) throw new ActionError('Failed to mute. Is the emulator running?')
logAudit({
userId: ctx.session.user.id,
action: "user_mute",
target: "User",
targetId: ctx.data.userId,
after: { duration: ctx.data.duration },
});
logAudit({
userId: ctx.session.user.id,
action: 'user_mute',
target: 'User',
targetId: ctx.data.userId,
after: { duration: ctx.data.duration },
})
return actionOk();
});
return actionOk()
},
)
// ── Unmute User ─────────────────────────────────────────────────────
const unmuteSchema = z.object({
userId: z.coerce.number().int().positive(),
});
})
export const unmuteUser = adminAction({ permission: PERMS.USERS_EDIT, schema: unmuteSchema }, async (ctx) => {
await guardRank(ctx.data.userId, ctx.session.user.rank);
const success = await rcon.unmuteUser(ctx.data.userId);
if (!success) throw new ActionError("Failed to unmute. Is the emulator running?");
export const unmuteUser = adminAction(
{ permission: PERMS.USERS_EDIT, schema: unmuteSchema },
async (ctx) => {
await guardRank(ctx.data.userId, ctx.session.user.rank)
const success = await rcon.unmuteUser(ctx.data.userId)
if (!success) throw new ActionError('Failed to unmute. Is the emulator running?')
logAudit({
userId: ctx.session.user.id,
action: "user_unmute",
target: "User",
targetId: ctx.data.userId,
});
logAudit({
userId: ctx.session.user.id,
action: 'user_unmute',
target: 'User',
targetId: ctx.data.userId,
})
return actionOk();
});
return actionOk()
},
)
// ── Send Credits via RCON ───────────────────────────────────────────
const sendCreditsSchema = z.object({
userId: z.coerce.number().int().positive(),
amount: z.coerce.number().int().min(1).max(1000000),
});
})
export const sendCredits = adminAction(
{ permission: PERMS.USERS_EDIT, schema: sendCreditsSchema },
async (ctx) => {
const _target = await guardRank(ctx.data.userId, ctx.session.user.rank);
const success = await rcon.giveCredits(ctx.data.userId, ctx.data.amount);
if (!success) throw new ActionError("Failed to send credits. Is the emulator running?");
const _target = await guardRank(ctx.data.userId, ctx.session.user.rank)
const success = await rcon.giveCredits(ctx.data.userId, ctx.data.amount)
if (!success) throw new ActionError('Failed to send credits. Is the emulator running?')
logAudit({
userId: ctx.session.user.id,
action: "user_send_credits",
target: "User",
action: 'user_send_credits',
target: 'User',
targetId: ctx.data.userId,
after: { amount: ctx.data.amount },
});
})
return actionOk();
return actionOk()
},
);
)