Browse Source

Bug-fixes

Nostr-Signature: f0bc6f3d6edde211b36392f9752275efe6853f3c3f8eb26a08c8fe2017974bcd 573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc 8545518302ccc35759cb10dcc2b24c6fc93389bfdb88c2a691e681fafcd06802cc702a1f3d784f404075c41c737cb001e1aa422a72d42eb3e4b6f341afe96c27
main
Silberengel 2 weeks ago
parent
commit
0d53bf2366
  1. 1
      nostr/commit-signatures.jsonl
  2. 53
      src/routes/admin/repos/+page.svelte
  3. 38
      src/routes/repos/+page.svelte
  4. 128
      src/routes/repos/[npub]/[repo]/+page.ts
  5. 44
      src/routes/repos/[npub]/[repo]/hooks/use-repo-effects.ts
  6. 27
      src/routes/repos/[npub]/[repo]/read-announcement-from-fs.ts
  7. 19
      src/routes/signup/+page.svelte

1
nostr/commit-signatures.jsonl

@ -131,3 +131,4 @@
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772303976,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fix"]],"content":"Signed commit: bug-fix","id":"a8e7a4f38f815abaa8cc807e43da842cc4715ff41e722ee6657cae57915b753e","sig":"9c427e839796099f8fdfc0dc4a6f4500ecb1835dbf62cf0b1d3dbe8f98c98a1bc7b28266cbc7a993a18f905ec6e57c610e10492c88e7f863319cefb2eab58fdc"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772303976,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fix"]],"content":"Signed commit: bug-fix","id":"a8e7a4f38f815abaa8cc807e43da842cc4715ff41e722ee6657cae57915b753e","sig":"9c427e839796099f8fdfc0dc4a6f4500ecb1835dbf62cf0b1d3dbe8f98c98a1bc7b28266cbc7a993a18f905ec6e57c610e10492c88e7f863319cefb2eab58fdc"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772305338,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","allow forks"]],"content":"Signed commit: allow forks","id":"47f1aa9a47f4488a9babf752466bb2e4cb7974bd67aa827a4b70c57bac839750","sig":"3652f31ee120f894f7dbb04bb2e625dc2f97758b9fe99fa24aa0efe23872851aceb6f460d2c479bbfc9aa495b5c6d993b2ec7d65e21c90ee8ca6a9f23bfac498"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772305338,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","allow forks"]],"content":"Signed commit: allow forks","id":"47f1aa9a47f4488a9babf752466bb2e4cb7974bd67aa827a4b70c57bac839750","sig":"3652f31ee120f894f7dbb04bb2e625dc2f97758b9fe99fa24aa0efe23872851aceb6f460d2c479bbfc9aa495b5c6d993b2ec7d65e21c90ee8ca6a9f23bfac498"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772305971,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","correct search page"]],"content":"Signed commit: correct search page","id":"2a93ec13a9ae177dfd3f4b59cfc7341e9a3a073367b43976ea161802efc76c44","sig":"7821315a6b3b5761c8938fd9b247db0f3344336fbf706b1fb5921ee5645b0f77298cb41a96cd5d1afa8e05c25eb7d64d69d42de585cdd016ad1425ca5b1f4772"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772305971,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","correct search page"]],"content":"Signed commit: correct search page","id":"2a93ec13a9ae177dfd3f4b59cfc7341e9a3a073367b43976ea161802efc76c44","sig":"7821315a6b3b5761c8938fd9b247db0f3344336fbf706b1fb5921ee5645b0f77298cb41a96cd5d1afa8e05c25eb7d64d69d42de585cdd016ad1425ca5b1f4772"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772312712,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"e4db3dfc316a2ae2edebbe25fef26a917710b9066df4ee168f83f3342359cdb5","sig":"a3f3ab6208ef1438778ffb399fdb40e0d8317812987e1360f896b106ab2a948b2d9878d23033c8d3c2a4fe70c5f2fa215ed81962162d94c1ec79767d5a4ebca5"}

53
src/routes/admin/repos/+page.svelte

@ -2,6 +2,7 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { userStore } from '$lib/stores/user-store.js'; import { userStore } from '$lib/stores/user-store.js';
import type { NostrEvent } from '$lib/types/nostr.js';
interface AdminRepo { interface AdminRepo {
npub: string; npub: string;
@ -10,6 +11,7 @@
size: number; size: number;
lastModified: number; lastModified: number;
createdAt: number; createdAt: number;
announcement?: NostrEvent | null;
} }
let repos = $state<AdminRepo[]>([]); let repos = $state<AdminRepo[]>([]);
@ -160,6 +162,39 @@
function formatDate(timestamp: number): string { function formatDate(timestamp: number): string {
return new Date(timestamp).toLocaleString(); return new Date(timestamp).toLocaleString();
} }
// Helper function to navigate to a repo with announcement in sessionStorage
async function navigateToRepo(npub: string, repoName: string, announcement?: NostrEvent | null) {
// If we have the announcement, store it in sessionStorage
if (announcement && typeof window !== 'undefined') {
const repoKey = `${npub}/${repoName}`;
try {
sessionStorage.setItem(`repo_announcement_${repoKey}`, JSON.stringify(announcement));
console.log(`[Admin] Stored announcement in sessionStorage for ${repoKey}`);
} catch (err) {
console.warn('[Admin] Failed to store announcement in sessionStorage:', err);
}
} else {
// Try to fetch announcement from API
try {
const response = await fetch(`/api/repos/local?domain=${encodeURIComponent(window.location.hostname)}`);
if (response.ok) {
const data = await response.json();
const repo = data.find((r: { npub: string; repoName: string; announcement: NostrEvent | null }) =>
r.npub === npub && r.repoName === repoName
);
if (repo?.announcement) {
const repoKey = `${npub}/${repoName}`;
sessionStorage.setItem(`repo_announcement_${repoKey}`, JSON.stringify(repo.announcement));
console.log(`[Admin] Fetched and stored announcement from API for ${repoKey}`);
}
}
} catch (err) {
console.warn('[Admin] Failed to fetch announcement from API:', err);
}
}
goto(`/repos/${npub}/${repoName}`);
}
</script> </script>
<svelte:head> <svelte:head>
@ -218,9 +253,13 @@
<code>{repo.npub.substring(0, 20)}...</code> <code>{repo.npub.substring(0, 20)}...</code>
</td> </td>
<td class="repo-name-cell"> <td class="repo-name-cell">
<a href="/repos/{repo.npub}/{repo.repoName}" target="_blank"> <button
onclick={() => navigateToRepo(repo.npub, repo.repoName, repo.announcement)}
class="repo-link-button"
title="View repository"
>
{repo.repoName} {repo.repoName}
</a> </button>
</td> </td>
<td>{formatBytes(repo.size)}</td> <td>{formatBytes(repo.size)}</td>
<td>{formatDate(repo.lastModified)}</td> <td>{formatDate(repo.lastModified)}</td>
@ -378,13 +417,19 @@
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
} }
.repo-name-cell a { .repo-link-button {
background: none;
border: none;
color: var(--link-color); color: var(--link-color);
text-decoration: none; text-decoration: none;
font-weight: 500; font-weight: 500;
cursor: pointer;
padding: 0;
font-size: inherit;
font-family: inherit;
} }
.repo-name-cell a:hover { .repo-link-button:hover {
color: var(--link-hover); color: var(--link-hover);
text-decoration: underline; text-decoration: underline;
} }

38
src/routes/repos/+page.svelte

@ -46,6 +46,20 @@
const nostrClient = new NostrClient(DEFAULT_NOSTR_RELAYS); const nostrClient = new NostrClient(DEFAULT_NOSTR_RELAYS);
// Helper function to navigate to a repo with announcement in sessionStorage
function navigateToRepo(npub: string, repoName: string, announcement: NostrEvent | null) {
if (announcement && typeof window !== 'undefined') {
const repoKey = `${npub}/${repoName}`;
try {
sessionStorage.setItem(`repo_announcement_${repoKey}`, JSON.stringify(announcement));
console.log(`[Repos Page] Stored announcement in sessionStorage for ${repoKey}`);
} catch (err) {
console.warn('[Repos Page] Failed to store announcement in sessionStorage:', err);
}
}
goto(`/repos/${npub}/${repoName}`);
}
onMount(async () => { onMount(async () => {
await loadRepos(); await loadRepos();
await loadUserAndContacts(); await loadUserAndContacts();
@ -719,9 +733,13 @@
<p class="description">{getRepoDescription(repo)}</p> <p class="description">{getRepoDescription(repo)}</p>
{/if} {/if}
</div> </div>
<a href="/repos/{item.npub}/{item.repoName}" class="view-button" title="View repository"> <button
onclick={() => navigateToRepo(item.npub, item.repoName, repo)}
class="view-button"
title="View repository"
>
<img src="/icons/arrow-right.svg" alt="View" /> <img src="/icons/arrow-right.svg" alt="View" />
</a> </button>
</div> </div>
<div class="repo-meta"> <div class="repo-meta">
<span>Created: {new Date(repo.created_at * 1000).toLocaleDateString()}</span> <span>Created: {new Date(repo.created_at * 1000).toLocaleDateString()}</span>
@ -772,9 +790,13 @@
<p class="description">{getRepoDescription(repo)}</p> <p class="description">{getRepoDescription(repo)}</p>
{/if} {/if}
</div> </div>
<a href="/repos/{item.npub}/{item.repoName}" class="view-button" title="View repository"> <button
onclick={() => navigateToRepo(item.npub, item.repoName, repo)}
class="view-button"
title="View repository"
>
<img src="/icons/arrow-right.svg" alt="View" /> <img src="/icons/arrow-right.svg" alt="View" />
</a> </button>
</div> </div>
<div class="repo-meta"> <div class="repo-meta">
<span>Created: {new Date(repo.created_at * 1000).toLocaleDateString()}</span> <span>Created: {new Date(repo.created_at * 1000).toLocaleDateString()}</span>
@ -856,9 +878,13 @@
<p class="description">{getRepoDescription(repo)}</p> <p class="description">{getRepoDescription(repo)}</p>
{/if} {/if}
</div> </div>
<a href="/repos/{item.npub}/{item.repoName}" class="view-button" title="View repository"> <button
onclick={() => navigateToRepo(item.npub, item.repoName, repo)}
class="view-button"
title="View repository"
>
<img src="/icons/arrow-right.svg" alt="View" /> <img src="/icons/arrow-right.svg" alt="View" />
</a> </button>
</div> </div>
<div class="repo-meta"> <div class="repo-meta">
<span>Created: {new Date(repo.created_at * 1000).toLocaleDateString()}</span> <span>Created: {new Date(repo.created_at * 1000).toLocaleDateString()}</span>

128
src/routes/repos/[npub]/[repo]/+page.ts

@ -35,22 +35,28 @@ export const load: PageLoad = async ({ params, url, parent }) => {
// Check if announcement was passed from search results via sessionStorage // Check if announcement was passed from search results via sessionStorage
let announcement: NostrEvent | null = null; let announcement: NostrEvent | null = null;
let foundInSessionStorage = false;
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
const repoKey = `${npub}/${repo}`; const repoKey = `${npub}/${repo}`;
const storedAnnouncement = sessionStorage.getItem(`repo_announcement_${repoKey}`); const storedAnnouncement = sessionStorage.getItem(`repo_announcement_${repoKey}`);
if (storedAnnouncement) { if (storedAnnouncement) {
try { try {
announcement = JSON.parse(storedAnnouncement); announcement = JSON.parse(storedAnnouncement);
// Clean up after using it foundInSessionStorage = true;
sessionStorage.removeItem(`repo_announcement_${repoKey}`); console.log(`[Page Load] Found announcement in sessionStorage for ${npub}/${repo}`, { eventId: announcement?.id });
} catch { // Don't remove it yet - keep it as fallback if server-side load fails
} catch (err) {
console.warn(`[Page Load] Failed to parse announcement from sessionStorage:`, err);
// Invalid JSON, continue to fetch // Invalid JSON, continue to fetch
} }
} else {
console.log(`[Page Load] No announcement in sessionStorage for ${npub}/${repo}`);
} }
} }
// If not found in sessionStorage, fetch from Nostr (case-insensitive) // If not found in sessionStorage, fetch from Nostr (case-insensitive)
if (!announcement) { if (!announcement) {
try {
const nostrClient = new NostrClient(DEFAULT_NOSTR_RELAYS); const nostrClient = new NostrClient(DEFAULT_NOSTR_RELAYS);
// Fetch all announcements by this author and filter case-insensitively // Fetch all announcements by this author and filter case-insensitively
const allEvents = await nostrClient.fetchEvents([ const allEvents = await nostrClient.fetchEvents([
@ -73,43 +79,119 @@ export const load: PageLoad = async ({ params, url, parent }) => {
// This is important for private forks that weren't published to relays // This is important for private forks that weren't published to relays
// Only attempt on server-side using SvelteKit's SSR flag // Only attempt on server-side using SvelteKit's SSR flag
if (import.meta.env.SSR) { if (import.meta.env.SSR) {
// Retry filesystem read with exponential backoff (commit might still be in progress)
let retries = 3;
let delay = 500; // Start with 500ms delay
while (retries > 0 && !announcement) {
try { try {
// Dynamic import to prevent client-side bundling // Dynamic import to prevent client-side bundling
// Wrap in additional try-catch to handle import errors gracefully
try {
const fsModule = await import('./read-announcement-from-fs.js'); const fsModule = await import('./read-announcement-from-fs.js');
if (fsModule && typeof fsModule.readAnnouncementFromFilesystem === 'function') {
console.log(`[Page Load] Attempting to read announcement from filesystem for ${npub}/${repo} (${4 - retries}/3)`);
const announcementFromRepo = await fsModule.readAnnouncementFromFilesystem(npub, repo, repoOwnerPubkey); const announcementFromRepo = await fsModule.readAnnouncementFromFilesystem(npub, repo, repoOwnerPubkey);
if (announcementFromRepo) { if (announcementFromRepo) {
console.log(`[Page Load] Successfully read announcement from filesystem for ${npub}/${repo}`);
announcement = announcementFromRepo; announcement = announcementFromRepo;
break; // Success, exit retry loop
} else {
console.log(`[Page Load] Announcement not found in filesystem for ${npub}/${repo}, retrying...`);
} }
} catch (err) { } else {
// If filesystem read fails, log on server-side but continue console.warn(`[Page Load] readAnnouncementFromFilesystem function not found in module`);
// This is expected on client-side, so we silently continue break; // Don't retry if function doesn't exist
console.debug('Failed to read announcement from filesystem:', err); }
} catch (importError) {
// Handle import errors (e.g., Vite SSR circular dependency issues)
// Don't retry on import errors - they won't be fixed by waiting
console.error('[Page Load] Failed to import readAnnouncementFromFilesystem (Vite SSR issue):', importError);
break; // Exit retry loop on import errors
} }
} catch (err) {
// If filesystem read fails, log and retry
console.error(`[Page Load] Failed to read announcement from filesystem (attempt ${4 - retries}/3):`, err);
} }
if (!announcement) { if (!announcement && retries > 1) {
return { // Wait before retrying (exponential backoff)
title: `${repo} - Repository Not Found`, await new Promise(resolve => setTimeout(resolve, delay));
description: 'Repository announcement not found', delay *= 2; // Double the delay for next retry
announcement: null, // Explicitly set to null so component knows it's missing }
repoNotFound: true // Flag to indicate repo not found retries--;
}; }
} else {
// Client-side: log that we're skipping filesystem read
console.debug(`[Page Load] Skipping filesystem read (client-side) for ${npub}/${repo}`);
} }
} else { } else {
announcement = matchingEvents[0]; announcement = matchingEvents[0];
} }
} catch (nostrError) {
// If Nostr fetch fails, log but continue - we might have it in sessionStorage or filesystem
console.error('[Page Load] Failed to fetch from Nostr relays:', nostrError);
}
}
// Clean up sessionStorage only if we successfully loaded from Nostr (public repos)
// For private repos loaded from filesystem or sessionStorage, keep it for client-side navigation
if (foundInSessionStorage && announcement && typeof window !== 'undefined') {
// Check if we loaded from Nostr (not from sessionStorage or filesystem)
// If the announcement came from Nostr, it means it's public and we don't need sessionStorage
const repoKey = `${npub}/${repo}`;
const stillInStorage = sessionStorage.getItem(`repo_announcement_${repoKey}`);
// Only remove if we have the announcement AND it's not from sessionStorage (meaning we got it from Nostr)
// This is a bit tricky: if foundInSessionStorage is true, we used sessionStorage, so don't remove it
// We only want to remove if we found it in Nostr AFTER checking sessionStorage
if (stillInStorage && !foundInSessionStorage) {
// This shouldn't happen, but if it does, it means we loaded from Nostr
sessionStorage.removeItem(`repo_announcement_${repoKey}`);
console.log(`[Page Load] Removed announcement from sessionStorage (loaded from Nostr)`);
} else if (foundInSessionStorage) {
// Keep sessionStorage - we're using it and client-side can't read filesystem
console.log(`[Page Load] Keeping announcement in sessionStorage (using it as source)`);
}
}
// If still no announcement found, check sessionStorage one more time (client-side fallback)
if (!announcement && !import.meta.env.SSR && typeof window !== 'undefined') {
const repoKey = `${npub}/${repo}`;
const lastChanceAnnouncement = sessionStorage.getItem(`repo_announcement_${repoKey}`);
if (lastChanceAnnouncement) {
try {
announcement = JSON.parse(lastChanceAnnouncement);
console.log(`[Page Load] Found announcement in sessionStorage on final check (client-side)`);
} catch {
// Invalid JSON
}
}
} }
// Ensure announcement exists before proceeding // If still no announcement found, don't immediately return "not found"
// The repo might exist but announcement not found yet (e.g., private fork being committed)
// Let the component check if repo exists via API before showing "not found"
if (!announcement) { if (!announcement) {
console.warn(`[Page Load] Announcement not found for ${npub}/${repo} after all attempts`, {
foundInSessionStorage,
isSSR: import.meta.env.SSR,
hasWindow: typeof window !== 'undefined'
});
// Return announcement as null but don't set repoNotFound - let component verify repo exists
return { return {
title: `${repo} - Repository Not Found`, title: `${repo} - Repository`,
description: 'Repository announcement not found', description: 'Repository',
announcement: null, announcement: null, // Explicitly set to null so component knows it's missing
repoNotFound: true repoNotFound: false // Don't assume repo doesn't exist - component will check
}; };
} }
console.log(`[Page Load] Successfully loaded announcement for ${npub}/${repo}`, {
eventId: announcement.id,
foundInSessionStorage,
isSSR: import.meta.env.SSR
});
// Check privacy - for private repos, we'll let the API endpoints handle access control // Check privacy - for private repos, we'll let the API endpoints handle access control
// The page load function runs server-side but doesn't have access to client auth headers // The page load function runs server-side but doesn't have access to client auth headers
// So we'll mark it as private and let the frontend handle access denial // So we'll mark it as private and let the frontend handle access denial
@ -130,8 +212,14 @@ export const load: PageLoad = async ({ params, url, parent }) => {
const banner = announcement.tags.find((t: string[]) => t[0] === 'banner')?.[1]; const banner = announcement.tags.find((t: string[]) => t[0] === 'banner')?.[1];
// Get git domain for constructing URLs // Get git domain for constructing URLs
let gitDomain = url.host || 'localhost:6543';
try {
const layoutData = await parent(); const layoutData = await parent();
const gitDomain = (layoutData as { gitDomain?: string }).gitDomain || url.host || 'localhost:6543'; gitDomain = (layoutData as { gitDomain?: string }).gitDomain || gitDomain;
} catch (parentError) {
// If parent() fails (e.g., due to SSR issues), use fallback
console.debug('Failed to get layout data, using fallback:', parentError);
}
const protocol = gitDomain.startsWith('localhost') ? 'http' : 'https'; const protocol = gitDomain.startsWith('localhost') ? 'http' : 'https';
const repoUrl = `${protocol}://${gitDomain}/repos/${npub}/${repo}`; const repoUrl = `${protocol}://${gitDomain}/repos/${npub}/${repo}`;

44
src/routes/repos/[npub]/[repo]/hooks/use-repo-effects.ts

@ -20,21 +20,55 @@ export function usePageDataEffect(state: RepoState, getPageData: () => any): ()
const data = getPageData(); const data = getPageData();
if (data && state.isMounted) { if (data && state.isMounted) {
state.pageData = data || {}; state.pageData = data || {};
// Set repoNotFound flag if announcement is missing or repoNotFound is explicitly set
if (data.repoNotFound === true || (data.announcement === null || data.announcement === undefined)) { // Only set repoNotFound if explicitly set to true AND we've verified the repo doesn't exist
// Don't set it just because announcement is null - the repo might exist but announcement not found yet
if (data.repoNotFound === true) {
// Check if repo actually exists by trying to verify via API
// If repo exists (e.g., branches endpoint returns 200), don't show "not found"
fetch(`/api/repos/${state.npub}/${state.repo}/branches?skipApiFallback=true`)
.then(response => {
if (state.isMounted) {
if (response.ok || response.status === 200) {
// Repo exists! Clear repoNotFound even if announcement is missing
console.log(`[Page Data Effect] Repo exists (status ${response.status}), clearing repoNotFound flag`);
state.repoNotFound = false;
} else if (response.status === 404) {
// Repo truly doesn't exist
console.log(`[Page Data Effect] Repo doesn't exist (status 404), setting repoNotFound`);
state.repoNotFound = true; state.repoNotFound = true;
state.loading.main = false; state.loading.main = false;
} else {
// Other error - don't assume repo doesn't exist
console.log(`[Page Data Effect] Repo check returned status ${response.status}, keeping current state`);
}
}
})
.catch(err => {
if (state.isMounted) {
console.warn('[Page Data Effect] Failed to verify repo existence:', err);
// On error checking, if repoNotFound was explicitly set, keep it
// Otherwise, don't assume repo doesn't exist
if (data.repoNotFound === true) {
state.repoNotFound = true;
state.loading.main = false;
}
}
});
} else if (data.announcement) { } else if (data.announcement) {
// Clear repoNotFound if we have a valid announcement // Clear repoNotFound if we have a valid announcement
state.repoNotFound = false; state.repoNotFound = false;
} else {
// Announcement is null but repoNotFound wasn't explicitly set
// Don't set repoNotFound - let the component try to load the repo anyway
// The repo might exist but announcement not found yet (e.g., private fork)
console.log('[Page Data Effect] Announcement is null but repoNotFound not explicitly set - allowing page to load');
} }
} }
} catch (err) { } catch (err) {
if (state.isMounted) { if (state.isMounted) {
console.warn('Failed to update pageData:', err); console.warn('Failed to update pageData:', err);
// On error, mark as not found to prevent blank page // On error, don't automatically mark as not found - might be a transient error
state.repoNotFound = true;
state.loading.main = false;
} }
} }
}; };

27
src/routes/repos/[npub]/[repo]/read-announcement-from-fs.ts

@ -30,29 +30,46 @@ export async function readAnnouncementFromFilesystem(npub: string, repoName: str
try { try {
const repoPath = join(repoRoot, npub, `${repoName}.git`); const repoPath = join(repoRoot, npub, `${repoName}.git`);
logger.debug({ npub, repoName, repoPath }, '[readAnnouncementFromFilesystem] Checking repo path');
if (!existsSync(repoPath)) { if (!existsSync(repoPath)) {
logger.debug({ npub, repoName, repoPath }, '[readAnnouncementFromFilesystem] Repo path does not exist');
return null; return null;
} }
logger.debug({ npub, repoName }, '[readAnnouncementFromFilesystem] Repo exists, reading git log');
const git = simpleGit(repoPath); const git = simpleGit(repoPath);
// Get the most recent commit that modified repo-events.jsonl // Get the most recent commit that modified repo-events.jsonl
const logOutput = await git.raw(['log', '--all', '--format=%H', '--reverse', '--', 'nostr/repo-events.jsonl']).catch(() => ''); const logOutput = await git.raw(['log', '--all', '--format=%H', '--reverse', '--', 'nostr/repo-events.jsonl']).catch((err) => {
logger.debug({ npub, repoName, error: err }, '[readAnnouncementFromFilesystem] Failed to get git log');
return '';
});
const commitHashes = logOutput.trim().split('\n').filter(Boolean); const commitHashes = logOutput.trim().split('\n').filter(Boolean);
logger.debug({ npub, repoName, commitCount: commitHashes.length }, '[readAnnouncementFromFilesystem] Found commits');
if (commitHashes.length === 0) { if (commitHashes.length === 0) {
logger.debug({ npub, repoName }, '[readAnnouncementFromFilesystem] No commits found for repo-events.jsonl');
return null; return null;
} }
const mostRecentCommit = commitHashes[commitHashes.length - 1]; const mostRecentCommit = commitHashes[commitHashes.length - 1];
logger.debug({ npub, repoName, commit: mostRecentCommit }, '[readAnnouncementFromFilesystem] Using most recent commit');
// Read the file content from git // Read the file content from git
const fileContent = await git.show([`${mostRecentCommit}:nostr/repo-events.jsonl`]).catch(() => null); const fileContent = await git.show([`${mostRecentCommit}:nostr/repo-events.jsonl`]).catch((err) => {
logger.debug({ npub, repoName, commit: mostRecentCommit, error: err }, '[readAnnouncementFromFilesystem] Failed to read file from git');
return null;
});
if (!fileContent) { if (!fileContent) {
logger.debug({ npub, repoName }, '[readAnnouncementFromFilesystem] File content is empty');
return null; return null;
} }
logger.debug({ npub, repoName, contentLength: fileContent.length }, '[readAnnouncementFromFilesystem] File content read successfully');
// Parse repo-events.jsonl to find the most recent announcement // Parse repo-events.jsonl to find the most recent announcement
let announcementEvent: NostrEvent | null = null; let announcementEvent: NostrEvent | null = null;
let latestTimestamp = 0; let latestTimestamp = 0;
@ -90,17 +107,19 @@ export async function readAnnouncementFromFilesystem(npub: string, repoName: str
// Check if d-tag matches repo name (case-insensitive) // Check if d-tag matches repo name (case-insensitive)
if (!dTag || dTag.toLowerCase() !== repoName.toLowerCase()) { if (!dTag || dTag.toLowerCase() !== repoName.toLowerCase()) {
logger.debug({ npub, repoName, dTag }, 'Announcement d-tag does not match repo name (case-insensitive)'); logger.debug({ npub, repoName, dTag, expectedRepoName: repoName.toLowerCase(), actualDTag: dTag?.toLowerCase() }, '[readAnnouncementFromFilesystem] Announcement d-tag does not match repo name (case-insensitive)');
return null; return null;
} }
logger.debug({ npub, repoName, dTag }, '[readAnnouncementFromFilesystem] d-tag matches, validating announcement');
const validation = validateAnnouncementEvent(announcementEvent, repoName); const validation = validateAnnouncementEvent(announcementEvent, repoName);
if (!validation.valid) { if (!validation.valid) {
logger.debug({ error: validation.error, npub, repoName }, 'Announcement validation failed'); logger.debug({ error: validation.error, npub, repoName }, '[readAnnouncementFromFilesystem] Announcement validation failed');
return null; return null;
} }
logger.info({ npub, repoName, eventId: announcementEvent.id }, '[readAnnouncementFromFilesystem] Successfully read and validated announcement');
return announcementEvent; return announcementEvent;
} catch (error) { } catch (error) {
logger.debug({ error, npub, repoName }, 'Error reading announcement from filesystem'); logger.debug({ error, npub, repoName }, 'Error reading announcement from filesystem');

19
src/routes/signup/+page.svelte

@ -2271,7 +2271,8 @@
fetch(`/api/repos/${userNpub}/${dTag}/clone`, { fetch(`/api/repos/${userNpub}/${dTag}/clone`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json',
'X-User-Pubkey': pubkey
}, },
body: JSON.stringify({ body: JSON.stringify({
announcementEvent: signedEvent // Pass the signed announcement event directly announcementEvent: signedEvent // Pass the signed announcement event directly
@ -2296,10 +2297,24 @@
// Redirect to the newly created repository page // Redirect to the newly created repository page
const userNpub = nip19.npubEncode(pubkey); const userNpub = nip19.npubEncode(pubkey);
// Store announcement in sessionStorage for private repos (not on relays yet)
// This allows the page load function to find it immediately
if (typeof window !== 'undefined' && signedEvent) {
const repoKey = `${userNpub}/${dTag}`;
try {
sessionStorage.setItem(`repo_announcement_${repoKey}`, JSON.stringify(signedEvent));
console.log(`[Signup] Stored announcement in sessionStorage for ${repoKey}`);
} catch (err) {
console.warn('[Signup] Failed to store announcement in sessionStorage:', err);
}
}
// Check if this is a transfer completion (from query params) // Check if this is a transfer completion (from query params)
const urlParams = $page.url.searchParams; const urlParams = $page.url.searchParams;
const isTransfer = urlParams.get('transfer') === 'true'; const isTransfer = urlParams.get('transfer') === 'true';
// Wait longer for commit to complete and filesystem to sync
// The Vite SSR error during commit is harmless, but we need to wait for the commit to finish
setTimeout(() => { setTimeout(() => {
// Invalidate all caches and redirect // Invalidate all caches and redirect
if (isTransfer) { if (isTransfer) {
@ -2308,7 +2323,7 @@
} else { } else {
goto(`/repos/${userNpub}/${dTag}`, { invalidateAll: true, replaceState: false }); goto(`/repos/${userNpub}/${dTag}`, { invalidateAll: true, replaceState: false });
} }
}, 1000); }, 3000); // Increased from 1000ms to 3000ms to allow commit to complete
return; return;
} }

Loading…
Cancel
Save