feat: V2 microservices stack — backend services, gateway, JWT auth
Add full V2 architecture: identity, content, studio (.NET 10) and file, render, notification, gateway (Go) services with vendored deps, plus DB migrations, event/API contracts, and an init-db script. Wire the Next.js frontend to the gateway: server-side JWT auth routes (login/register/refresh/logout/me), gateway fetch helper, and session/ cookie/jwt helpers under src/lib. Containerize the stack via docker-compose.v2.yml and per-service Dockerfiles. Base images resolve through a Nexus mirror (Docker Hub) and MCR directly; npm/NuGet pull from Nexus groups. Self-host fonts via next/font/local to avoid Google Fonts (geo-blocked). Add CI workflow and ignore .env.v2, *.stackdump, and .NET bin/obj. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
# =====================================================================
|
||||
# Identity Events — published by Identity Service
|
||||
# =====================================================================
|
||||
events:
|
||||
|
||||
identity.user.registered.v1:
|
||||
routing_key: identity.user.registered.v1
|
||||
description: New user account created (any tenant).
|
||||
payload:
|
||||
type: object
|
||||
required: [user_id, tenant_id, register_mode]
|
||||
properties:
|
||||
user_id: { type: string, format: uuid }
|
||||
tenant_id: { type: string, format: uuid }
|
||||
email: { type: string, nullable: true }
|
||||
phone_number: { type: string, nullable: true }
|
||||
full_name: { type: string, nullable: true }
|
||||
register_mode:
|
||||
type: string
|
||||
enum: [Email, Mobile, Google, Telegram, SSO, Reseller]
|
||||
affiliate_owner_id: { type: string, format: uuid, nullable: true }
|
||||
registered_with_mobile_app: { type: boolean }
|
||||
|
||||
identity.user.email_verified.v1:
|
||||
routing_key: identity.user.email_verified.v1
|
||||
payload:
|
||||
type: object
|
||||
required: [user_id, email]
|
||||
properties:
|
||||
user_id: { type: string, format: uuid }
|
||||
tenant_id: { type: string, format: uuid }
|
||||
email: { type: string }
|
||||
|
||||
identity.user.banned.v1:
|
||||
routing_key: identity.user.banned.v1
|
||||
payload:
|
||||
type: object
|
||||
required: [user_id, banned_by, reason]
|
||||
properties:
|
||||
user_id: { type: string, format: uuid }
|
||||
tenant_id: { type: string, format: uuid }
|
||||
banned_by: { type: string, format: uuid }
|
||||
reason: { type: string }
|
||||
unblock_date: { type: string, format: date-time, nullable: true }
|
||||
|
||||
identity.tenant.created.v1:
|
||||
routing_key: identity.tenant.created.v1
|
||||
payload:
|
||||
type: object
|
||||
required: [tenant_id, slug, kind]
|
||||
properties:
|
||||
tenant_id: { type: string, format: uuid }
|
||||
slug: { type: string }
|
||||
name: { type: string }
|
||||
kind: { type: string, enum: [Internal, Reseller, Enterprise] }
|
||||
contact_email: { type: string, nullable: true }
|
||||
|
||||
identity.tenant.suspended.v1:
|
||||
routing_key: identity.tenant.suspended.v1
|
||||
payload:
|
||||
type: object
|
||||
required: [tenant_id, reason]
|
||||
properties:
|
||||
tenant_id: { type: string, format: uuid }
|
||||
reason: { type: string }
|
||||
suspended_at: { type: string, format: date-time }
|
||||
|
||||
identity.plan.activated.v1:
|
||||
routing_key: identity.plan.activated.v1
|
||||
payload:
|
||||
type: object
|
||||
required: [user_id, user_plan_id, plan_code, expires_at]
|
||||
properties:
|
||||
user_id: { type: string, format: uuid }
|
||||
tenant_id: { type: string, format: uuid }
|
||||
user_plan_id: { type: string, format: uuid }
|
||||
plan_id: { type: string, format: uuid }
|
||||
plan_code: { type: string }
|
||||
plan_name: { type: string }
|
||||
seconds_charge: { type: integer }
|
||||
storage_gb: { type: integer }
|
||||
parallel_renders: { type: integer }
|
||||
expires_at: { type: string, format: date-time }
|
||||
payment_id: { type: string, format: uuid, nullable: true }
|
||||
|
||||
identity.plan.expired.v1:
|
||||
routing_key: identity.plan.expired.v1
|
||||
payload:
|
||||
type: object
|
||||
required: [user_id, user_plan_id]
|
||||
properties:
|
||||
user_id: { type: string, format: uuid }
|
||||
tenant_id: { type: string, format: uuid }
|
||||
user_plan_id: { type: string, format: uuid }
|
||||
plan_code: { type: string }
|
||||
days_overdue: { type: integer }
|
||||
|
||||
identity.payment.succeeded.v1:
|
||||
routing_key: identity.payment.succeeded.v1
|
||||
payload:
|
||||
type: object
|
||||
required: [payment_id, user_id, amount_minor, gateway, action]
|
||||
properties:
|
||||
payment_id: { type: string, format: uuid }
|
||||
tenant_id: { type: string, format: uuid }
|
||||
user_id: { type: string, format: uuid }
|
||||
amount_minor: { type: integer, format: int64 }
|
||||
currency: { type: string }
|
||||
gateway:
|
||||
type: string
|
||||
enum: [ZarinPal, IdPay, Bazaar, Stripe, Balance, Manual]
|
||||
action:
|
||||
type: string
|
||||
enum: [PlanPurchase, BalanceCharge, ProjectRender, UserProject, StorageUpgrade, Other]
|
||||
gateway_track_id: { type: string, nullable: true }
|
||||
plan_id: { type: string, format: uuid, nullable: true }
|
||||
affiliate_owner_id: { type: string, format: uuid, nullable: true }
|
||||
affiliate_profit_minor: { type: integer, format: int64 }
|
||||
|
||||
identity.payment.failed.v1:
|
||||
routing_key: identity.payment.failed.v1
|
||||
payload:
|
||||
type: object
|
||||
required: [payment_id, user_id, gateway, failure_reason]
|
||||
properties:
|
||||
payment_id: { type: string, format: uuid }
|
||||
tenant_id: { type: string, format: uuid }
|
||||
user_id: { type: string, format: uuid }
|
||||
amount_minor: { type: integer, format: int64 }
|
||||
currency: { type: string }
|
||||
gateway: { type: string }
|
||||
failure_reason: { type: string }
|
||||
action: { type: string }
|
||||
|
||||
identity.payment.refunded.v1:
|
||||
routing_key: identity.payment.refunded.v1
|
||||
payload:
|
||||
type: object
|
||||
required: [payment_id, user_id, refund_amount_minor, reason]
|
||||
properties:
|
||||
payment_id: { type: string, format: uuid }
|
||||
tenant_id: { type: string, format: uuid }
|
||||
user_id: { type: string, format: uuid }
|
||||
refund_amount_minor: { type: integer, format: int64 }
|
||||
currency: { type: string }
|
||||
reason: { type: string }
|
||||
refunded_to:
|
||||
type: string
|
||||
enum: [Balance, OriginalMethod, Plan]
|
||||
|
||||
identity.api_key.created.v1:
|
||||
routing_key: identity.api_key.created.v1
|
||||
payload:
|
||||
type: object
|
||||
required: [api_key_id, tenant_id, name, environment, created_by_user_id]
|
||||
properties:
|
||||
api_key_id: { type: string, format: uuid }
|
||||
tenant_id: { type: string, format: uuid }
|
||||
name: { type: string }
|
||||
environment: { type: string, enum: [Live, Test] }
|
||||
key_prefix: { type: string }
|
||||
last4: { type: string }
|
||||
scopes:
|
||||
type: array
|
||||
items: { type: string }
|
||||
created_by_user_id: { type: string, format: uuid }
|
||||
|
||||
identity.api_key.revoked.v1:
|
||||
routing_key: identity.api_key.revoked.v1
|
||||
payload:
|
||||
type: object
|
||||
required: [api_key_id, tenant_id, revoked_by_user_id, reason]
|
||||
properties:
|
||||
api_key_id: { type: string, format: uuid }
|
||||
tenant_id: { type: string, format: uuid }
|
||||
revoked_by_user_id: { type: string, format: uuid }
|
||||
reason: { type: string }
|
||||
Reference in New Issue
Block a user