fix: address Bugbot review #3 — mutation invalidation gaps
- Dual-layer cache invalidation on interact mutations (dns-hole, media-requests, smart-home, indexer-manager, anchor-notes) - importOldmarrConfig: invalidate created boards - createBoard: invalidateUserCache when auto-setting homeBoardId - group.savePositions: invalidate user caches for affected members - integration.requestMedia: invalidate integration data cache - user.delete: invalidate user cache
This commit is contained in:
11 files changed
+31
-4
No files matched your search
@@ -326,6 +326,7 @@ export const boardRouter = createTRPCRouter({
|
||||
|
||||
if (!user?.homeBoardId) {
|
||||
await ctx.db.update(users).set({ homeBoardId: boardId }).where(eq(users.id, ctx.session.user.id));
|
||||
invalidateUserCache(ctx.session.user.id);
|
||||
}
|
||||
|
||||
invalidateBoardCache(boardId, input.name);
|
||||
@@ -1497,7 +1498,10 @@ export const boardRouter = createTRPCRouter({
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const content = await input.file.text();
|
||||
const oldmarr = oldmarrConfigSchema.parse(JSON.parse(content));
|
||||
await importOldmarrAsync(ctx.db, oldmarr, input.configuration);
|
||||
const importedBoards = await importOldmarrAsync(ctx.db, oldmarr, input.configuration);
|
||||
for (const board of importedBoards) {
|
||||
invalidateBoardCache(board.id, board.name);
|
||||
}
|
||||
}),
|
||||
addItem: protectedProcedure
|
||||
.meta({
|
||||
|
||||
@@ -4,7 +4,7 @@ import { z } from "zod/v4";
|
||||
import { canManageGroupMembersLocally, isGroupMembershipManagedLocally } from "@homarr/auth/server";
|
||||
import { createId } from "@homarr/common";
|
||||
import type { Database } from "@homarr/db";
|
||||
import { and, eq, handleTransactionsAsync, like, not } from "@homarr/db";
|
||||
import { and, eq, handleTransactionsAsync, inArray, like, not } from "@homarr/db";
|
||||
import { getMaxGroupPositionAsync } from "@homarr/db/queries";
|
||||
import { groupMembers, groupPermissions, groups, users } from "@homarr/db/schema";
|
||||
import { everyoneGroup } from "@homarr/definitions";
|
||||
@@ -277,6 +277,14 @@ export const groupRouter = createTRPCRouter({
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const members = await ctx.db.query.groupMembers.findMany({
|
||||
columns: { userId: true },
|
||||
where: inArray(groupMembers.groupId, input.positions),
|
||||
});
|
||||
for (const userId of new Set(members.map((member) => member.userId))) {
|
||||
invalidateUserCache(userId);
|
||||
}
|
||||
}),
|
||||
savePermissions: permissionRequiredProcedure
|
||||
.requiresPermission("admin")
|
||||
|
||||
@@ -38,6 +38,7 @@ import { mediaRequestOptionsSchema, mediaRequestRequestSchema } from "@homarr/va
|
||||
|
||||
import { createOneIntegrationMiddleware } from "../../middlewares/integration";
|
||||
import { invalidateIntegrationCache, invalidateUserCache } from "../../cache-invalidation";
|
||||
import { invalidateIntegrationDataCache } from "../../integration-data-cache";
|
||||
import { createTRPCRouter, permissionRequiredProcedure, protectedProcedure, publicProcedure } from "../../trpc";
|
||||
import { throwIfActionForbiddenAsync } from "./integration-access";
|
||||
import { MissingSecretError, testConnectionAsync } from "./integration-test-connection";
|
||||
@@ -824,7 +825,9 @@ export const integrationRouter = createTRPCRouter({
|
||||
.input(mediaRequestRequestSchema)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const integration = await createIntegrationAsync(ctx.integration);
|
||||
return await integration.requestMediaAsync(input.mediaType, input.mediaId, input.seasons);
|
||||
const result = await integration.requestMediaAsync(input.mediaType, input.mediaId, input.seasons);
|
||||
invalidateIntegrationDataCache(ctx.integration.id);
|
||||
return result;
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -480,6 +480,7 @@ export const userRouter = createTRPCRouter({
|
||||
}
|
||||
|
||||
await ctx.db.delete(users).where(eq(users.id, input.userId));
|
||||
invalidateUserCache(input.userId);
|
||||
}),
|
||||
changePassword: protectedProcedure
|
||||
.input(convertIntersectionToZodObject(userChangePasswordApiSchema))
|
||||
|
||||
@@ -77,6 +77,8 @@ export const anchorNotesRouter = createTRPCRouter({
|
||||
});
|
||||
|
||||
invalidateIntegrationDataCache(ctx.integration.id);
|
||||
anchorNoteRequestHandler.invalidateCache();
|
||||
anchorNotesListRequestHandler.invalidateCache();
|
||||
return updatedNote;
|
||||
} catch (error) {
|
||||
if (error instanceof ResponseError && error.statusCode === 400) {
|
||||
|
||||
@@ -46,6 +46,7 @@ export const dnsHoleRouter = createTRPCRouter({
|
||||
const client = await createIntegrationAsync(integration);
|
||||
await client.enableAsync();
|
||||
invalidateIntegrationDataCache(integration.id);
|
||||
dnsHoleRequestHandler.invalidateCache();
|
||||
}),
|
||||
|
||||
disable: protectedProcedure
|
||||
@@ -66,5 +67,6 @@ export const dnsHoleRouter = createTRPCRouter({
|
||||
const client = await createIntegrationAsync(integration);
|
||||
await client.disableAsync(input.duration);
|
||||
invalidateIntegrationDataCache(integration.id);
|
||||
dnsHoleRequestHandler.invalidateCache();
|
||||
}),
|
||||
});
|
||||
@@ -49,5 +49,6 @@ export const indexerManagerRouter = createTRPCRouter({
|
||||
for (const integration of ctx.integrations) {
|
||||
invalidateIntegrationDataCache(integration.id);
|
||||
}
|
||||
indexerManagerRequestHandler.invalidateCache();
|
||||
}),
|
||||
});
|
||||
@@ -109,5 +109,7 @@ export const mediaRequestsRouter = createTRPCRouter({
|
||||
|
||||
await answerActions[input.answer](input.requestId);
|
||||
invalidateIntegrationDataCache(integration.id);
|
||||
mediaRequestListRequestHandler.invalidateCache();
|
||||
mediaRequestStatsRequestHandler.invalidateCache();
|
||||
}),
|
||||
});
|
||||
@@ -43,6 +43,7 @@ export const smartHomeRouter = createTRPCRouter({
|
||||
const success = await client.triggerToggleAsync(input.entityId);
|
||||
|
||||
invalidateIntegrationDataCache(integration.id);
|
||||
smartHomeEntityStateRequestHandler.invalidateCache();
|
||||
return success;
|
||||
}),
|
||||
executeAutomation: protectedProcedure
|
||||
@@ -59,5 +60,6 @@ export const smartHomeRouter = createTRPCRouter({
|
||||
const client = await createIntegrationAsync(integration);
|
||||
await client.triggerAutomationAsync(input.automationId);
|
||||
invalidateIntegrationDataCache(integration.id);
|
||||
smartHomeEntityStateRequestHandler.invalidateCache();
|
||||
}),
|
||||
});
|
||||
@@ -39,4 +39,6 @@ export const importSingleOldmarrConfigAsync = async (
|
||||
boardInsertCollection.insertAll(db);
|
||||
},
|
||||
});
|
||||
|
||||
return boardInsertCollection.boards.map(({ id, name }) => ({ id, name }));
|
||||
};
|
||||
@@ -9,5 +9,5 @@ export const importOldmarrAsync = async (
|
||||
old: OldmarrConfig,
|
||||
configuration: OldmarrImportConfiguration,
|
||||
) => {
|
||||
await importSingleOldmarrConfigAsync(db, old, configuration);
|
||||
return await importSingleOldmarrConfigAsync(db, old, configuration);
|
||||
};
|
||||
Reference in new issue
Block a user