feat: admin controls
This commit is contained in:
@@ -1,68 +1,63 @@
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { providers } from "~~/drizzle/schema";
|
||||
import { and, eq, gt } from "drizzle-orm";
|
||||
import * as z from "zod";
|
||||
import { providers, sessions } from "~~/drizzle/schema";
|
||||
import { lockAccount } from "~~/server/lib/account-security";
|
||||
import { db } from "~~/server/lib/db";
|
||||
import * as z from 'zod';
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
await protectRoute(event);
|
||||
|
||||
const userId = event.context.user!.id;
|
||||
|
||||
const result = await readValidatedBody(event, (body) =>
|
||||
z
|
||||
.object({
|
||||
config: z.object({
|
||||
apiKey: z.string().optional(),
|
||||
apiProxyUrl: z.string().optional(),
|
||||
}).optional(),
|
||||
enabled: z.boolean().optional(),
|
||||
})
|
||||
.safeParse(body),
|
||||
);
|
||||
const { user, session } = await protectRoute(event);
|
||||
const result = await readValidatedBody(event, (body) => z.object({
|
||||
config: z.object({
|
||||
apiKey: z.string().optional(),
|
||||
apiProxyUrl: z.string().optional(),
|
||||
}).optional(),
|
||||
expectedUpdatedAt: z.iso.datetime().optional(),
|
||||
enabled: z.boolean().optional(),
|
||||
}).refine(body => body.config === undefined || body.expectedUpdatedAt !== undefined, {
|
||||
message: "Provider config updates require a revision",
|
||||
}).safeParse(body));
|
||||
|
||||
if (!result.success) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: result.error.issues[0]!.message,
|
||||
});
|
||||
throw createError({ statusCode: 400, message: result.error.issues[0]?.message });
|
||||
}
|
||||
|
||||
const providerId = getRouterParam(event, 'providerId')!;
|
||||
|
||||
const { config, enabled } = result.data;
|
||||
|
||||
const existing = await db.query.providers.findFirst({
|
||||
where: {
|
||||
id: providerId,
|
||||
userId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Invalid provider',
|
||||
});
|
||||
const providerId = getRouterParam(event, "providerId");
|
||||
if (!providerId) {
|
||||
throw createError({ statusCode: 400, statusMessage: "Invalid provider" });
|
||||
}
|
||||
const { config, enabled, expectedUpdatedAt } = result.data;
|
||||
|
||||
const res = await db.update(providers)
|
||||
.set({
|
||||
config,
|
||||
return await db.transaction(async (tx) => {
|
||||
await lockAccount(tx, user.id);
|
||||
// A request may have waited behind a password rotation or recovery.
|
||||
const [activeSession] = await tx.select({ id: sessions.id }).from(sessions).where(and(
|
||||
eq(sessions.id, session.id), eq(sessions.userId, user.id), gt(sessions.expiresAt, new Date()),
|
||||
));
|
||||
if (!activeSession) {
|
||||
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
||||
}
|
||||
|
||||
const [existing] = await tx.select().from(providers).where(and(
|
||||
eq(providers.id, providerId), eq(providers.userId, user.id),
|
||||
));
|
||||
if (!existing) {
|
||||
throw createError({ statusCode: 404, statusMessage: "Invalid provider" });
|
||||
}
|
||||
if (config !== undefined && existing.updatedAt.getTime() !== new Date(expectedUpdatedAt ?? "").getTime()) {
|
||||
throw createError({
|
||||
statusCode: 409,
|
||||
statusMessage: "Provider config out of date",
|
||||
data: { code: "PROVIDER_KEYS_OUT_OF_DATE" },
|
||||
});
|
||||
}
|
||||
|
||||
const updatedAt = new Date(Math.max(Date.now(), existing.updatedAt.getTime() + 1));
|
||||
await tx.update(providers).set({
|
||||
config: config === undefined ? undefined : { ...existing.config, ...config },
|
||||
enabled,
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
eq(providers.id, providerId),
|
||||
eq(providers.userId, userId),
|
||||
)
|
||||
);
|
||||
updatedAt,
|
||||
}).where(and(eq(providers.id, providerId), eq(providers.userId, user.id)));
|
||||
|
||||
if (res.rowCount === 0) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Invalid provider',
|
||||
});
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
});
|
||||
return { ok: true, updatedAt };
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user