diff --git a/.env.development b/.env.development index 19749d95d..874fc7663 100644 --- a/.env.development +++ b/.env.development @@ -23,7 +23,9 @@ PUBLIC_GLITCHTIP_DSN= # Mana Core Auth Service MANA_CORE_AUTH_URL=http://localhost:3001 -# Service key for bot-to-auth communication (Matrix-SSO-Link) +# Mana Credits Service +MANA_CREDITS_URL=http://localhost:3060 +# Service key for service-to-service communication MANA_CORE_SERVICE_KEY=dev-service-key-for-bot-sso-2024 # WebAuthn / Passkeys (localhost for dev, mana.how for production) diff --git a/CLAUDE.md b/CLAUDE.md index caa78a607..502fb35fd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -130,6 +130,7 @@ manacore-monorepo/ │ └── {game-name}/ # Individual games ├── services/ # Standalone microservices │ ├── mana-core-auth/ # Central authentication service +│ ├── mana-credits/ # Credit system (Hono + Bun, extracted from auth) │ ├── mana-search/ # Central search & content extraction (NestJS, legacy) │ ├── mana-search-go/ # Central search & content extraction (Go, active) │ ├── mana-crawler/ # Web crawler service diff --git a/packages/mana-core-nestjs-integration/src/services/credit-client.service.ts b/packages/mana-core-nestjs-integration/src/services/credit-client.service.ts index 3d1b7cdf8..1b2b83dad 100644 --- a/packages/mana-core-nestjs-integration/src/services/credit-client.service.ts +++ b/packages/mana-core-nestjs-integration/src/services/credit-client.service.ts @@ -35,6 +35,18 @@ export class CreditClientService { ); } + /** + * Get the credits service URL. Uses MANA_CREDITS_URL if available, + * falls back to MANA_CORE_AUTH_URL for backward compatibility. + */ + private getCreditsUrl(): string { + return ( + this.configService?.get('MANA_CREDITS_URL') || + process.env.MANA_CREDITS_URL || + this.getAuthUrl() + ); + } + private getServiceKey(): string { return ( this.options?.serviceKey || @@ -76,7 +88,7 @@ export class CreditClientService { } async getBalance(userId: string): Promise { - const authUrl = this.getAuthUrl(); + const creditsUrl = this.getCreditsUrl(); const serviceKey = this.getServiceKey(); if (!serviceKey) { @@ -89,7 +101,7 @@ export class CreditClientService { } try { - const response = await fetch(`${authUrl}/api/v1/credits/balance/${userId}`, { + const response = await fetch(`${creditsUrl}/api/v1/internal/credits/balance/${userId}`, { method: 'GET', headers: { 'Content-Type': 'application/json', @@ -127,7 +139,7 @@ export class CreditClientService { metadata?: Record, creditSource?: { type: 'personal' } | { type: 'guild'; guildId: string } ): Promise { - const authUrl = this.getAuthUrl(); + const creditsUrl = this.getCreditsUrl(); const serviceKey = this.getServiceKey(); if (!serviceKey) { @@ -136,7 +148,7 @@ export class CreditClientService { } try { - const response = await fetch(`${authUrl}/api/v1/credits/use`, { + const response = await fetch(`${creditsUrl}/api/v1/internal/credits/use`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -179,7 +191,7 @@ export class CreditClientService { description: string, metadata?: Record ): Promise { - const authUrl = this.getAuthUrl(); + const creditsUrl = this.getCreditsUrl(); const serviceKey = this.getServiceKey(); if (!serviceKey) { @@ -188,7 +200,7 @@ export class CreditClientService { } try { - const response = await fetch(`${authUrl}/api/v1/credits/refund`, { + const response = await fetch(`${creditsUrl}/api/v1/internal/credits/refund`, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/services/mana-core-auth/src/auth/services/better-auth.service.ts b/services/mana-core-auth/src/auth/services/better-auth.service.ts index a843ec1b9..71b46ad17 100644 --- a/services/mana-core-auth/src/auth/services/better-auth.service.ts +++ b/services/mana-core-auth/src/auth/services/better-auth.service.ts @@ -158,22 +158,29 @@ export class BetterAuthService { // Create personal credit balance await this.createPersonalCreditBalance(user.id); - // Redeem any pending gift codes sent to this email - if (this.giftCodeService) { - try { - const giftResult = await this.giftCodeService.redeemPendingGifts(user.id, dto.email); - if (giftResult.redeemedCount > 0) { + // Redeem any pending gift codes via mana-credits service + try { + const creditsUrl = process.env.MANA_CREDITS_URL || 'http://localhost:3060'; + const serviceKey = process.env.MANA_CORE_SERVICE_KEY || ''; + const giftRes = await fetch(`${creditsUrl}/api/v1/internal/gifts/redeem-pending`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'X-Service-Key': serviceKey }, + body: JSON.stringify({ userId: user.id, email: dto.email }), + }); + if (giftRes.ok) { + const giftResult = await giftRes.json(); + if (giftResult.redeemed > 0) { this.logger.log('Redeemed pending gifts on registration', { userId: user.id, - redeemedCount: giftResult.redeemedCount, + redeemedCount: giftResult.redeemed, totalCredits: giftResult.totalCredits, }); } - } catch (error) { - this.logger.warn('Failed to redeem pending gifts (non-critical)', { - error: error instanceof Error ? error.message : 'Unknown error', - }); } + } catch (error) { + this.logger.warn('Failed to redeem pending gifts via mana-credits (non-critical)', { + error: error instanceof Error ? error.message : 'Unknown error', + }); } return { @@ -1754,37 +1761,41 @@ export class BetterAuthService { * @param userId - User ID * @private */ + /** + * Initialize credit balance via mana-credits service. + * Non-critical — lazy init on first access if this fails. + */ private async createPersonalCreditBalance(userId: string) { - const db = getDb(this.databaseUrl); - try { - await db.insert(balances).values({ - userId: userId as any, // Cast to handle UUID type - balance: 0, - totalEarned: 0, - totalSpent: 0, + const creditsUrl = process.env.MANA_CREDITS_URL || 'http://localhost:3060'; + const serviceKey = process.env.MANA_CORE_SERVICE_KEY || ''; + await fetch(`${creditsUrl}/api/v1/internal/credits/init`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'X-Service-Key': serviceKey }, + body: JSON.stringify({ userId }), }); } catch (error) { - this.logger.warn('Failed to create personal credit balance (non-critical)', { + this.logger.warn('Failed to init credit balance via mana-credits (non-critical)', { error: error instanceof Error ? error.message : 'Unknown error', }); - // Don't throw - this is a non-critical operation } } /** - * Initialize a guild pool for an organization. - * Non-critical — if it fails, the pool can be created later. + * Initialize guild pool via mana-credits service. + * Non-critical — lazy init on first access if this fails. */ private async initializeGuildPool(organizationId: string) { - const db = getDb(this.databaseUrl); - try { - await db.insert(guildPools).values({ - organizationId, + const creditsUrl = process.env.MANA_CREDITS_URL || 'http://localhost:3060'; + const serviceKey = process.env.MANA_CORE_SERVICE_KEY || ''; + await fetch(`${creditsUrl}/api/v1/internal/guild-pool/init`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'X-Service-Key': serviceKey }, + body: JSON.stringify({ organizationId }), }); } catch (error) { - this.logger.warn('Failed to initialize guild pool (non-critical)', { + this.logger.warn('Failed to init guild pool via mana-credits (non-critical)', { organizationId, error: error instanceof Error ? error.message : 'Unknown error', });