Merge remote-tracking branch 'origin/release/v2' into feat/v2-release-readiness

This commit is contained in:
ajnart committed 2026-09-12 10:50:31 +02:00
commit 8bf8c5e0ea
47 files changed
+2176 -145

No files matched your search

+22 -1
View File
@@ -454,7 +454,7 @@ export const userRouter = createTRPCRouter({
}
const user = await ctx.db.query.users.findFirst({
columns: { email: true, provider: true },
columns: { name: true, email: true, provider: true },
where: eq(users.id, input.id),
});
@@ -472,6 +472,13 @@ export const userRouter = createTRPCRouter({
});
}
if (isDemoMode && input.name !== user.name) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Username changes are disabled in demo mode",
});
}
await checkUsernameAlreadyTakenAndThrowAsync(ctx.db, "credentials", input.name, input.id);
const emailDirty = input.email && user.email !== input.email;
@@ -497,6 +504,13 @@ export const userRouter = createTRPCRouter({
mcp: { enabled: true, description: "Delete a user by ID. REQUIRED: userId (string)" },
})
.mutation(async ({ input, ctx }) => {
if (isDemoMode) {
throw new TRPCError({
code: "FORBIDDEN",
message: "User deletion is disabled in demo mode",
});
}
// Only admins and user itself can delete a user
if (ctx.session.user.id !== input.userId && !ctx.session.user.permissions.includes("admin")) {
throw new TRPCError({
@@ -519,6 +533,13 @@ export const userRouter = createTRPCRouter({
},
})
.mutation(async ({ ctx, input }) => {
if (isDemoMode) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Password changes are disabled in demo mode",
});
}
const user = ctx.session.user;
// Only admins can change other users' passwords
if (!user.permissions.includes("admin") && user.id !== input.userId) {
+1
View File
@@ -46,4 +46,5 @@ export const widgetRouter = createTRPCRouter({
customApi: lazy(() => import("./custom-api").then((mod) => mod.customApiRouter)),
secrets: lazy(() => import("./widget-secrets").then((mod) => mod.widgetSecretsRouter)),
wud: lazy(() => import("./wud").then((mod) => mod.wudRouter)),
llamacpp: lazy(() => import("./llamacpp").then((mod) => mod.llamacppRouter)),
});
@@ -0,0 +1,29 @@
import { llamacppStatsRequestHandler } from "@homarr/request-handler/llamacpp";
import { mockWidgetData } from "@homarr/integrations";
import { createOneWidgetIntegrationMiddleware } from "../../middlewares/integration";
import { createTRPCRouter, publicProcedure } from "../../trpc";
export const llamacppRouter = createTRPCRouter({
getStats: publicProcedure
.meta({
mcp: {
enabled: true,
description:
"Returns health, loaded model and Prometheus metrics (generation speed, token counts, request queue) from a llama.cpp (llama-server) integration. REQUIRED: integrationId from integration_all. The caller needs query permission for that integration.",
},
})
.concat(createOneWidgetIntegrationMiddleware("query", "llamacpp"))
.query(async ({ ctx }) => {
if (ctx.integration.kind === "mock") {
return { stats: mockWidgetData.llamacpp, updatedAt: new Date(mockWidgetData.timestamp) };
}
const handler = llamacppStatsRequestHandler.handler({ ...ctx.integration, kind: "llamacpp" }, {});
const { data, timestamp } = await handler.getDataAsync();
return {
stats: data,
updatedAt: timestamp,
};
}),
});