diff --git a/.gitignore b/.gitignore index 1e5c5c0..c83eaef 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,4 @@ npm-debug.log yarn-error.log # Cloned repositories - should never be committed -/repos/ -repos/ \ No newline at end of file +/repos/ \ No newline at end of file diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 47826c4..5f52ead 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -96,7 +96,6 @@
-

GitRepublic

Decentralized Git Hosting on Nostr

@@ -131,20 +130,6 @@ {/if}
-
-
- 🔐 Private Repositories -

Control who can access your code

-
-
- 🌐 Decentralized -

Built on Nostr protocol

-
-
- 🔧 Full Git Support -

All standard Git operations

-
-
@@ -192,14 +177,6 @@ margin-bottom: 3rem; } - .splash-logo { - width: 120px; - height: 120px; - margin: 0 auto 1.5rem; - display: block; - filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.1)); - } - .splash-title { font-size: 3.5rem; font-weight: 700; @@ -284,32 +261,6 @@ padding: 2rem; } - .splash-features { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 2rem; - margin-top: 4rem; - padding-top: 3rem; - border-top: 1px solid var(--border-color, #ddd); - } - - .feature-item { - text-align: center; - } - - .feature-item strong { - display: block; - font-size: 1.2rem; - color: var(--text-primary, #1a1a1a); - margin-bottom: 0.5rem; - } - - .feature-item p { - font-size: 0.95rem; - color: var(--text-secondary, #666); - margin: 0; - } - @media (max-width: 768px) { .splash-title { font-size: 2.5rem; @@ -323,20 +274,10 @@ font-size: 1.2rem; } - .splash-logo { - width: 80px; - height: 80px; - } - .splash-button { width: 100%; min-width: unset; } - - .splash-features { - grid-template-columns: 1fr; - gap: 1.5rem; - } } @media (prefers-color-scheme: dark) { diff --git a/src/routes/api/repos/[npub]/[repo]/access/+server.ts b/src/routes/api/repos/[npub]/[repo]/access/+server.ts new file mode 100644 index 0000000..76836c2 --- /dev/null +++ b/src/routes/api/repos/[npub]/[repo]/access/+server.ts @@ -0,0 +1,34 @@ +/** + * API endpoint for checking repository access + * Returns whether the current user can view the repository + */ + +import { json } from '@sveltejs/kit'; +import type { RequestHandler } from './$types'; +import { maintainerService } from '$lib/services/service-registry.js'; +import { createRepoGetHandler } from '$lib/utils/api-handlers.js'; +import type { RepoRequestContext } from '$lib/utils/api-context.js'; + +export const GET: RequestHandler = createRepoGetHandler( + async (context: RepoRequestContext) => { + const { isPrivate, maintainers, owner } = await maintainerService.getMaintainers( + context.repoOwnerPubkey, + context.repo + ); + + // Check if user can view + const canView = await maintainerService.canView( + context.userPubkeyHex || null, + context.repoOwnerPubkey, + context.repo + ); + + return json({ + canView, + isPrivate, + isMaintainer: context.userPubkeyHex ? maintainers.includes(context.userPubkeyHex) : false, + isOwner: context.userPubkeyHex ? context.userPubkeyHex === owner : false + }); + }, + { operation: 'checkAccess', requireRepoExists: false, requireRepoAccess: false } // This endpoint IS the access check +); diff --git a/src/routes/api/repos/local/+server.ts b/src/routes/api/repos/local/+server.ts index de45f19..34c8f62 100644 --- a/src/routes/api/repos/local/+server.ts +++ b/src/routes/api/repos/local/+server.ts @@ -212,7 +212,7 @@ export const GET: RequestHandler = async (event) => { const requestContext = extractRequestContext(event); const userPubkey = requestContext.userPubkeyHex || null; const gitDomain = event.url.searchParams.get('domain') || GIT_DOMAIN; - const forceRefresh = url.searchParams.get('refresh') === 'true'; + const forceRefresh = event.url.searchParams.get('refresh') === 'true'; // Check cache if (!forceRefresh && cache && (Date.now() - cache.timestamp) < CACHE_TTL) { diff --git a/src/routes/repos/+page.svelte b/src/routes/repos/+page.svelte new file mode 100644 index 0000000..a311ac9 --- /dev/null +++ b/src/routes/repos/+page.svelte @@ -0,0 +1,562 @@ + + + + Repositories - GitRepublic + + + +
+
+
+

Repositories on {$page.data.gitDomain || 'localhost:6543'}

+ +
+ +
+
+ +
+ {#if isNIP07Available() && userPubkey} + + {/if} +
+ + {#if error} +
+ Error loading repositories: {error} +
+ {:else if loading} +
Loading repositories...
+ {:else} + +
+
+

Registered Repositories

+ {registeredRepos.length} +
+ {#if registeredRepos.length === 0} +
No registered repositories found.
+ {:else} +
+ {#each registeredRepos as item} + {@const repo = item.event} + {@const repoImage = getRepoImage(repo)} + {@const repoBanner = getRepoBanner(repo)} +
+ {#if repoBanner} +
+ Banner +
+ {/if} +
+
+ {#if repoImage} + Repository + {/if} +
+

{getRepoName(repo)}

+ {#if getRepoDescription(repo)} +

{getRepoDescription(repo)}

+ {/if} +
+ + View & Edit → + +
+
+ Clone URLs: + {#each getCloneUrls(repo) as url} + {url} + {/each} +
+
+ Created: {new Date(repo.created_at * 1000).toLocaleDateString()} + {#if getForkCount(repo) > 0} + {@const forkCount = getForkCount(repo)} + 🍴 {forkCount} fork{forkCount === 1 ? '' : 's'} + {/if} +
+
+
+ {/each} +
+ {/if} +
+ + +
+
+

Local Clones

+ {localRepos.length} + Repositories cloned locally but not registered with this domain +
+ {#if loadingLocal} +
Loading local repositories...
+ {:else if localRepos.length === 0} +
No local clones found.
+ {:else} +
+ {#each localRepos as item} + {@const repo = item.announcement} + {@const repoImage = repo ? getRepoImage(repo) : null} + {@const repoBanner = repo ? getRepoBanner(repo) : null} + {@const canDelete = isOwner(item.npub, item.repoName)} +
+ {#if repoBanner} +
+ Banner +
+ {/if} +
+
+ {#if repoImage} + Repository + {/if} +
+

{repo ? getRepoName(repo) : item.repoName}

+ {#if repo && getRepoDescription(repo)} +

{getRepoDescription(repo)}

+ {:else} +

No description available

+ {/if} +
+
+ + View & Edit → + + {#if canDelete} + + {/if} + +
+
+ {#if repo} +
+ Clone URLs: + {#each getCloneUrls(repo) as url} + {url} + {/each} +
+ {/if} +
+ Last modified: {new Date(item.lastModified).toLocaleDateString()} + {#if repo} + Created: {new Date(repo.created_at * 1000).toLocaleDateString()} + {#if getForkCount(repo) > 0} + {@const forkCount = getForkCount(repo)} + 🍴 {forkCount} fork{forkCount === 1 ? '' : 's'} + {/if} + {/if} +
+
+
+ {/each} +
+ {/if} +
+ {/if} +
+