From d56286adf990000c0b8ead4f4a4ef28380317247 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Wed, 18 Feb 2026 19:33:44 +0100 Subject: [PATCH] block git commands on uncloned repos --- .../api/repos/[npub]/[repo]/file/+server.ts | 27 ++++- src/routes/repos/[npub]/[repo]/+page.svelte | 106 +++++++++++++----- 2 files changed, 105 insertions(+), 28 deletions(-) diff --git a/src/routes/api/repos/[npub]/[repo]/file/+server.ts b/src/routes/api/repos/[npub]/[repo]/file/+server.ts index fd83dd7..95fdfdc 100644 --- a/src/routes/api/repos/[npub]/[repo]/file/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/file/+server.ts @@ -189,8 +189,33 @@ export const POST: RequestHandler = async ({ params, url, request }: { params: { return error(401, 'Authentication required. Please provide userPubkey.'); } + // Check if repo exists locally if (!fileManager.repoExists(npub, repo)) { - return error(404, 'Repository not found'); + // Try to fetch announcement to see if repo exists in Nostr + let repoOwnerPubkey: string; + try { + repoOwnerPubkey = requireNpubHex(npub); + } catch { + return error(400, 'Invalid npub format'); + } + + // Fetch repository announcement from Nostr + const events = await nostrClient.fetchEvents([ + { + kinds: [KIND.REPO_ANNOUNCEMENT], + authors: [repoOwnerPubkey], + '#d': [repo], + limit: 1 + } + ]); + + if (events.length > 0) { + // Repository exists in Nostr but is not cloned locally + // For file editing, we need a local clone + return error(404, 'Repository is not cloned locally. To edit files, the repository must be cloned to the server first. Please use the "Clone to Server" button if you have unlimited access, or contact a server administrator.'); + } else { + return error(404, 'Repository not found'); + } } // Check if user is a maintainer diff --git a/src/routes/repos/[npub]/[repo]/+page.svelte b/src/routes/repos/[npub]/[repo]/+page.svelte index 874d62b..f5b8845 100644 --- a/src/routes/repos/[npub]/[repo]/+page.svelte +++ b/src/routes/repos/[npub]/[repo]/+page.svelte @@ -138,6 +138,10 @@ let checkingCloneStatus = $state(false); let cloning = $state(false); + // Helper: Check if repo needs to be cloned for write operations + const needsClone = $derived(isRepoCloned === false); + const cloneTooltip = 'Please clone this repo to use this feature.'; + // Verification status let verificationStatus = $state<{ verified: boolean; error?: string; message?: string } | null>(null); let showVerificationDialog = $state(false); @@ -1292,10 +1296,8 @@ await checkMaintainerStatus(); await loadBookmarkStatus(); - // Check clone status if user has unlimited access - if (hasUnlimitedAccess($userStore.userLevel)) { - await checkCloneStatus(); - } + // Check clone status (needed to disable write operations) + await checkCloneStatus(); await checkVerification(); await loadReadme(); await loadForkInfo(); @@ -1766,8 +1768,9 @@ }); if (!response.ok) { - const errorData = await response.json(); - throw new Error(errorData.message || 'Failed to save file'); + const errorData = await response.json().catch(() => ({ message: response.statusText })); + const errorMessage = errorData.message || errorData.error || 'Failed to save file'; + throw new Error(errorMessage); } // Reload file to get updated content @@ -2400,10 +2403,15 @@ {/if} {#if isMaintainer} - + {/if} {/if} @@ -2503,10 +2511,15 @@ {/if} {#if userPubkey && isMaintainer} - + {/if} {#if userPubkey && isMaintainer && file.type === 'file'} - {/if} @@ -2589,10 +2610,15 @@

Tags

{#if userPubkey && isMaintainer} - + {/if}
{#if tags.length === 0} @@ -2739,10 +2765,15 @@ ● Unsaved changes {/if} {#if isMaintainer} - {:else if userPubkey} @@ -2771,6 +2802,7 @@ content={editedContent} language={fileLanguage} onChange={handleContentChange} + readOnly={needsClone} /> {:else}
@@ -3207,7 +3239,12 @@ @@ -3249,7 +3286,12 @@ @@ -3290,7 +3332,12 @@ @@ -3506,7 +3553,12 @@