diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 5a2076f..69c6e5b 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -9,6 +9,7 @@ import { RepoPollingService } from './lib/services/nostr/repo-polling.js'; import { GIT_DOMAIN, DEFAULT_NOSTR_RELAYS } from './lib/config.js'; import { rateLimiter } from './lib/services/security/rate-limiter.js'; import { auditLogger } from './lib/services/security/audit-logger.js'; +import logger from './lib/services/logger.js'; // Initialize polling service const repoRoot = process.env.GIT_REPO_ROOT || '/repos'; @@ -19,7 +20,7 @@ let pollingService: RepoPollingService | null = null; if (typeof process !== 'undefined') { pollingService = new RepoPollingService(DEFAULT_NOSTR_RELAYS, repoRoot, domain); pollingService.start(); - console.log('Started repo polling service'); + logger.info('Started repo polling service'); } export const handle: Handle = async ({ event, resolve }) => { diff --git a/src/lib/services/git/file-manager.ts b/src/lib/services/git/file-manager.ts index be8630c..fe3de4b 100644 --- a/src/lib/services/git/file-manager.ts +++ b/src/lib/services/git/file-manager.ts @@ -10,6 +10,7 @@ import { existsSync } from 'fs'; import { RepoManager } from './repo-manager.js'; import { createGitCommitSignature } from './commit-signer.js'; import type { NostrEvent } from '../../types/nostr.js'; +import logger from '../logger.js'; export interface FileEntry { name: string; @@ -229,7 +230,7 @@ export class FileManager { return a.name.localeCompare(b.name); }); } catch (error) { - console.error('Error listing files:', error); + logger.error({ error, repoPath, ref }, 'Error listing files'); throw new Error(`Failed to list files: ${error instanceof Error ? error.message : String(error)}`); } } @@ -275,7 +276,7 @@ export class FileManager { size }; } catch (error) { - console.error('Error reading file:', error); + logger.error({ error, repoPath, filePath, ref }, 'Error reading file'); throw new Error(`Failed to read file: ${error instanceof Error ? error.message : String(error)}`); } } @@ -414,7 +415,7 @@ export class FileManager { } catch (err) { // Security: Sanitize error messages (never log private keys) const sanitizedErr = err instanceof Error ? err.message.replace(/nsec[0-9a-z]+/gi, '[REDACTED]').replace(/[0-9a-f]{64}/g, '[REDACTED]') : String(err); - console.warn('Failed to sign commit:', sanitizedErr); + logger.warn({ error: sanitizedErr, repoPath, filePath }, 'Failed to sign commit'); // Continue without signature if signing fails } } @@ -430,7 +431,7 @@ export class FileManager { // Clean up work directory await rm(workDir, { recursive: true, force: true }); } catch (error) { - console.error('Error writing file:', error); + logger.error({ error, repoPath, filePath, npub }, 'Error writing file'); throw new Error(`Failed to write file: ${error instanceof Error ? error.message : String(error)}`); } } @@ -453,7 +454,7 @@ export class FileManager { .map(b => b.replace(/^origin\//, '')) .filter(b => !b.includes('HEAD')); } catch (error) { - console.error('Error getting branches:', error); + logger.error({ error, repoPath }, 'Error getting branches'); return ['main', 'master']; // Default branches } } @@ -585,7 +586,7 @@ export class FileManager { } catch (err) { // Security: Sanitize error messages (never log private keys) const sanitizedErr = err instanceof Error ? err.message.replace(/nsec[0-9a-z]+/gi, '[REDACTED]').replace(/[0-9a-f]{64}/g, '[REDACTED]') : String(err); - console.warn('Failed to sign commit:', sanitizedErr); + logger.warn({ error: sanitizedErr, repoPath, filePath }, 'Failed to sign commit'); // Continue without signature if signing fails } } @@ -601,7 +602,7 @@ export class FileManager { // Clean up await rm(workDir, { recursive: true, force: true }); } catch (error) { - console.error('Error deleting file:', error); + logger.error({ error, repoPath, filePath, npub }, 'Error deleting file'); throw new Error(`Failed to delete file: ${error instanceof Error ? error.message : String(error)}`); } } @@ -646,7 +647,7 @@ export class FileManager { // Clean up await rm(workDir, { recursive: true, force: true }); } catch (error) { - console.error('Error creating branch:', error); + logger.error({ error, repoPath, branchName, npub }, 'Error creating branch'); throw new Error(`Failed to create branch: ${error instanceof Error ? error.message : String(error)}`); } } @@ -689,7 +690,7 @@ export class FileManager { files: commit.diff?.files?.map((f: any) => f.file) || [] })); } catch (error) { - console.error('Error getting commit history:', error); + logger.error({ error, repoPath, branch, limit }, 'Error getting commit history'); throw new Error(`Failed to get commit history: ${error instanceof Error ? error.message : String(error)}`); } } @@ -777,7 +778,7 @@ export class FileManager { return files; } catch (error) { - console.error('Error getting diff:', error); + logger.error({ error, repoPath, fromRef, toRef }, 'Error getting diff'); throw new Error(`Failed to get diff: ${error instanceof Error ? error.message : String(error)}`); } } @@ -821,7 +822,7 @@ export class FileManager { } } } catch (error) { - console.error('Error creating tag:', error); + logger.error({ error, repoPath, tagName, ref, message }, 'Error creating tag'); throw new Error(`Failed to create tag: ${error instanceof Error ? error.message : String(error)}`); } } @@ -866,7 +867,7 @@ export class FileManager { return tagList; } catch (error) { - console.error('Error getting tags:', error); + logger.error({ error, repoPath }, 'Error getting tags'); return []; } } diff --git a/src/lib/services/git/repo-manager.ts b/src/lib/services/git/repo-manager.ts index 09b5acf..a6e11b4 100644 --- a/src/lib/services/git/repo-manager.ts +++ b/src/lib/services/git/repo-manager.ts @@ -12,6 +12,7 @@ import type { NostrEvent } from '../../types/nostr.js'; import { GIT_DOMAIN } from '../../config.js'; import { generateVerificationFile, VERIFICATION_FILE_PATH } from '../nostr/repo-verification.js'; import simpleGit, { type SimpleGit } from 'simple-git'; +import logger from '../logger.js'; const execAsync = promisify(exec); @@ -99,7 +100,7 @@ export class RepoManager { // But we should be careful not to overwrite existing history // For now, we'll just ensure the verification file exists // The self-transfer event should already be published to relays - console.log(`Existing repo ${repoPath.fullPath} - self-transfer event should be published to relays`); + logger.info({ repoPath: repoPath.fullPath }, 'Existing repo - self-transfer event should be published to relays'); } } @@ -119,7 +120,7 @@ export class RepoManager { // Update all branches await execAsync(`cd "${repoPath}" && git remote set-head ${remoteName} -a`); } catch (error) { - console.error(`Failed to sync from ${url}:`, error); + logger.error({ error, url, repoPath }, 'Failed to sync from remote'); // Continue with other remotes } } @@ -136,7 +137,7 @@ export class RepoManager { await execAsync(`cd "${repoPath}" && git push ${remoteName} --all --force`); await execAsync(`cd "${repoPath}" && git push ${remoteName} --tags --force`); } catch (error) { - console.error(`Failed to sync to ${url}:`, error); + logger.error({ error, url, repoPath }, 'Failed to sync to remote'); // Continue with other remotes } } @@ -311,7 +312,7 @@ export class RepoManager { ); commitMessage = signedMessage; } catch (err) { - console.warn('Failed to sign initial commit:', err); + logger.warn({ error: err, repoPath: repoPath.fullPath }, 'Failed to sign initial commit'); // Continue without signature if signing fails } } @@ -331,7 +332,7 @@ export class RepoManager { // Clean up await rm(workDir, { recursive: true, force: true }); } catch (error) { - console.error('Failed to create verification file:', error); + logger.error({ error, repoPath: repoPath.fullPath }, 'Failed to create verification file'); // Don't throw - verification file creation is important but shouldn't block provisioning } } diff --git a/src/lib/services/logger.ts b/src/lib/services/logger.ts new file mode 100644 index 0000000..2ffdc48 --- /dev/null +++ b/src/lib/services/logger.ts @@ -0,0 +1,22 @@ +/** + * Pino logger service + * Provides structured logging with pino-pretty for development + */ + +import pino from 'pino'; + +const logger = pino({ + level: process.env.LOG_LEVEL || 'info', + ...(process.env.NODE_ENV === 'development' && { + transport: { + target: 'pino-pretty', + options: { + colorize: true, + translateTime: 'HH:MM:ss Z', + ignore: 'pid,hostname' + } + } + }) +}); + +export default logger; diff --git a/src/lib/services/nostr/branch-protection-service.ts b/src/lib/services/nostr/branch-protection-service.ts index 2e41f72..d20a1b1 100644 --- a/src/lib/services/nostr/branch-protection-service.ts +++ b/src/lib/services/nostr/branch-protection-service.ts @@ -6,6 +6,7 @@ import { NostrClient } from './nostr-client.js'; import { KIND } from '../../types/nostr.js'; import type { NostrEvent } from '../../types/nostr.js'; +import logger from '../logger.js'; export interface BranchProtectionRule { branch: string; @@ -51,7 +52,7 @@ export class BranchProtectionService { const event = events[0]; return this.parseProtectionEvent(event); } catch (error) { - console.error('Error fetching branch protection:', error); + logger.error({ error, ownerPubkey, repoName }, 'Error fetching branch protection'); return null; } } diff --git a/src/lib/services/nostr/fork-count-service.ts b/src/lib/services/nostr/fork-count-service.ts index 1585298..6af0837 100644 --- a/src/lib/services/nostr/fork-count-service.ts +++ b/src/lib/services/nostr/fork-count-service.ts @@ -5,6 +5,7 @@ import { NostrClient } from './nostr-client.js'; import { KIND } from '../../types/nostr.js'; import type { NostrEvent } from '../../types/nostr.js'; +import logger from '../logger.js'; export class ForkCountService { private nostrClient: NostrClient; @@ -52,7 +53,7 @@ export class ForkCountService { return count; } catch (error) { - console.error(`[ForkCount] Error counting forks for ${originalOwnerPubkey}/${originalRepoName}:`, error); + logger.error({ error, originalOwnerPubkey, originalRepoName }, '[ForkCount] Error counting forks'); // Return cached value if available, otherwise 0 return cached?.count || 0; } diff --git a/src/lib/services/nostr/maintainer-service.ts b/src/lib/services/nostr/maintainer-service.ts index 04caa3b..3545fa1 100644 --- a/src/lib/services/nostr/maintainer-service.ts +++ b/src/lib/services/nostr/maintainer-service.ts @@ -8,6 +8,7 @@ import { KIND } from '../../types/nostr.js'; import type { NostrEvent } from '../../types/nostr.js'; import { nip19 } from 'nostr-tools'; import { OwnershipTransferService } from './ownership-transfer-service.js'; +import logger from '../logger.js'; export interface RepoPrivacyInfo { isPrivate: boolean; @@ -109,7 +110,7 @@ export class MaintainerService { this.cache.set(cacheKey, { ...result, timestamp: Date.now() }); return result; } catch (error) { - console.error('Error fetching maintainers:', error); + logger.error({ error, ownerPubkey, repoName }, 'Error fetching maintainers'); // Fallback: only owner is maintainer, repo is public by default const result = { owner: repoOwnerPubkey, maintainers: [repoOwnerPubkey], isPrivate: false }; this.cache.set(cacheKey, { ...result, timestamp: Date.now() }); diff --git a/src/lib/services/nostr/nostr-client.ts b/src/lib/services/nostr/nostr-client.ts index dd14deb..f19cee4 100644 --- a/src/lib/services/nostr/nostr-client.ts +++ b/src/lib/services/nostr/nostr-client.ts @@ -4,6 +4,7 @@ import type { NostrEvent, NostrFilter } from '../../types/nostr.js'; import { createRequire } from 'module'; +import logger from '../logger.js'; // Polyfill WebSocket for Node.js environments (lazy initialization) let wsPolyfillInitialized = false; @@ -22,7 +23,7 @@ function initializeWebSocketPolyfill() { wsPolyfillInitialized = true; } catch { // ws package not available, will fail at runtime in Node.js - console.warn('WebSocket polyfill not available. Install "ws" package for Node.js support.'); + logger.warn('WebSocket polyfill not available. Install "ws" package for Node.js support.'); wsPolyfillInitialized = true; // Mark as initialized to avoid repeated warnings } } diff --git a/src/lib/services/nostr/ownership-transfer-service.ts b/src/lib/services/nostr/ownership-transfer-service.ts index 5e72aea..95509cd 100644 --- a/src/lib/services/nostr/ownership-transfer-service.ts +++ b/src/lib/services/nostr/ownership-transfer-service.ts @@ -8,6 +8,7 @@ import { KIND } from '../../types/nostr.js'; import type { NostrEvent } from '../../types/nostr.js'; import { verifyEvent } from 'nostr-tools'; import { nip19 } from 'nostr-tools'; +import logger from '../logger.js'; export interface OwnershipTransfer { event: NostrEvent; @@ -100,7 +101,7 @@ export class OwnershipTransferService { this.cache.set(cacheKey, { owner: currentOwner, timestamp: Date.now() }); return currentOwner; } catch (error) { - console.error('Error fetching ownership transfers:', error); + logger.error({ error, originalOwnerPubkey, repoName }, 'Error fetching ownership transfers'); // Fallback to original owner return originalOwnerPubkey; } @@ -305,7 +306,7 @@ export class OwnershipTransferService { transfers.sort((a, b) => b.timestamp - a.timestamp); return transfers; } catch (error) { - console.error('Error fetching transfer history:', error); + logger.error({ error, ownerPubkey, repoName }, 'Error fetching transfer history'); return []; } } diff --git a/src/lib/services/nostr/repo-polling.ts b/src/lib/services/nostr/repo-polling.ts index 07913bc..96570e8 100644 --- a/src/lib/services/nostr/repo-polling.ts +++ b/src/lib/services/nostr/repo-polling.ts @@ -7,6 +7,7 @@ import { KIND } from '../../types/nostr.js'; import type { NostrEvent } from '../../types/nostr.js'; import { RepoManager } from '../git/repo-manager.js'; import { OwnershipTransferService } from './ownership-transfer-service.js'; +import logger from '../logger.js'; export class RepoPollingService { private nostrClient: NostrClient; @@ -80,7 +81,7 @@ export class RepoPollingService { // Extract repo ID from d-tag const dTag = event.tags.find(t => t[0] === 'd')?.[1]; if (!dTag) { - console.warn(`Repo announcement ${event.id} missing d-tag`); + logger.warn({ eventId: event.id }, 'Repo announcement missing d-tag'); continue; } @@ -136,7 +137,7 @@ export class RepoPollingService { if (isExistingRepo && !selfTransferEvent) { // Security: Truncate pubkey in logs const truncatedPubkey = event.pubkey.length > 16 ? `${event.pubkey.slice(0, 8)}...${event.pubkey.slice(-4)}` : event.pubkey; - console.log(`Existing repo ${dTag} from ${truncatedPubkey} has no self-transfer event. Creating template for owner to sign and publish.`); + logger.info({ repoId: dTag, pubkey: truncatedPubkey }, 'Existing repo has no self-transfer event. Creating template for owner to sign and publish.'); try { // Create a self-transfer event template for the existing repo @@ -155,21 +156,21 @@ export class RepoPollingService { // Use the template (even though it's unsigned, it will be included in the repo) selfTransferEvent = selfTransferTemplate; - console.warn(`Self-transfer event template created for ${dTag}. Owner ${event.pubkey} should sign and publish it to relays.`); + logger.warn({ repoId: dTag, pubkey: event.pubkey }, 'Self-transfer event template created. Owner should sign and publish it to relays.'); } catch (err) { - console.error(`Failed to create self-transfer event template for ${dTag}:`, err); + logger.error({ error: err, repoId: dTag }, 'Failed to create self-transfer event template'); } } // Provision the repo with self-transfer event if available await this.repoManager.provisionRepo(event, selfTransferEvent, isExistingRepo); - console.log(`Provisioned repo from announcement ${event.id}${isExistingRepo ? ' (existing)' : ' (new)'}`); + logger.info({ eventId: event.id, isExistingRepo }, 'Provisioned repo from announcement'); } catch (error) { - console.error(`Failed to provision repo from ${event.id}:`, error); + logger.error({ error, eventId: event.id }, 'Failed to provision repo from announcement'); } } } catch (error) { - console.error('Error polling for repo announcements:', error); + logger.error({ error }, 'Error polling for repo announcements'); } } diff --git a/src/lib/services/nostr/user-relays.ts b/src/lib/services/nostr/user-relays.ts index 419551d..0deb533 100644 --- a/src/lib/services/nostr/user-relays.ts +++ b/src/lib/services/nostr/user-relays.ts @@ -5,6 +5,8 @@ import { NostrClient } from './nostr-client.js'; import type { NostrEvent } from '../../types/nostr.js'; import { KIND } from '../../types/nostr.js'; +import logger from '../logger.js'; +import { truncatePubkey } from '../../utils/security.js'; export async function getUserRelays( pubkey: string, @@ -68,8 +70,8 @@ export async function getUserRelays( } } } catch (error) { - console.error('Failed to fetch user relays:', error); + logger.error({ error, pubkey: truncatePubkey(pubkey) }, 'Failed to fetch user relays'); } return { inbox, outbox }; -} +} \ No newline at end of file diff --git a/src/lib/services/security/audit-logger.ts b/src/lib/services/security/audit-logger.ts index 1ebaaa6..0e3e78d 100644 --- a/src/lib/services/security/audit-logger.ts +++ b/src/lib/services/security/audit-logger.ts @@ -15,6 +15,7 @@ import { appendFile, mkdir, readdir, unlink, stat } from 'fs/promises'; import { join, dirname } from 'path'; import { existsSync } from 'fs'; import { truncatePubkey, sanitizeError, redactSensitiveData } from '../../utils/security.js'; +import logger from '../logger.js'; export interface AuditLogEntry { timestamp: string; @@ -72,7 +73,7 @@ export class AuditLogger { await mkdir(this.logDir, { recursive: true }); } } catch (error) { - console.error('[AUDIT] Failed to create log directory:', error); + logger.error({ error }, '[AUDIT] Failed to create log directory'); } } @@ -98,13 +99,13 @@ export class AuditLogger { // Run cleanup daily this.cleanupInterval = setInterval(() => { this.cleanupOldLogs().catch(err => { - console.error('[AUDIT] Failed to cleanup old logs:', err); + logger.error({ error: err }, '[AUDIT] Failed to cleanup old logs'); }); }, 24 * 60 * 60 * 1000); // 24 hours // Run initial cleanup this.cleanupOldLogs().catch(err => { - console.error('[AUDIT] Failed to cleanup old logs:', err); + logger.error({ error: err }, '[AUDIT] Failed to cleanup old logs'); }); } @@ -128,14 +129,14 @@ export class AuditLogger { const stats = await stat(filePath); if (stats.mtime.getTime() < cutoffTime) { await unlink(filePath); - console.log(`[AUDIT] Deleted old log file: ${file}`); + logger.info({ file }, '[AUDIT] Deleted old log file'); } } catch (err) { // Ignore errors for individual files } } } catch (error) { - console.error('[AUDIT] Error during log cleanup:', error); + logger.error({ error }, '[AUDIT] Error during log cleanup'); } } @@ -158,7 +159,7 @@ export class AuditLogger { await appendFile(join(this.logDir, this.currentLogFile), content, 'utf8'); } } catch (error) { - console.error('[AUDIT] Failed to write to log file:', error); + logger.error({ error }, '[AUDIT] Failed to write to log file'); // Put items back in queue (but limit queue size to prevent memory issues) this.writeQueue = [...this.writeQueue, ...this.writeQueue].slice(0, 1000); } finally { @@ -191,14 +192,14 @@ export class AuditLogger { timestamp: new Date().toISOString() }; - // Log to console (structured JSON) + // Log using pino (structured JSON) const logLine = JSON.stringify(sanitizedEntry); - console.log(`[AUDIT] ${logLine}`); + logger.info(sanitizedEntry, '[AUDIT]'); // Write to file if configured (async, non-blocking) if (this.logFile) { this.writeToFile(logLine).catch(err => { - console.error('[AUDIT] Failed to write log entry:', sanitizeError(err)); + logger.error({ error: sanitizeError(err) }, '[AUDIT] Failed to write log entry'); }); } } diff --git a/src/routes/api/git/[...path]/+server.ts b/src/routes/api/git/[...path]/+server.ts index c221ad0..f13b92f 100644 --- a/src/routes/api/git/[...path]/+server.ts +++ b/src/routes/api/git/[...path]/+server.ts @@ -18,6 +18,7 @@ import { verifyNIP98Auth } from '$lib/services/nostr/nip98-auth.js'; import { OwnershipTransferService } from '$lib/services/nostr/ownership-transfer-service.js'; import { MaintainerService } from '$lib/services/nostr/maintainer-service.js'; import { BranchProtectionService } from '$lib/services/nostr/branch-protection-service.js'; +import logger from '$lib/services/logger.js'; import { auditLogger } from '$lib/services/security/audit-logger.js'; const repoRoot = process.env.GIT_REPO_ROOT || '/repos'; @@ -412,11 +413,12 @@ export const POST: RequestHandler = async ({ params, url, request }) => { // Note: We need to extract the target branch from the git push request // This is a simplified check - in production, you'd parse the git protocol // to determine the exact branch being pushed + let targetBranch = 'main'; // Default to main if can't determine try { // Try to extract branch from request body (git protocol) const bodyText = bodyBuffer.toString('utf-8', 0, Math.min(bodyBuffer.length, 1000)); const branchMatch = bodyText.match(/refs\/heads\/([^\s\n]+)/); - const targetBranch = branchMatch ? branchMatch[1] : 'main'; // Default to main if can't determine + targetBranch = branchMatch ? branchMatch[1] : 'main'; // Default to main if can't determine const protectionCheck = await branchProtectionService.canPushToBranch( authResult.pubkey || '', @@ -433,7 +435,7 @@ export const POST: RequestHandler = async ({ params, url, request }) => { // If we can't check protection, log but don't block (fail open for now) // Security: Sanitize error messages const sanitizedError = error instanceof Error ? error.message.replace(/nsec[0-9a-z]+/gi, '[REDACTED]').replace(/[0-9a-f]{64}/g, '[REDACTED]') : String(error); - console.warn('Failed to check branch protection:', sanitizedError); + logger.warn({ error: sanitizedError, npub, repoName, targetBranch }, 'Failed to check branch protection'); } } @@ -545,14 +547,14 @@ export const POST: RequestHandler = async ({ params, url, request }) => { repoManager.syncToRemotes(repoPath, otherUrls).catch(err => { // Security: Sanitize error messages const sanitizedErr = err instanceof Error ? err.message.replace(/nsec[0-9a-z]+/gi, '[REDACTED]').replace(/[0-9a-f]{64}/g, '[REDACTED]') : String(err); - console.error('Failed to sync to remotes after push:', sanitizedErr); + logger.error({ error: sanitizedErr, npub, repoName }, 'Failed to sync to remotes after push'); }); } } } catch (err) { // Security: Sanitize error messages const sanitizedErr = err instanceof Error ? err.message.replace(/nsec[0-9a-z]+/gi, '[REDACTED]').replace(/[0-9a-f]{64}/g, '[REDACTED]') : String(err); - console.error('Failed to sync to remotes:', sanitizedErr); + logger.error({ error: sanitizedErr, npub, repoName }, 'Failed to sync to remotes'); // Don't fail the request if sync fails } } diff --git a/src/routes/api/repos/[npub]/[repo]/branch-protection/+server.ts b/src/routes/api/repos/[npub]/[repo]/branch-protection/+server.ts index a6ab43a..db87141 100644 --- a/src/routes/api/repos/[npub]/[repo]/branch-protection/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/branch-protection/+server.ts @@ -12,6 +12,7 @@ import { signEventWithNIP07 } from '$lib/services/nostr/nip07-signer.js'; import { NostrClient } from '$lib/services/nostr/nostr-client.js'; import { nip19 } from 'nostr-tools'; import type { BranchProtectionRule } from '$lib/services/nostr/branch-protection-service.js'; +import logger from '$lib/services/logger.js'; const branchProtectionService = new BranchProtectionService(DEFAULT_NOSTR_RELAYS); const ownershipTransferService = new OwnershipTransferService(DEFAULT_NOSTR_RELAYS); @@ -51,7 +52,7 @@ export const GET: RequestHandler = async ({ params }: { params: { npub?: string; } catch (err) { // Security: Sanitize error messages const sanitizedError = err instanceof Error ? err.message.replace(/nsec[0-9a-z]+/gi, '[REDACTED]').replace(/[0-9a-f]{64}/g, '[REDACTED]') : 'Failed to get branch protection'; - console.error('Error getting branch protection:', sanitizedError); + logger.error({ error: sanitizedError, npub, repo }, 'Error getting branch protection'); return error(500, sanitizedError); } }; @@ -142,7 +143,7 @@ export const POST: RequestHandler = async ({ params, request }: { params: { npub } catch (err) { // Security: Sanitize error messages const sanitizedError = err instanceof Error ? err.message.replace(/nsec[0-9a-z]+/gi, '[REDACTED]').replace(/[0-9a-f]{64}/g, '[REDACTED]') : 'Failed to update branch protection'; - console.error('Error updating branch protection:', sanitizedError); + logger.error({ error: sanitizedError, npub, repo }, 'Error updating branch protection'); return error(500, sanitizedError); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/branches/+server.ts b/src/routes/api/repos/[npub]/[repo]/branches/+server.ts index 381be0e..bb16a0f 100644 --- a/src/routes/api/repos/[npub]/[repo]/branches/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/branches/+server.ts @@ -9,6 +9,7 @@ import { FileManager } from '$lib/services/git/file-manager.js'; import { MaintainerService } from '$lib/services/nostr/maintainer-service.js'; import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js'; import { nip19 } from 'nostr-tools'; +import logger from '$lib/services/logger.js'; const repoRoot = process.env.GIT_REPO_ROOT || '/repos'; const fileManager = new FileManager(repoRoot); @@ -29,7 +30,7 @@ export const GET: RequestHandler = async ({ params }: { params: { npub?: string; const branches = await fileManager.getBranches(npub, repo); return json(branches); } catch (err) { - console.error('Error getting branches:', err); + logger.error({ error: err, npub, repo }, 'Error getting branches'); return error(500, err instanceof Error ? err.message : 'Failed to get branches'); } }; @@ -90,7 +91,7 @@ export const POST: RequestHandler = async ({ params, request }: { params: { npub await fileManager.createBranch(npub, repo, branchName, fromBranch || 'main'); return json({ success: true, message: 'Branch created successfully' }); } catch (err) { - console.error('Error creating branch:', err); + logger.error({ error: err, npub, repo, branchName }, 'Error creating branch'); return error(500, err instanceof Error ? err.message : 'Failed to create branch'); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/commits/+server.ts b/src/routes/api/repos/[npub]/[repo]/commits/+server.ts index 4b4e991..ed77066 100644 --- a/src/routes/api/repos/[npub]/[repo]/commits/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/commits/+server.ts @@ -5,6 +5,7 @@ import { json, error } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; import { FileManager } from '$lib/services/git/file-manager.js'; +import logger from '$lib/services/logger.js'; const repoRoot = process.env.GIT_REPO_ROOT || '/repos'; const fileManager = new FileManager(repoRoot); @@ -35,7 +36,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { const commits = await fileManager.getCommitHistory(npub, repo, branch, limit, path); return json(commits); } catch (err) { - console.error('Error getting commit history:', err); + logger.error({ error: err, npub, repo, branch }, 'Error getting commit history'); return error(500, err instanceof Error ? err.message : 'Failed to get commit history'); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/diff/+server.ts b/src/routes/api/repos/[npub]/[repo]/diff/+server.ts index 145e1da..8791443 100644 --- a/src/routes/api/repos/[npub]/[repo]/diff/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/diff/+server.ts @@ -5,6 +5,7 @@ import { json, error } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; import { FileManager } from '$lib/services/git/file-manager.js'; +import logger from '$lib/services/logger.js'; const repoRoot = process.env.GIT_REPO_ROOT || '/repos'; const fileManager = new FileManager(repoRoot); @@ -35,7 +36,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { const diffs = await fileManager.getDiff(npub, repo, fromRef, toRef, filePath); return json(diffs); } catch (err) { - console.error('Error getting diff:', err); + logger.error({ error: err, npub, repo, fromRef, toRef }, 'Error getting diff'); return error(500, err instanceof Error ? err.message : 'Failed to get diff'); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/download/+server.ts b/src/routes/api/repos/[npub]/[repo]/download/+server.ts index e79dcfa..53d44ce 100644 --- a/src/routes/api/repos/[npub]/[repo]/download/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/download/+server.ts @@ -13,6 +13,7 @@ import { promisify } from 'util'; import { existsSync, readFileSync } from 'fs'; import { join } from 'path'; import { createReadStream } from 'fs'; +import logger from '$lib/services/logger.js'; const execAsync = promisify(exec); const repoRoot = process.env.GIT_REPO_ROOT || '/repos'; @@ -104,7 +105,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { throw archiveError; } } catch (err) { - console.error('Error creating repository archive:', err); + logger.error({ error: err, npub, repo }, 'Error creating repository archive'); return error(500, err instanceof Error ? err.message : 'Failed to create repository archive'); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/file/+server.ts b/src/routes/api/repos/[npub]/[repo]/file/+server.ts index 0a46893..0e5e8d2 100644 --- a/src/routes/api/repos/[npub]/[repo]/file/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/file/+server.ts @@ -11,6 +11,7 @@ import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js'; import { nip19 } from 'nostr-tools'; import { verifyNIP98Auth } from '$lib/services/nostr/nip98-auth.js'; import { auditLogger } from '$lib/services/security/audit-logger.js'; +import logger from '$lib/services/logger.js'; const repoRoot = process.env.GIT_REPO_ROOT || '/repos'; const fileManager = new FileManager(repoRoot); @@ -86,7 +87,7 @@ export const GET: RequestHandler = async ({ params, url, request }: { params: { } catch (err) { // Security: Sanitize error messages to prevent leaking sensitive data const sanitizedError = err instanceof Error ? err.message.replace(/nsec[0-9a-z]+/gi, '[REDACTED]').replace(/[0-9a-f]{64}/g, '[REDACTED]') : 'Failed to read file'; - console.error('Error reading file:', sanitizedError); + logger.error({ error: sanitizedError, npub, repo, filePath }, 'Error reading file'); return error(500, sanitizedError); } }; @@ -98,9 +99,11 @@ export const POST: RequestHandler = async ({ params, url, request }: { params: { return error(400, 'Missing npub or repo parameter'); } + let path: string | undefined; try { const body = await request.json(); - const { path, content, commitMessage, authorName, authorEmail, branch, action, userPubkey, useNIP07, nsecKey } = body; + path = body.path; + const { content, commitMessage, authorName, authorEmail, branch, action, userPubkey, useNIP07, nsecKey } = body; // Check for NIP-98 authentication (for git operations) const authHeader = request.headers.get('Authorization'); @@ -175,7 +178,7 @@ export const POST: RequestHandler = async ({ params, url, request }: { params: { if (nsecKey) { // Security: Log warning but never log the actual key value const clientIp = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip') || 'unknown'; - console.warn(`[SECURITY] Client attempted to send nsecKey in request from IP ${clientIp}. This is not allowed for security reasons.`); + logger.warn({ clientIp, npub, repo }, '[SECURITY] Client attempted to send nsecKey in request. This is not allowed for security reasons.'); auditLogger.log({ user: userPubkeyHex || undefined, ip: clientIp, @@ -265,7 +268,7 @@ export const POST: RequestHandler = async ({ params, url, request }: { params: { } catch (err) { // Security: Sanitize error messages to prevent leaking sensitive data const sanitizedError = err instanceof Error ? err.message.replace(/nsec[0-9a-z]+/gi, '[REDACTED]').replace(/[0-9a-f]{64}/g, '[REDACTED]') : 'Failed to write file'; - console.error('Error writing file:', sanitizedError); + logger.error({ error: sanitizedError, npub, repo, path }, 'Error writing file'); return error(500, sanitizedError); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/fork/+server.ts b/src/routes/api/repos/[npub]/[repo]/fork/+server.ts index 33cfdfb..a9ef6e5 100644 --- a/src/routes/api/repos/[npub]/[repo]/fork/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/fork/+server.ts @@ -19,6 +19,7 @@ import { join } from 'path'; import { ResourceLimits } from '$lib/services/security/resource-limits.js'; import { auditLogger } from '$lib/services/security/audit-logger.js'; import { ForkCountService } from '$lib/services/nostr/fork-count-service.js'; +import logger from '$lib/services/logger.js'; const execAsync = promisify(exec); const repoRoot = process.env.GIT_REPO_ROOT || '/repos'; @@ -45,29 +46,27 @@ async function publishEventWithRetry( const logContext = context || `[event:${eventId}]`; for (let attempt = 1; attempt <= maxAttempts; attempt++) { - console.log(`[Fork] ${logContext} Publishing ${eventName} - Attempt ${attempt}/${maxAttempts}...`); + logger.info({ logContext, eventName, attempt, maxAttempts }, `[Fork] Publishing ${eventName} - Attempt ${attempt}/${maxAttempts}...`); lastResult = await nostrClient.publishEvent(event, relays); if (lastResult.success.length > 0) { - console.log(`[Fork] ${logContext} ✓ ${eventName} published successfully to ${lastResult.success.length} relay(s): ${lastResult.success.join(', ')}`); + logger.info({ logContext, eventName, successCount: lastResult.success.length, relays: lastResult.success }, `[Fork] ${eventName} published successfully`); if (lastResult.failed.length > 0) { - console.warn(`[Fork] ${logContext} ⚠ Some relays failed: ${lastResult.failed.map(f => `${f.relay}: ${f.error}`).join(', ')}`); + logger.warn({ logContext, eventName, failed: lastResult.failed }, `[Fork] Some relays failed`); } return lastResult; } if (attempt < maxAttempts) { const delayMs = Math.pow(2, attempt - 1) * 1000; // 1s, 2s, 4s - console.warn(`[Fork] ${logContext} ✗ ${eventName} failed on attempt ${attempt}. Retrying in ${delayMs}ms...`); - console.warn(`[Fork] ${logContext} Failed relays: ${lastResult.failed.map(f => `${f.relay}: ${f.error}`).join(', ')}`); + logger.warn({ logContext, eventName, attempt, delayMs, failed: lastResult.failed }, `[Fork] ${eventName} failed on attempt ${attempt}. Retrying...`); await new Promise(resolve => setTimeout(resolve, delayMs)); } } // All attempts failed - console.error(`[Fork] ${logContext} ✗ ${eventName} failed after ${maxAttempts} attempts`); - console.error(`[Fork] ${logContext} All relay failures: ${lastResult?.failed.map(f => `${f.relay}: ${f.error}`).join(', ')}`); + logger.error({ logContext, eventName, maxAttempts, failed: lastResult?.failed }, `[Fork] ${eventName} failed after ${maxAttempts} attempts`); return lastResult!; } @@ -233,8 +232,8 @@ export const POST: RequestHandler = async ({ params, request }) => { const truncatedOriginalNpub = npub.length > 16 ? `${npub.slice(0, 12)}...` : npub; const context = `[${truncatedOriginalNpub}/${repo} → ${truncatedNpub}/${forkRepoName}]`; - console.log(`[Fork] ${context} Starting fork process`); - console.log(`[Fork] ${context} Using ${combinedRelays.length} relay(s): ${combinedRelays.join(', ')}`); + const forkLogger = logger.child({ operation: 'fork', originalRepo: `${npub}/${repo}`, forkRepo: `${userNpub}/${forkRepoName}` }); + forkLogger.info({ relayCount: combinedRelays.length, relays: combinedRelays }, 'Starting fork process'); const publishResult = await publishEventWithRetry( signedForkAnnouncement, @@ -246,7 +245,7 @@ export const POST: RequestHandler = async ({ params, request }) => { if (publishResult.success.length === 0) { // Clean up repo if announcement failed - console.error(`[Fork] ${context} ✗ Fork announcement failed after all retries. Cleaning up repository.`); + forkLogger.error({ failed: publishResult.failed }, 'Fork announcement failed after all retries. Cleaning up repository.'); await execAsync(`rm -rf "${forkRepoPath}"`).catch(() => {}); const errorDetails = `All relays failed: ${publishResult.failed.map(f => `${f.relay}: ${f.error}`).join('; ')}`; return json({ @@ -273,11 +272,11 @@ export const POST: RequestHandler = async ({ params, request }) => { if (ownershipPublishResult.success.length === 0) { // Clean up repo if ownership proof failed - console.error(`[Fork] ${context} ✗ Ownership transfer event failed after all retries. Cleaning up repository and publishing deletion request.`); + forkLogger.error({ failed: ownershipPublishResult.failed }, 'Ownership transfer event failed after all retries. Cleaning up repository and publishing deletion request.'); await execAsync(`rm -rf "${forkRepoPath}"`).catch(() => {}); // Publish deletion request (NIP-09) for the announcement since it's invalid without ownership proof - console.log(`[Fork] ${context} Publishing deletion request for invalid fork announcement...`); + forkLogger.info('Publishing deletion request for invalid fork announcement...'); const deletionRequest = { kind: KIND.DELETION_REQUEST, // NIP-09: Event Deletion Request pubkey: userPubkeyHex, @@ -299,9 +298,9 @@ export const POST: RequestHandler = async ({ params, request }) => { ); if (deletionResult.success.length > 0) { - console.log(`[Fork] ${context} ✓ Deletion request published successfully`); + forkLogger.info('Deletion request published successfully'); } else { - console.error(`[Fork] ${context} ✗ Failed to publish deletion request: ${deletionResult.failed.map(f => `${f.relay}: ${f.error}`).join(', ')}`); + forkLogger.error({ failed: deletionResult.failed }, 'Failed to publish deletion request'); } const errorDetails = `Fork is invalid without ownership proof. All relays failed: ${ownershipPublishResult.failed.map(f => `${f.relay}: ${f.error}`).join('; ')}. Deletion request ${deletionResult.success.length > 0 ? 'published' : 'failed to publish'}.`; @@ -314,17 +313,15 @@ export const POST: RequestHandler = async ({ params, request }) => { } // Provision the fork repo (this will create verification file and include self-transfer) - console.log(`[Fork] Provisioning fork repository...`); + forkLogger.info('Provisioning fork repository...'); await repoManager.provisionRepo(signedForkAnnouncement, signedOwnershipEvent, false); - console.log(`[Fork] ✓ Fork completed successfully!`); - // Security: Truncate npub in logs - const truncatedNpub2 = userNpub.length > 16 ? `${userNpub.slice(0, 12)}...` : userNpub; - console.log(`[Fork] - Repository: ${truncatedNpub2}/${forkRepoName}`); - console.log(`[Fork] - Announcement ID: ${signedForkAnnouncement.id}`); - console.log(`[Fork] - Ownership transfer ID: ${signedOwnershipEvent.id}`); - console.log(`[Fork] - Published to ${publishResult.success.length} relay(s) for announcement`); - console.log(`[Fork] - Published to ${ownershipPublishResult.success.length} relay(s) for ownership transfer`); + forkLogger.info({ + announcementId: signedForkAnnouncement.id, + ownershipTransferId: signedOwnershipEvent.id, + announcementRelays: publishResult.success.length, + ownershipRelays: ownershipPublishResult.success.length + }, 'Fork completed successfully'); return json({ success: true, @@ -345,7 +342,7 @@ export const POST: RequestHandler = async ({ params, request }) => { // Security: Sanitize error messages to prevent leaking sensitive data const sanitizedError = err instanceof Error ? err.message.replace(/nsec[0-9a-z]+/gi, '[REDACTED]').replace(/[0-9a-f]{64}/g, '[REDACTED]') : 'Failed to fork repository'; const context = npub && repo ? `[${npub}/${repo}]` : '[unknown]'; - console.error(`[Fork] ${context} Error forking repository:`, sanitizedError); + logger.error({ error: sanitizedError, npub, repo }, `[Fork] ${context} Error forking repository`); return error(500, sanitizedError); } }; @@ -418,7 +415,7 @@ export const GET: RequestHandler = async ({ params }) => { } catch (err) { // Log but don't fail the request const context = npub && repo ? `[${npub}/${repo}]` : '[unknown]'; - console.warn(`[Fork] ${context} Failed to get fork count:`, err instanceof Error ? err.message : String(err)); + logger.warn({ error: err, npub, repo }, `[Fork] ${context} Failed to get fork count`); } } @@ -431,7 +428,7 @@ export const GET: RequestHandler = async ({ params }) => { // Security: Sanitize error messages const sanitizedError = err instanceof Error ? err.message.replace(/nsec[0-9a-z]+/gi, '[REDACTED]').replace(/[0-9a-f]{64}/g, '[REDACTED]') : 'Failed to get fork information'; const context = npub && repo ? `[${npub}/${repo}]` : '[unknown]'; - console.error(`[Fork] ${context} Error getting fork information:`, sanitizedError); + logger.error({ error: sanitizedError, npub, repo }, `[Fork] ${context} Error getting fork information`); return error(500, sanitizedError); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/highlights/+server.ts b/src/routes/api/repos/[npub]/[repo]/highlights/+server.ts index 3ac1716..6c2bf24 100644 --- a/src/routes/api/repos/[npub]/[repo]/highlights/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/highlights/+server.ts @@ -12,6 +12,7 @@ import type { NostrEvent } from '$lib/types/nostr.js'; import { combineRelays } from '$lib/config.js'; import { getUserRelays } from '$lib/services/nostr/user-relays.js'; import { NostrClient } from '$lib/services/nostr/nostr-client.js'; +import logger from '$lib/services/logger.js'; const highlightsService = new HighlightsService(DEFAULT_NOSTR_RELAYS); const nostrClient = new NostrClient(DEFAULT_NOSTR_RELAYS); @@ -74,7 +75,7 @@ export const GET: RequestHandler = async ({ params, url }) => { comments: prComments }); } catch (err) { - console.error('Error fetching highlights:', err); + logger.error({ error: err, npub, repo }, 'Error fetching highlights'); return error(500, err instanceof Error ? err.message : 'Failed to fetch highlights'); } }; @@ -123,7 +124,7 @@ export const POST: RequestHandler = async ({ params, request }) => { return json({ success: true, event, published: result }); } catch (err) { - console.error('Error creating highlight/comment:', err); + logger.error({ error: err, npub, repo }, 'Error creating highlight/comment'); return error(500, err instanceof Error ? err.message : 'Failed to create highlight/comment'); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/issues/+server.ts b/src/routes/api/repos/[npub]/[repo]/issues/+server.ts index a6fab84..591d86b 100644 --- a/src/routes/api/repos/[npub]/[repo]/issues/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/issues/+server.ts @@ -7,6 +7,7 @@ import type { RequestHandler } from './$types'; import { IssuesService } from '$lib/services/nostr/issues-service.js'; import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js'; import { nip19 } from 'nostr-tools'; +import logger from '$lib/services/logger.js'; export const GET: RequestHandler = async ({ params, url, request }) => { const { npub, repo } = params; @@ -36,7 +37,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { return json(issues); } catch (err) { - console.error('Error fetching issues:', err); + logger.error({ error: err, npub, repo }, 'Error fetching issues'); return error(500, err instanceof Error ? err.message : 'Failed to fetch issues'); } }; @@ -73,7 +74,7 @@ export const POST: RequestHandler = async ({ params, request }) => { return json({ success: true, event, published: result }); } catch (err) { - console.error('Error creating issue:', err); + logger.error({ error: err, npub, repo }, 'Error creating issue'); return error(500, err instanceof Error ? err.message : 'Failed to create issue'); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/maintainers/+server.ts b/src/routes/api/repos/[npub]/[repo]/maintainers/+server.ts index 6374109..f07dbca 100644 --- a/src/routes/api/repos/[npub]/[repo]/maintainers/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/maintainers/+server.ts @@ -8,6 +8,7 @@ import type { RequestHandler } from './$types'; import { MaintainerService } from '$lib/services/nostr/maintainer-service.js'; import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js'; import { nip19 } from 'nostr-tools'; +import logger from '$lib/services/logger.js'; const maintainerService = new MaintainerService(DEFAULT_NOSTR_RELAYS); @@ -60,7 +61,7 @@ export const GET: RequestHandler = async ({ params, url }: { params: { npub?: st return json({ maintainers, owner }); } catch (err) { - console.error('Error checking maintainers:', err); + logger.error({ error: err, npub, repo }, 'Error checking maintainers'); return error(500, err instanceof Error ? err.message : 'Failed to check maintainers'); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/prs/+server.ts b/src/routes/api/repos/[npub]/[repo]/prs/+server.ts index 106cf19..17653b7 100644 --- a/src/routes/api/repos/[npub]/[repo]/prs/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/prs/+server.ts @@ -8,6 +8,7 @@ import type { RequestHandler } from './$types'; import { PRsService } from '$lib/services/nostr/prs-service.js'; import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js'; import { nip19 } from 'nostr-tools'; +import logger from '$lib/services/logger.js'; export const GET: RequestHandler = async ({ params, url, request }: { params: { npub?: string; repo?: string }; url: URL; request: Request }) => { const { npub, repo } = params; @@ -43,7 +44,7 @@ export const GET: RequestHandler = async ({ params, url, request }: { params: { return json(prs); } catch (err) { - console.error('Error fetching pull requests:', err); + logger.error({ error: err, npub, repo }, 'Error fetching pull requests'); return error(500, err instanceof Error ? err.message : 'Failed to fetch pull requests'); } }; @@ -79,7 +80,7 @@ export const POST: RequestHandler = async ({ params, request }: { params: { npub return json({ success: true, event, published: result }); } catch (err) { - console.error('Error creating pull request:', err); + logger.error({ error: err, npub, repo }, 'Error creating pull request'); return error(500, err instanceof Error ? err.message : 'Failed to create pull request'); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/raw/+server.ts b/src/routes/api/repos/[npub]/[repo]/raw/+server.ts index 358b87d..1642713 100644 --- a/src/routes/api/repos/[npub]/[repo]/raw/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/raw/+server.ts @@ -8,6 +8,7 @@ import { FileManager } from '$lib/services/git/file-manager.js'; import { MaintainerService } from '$lib/services/nostr/maintainer-service.js'; import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js'; import { nip19 } from 'nostr-tools'; +import logger from '$lib/services/logger.js'; const repoRoot = process.env.GIT_REPO_ROOT || '/repos'; const fileManager = new FileManager(repoRoot); @@ -82,7 +83,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { } }); } catch (err) { - console.error('Error getting raw file:', err); + logger.error({ error: err, npub, repo, filePath }, 'Error getting raw file'); return error(500, err instanceof Error ? err.message : 'Failed to get raw file'); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/readme/+server.ts b/src/routes/api/repos/[npub]/[repo]/readme/+server.ts index dbada51..83c0903 100644 --- a/src/routes/api/repos/[npub]/[repo]/readme/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/readme/+server.ts @@ -8,6 +8,7 @@ import { FileManager } from '$lib/services/git/file-manager.js'; import { MaintainerService } from '$lib/services/nostr/maintainer-service.js'; import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js'; import { nip19 } from 'nostr-tools'; +import logger from '$lib/services/logger.js'; const repoRoot = process.env.GIT_REPO_ROOT || '/repos'; const fileManager = new FileManager(repoRoot); @@ -91,7 +92,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { isMarkdown: readmePath?.toLowerCase().endsWith('.md') || readmePath?.toLowerCase().endsWith('.markdown') }); } catch (err) { - console.error('Error getting README:', err); + logger.error({ error: err, npub, repo }, 'Error getting README'); return error(500, err instanceof Error ? err.message : 'Failed to get README'); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/settings/+server.ts b/src/routes/api/repos/[npub]/[repo]/settings/+server.ts index ae692c1..79071d7 100644 --- a/src/routes/api/repos/[npub]/[repo]/settings/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/settings/+server.ts @@ -11,6 +11,7 @@ import { KIND } from '$lib/types/nostr.js'; import { nip19 } from 'nostr-tools'; import { signEventWithNIP07 } from '$lib/services/nostr/nip07-signer.js'; import { MaintainerService } from '$lib/services/nostr/maintainer-service.js'; +import logger from '$lib/services/logger.js'; import { OwnershipTransferService } from '$lib/services/nostr/ownership-transfer-service.js'; const nostrClient = new NostrClient(DEFAULT_NOSTR_RELAYS); @@ -100,7 +101,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { npub }); } catch (err) { - console.error('Error getting repository settings:', err); + logger.error({ error: err, npub, repo }, 'Error getting repository settings'); return error(500, err instanceof Error ? err.message : 'Failed to get repository settings'); } }; @@ -214,7 +215,7 @@ export const POST: RequestHandler = async ({ params, request }) => { return json({ success: true, event: signedEvent }); } catch (err) { - console.error('Error updating repository settings:', err); + logger.error({ error: err, npub, repo }, 'Error updating repository settings'); return error(500, err instanceof Error ? err.message : 'Failed to update repository settings'); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/tags/+server.ts b/src/routes/api/repos/[npub]/[repo]/tags/+server.ts index 3e5b888..f668b2e 100644 --- a/src/routes/api/repos/[npub]/[repo]/tags/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/tags/+server.ts @@ -9,6 +9,7 @@ import { FileManager } from '$lib/services/git/file-manager.js'; import { MaintainerService } from '$lib/services/nostr/maintainer-service.js'; import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js'; import { nip19 } from 'nostr-tools'; +import logger from '$lib/services/logger.js'; const repoRoot = process.env.GIT_REPO_ROOT || '/repos'; const fileManager = new FileManager(repoRoot); @@ -37,7 +38,7 @@ export const GET: RequestHandler = async ({ params, url, request }: { params: { const tags = await fileManager.getTags(npub, repo); return json(tags); } catch (err) { - console.error('Error getting tags:', err); + logger.error({ error: err, npub, repo }, 'Error getting tags'); return error(500, err instanceof Error ? err.message : 'Failed to get tags'); } }; @@ -98,7 +99,7 @@ export const POST: RequestHandler = async ({ params, request }: { params: { npub await fileManager.createTag(npub, repo, tagName, ref || 'HEAD', message); return json({ success: true, message: 'Tag created successfully' }); } catch (err) { - console.error('Error creating tag:', err); + logger.error({ error: err, npub, repo, tagName }, 'Error creating tag'); return error(500, err instanceof Error ? err.message : 'Failed to create tag'); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/transfer/+server.ts b/src/routes/api/repos/[npub]/[repo]/transfer/+server.ts index 3e91689..3e70157 100644 --- a/src/routes/api/repos/[npub]/[repo]/transfer/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/transfer/+server.ts @@ -12,6 +12,7 @@ import { nip19 } from 'nostr-tools'; import { verifyEvent } from 'nostr-tools'; import type { NostrEvent } from '$lib/types/nostr.js'; import { getUserRelays } from '$lib/services/nostr/user-relays.js'; +import logger from '$lib/services/logger.js'; const ownershipTransferService = new OwnershipTransferService(DEFAULT_NOSTR_RELAYS); const nostrClient = new NostrClient(DEFAULT_NOSTR_RELAYS); @@ -72,7 +73,7 @@ export const GET: RequestHandler = async ({ params }) => { }) }); } catch (err) { - console.error('Error fetching ownership info:', err); + logger.error({ error: err, npub, repo }, 'Error fetching ownership info'); return error(500, err instanceof Error ? err.message : 'Failed to fetch ownership info'); } }; @@ -166,7 +167,7 @@ export const POST: RequestHandler = async ({ params, request }) => { message: 'Ownership transfer initiated successfully' }); } catch (err) { - console.error('Error transferring ownership:', err); + logger.error({ error: err, npub, repo }, 'Error transferring ownership'); return error(500, err instanceof Error ? err.message : 'Failed to transfer ownership'); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/tree/+server.ts b/src/routes/api/repos/[npub]/[repo]/tree/+server.ts index 121a599..fa455f1 100644 --- a/src/routes/api/repos/[npub]/[repo]/tree/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/tree/+server.ts @@ -8,6 +8,7 @@ import { FileManager } from '$lib/services/git/file-manager.js'; import { MaintainerService } from '$lib/services/nostr/maintainer-service.js'; import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js'; import { nip19 } from 'nostr-tools'; +import logger from '$lib/services/logger.js'; const repoRoot = process.env.GIT_REPO_ROOT || '/repos'; const fileManager = new FileManager(repoRoot); @@ -49,7 +50,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { const files = await fileManager.listFiles(npub, repo, ref, path); return json(files); } catch (err) { - console.error('Error listing files:', err); + logger.error({ error: err, npub, repo, path, branch }, 'Error listing files'); return error(500, err instanceof Error ? err.message : 'Failed to list files'); } }; diff --git a/src/routes/api/repos/[npub]/[repo]/verify/+server.ts b/src/routes/api/repos/[npub]/[repo]/verify/+server.ts index 46f4f67..e78a052 100644 --- a/src/routes/api/repos/[npub]/[repo]/verify/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/verify/+server.ts @@ -13,6 +13,7 @@ import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js'; import { KIND } from '$lib/types/nostr.js'; import { nip19 } from 'nostr-tools'; import { existsSync } from 'fs'; +import logger from '$lib/services/logger.js'; import { join } from 'path'; const repoRoot = process.env.GIT_REPO_ROOT || '/repos'; @@ -149,7 +150,7 @@ export const GET: RequestHandler = async ({ params }: { params: { npub?: string; }); } } catch (err) { - console.error('Error verifying repository:', err); + logger.error({ error: err, npub, repo }, 'Error verifying repository'); return error(500, err instanceof Error ? err.message : 'Failed to verify repository'); } }; diff --git a/src/routes/api/search/+server.ts b/src/routes/api/search/+server.ts index fb7d222..2b999e7 100644 --- a/src/routes/api/search/+server.ts +++ b/src/routes/api/search/+server.ts @@ -11,6 +11,7 @@ import { FileManager } from '$lib/services/git/file-manager.js'; import { nip19 } from 'nostr-tools'; import { existsSync } from 'fs'; import { join } from 'path'; +import logger from '$lib/services/logger.js'; const repoRoot = process.env.GIT_REPO_ROOT || '/repos'; const fileManager = new FileManager(repoRoot); @@ -154,7 +155,7 @@ export const GET: RequestHandler = async ({ url }) => { total: results.repos.length + results.code.length }); } catch (err) { - console.error('Error searching:', err); + logger.error({ error: err, query }, 'Error searching'); return error(500, err instanceof Error ? err.message : 'Failed to search'); } }; diff --git a/src/routes/docs/nip34/+page.server.ts b/src/routes/docs/nip34/+page.server.ts index be6fc8b..3a3339e 100644 --- a/src/routes/docs/nip34/+page.server.ts +++ b/src/routes/docs/nip34/+page.server.ts @@ -5,6 +5,7 @@ import { readFile } from 'fs/promises'; import { join } from 'path'; import type { PageServerLoad } from './$types'; +import logger from '$lib/services/logger.js'; export const load: PageServerLoad = async () => { try { @@ -13,7 +14,7 @@ export const load: PageServerLoad = async () => { const content = await readFile(filePath, 'utf-8'); return { content }; } catch (error) { - console.error('Error loading NIP-34.md:', error); + logger.error({ error }, 'Error loading NIP-34.md'); return { content: null, error: 'Failed to load documentation' }; } };