Enhance environment configuration and expand Prisma schema for bot management
- Added OWNER_USER_IDS to .env.example for specifying global bot owners. - Introduced new models in the Prisma schema: CommandOverride, OwnerTeamMember, GlobalUserBlacklist, GuildBlacklist, FeatureFlag, BotPresenceConfig, ChangelogEntry, and OwnerAuditLog to support advanced bot management features. - Updated env.ts to preprocess OWNER_USER_IDS for better handling of global bot owner IDs. - Modified ModulePlaceholderPage to ensure proper routing for remaining dashboard modules.
This commit is contained in:
46
apps/webui/src/app/api/guilds/[guildId]/automod/route.ts
Normal file
46
apps/webui/src/app/api/guilds/[guildId]/automod/route.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { AutomodConfigDashboardPatchSchema } from '@nexumi/shared';
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
import { requireGuildAccess, toApiErrorResponse } from '@/lib/auth';
|
||||
import { writeDashboardAudit } from '@/lib/audit';
|
||||
import { getAutomodDashboard, updateAutomodDashboard } from '@/lib/module-configs/automod';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
|
||||
interface RouteParams {
|
||||
params: Promise<{ guildId: string }>;
|
||||
}
|
||||
|
||||
export async function GET(_request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
await requireGuildAccess(guildId);
|
||||
const config = await getAutomodDashboard(guildId);
|
||||
return NextResponse.json(config);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function PATCH(request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
const session = await requireGuildAccess(guildId);
|
||||
const body = await request.json();
|
||||
const patch = AutomodConfigDashboardPatchSchema.parse(body);
|
||||
|
||||
const before = await getAutomodDashboard(guildId);
|
||||
const after = await updateAutomodDashboard(guildId, patch);
|
||||
|
||||
await writeDashboardAudit(prisma, {
|
||||
guildId,
|
||||
actorUserId: session.user.id,
|
||||
action: 'automod.update',
|
||||
path: `/dashboard/${guildId}/automod`,
|
||||
before,
|
||||
after
|
||||
});
|
||||
|
||||
return NextResponse.json(after);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
25
apps/webui/src/app/api/guilds/[guildId]/cases/route.ts
Normal file
25
apps/webui/src/app/api/guilds/[guildId]/cases/route.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { CaseListQuerySchema } from '@nexumi/shared';
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
import { requireGuildAccess, toApiErrorResponse } from '@/lib/auth';
|
||||
import { listCases } from '@/lib/module-configs/cases';
|
||||
|
||||
interface RouteParams {
|
||||
params: Promise<{ guildId: string }>;
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
await requireGuildAccess(guildId);
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const query = CaseListQuerySchema.parse({
|
||||
search: searchParams.get('search') ?? undefined,
|
||||
action: searchParams.get('action') ?? undefined,
|
||||
limit: searchParams.get('limit') ?? undefined
|
||||
});
|
||||
const cases = await listCases(guildId, query);
|
||||
return NextResponse.json({ cases });
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
46
apps/webui/src/app/api/guilds/[guildId]/economy/route.ts
Normal file
46
apps/webui/src/app/api/guilds/[guildId]/economy/route.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { EconomyConfigDashboardPatchSchema } from '@nexumi/shared';
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
import { requireGuildAccess, toApiErrorResponse } from '@/lib/auth';
|
||||
import { writeDashboardAudit } from '@/lib/audit';
|
||||
import { getEconomyDashboard, updateEconomyDashboard } from '@/lib/module-configs/economy';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
|
||||
interface RouteParams {
|
||||
params: Promise<{ guildId: string }>;
|
||||
}
|
||||
|
||||
export async function GET(_request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
await requireGuildAccess(guildId);
|
||||
const config = await getEconomyDashboard(guildId);
|
||||
return NextResponse.json(config);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function PATCH(request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
const session = await requireGuildAccess(guildId);
|
||||
const body = await request.json();
|
||||
const patch = EconomyConfigDashboardPatchSchema.parse(body);
|
||||
|
||||
const before = await getEconomyDashboard(guildId);
|
||||
const after = await updateEconomyDashboard(guildId, patch);
|
||||
|
||||
await writeDashboardAudit(prisma, {
|
||||
guildId,
|
||||
actorUserId: session.user.id,
|
||||
action: 'economy.update',
|
||||
path: `/dashboard/${guildId}/economy`,
|
||||
before,
|
||||
after
|
||||
});
|
||||
|
||||
return NextResponse.json(after);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
46
apps/webui/src/app/api/guilds/[guildId]/fun/route.ts
Normal file
46
apps/webui/src/app/api/guilds/[guildId]/fun/route.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { FunConfigDashboardPatchSchema } from '@nexumi/shared';
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
import { requireGuildAccess, toApiErrorResponse } from '@/lib/auth';
|
||||
import { writeDashboardAudit } from '@/lib/audit';
|
||||
import { getFunDashboard, updateFunDashboard } from '@/lib/module-configs/fun';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
|
||||
interface RouteParams {
|
||||
params: Promise<{ guildId: string }>;
|
||||
}
|
||||
|
||||
export async function GET(_request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
await requireGuildAccess(guildId);
|
||||
const config = await getFunDashboard(guildId);
|
||||
return NextResponse.json(config);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function PATCH(request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
const session = await requireGuildAccess(guildId);
|
||||
const body = await request.json();
|
||||
const patch = FunConfigDashboardPatchSchema.parse(body);
|
||||
|
||||
const before = await getFunDashboard(guildId);
|
||||
const after = await updateFunDashboard(guildId, patch);
|
||||
|
||||
await writeDashboardAudit(prisma, {
|
||||
guildId,
|
||||
actorUserId: session.user.id,
|
||||
action: 'fun.update',
|
||||
path: `/dashboard/${guildId}/fun`,
|
||||
before,
|
||||
after
|
||||
});
|
||||
|
||||
return NextResponse.json(after);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
46
apps/webui/src/app/api/guilds/[guildId]/leveling/route.ts
Normal file
46
apps/webui/src/app/api/guilds/[guildId]/leveling/route.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { LevelingConfigDashboardPatchSchema } from '@nexumi/shared';
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
import { requireGuildAccess, toApiErrorResponse } from '@/lib/auth';
|
||||
import { writeDashboardAudit } from '@/lib/audit';
|
||||
import { getLevelingDashboard, updateLevelingDashboard } from '@/lib/module-configs/leveling';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
|
||||
interface RouteParams {
|
||||
params: Promise<{ guildId: string }>;
|
||||
}
|
||||
|
||||
export async function GET(_request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
await requireGuildAccess(guildId);
|
||||
const config = await getLevelingDashboard(guildId);
|
||||
return NextResponse.json(config);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function PATCH(request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
const session = await requireGuildAccess(guildId);
|
||||
const body = await request.json();
|
||||
const patch = LevelingConfigDashboardPatchSchema.parse(body);
|
||||
|
||||
const before = await getLevelingDashboard(guildId);
|
||||
const after = await updateLevelingDashboard(guildId, patch);
|
||||
|
||||
await writeDashboardAudit(prisma, {
|
||||
guildId,
|
||||
actorUserId: session.user.id,
|
||||
action: 'leveling.update',
|
||||
path: `/dashboard/${guildId}/leveling`,
|
||||
before,
|
||||
after
|
||||
});
|
||||
|
||||
return NextResponse.json(after);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
46
apps/webui/src/app/api/guilds/[guildId]/logging/route.ts
Normal file
46
apps/webui/src/app/api/guilds/[guildId]/logging/route.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { LoggingConfigDashboardPatchSchema } from '@nexumi/shared';
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
import { requireGuildAccess, toApiErrorResponse } from '@/lib/auth';
|
||||
import { writeDashboardAudit } from '@/lib/audit';
|
||||
import { getLoggingDashboard, updateLoggingDashboard } from '@/lib/module-configs/logging';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
|
||||
interface RouteParams {
|
||||
params: Promise<{ guildId: string }>;
|
||||
}
|
||||
|
||||
export async function GET(_request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
await requireGuildAccess(guildId);
|
||||
const config = await getLoggingDashboard(guildId);
|
||||
return NextResponse.json(config);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function PATCH(request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
const session = await requireGuildAccess(guildId);
|
||||
const body = await request.json();
|
||||
const patch = LoggingConfigDashboardPatchSchema.parse(body);
|
||||
|
||||
const before = await getLoggingDashboard(guildId);
|
||||
const after = await updateLoggingDashboard(guildId, patch);
|
||||
|
||||
await writeDashboardAudit(prisma, {
|
||||
guildId,
|
||||
actorUserId: session.user.id,
|
||||
action: 'logging.update',
|
||||
path: `/dashboard/${guildId}/logging`,
|
||||
before,
|
||||
after
|
||||
});
|
||||
|
||||
return NextResponse.json(after);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
46
apps/webui/src/app/api/guilds/[guildId]/moderation/route.ts
Normal file
46
apps/webui/src/app/api/guilds/[guildId]/moderation/route.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { ModerationDashboardPatchSchema } from '@nexumi/shared';
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
import { requireGuildAccess, toApiErrorResponse } from '@/lib/auth';
|
||||
import { writeDashboardAudit } from '@/lib/audit';
|
||||
import { getModerationDashboard, updateModerationDashboard } from '@/lib/module-configs/moderation';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
|
||||
interface RouteParams {
|
||||
params: Promise<{ guildId: string }>;
|
||||
}
|
||||
|
||||
export async function GET(_request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
await requireGuildAccess(guildId);
|
||||
const config = await getModerationDashboard(guildId);
|
||||
return NextResponse.json(config);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function PATCH(request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
const session = await requireGuildAccess(guildId);
|
||||
const body = await request.json();
|
||||
const patch = ModerationDashboardPatchSchema.parse(body);
|
||||
|
||||
const before = await getModerationDashboard(guildId);
|
||||
const after = await updateModerationDashboard(guildId, patch);
|
||||
|
||||
await writeDashboardAudit(prisma, {
|
||||
guildId,
|
||||
actorUserId: session.user.id,
|
||||
action: 'moderation.update',
|
||||
path: `/dashboard/${guildId}/moderation`,
|
||||
before,
|
||||
after
|
||||
});
|
||||
|
||||
return NextResponse.json(after);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { VerificationConfigDashboardPatchSchema } from '@nexumi/shared';
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
import { requireGuildAccess, toApiErrorResponse } from '@/lib/auth';
|
||||
import { writeDashboardAudit } from '@/lib/audit';
|
||||
import { getVerificationDashboard, updateVerificationDashboard } from '@/lib/module-configs/verification';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
|
||||
interface RouteParams {
|
||||
params: Promise<{ guildId: string }>;
|
||||
}
|
||||
|
||||
export async function GET(_request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
await requireGuildAccess(guildId);
|
||||
const config = await getVerificationDashboard(guildId);
|
||||
return NextResponse.json(config);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function PATCH(request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
const session = await requireGuildAccess(guildId);
|
||||
const body = await request.json();
|
||||
const patch = VerificationConfigDashboardPatchSchema.parse(body);
|
||||
|
||||
const before = await getVerificationDashboard(guildId);
|
||||
const after = await updateVerificationDashboard(guildId, patch);
|
||||
|
||||
await writeDashboardAudit(prisma, {
|
||||
guildId,
|
||||
actorUserId: session.user.id,
|
||||
action: 'verification.update',
|
||||
path: `/dashboard/${guildId}/verification`,
|
||||
before,
|
||||
after
|
||||
});
|
||||
|
||||
return NextResponse.json(after);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
23
apps/webui/src/app/api/guilds/[guildId]/warnings/route.ts
Normal file
23
apps/webui/src/app/api/guilds/[guildId]/warnings/route.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { WarningListQuerySchema } from '@nexumi/shared';
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
import { requireGuildAccess, toApiErrorResponse } from '@/lib/auth';
|
||||
import { listWarnings } from '@/lib/module-configs/warnings';
|
||||
|
||||
interface RouteParams {
|
||||
params: Promise<{ guildId: string }>;
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
await requireGuildAccess(guildId);
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const query = WarningListQuerySchema.parse({
|
||||
userId: searchParams.get('userId') ?? undefined
|
||||
});
|
||||
const warnings = await listWarnings(guildId, query);
|
||||
return NextResponse.json({ warnings });
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
46
apps/webui/src/app/api/guilds/[guildId]/welcome/route.ts
Normal file
46
apps/webui/src/app/api/guilds/[guildId]/welcome/route.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { WelcomeConfigDashboardPatchSchema } from '@nexumi/shared';
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
import { requireGuildAccess, toApiErrorResponse } from '@/lib/auth';
|
||||
import { writeDashboardAudit } from '@/lib/audit';
|
||||
import { getWelcomeDashboard, updateWelcomeDashboard } from '@/lib/module-configs/welcome';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
|
||||
interface RouteParams {
|
||||
params: Promise<{ guildId: string }>;
|
||||
}
|
||||
|
||||
export async function GET(_request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
await requireGuildAccess(guildId);
|
||||
const config = await getWelcomeDashboard(guildId);
|
||||
return NextResponse.json(config);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function PATCH(request: NextRequest, { params }: RouteParams) {
|
||||
const { guildId } = await params;
|
||||
try {
|
||||
const session = await requireGuildAccess(guildId);
|
||||
const body = await request.json();
|
||||
const patch = WelcomeConfigDashboardPatchSchema.parse(body);
|
||||
|
||||
const before = await getWelcomeDashboard(guildId);
|
||||
const after = await updateWelcomeDashboard(guildId, patch);
|
||||
|
||||
await writeDashboardAudit(prisma, {
|
||||
guildId,
|
||||
actorUserId: session.user.id,
|
||||
action: 'welcome.update',
|
||||
path: `/dashboard/${guildId}/welcome`,
|
||||
before,
|
||||
after
|
||||
});
|
||||
|
||||
return NextResponse.json(after);
|
||||
} catch (error) {
|
||||
return toApiErrorResponse(error);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user