Skip to content

Commit ae921c3

Browse files
fix(permissions): scope instance dispose to the repo directory
Drop the session-list cache invalidation from ResetPermissionsDialog: disposing the OpenCode instance does not change the persisted session list, so the refetch was unnecessary, and the prior cache key no longer matched the paginated shape. Guard the reset-permissions route against an empty repo directory so it never forwards /instance/dispose without a directory, which would dispose the entire server instead of only the repo's instance.
1 parent 553621b commit ae921c3

3 files changed

Lines changed: 65 additions & 6 deletions

File tree

backend/src/routes/repos.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,11 @@ app.get('/', async (c) => {
470470
if (!repo) {
471471
return c.json({ error: 'Repo not found' }, 404)
472472
}
473-
473+
474+
if (!repo.fullPath) {
475+
return c.json({ error: 'Repo has no directory to reset' }, 400)
476+
}
477+
474478
const response = await openCodeClient.forward({
475479
method: 'POST',
476480
path: '/instance/dispose',

backend/test/routes/repos.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,4 +248,63 @@ describe('Repo Routes', () => {
248248
expect(res.status).toBe(500)
249249
})
250250
})
251+
252+
describe('POST /:id/reset-permissions', () => {
253+
it('should return 404 when repo not found', async () => {
254+
vi.mocked(db.getRepoById).mockReturnValue(null)
255+
256+
const app = createRepoRoutes(mockDb, mockGitAuthService, mockScheduleService, createStubOpenCodeClient())
257+
const res = await app.request('/1/reset-permissions', { method: 'POST' })
258+
259+
expect(res.status).toBe(404)
260+
})
261+
262+
it('should return 400 without disposing when repo has no directory', async () => {
263+
const mockRepo = {
264+
id: 1,
265+
repoUrl: undefined,
266+
localPath: 'assistant',
267+
fullPath: '',
268+
sourcePath: undefined,
269+
branch: undefined,
270+
defaultBranch: 'main',
271+
cloneStatus: 'ready' as const,
272+
clonedAt: Date.now(),
273+
}
274+
vi.mocked(db.getRepoById).mockReturnValue(mockRepo)
275+
276+
const forward = vi.fn(async () => new Response(JSON.stringify(true), { status: 200 }))
277+
const app = createRepoRoutes(mockDb, mockGitAuthService, mockScheduleService, createStubOpenCodeClient({ forward }))
278+
const res = await app.request('/1/reset-permissions', { method: 'POST' })
279+
280+
expect(res.status).toBe(400)
281+
expect(forward).not.toHaveBeenCalled()
282+
})
283+
284+
it('should dispose only the repo directory and return success', async () => {
285+
const mockRepo = {
286+
id: 1,
287+
repoUrl: 'https://github.com/test/repo',
288+
localPath: 'repos/test-repo',
289+
fullPath: '/tmp/test-repo',
290+
sourcePath: '/tmp/test-repo/.git',
291+
branch: 'main',
292+
defaultBranch: 'main',
293+
cloneStatus: 'ready' as const,
294+
clonedAt: Date.now(),
295+
}
296+
vi.mocked(db.getRepoById).mockReturnValue(mockRepo)
297+
298+
const forward = vi.fn(async () => new Response(JSON.stringify(true), { status: 200 }))
299+
const app = createRepoRoutes(mockDb, mockGitAuthService, mockScheduleService, createStubOpenCodeClient({ forward }))
300+
const res = await app.request('/1/reset-permissions', { method: 'POST' })
301+
302+
expect(res.status).toBe(200)
303+
expect(forward).toHaveBeenCalledWith({
304+
method: 'POST',
305+
path: '/instance/dispose',
306+
directory: '/tmp/test-repo',
307+
})
308+
})
309+
})
251310
})

frontend/src/components/repo/ResetPermissionsDialog.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { useMutation, useQueryClient } from "@tanstack/react-query";
1+
import { useMutation } from "@tanstack/react-query";
22
import { resetRepoPermissions } from "@/api/repos";
3-
import { invalidateSessionListCaches } from "@/lib/queryInvalidation";
43
import {
54
Dialog,
65
DialogContent,
@@ -24,12 +23,9 @@ export function ResetPermissionsDialog({
2423
onOpenChange,
2524
repoId,
2625
}: ResetPermissionsDialogProps) {
27-
const queryClient = useQueryClient();
28-
2926
const resetPermissionsMutation = useMutation({
3027
mutationFn: () => resetRepoPermissions(repoId),
3128
onSuccess: () => {
32-
invalidateSessionListCaches(queryClient);
3329
showToast.success("Permissions reset successfully");
3430
onOpenChange(false);
3531
},

0 commit comments

Comments
 (0)