| … | … | @@ -14,6 +14,7 @@ export class RepoRegistry { |
| 14 | 14 | constructor(options = {}) { |
| 15 | 15 | this.registry = options.registry // Hyperbee instance |
| 16 | 16 | this.gitReposPath = options.gitReposPath || path.join(os.homedir(), 'git-repos') |
| 17 | this.onRepoRegistered = options.onRepoRegistered || null // callback(repoName) — called on NEW registrations | |
| 17 | 18 | } |
| 18 | 19 | |
| 19 | 20 | /** |
| … | … | @@ -481,6 +482,7 @@ echo "[post-receive] Auto-deploy triggered for $REPO_NAME" |
| 481 | 482 | |
| 482 | 483 | // Merge with existing entry to preserve user-configured fields |
| 483 | 484 | const existing = await this.registry.get(`repo:${repoInfo.name}`).catch(() => null) |
| 485 | const isNew = !existing || !existing.value | |
| 484 | 486 | if (existing && existing.value) { |
| 485 | 487 | const prev = existing.value |
| 486 | 488 | // Preserve fields that are set via separate APIs (not from scan). |
| … | … | @@ -523,6 +525,15 @@ echo "[post-receive] Auto-deploy triggered for $REPO_NAME" |
| 523 | 525 | } |
| 524 | 526 | |
| 525 | 527 | console.log(` ✓ Registered ${repoInfo.name}${repoInfo.namespace ? ` (${repoInfo.namespace})` : ''}`) |
| 528 | ||
| 529 | // Notify listener on NEW registrations (not re-registrations) | |
| 530 | if (isNew && this.onRepoRegistered) { | |
| 531 | try { | |
| 532 | await this.onRepoRegistered(repoInfo.name) | |
| 533 | } catch (err) { | |
| 534 | console.error(` ⚠ onRepoRegistered callback failed for ${repoInfo.name}: ${err.message}`) | |
| 535 | } | |
| 536 | } | |
| 526 | 537 | } |
| 527 | 538 | |
| 528 | 539 | /** |
| … | … | @@ -149,6 +149,8 @@ async function handleScan(req, res, server) { |
| 149 | 149 | async function handleFastScan(req, res, server) { |
| 150 | 150 | const { repoRegistry } = server.repoData |
| 151 | 151 | try { |
| 152 | const previousNames = new Set(server.repos.map(r => r.name)) | |
| 153 | ||
| 152 | 154 | const repos = await repoRegistry.scanAndRegister({ |
| 153 | 155 | paths: [repoRegistry.gitReposPath], |
| 154 | 156 | maxDepth: 1 |
| … | … | @@ -156,6 +158,15 @@ async function handleFastScan(req, res, server) { |
| 156 | 158 | |
| 157 | 159 | server.repos = repos |
| 158 | 160 | |
| 161 | // Announce any newly discovered repos on DHT | |
| 162 | const topicManager = server.p2p?.topicManager | |
| 163 | if (topicManager) { | |
| 164 | const newRepos = repos.filter(r => !previousNames.has(r.name)) | |
| 165 | for (const r of newRepos) { | |
| 166 | await topicManager.announceRepo(r.name) | |
| 167 | } | |
| 168 | } | |
| 169 | ||
| 159 | 170 | json(res, { |
| 160 | 171 | success: true, |
| 161 | 172 | count: repos.length, |
| … | … | @@ -188,6 +199,12 @@ async function handleImport(req, res, server) { |
| 188 | 199 | server.repos.push(entry) |
| 189 | 200 | } |
| 190 | 201 | |
| 202 | // Announce on DHT so federation peers can fetch via punch:// | |
| 203 | const topicManager = server.p2p?.topicManager | |
| 204 | if (topicManager) { | |
| 205 | await topicManager.announceRepo(repoName) | |
| 206 | } | |
| 207 | ||
| 191 | 208 | json(res, { |
| 192 | 209 | success: true, |
| 193 | 210 | driveKey: imported.driveKey, |
| … | … | @@ -1176,6 +1176,8 @@ class PearGitServer { |
| 1176 | 1176 | this.gitP2PHandler.topicManager = this.topicManager |
| 1177 | 1177 | // Set topicManager on GitSync client (needed for announcing runtime-created repos) |
| 1178 | 1178 | if (this.gitSyncClientManager) this.gitSyncClientManager._topicManager = this.topicManager |
| 1179 | // Auto-announce new repos on DHT when registered at runtime | |
| 1180 | this.repoRegistry.onRepoRegistered = (repoName) => this.topicManager.announceRepo(repoName) | |
| 1179 | 1181 | // Set server ref so admin create-repo can update in-memory repos cache |
| 1180 | 1182 | this.gitP2PHandler.server = this |
| 1181 | 1183 |