Haven't checked yet
This commit is contained in:
36
Documentation/AGENTS.md
Normal file
36
Documentation/AGENTS.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Module Organization
|
||||
- `FictionArchive.sln` ties together the gateway and all subgraph services.
|
||||
- `FictionArchive.API`: Fusion gateway host; GraphQL endpoint at `/graphql`, health at `/healthz`, gateway configuration in `gateway.fgp`, and helper script `build_gateway.py`.
|
||||
- `FictionArchive.Service.*`: GraphQL subgraphs (`AuthenticationService`, `FileService`, `NovelService`, `SchedulerService`, `TranslationService`, `UserService`) plus shared helpers in `FictionArchive.Service.Shared`.
|
||||
- `FictionArchive.Common`: shared enums and hosting extensions used across services.
|
||||
- Environment/config files live beside each service (`appsettings*.json`, `Properties/launchSettings.json`); build outputs under `bin/` and `obj/` should stay untracked.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
- `dotnet restore` then `dotnet build FictionArchive.sln` (Debug by default) to validate all projects compile.
|
||||
- Run the gateway: `dotnet run --project FictionArchive.API` (serves HTTPS; ensure certificates are trusted locally).
|
||||
- Run a subgraph locally: `dotnet run --project FictionArchive.Service.NovelService` (or any other service) to debug a single domain.
|
||||
- Rebuild the Fusion gateway config after subgraph changes: `python FictionArchive.API/build_gateway.py` (requires Python 3 and the `fusion` CLI on PATH; uses `gateway_skip.txt` to omit services).
|
||||
- If tests are added, prefer `dotnet test FictionArchive.sln` to cover the whole solution.
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
- Target .NET 8/C# 12; use 4-space indentation and file-scoped namespaces where practical.
|
||||
- PascalCase for classes, records, interfaces, and public members; camelCase for locals/parameters; suffix async methods with `Async`.
|
||||
- Favor dependency injection and extension methods for service wiring (see `Program.cs` files and `FictionArchive.Service.Shared/Extensions`).
|
||||
- Keep GraphQL schema files and other generated artifacts out of commits unless intentionally versioned.
|
||||
|
||||
## Testing Guidelines
|
||||
- No dedicated test projects exist yet; when adding tests, create `*.Tests` projects aligned to each service (e.g., `FictionArchive.Service.NovelService.Tests`) and name test files `*Tests.cs`.
|
||||
- Prefer xUnit with fluent assertions; aim for coverage on controllers/resolvers, integration events, and critical extension methods.
|
||||
- Use in-memory fakes or test containers for external dependencies to keep tests deterministic.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
- Follow the observed pattern: `[FA-123] Short, imperative summary` (reference the tracker ID and keep scope focused).
|
||||
- Keep commits small and self-contained; include relevant config/schema updates produced by the gateway build script when behavior changes.
|
||||
- PRs should describe the problem, the solution, and any follow-up work; link to issues, attach GraphQL schema diffs or sample queries when applicable, and note any manual steps (migrations, secrets).
|
||||
|
||||
## Security & Configuration Tips
|
||||
- Do not commit secrets; use user secrets or environment variables for API keys and connection strings referenced in `appsettings*.json`.
|
||||
- Verify HTTPS is enabled locally; adjust `launchSettings.json` only when necessary and document non-default ports.
|
||||
- Regenerate `gateway.fgp` after changing subgraph schemas to avoid stale compositions.
|
||||
405
Documentation/ARCHITECTURE.md
Normal file
405
Documentation/ARCHITECTURE.md
Normal file
@@ -0,0 +1,405 @@
|
||||
# FictionArchive Architecture Overview
|
||||
|
||||
## High-Level Architecture
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────┐
|
||||
│ React 19 Frontend │
|
||||
│ (Apollo Client, TailwindCSS, OIDC Auth) │
|
||||
└───────────────────────────┬────────────────────────────────────┘
|
||||
│ GraphQL
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────────┐
|
||||
│ Hot Chocolate Fusion Gateway │
|
||||
│ (FictionArchive.API) │
|
||||
└──────┬────────┬────────┬────────┬────────┬─────────────────────┘
|
||||
│ │ │ │ │
|
||||
▼ ▼ ▼ ▼ ▼
|
||||
┌──────────┐┌──────────┐┌───────────┐┌──────────┐┌──────────────┐
|
||||
│ Novel ││ User ││Translation││Scheduler ││ File │
|
||||
│ Service ││ Service ││ Service ││ Service ││ Service │
|
||||
└────┬─────┘└────┬─────┘└─────┬─────┘└────┬─────┘└──────┬───────┘
|
||||
│ │ │ │ │
|
||||
└───────────┴────────────┴───────────┴─────────────┘
|
||||
│
|
||||
┌────────┴────────┐
|
||||
│ RabbitMQ │
|
||||
│ (Event Bus) │
|
||||
└─────────────────┘
|
||||
│
|
||||
┌────────┴────────┐
|
||||
│ PostgreSQL │
|
||||
│ (per service) │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
## Technology Stack
|
||||
|
||||
| Layer | Technology | Version |
|
||||
|-------|------------|---------|
|
||||
| Runtime | .NET | 8.0 |
|
||||
| GraphQL | Hot Chocolate / Fusion | 13+ |
|
||||
| Database | PostgreSQL | 12+ |
|
||||
| ORM | Entity Framework Core | 8.0 |
|
||||
| Message Broker | RabbitMQ | 3.12+ |
|
||||
| Job Scheduler | Quartz.NET | Latest |
|
||||
| Object Storage | AWS S3 / Garage | - |
|
||||
| Date/Time | NodaTime | Latest |
|
||||
| Frontend | React | 19.2 |
|
||||
| Frontend Build | Vite | 7.2 |
|
||||
| GraphQL Client | Apollo Client | 4.0 |
|
||||
| Auth | OIDC Client TS | 3.4 |
|
||||
| Styling | TailwindCSS | 3.4 |
|
||||
| UI Components | Radix UI | Latest |
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
FictionArchive.sln
|
||||
├── FictionArchive.Common # Shared enums and extensions
|
||||
├── FictionArchive.API # GraphQL Fusion Gateway
|
||||
├── FictionArchive.Service.Shared # Shared infrastructure
|
||||
├── FictionArchive.Service.NovelService
|
||||
├── FictionArchive.Service.UserService
|
||||
├── FictionArchive.Service.TranslationService
|
||||
├── FictionArchive.Service.FileService
|
||||
├── FictionArchive.Service.SchedulerService
|
||||
├── FictionArchive.Service.AuthenticationService
|
||||
├── FictionArchive.Service.NovelService.Tests
|
||||
└── fictionarchive-web # React frontend
|
||||
```
|
||||
|
||||
## Services
|
||||
|
||||
### FictionArchive.API - GraphQL Fusion Gateway
|
||||
|
||||
- **Role**: Single entry point for all GraphQL queries
|
||||
- **Port**: 5001 (HTTPS)
|
||||
- **Endpoints**:
|
||||
- `/graphql` - GraphQL endpoint
|
||||
- `/healthz` - Health check
|
||||
- **Responsibilities**:
|
||||
- Compose GraphQL schemas from all subgraphs
|
||||
- Route queries to appropriate services
|
||||
- CORS policy management
|
||||
|
||||
### FictionArchive.Service.NovelService
|
||||
|
||||
- **Role**: Novel/fiction content management
|
||||
- **Port**: 8081 (HTTPS)
|
||||
- **Database**: `FictionArchive_NovelService`
|
||||
- **GraphQL Operations**:
|
||||
- `GetNovels` - Paginated, filterable novel listing
|
||||
- `ImportNovel` - Trigger novel import
|
||||
- `FetchChapterContents` - Fetch chapter content
|
||||
- **Models**: Novel, Chapter, Source, NovelTag, Image, LocalizationKey
|
||||
- **External Integration**: Novelpia adapter
|
||||
- **Events Published**: `TranslationRequestCreatedEvent`, `FileUploadRequestCreatedEvent`
|
||||
- **Events Subscribed**: `TranslationRequestCompletedEvent`, `NovelUpdateRequestedEvent`, `ChapterPullRequestedEvent`, `FileUploadRequestStatusUpdateEvent`
|
||||
|
||||
### FictionArchive.Service.UserService
|
||||
|
||||
- **Role**: User identity and profile management
|
||||
- **Port**: 8081 (HTTPS)
|
||||
- **Database**: `FictionArchive_UserService`
|
||||
- **Models**: User (with OAuth provider linking)
|
||||
- **Events Subscribed**: `AuthUserAddedEvent`
|
||||
|
||||
### FictionArchive.Service.TranslationService
|
||||
|
||||
- **Role**: Text translation orchestration
|
||||
- **Port**: 8081 (HTTPS)
|
||||
- **Database**: `FictionArchive_TranslationService`
|
||||
- **External Integration**: DeepL API
|
||||
- **Models**: TranslationRequest
|
||||
- **Events Published**: `TranslationRequestCompletedEvent`
|
||||
- **Events Subscribed**: `TranslationRequestCreatedEvent`
|
||||
|
||||
### FictionArchive.Service.FileService
|
||||
|
||||
- **Role**: File storage and S3 proxy
|
||||
- **Port**: 8080 (HTTP)
|
||||
- **Protocol**: REST only (not GraphQL)
|
||||
- **Endpoints**: `GET /api/{*path}` - S3 file proxy
|
||||
- **External Integration**: S3-compatible storage (AWS S3 / Garage)
|
||||
- **Events Published**: `FileUploadRequestStatusUpdateEvent`
|
||||
- **Events Subscribed**: `FileUploadRequestCreatedEvent`
|
||||
|
||||
### FictionArchive.Service.SchedulerService
|
||||
|
||||
- **Role**: Job scheduling and automation
|
||||
- **Port**: 8081 (HTTPS)
|
||||
- **Database**: `FictionArchive_SchedulerService`
|
||||
- **Scheduler**: Quartz.NET with persistent job store
|
||||
- **GraphQL Operations**: `ScheduleEventJob`, `GetScheduledJobs`
|
||||
- **Models**: SchedulerJob, EventJobTemplate
|
||||
|
||||
### FictionArchive.Service.AuthenticationService
|
||||
|
||||
- **Role**: OAuth/OIDC webhook receiver
|
||||
- **Port**: 8080 (HTTP)
|
||||
- **Protocol**: REST only
|
||||
- **Endpoints**: `POST /api/AuthenticationWebhook/UserRegistered`
|
||||
- **Events Published**: `AuthUserAddedEvent`
|
||||
- **No Database** - Stateless webhook handler
|
||||
|
||||
## Communication Patterns
|
||||
|
||||
### GraphQL Federation
|
||||
|
||||
- Hot Chocolate Fusion Gateway composes subgraph schemas
|
||||
- Schema export automated via `build_gateway.py`
|
||||
- Each service defines its own Query/Mutation types
|
||||
|
||||
### Event-Driven Architecture (RabbitMQ)
|
||||
|
||||
- Direct exchange: `fiction-archive-event-bus`
|
||||
- Per-service queues based on `ClientIdentifier`
|
||||
- Routing key = event class name
|
||||
- Headers: `X-Created-At`, `X-Event-Id`
|
||||
- NodaTime JSON serialization
|
||||
|
||||
### Event Flow Examples
|
||||
|
||||
**Novel Import:**
|
||||
```
|
||||
1. Frontend → importNovel mutation
|
||||
2. NovelService publishes NovelUpdateRequestedEvent
|
||||
3. NovelUpdateRequestedEventHandler processes
|
||||
4. Fetches metadata via NovelpiaAdapter
|
||||
5. Publishes FileUploadRequestCreatedEvent (for cover)
|
||||
6. FileService uploads to S3
|
||||
7. FileService publishes FileUploadRequestStatusUpdateEvent
|
||||
8. NovelService updates image path
|
||||
```
|
||||
|
||||
**Translation:**
|
||||
```
|
||||
1. NovelService publishes TranslationRequestCreatedEvent
|
||||
2. TranslationService translates via DeepL
|
||||
3. TranslationService publishes TranslationRequestCompletedEvent
|
||||
4. NovelService updates chapter translation
|
||||
```
|
||||
|
||||
## Data Storage
|
||||
|
||||
### Database Pattern
|
||||
- Database per service (PostgreSQL)
|
||||
- Connection string format: `Host=localhost;Database=FictionArchive_{ServiceName};...`
|
||||
- Auto-migration on startup via `dbContext.UpdateDatabase()`
|
||||
|
||||
### Audit Trail
|
||||
- `AuditInterceptor` auto-sets `CreatedTime` and `LastUpdatedTime`
|
||||
- `IAuditable` interface with NodaTime `Instant` fields
|
||||
- `BaseEntity<TKey>` abstract base class
|
||||
|
||||
### Object Storage
|
||||
- S3-compatible (AWS S3 or Garage)
|
||||
- Path-style URLs for Garage compatibility
|
||||
- Proxied through FileService
|
||||
|
||||
## Frontend Architecture
|
||||
|
||||
### Structure
|
||||
```
|
||||
fictionarchive-web/
|
||||
├── src/
|
||||
│ ├── auth/ # OIDC authentication
|
||||
│ ├── components/ # React components
|
||||
│ │ └── ui/ # Radix-based primitives
|
||||
│ ├── pages/ # Route pages
|
||||
│ ├── layouts/ # Layout components
|
||||
│ ├── graphql/ # GraphQL queries
|
||||
│ ├── __generated__/ # Codegen output
|
||||
│ └── lib/ # Utilities
|
||||
└── codegen.ts # GraphQL Codegen config
|
||||
```
|
||||
|
||||
### Authentication
|
||||
- OIDC via `oidc-client-ts`
|
||||
- Environment variables for configuration
|
||||
- `useAuth` hook for state access
|
||||
|
||||
### State Management
|
||||
- Apollo Client for GraphQL state
|
||||
- React Context for auth state
|
||||
|
||||
## Infrastructure
|
||||
|
||||
### Docker
|
||||
- Multi-stage builds
|
||||
- Base: `mcr.microsoft.com/dotnet/aspnet:8.0`
|
||||
- Non-root user for security
|
||||
- Ports: 8080 (HTTP) or 8081 (HTTPS)
|
||||
|
||||
### Health Checks
|
||||
- All services expose `/healthz`
|
||||
|
||||
### Configuration
|
||||
- `appsettings.json` - Default settings
|
||||
- `appsettings.Development.json` - Dev overrides
|
||||
- `appsettings.Local.json` - Local secrets (not committed)
|
||||
|
||||
---
|
||||
|
||||
# Improvement Recommendations
|
||||
|
||||
## Critical
|
||||
|
||||
### 1. Event Bus - No Dead Letter Queue or Retry Logic
|
||||
**Location**: `FictionArchive.Service.Shared/Services/EventBus/Implementations/RabbitMQEventBus.cs:126-133`
|
||||
|
||||
**Issue**: Events are always ACK'd even on failure. No DLQ configuration for poison messages. Failed events are lost forever.
|
||||
|
||||
**Recommendation**: Implement retry with exponential backoff, dead-letter exchange, and poison message handling.
|
||||
|
||||
```csharp
|
||||
// Example: Add retry and DLQ
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error handling event");
|
||||
if (retryCount < maxRetries)
|
||||
{
|
||||
await channel.BasicNackAsync(@event.DeliveryTag, false, true); // requeue
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send to DLQ
|
||||
await channel.BasicNackAsync(@event.DeliveryTag, false, false);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. CORS Configuration is Insecure
|
||||
**Location**: `FictionArchive.API/Program.cs:24-33`
|
||||
|
||||
**Issue**: `AllowAnyOrigin()` allows requests from any domain, unsuitable for production.
|
||||
|
||||
**Recommendation**: Configure specific allowed origins via appsettings:
|
||||
```csharp
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("Production", policy =>
|
||||
{
|
||||
policy.WithOrigins(builder.Configuration.GetSection("Cors:AllowedOrigins").Get<string[]>())
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Auto-Migration on Startup
|
||||
**Location**: `FictionArchive.Service.Shared/Services/Database/FictionArchiveDbContext.cs:23-38`
|
||||
|
||||
**Issue**: Running migrations at startup can cause race conditions with multiple instances and potential data corruption during rolling deployments.
|
||||
|
||||
**Recommendation**: Use a migration job, init container, or CLI tool instead of startup code.
|
||||
|
||||
## Important
|
||||
|
||||
### 4. No Circuit Breaker Pattern
|
||||
**Issue**: External service calls (DeepL, Novelpia, S3) lack resilience patterns.
|
||||
|
||||
**Recommendation**: Add Polly for circuit breaker, retry, and timeout policies:
|
||||
```csharp
|
||||
builder.Services.AddHttpClient<ISourceAdapter, NovelpiaAdapter>()
|
||||
.AddPolicyHandler(GetRetryPolicy())
|
||||
.AddPolicyHandler(GetCircuitBreakerPolicy());
|
||||
```
|
||||
|
||||
### 5. Missing Request Validation/Rate Limiting
|
||||
**Issue**: No visible rate limiting on GraphQL mutations. `ImportNovel` could be abused.
|
||||
|
||||
**Recommendation**: Add rate limiting middleware and input validation.
|
||||
|
||||
### 6. Hardcoded Exchange Name
|
||||
**Location**: `RabbitMQEventBus.cs:24`
|
||||
|
||||
**Issue**: `fiction-archive-event-bus` is hardcoded.
|
||||
|
||||
**Recommendation**: Move to configuration for environment flexibility.
|
||||
|
||||
### 7. No Distributed Tracing
|
||||
**Issue**: Event correlation exists (`X-Event-Id` header) but not integrated with tracing.
|
||||
|
||||
**Recommendation**: Add OpenTelemetry for end-to-end request tracing across services.
|
||||
|
||||
### 8. Singleton AuditInterceptor
|
||||
**Location**: `FictionArchiveDbContext.cs:20`
|
||||
|
||||
**Issue**: `new AuditInterceptor()` created per DbContext instance.
|
||||
|
||||
**Recommendation**: Register as singleton in DI and inject.
|
||||
|
||||
## Minor / Code Quality
|
||||
|
||||
### 9. Limited Test Coverage
|
||||
**Issue**: Only `NovelService.Tests` exists. No integration tests for event handlers.
|
||||
|
||||
**Recommendation**: Add unit and integration tests for each service, especially event handlers.
|
||||
|
||||
### 10. Inconsistent Port Configuration
|
||||
**Issue**: Some services use 8080 (HTTP), others 8081 (HTTPS).
|
||||
|
||||
**Recommendation**: Standardize on HTTPS with proper cert management.
|
||||
|
||||
### 11. No API Versioning
|
||||
**Issue**: GraphQL schemas have no versioning strategy.
|
||||
|
||||
**Recommendation**: Consider schema versioning or deprecation annotations for breaking changes.
|
||||
|
||||
### 12. Frontend - No Error Boundary
|
||||
**Issue**: React app lacks error boundaries for graceful failure handling.
|
||||
|
||||
**Recommendation**: Add React Error Boundaries around routes.
|
||||
|
||||
### 13. Missing Health Check Depth
|
||||
**Issue**: Health checks only verify service is running, not dependencies.
|
||||
|
||||
**Recommendation**: Add database, RabbitMQ, and S3 health checks:
|
||||
```csharp
|
||||
builder.Services.AddHealthChecks()
|
||||
.AddNpgSql(connectionString)
|
||||
.AddRabbitMQ()
|
||||
.AddS3(options => { });
|
||||
```
|
||||
|
||||
### 14. Synchronous File Operations in Event Handlers
|
||||
**Issue**: File uploads may block event handling thread for large files.
|
||||
|
||||
**Recommendation**: Consider async streaming for large files.
|
||||
|
||||
## Architectural Suggestions
|
||||
|
||||
### 15. Consider Outbox Pattern
|
||||
**Issue**: Publishing events and saving to DB aren't transactional, could lead to inconsistent state.
|
||||
|
||||
**Recommendation**: Implement transactional outbox pattern for guaranteed delivery:
|
||||
```
|
||||
1. Save entity + outbox message in same transaction
|
||||
2. Background worker publishes from outbox
|
||||
3. Delete outbox message after successful publish
|
||||
```
|
||||
|
||||
### 16. Gateway Schema Build Process
|
||||
**Issue**: Python script (`build_gateway.py`) for schema composition requires manual execution.
|
||||
|
||||
**Recommendation**: Integrate into CI/CD pipeline or consider runtime schema polling.
|
||||
|
||||
### 17. Secret Management
|
||||
**Issue**: Credentials in appsettings files.
|
||||
|
||||
**Recommendation**: Use Azure Key Vault, AWS Secrets Manager, HashiCorp Vault, or similar secret management solution.
|
||||
|
||||
---
|
||||
|
||||
## Key Files Reference
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `FictionArchive.API/Program.cs` | Gateway setup |
|
||||
| `FictionArchive.API/build_gateway.py` | Schema composition script |
|
||||
| `FictionArchive.Service.Shared/Services/EventBus/` | Event bus implementation |
|
||||
| `FictionArchive.Service.Shared/Extensions/` | Service registration helpers |
|
||||
| `FictionArchive.Service.Shared/Services/Database/` | DB infrastructure |
|
||||
| `fictionarchive-web/src/auth/AuthContext.tsx` | Frontend auth state |
|
||||
220
Documentation/CICD.md
Normal file
220
Documentation/CICD.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# CI/CD Configuration
|
||||
|
||||
This document describes the CI/CD pipeline configuration for FictionArchive using Gitea Actions.
|
||||
|
||||
## Workflows Overview
|
||||
|
||||
| Workflow | File | Trigger | Purpose |
|
||||
|----------|------|---------|---------|
|
||||
| CI | `build.yml` | Push/PR to master | Build and test all projects |
|
||||
| Build Subgraphs | `build-subgraphs.yml` | Push to master (service changes) | Build GraphQL subgraph packages |
|
||||
| Build Gateway | `build-gateway.yml` | Manual or triggered by subgraphs | Compose gateway and build Docker image |
|
||||
| Release | `release.yml` | Tag `v*.*.*` | Build and push all Docker images |
|
||||
|
||||
## Pipeline Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Push to master │
|
||||
└─────────────────────────────┬───────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────┴───────────────┐
|
||||
▼ ▼
|
||||
┌─────────────────────────┐ ┌─────────────────────────┐
|
||||
│ build.yml │ │ build-subgraphs.yml │
|
||||
│ (CI checks - always) │ │ (if service changes) │
|
||||
└─────────────────────────┘ └────────────┬────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────┐
|
||||
│ build-gateway.yml │
|
||||
│ (compose & push API) │
|
||||
└─────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Push tag v*.*.* │
|
||||
└─────────────────────────────┬───────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────┐
|
||||
│ release.yml │
|
||||
│ (build & push all) │
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
## Required Configuration
|
||||
|
||||
### Repository Secrets
|
||||
|
||||
Configure these in **Settings → Actions → Secrets**:
|
||||
|
||||
| Secret | Description | Required By |
|
||||
|--------|-------------|-------------|
|
||||
| `REGISTRY_TOKEN` | Gitea access token with `write:package` scope | `release.yml`, `build-gateway.yml` |
|
||||
| `GITEA_TOKEN` | Gitea access token for API calls | `build-subgraphs.yml` |
|
||||
|
||||
#### Creating Access Tokens
|
||||
|
||||
1. Go to **Settings → Applications → Access Tokens**
|
||||
2. Create a new token with the following scopes:
|
||||
- `write:package` - Push container images
|
||||
- `write:repository` - Trigger workflows via API
|
||||
3. Copy the token and add it as a repository secret
|
||||
|
||||
### Repository Variables
|
||||
|
||||
Configure these in **Settings → Actions → Variables**:
|
||||
|
||||
| Variable | Description | Example | Required By |
|
||||
|----------|-------------|---------|-------------|
|
||||
| `VITE_GRAPHQL_URI` | GraphQL API endpoint URL | `https://api.fictionarchive.example.com/graphql/` | `release.yml` |
|
||||
| `VITE_OIDC_AUTHORITY` | OIDC provider authority URL | `https://auth.example.com/application/o/fiction-archive/` | `release.yml` |
|
||||
| `VITE_OIDC_CLIENT_ID` | OIDC client identifier | `your-client-id` | `release.yml` |
|
||||
| `VITE_OIDC_REDIRECT_URI` | Post-login redirect URL | `https://fictionarchive.example.com/` | `release.yml` |
|
||||
| `VITE_OIDC_POST_LOGOUT_REDIRECT_URI` | Post-logout redirect URL | `https://fictionarchive.example.com/` | `release.yml` |
|
||||
|
||||
## Workflow Details
|
||||
|
||||
### CI (`build.yml`)
|
||||
|
||||
**Trigger:** Push or pull request to `master`
|
||||
|
||||
**Jobs:**
|
||||
1. `build-backend` - Builds .NET solution and runs tests
|
||||
2. `build-frontend` - Builds React application with linting
|
||||
|
||||
**Requirements:**
|
||||
- .NET 8.0 SDK
|
||||
- Python 3.12
|
||||
- Node.js 20
|
||||
- HotChocolate Fusion CLI
|
||||
|
||||
### Build Subgraphs (`build-subgraphs.yml`)
|
||||
|
||||
**Trigger:** Push to `master` with changes in:
|
||||
- `FictionArchive.Service.*/**`
|
||||
- `FictionArchive.Common/**`
|
||||
- `FictionArchive.Service.Shared/**`
|
||||
|
||||
**Jobs:**
|
||||
1. `build-subgraphs` - Matrix job building each service's `.fsp` package
|
||||
2. `trigger-gateway` - Triggers gateway rebuild via API
|
||||
|
||||
**Subgraphs Built:**
|
||||
- Novel Service
|
||||
- Translation Service
|
||||
- Scheduler Service
|
||||
- User Service
|
||||
- File Service
|
||||
|
||||
**Artifacts:** Each subgraph produces a `.fsp` file retained for 30 days.
|
||||
|
||||
### Build Gateway (`build-gateway.yml`)
|
||||
|
||||
**Trigger:**
|
||||
- Manual dispatch (`workflow_dispatch`)
|
||||
- Push to `master` with changes in `FictionArchive.API/**`
|
||||
- Triggered by `build-subgraphs.yml` completion
|
||||
|
||||
**Process:**
|
||||
1. Downloads all subgraph `.fsp` artifacts
|
||||
2. Configures Docker-internal URLs for each subgraph
|
||||
3. Composes gateway schema using Fusion CLI
|
||||
4. Builds and pushes API Docker image
|
||||
|
||||
**Image Tags:**
|
||||
- `<registry>/<owner>/fictionarchive-api:latest`
|
||||
- `<registry>/<owner>/fictionarchive-api:<commit-sha>`
|
||||
|
||||
### Release (`release.yml`)
|
||||
|
||||
**Trigger:** Push tag matching `v*.*.*` (e.g., `v1.0.0`)
|
||||
|
||||
**Jobs:**
|
||||
1. `build-and-push` - Matrix job building all backend service images
|
||||
2. `build-frontend` - Builds and pushes frontend image
|
||||
|
||||
**Services Built:**
|
||||
- `fictionarchive-api`
|
||||
- `fictionarchive-novel-service`
|
||||
- `fictionarchive-user-service`
|
||||
- `fictionarchive-translation-service`
|
||||
- `fictionarchive-file-service`
|
||||
- `fictionarchive-scheduler-service`
|
||||
- `fictionarchive-authentication-service`
|
||||
- `fictionarchive-frontend`
|
||||
|
||||
**Image Tags:**
|
||||
- `<registry>/<owner>/fictionarchive-<service>:<version>`
|
||||
- `<registry>/<owner>/fictionarchive-<service>:latest`
|
||||
|
||||
## Container Registry
|
||||
|
||||
Images are pushed to the Gitea Container Registry at:
|
||||
```
|
||||
<gitea-server-url>/<repository-owner>/fictionarchive-<service>:<tag>
|
||||
```
|
||||
|
||||
### Pulling Images
|
||||
|
||||
```bash
|
||||
# Login to registry
|
||||
docker login <gitea-server-url> -u <username> -p <token>
|
||||
|
||||
# Pull an image
|
||||
docker pull <gitea-server-url>/<owner>/fictionarchive-api:latest
|
||||
```
|
||||
|
||||
## Creating a Release
|
||||
|
||||
1. Ensure all changes are committed and pushed to `master`
|
||||
2. Create and push a version tag:
|
||||
```bash
|
||||
git tag v1.0.0
|
||||
git push origin v1.0.0
|
||||
```
|
||||
3. The release workflow will automatically build and push all images
|
||||
4. Monitor progress in **Actions** tab
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build Failures
|
||||
|
||||
**"REGISTRY_TOKEN secret not found"**
|
||||
- Ensure the `REGISTRY_TOKEN` secret is configured in repository settings
|
||||
- Verify the token has `write:package` scope
|
||||
|
||||
**"Failed to trigger gateway workflow"**
|
||||
- Ensure `GITEA_TOKEN` secret is configured
|
||||
- Verify the token has `write:repository` scope
|
||||
|
||||
**"No subgraph artifacts found"**
|
||||
- The gateway build requires subgraph artifacts from a previous `build-subgraphs` run
|
||||
- Trigger `build-subgraphs.yml` manually or push a change to a service
|
||||
|
||||
### Frontend Build Failures
|
||||
|
||||
**"VITE_* variables are empty"**
|
||||
- Ensure all required variables are configured in repository settings
|
||||
- Variables use `vars.*` context, not `secrets.*`
|
||||
|
||||
### Docker Push Failures
|
||||
|
||||
**"unauthorized: authentication required"**
|
||||
- Verify `REGISTRY_TOKEN` has correct permissions
|
||||
- Check that the token hasn't expired
|
||||
|
||||
## Local Testing
|
||||
|
||||
To test workflows locally before pushing:
|
||||
|
||||
```bash
|
||||
# Install act (GitHub Actions local runner)
|
||||
# Note: act has partial Gitea Actions compatibility
|
||||
|
||||
# Run CI workflow
|
||||
act push -W .gitea/workflows/build.yml
|
||||
|
||||
# Run with specific event
|
||||
act push --eventpath .gitea/test-event.json
|
||||
```
|
||||
187
Documentation/README.md
Normal file
187
Documentation/README.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# FictionArchive
|
||||
|
||||
A distributed microservices-based web application for managing fiction and novel content. Features include importing from external sources, multi-language translation, file storage, and user management.
|
||||
|
||||
## Architecture
|
||||
|
||||
FictionArchive uses a GraphQL Fusion gateway pattern to orchestrate multiple domain services with event-driven communication via RabbitMQ.
|
||||
More information available in [ARCHITECTURE.md](ARCHITECTURE.md)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET SDK 8.0+
|
||||
- Node.js 20+
|
||||
- Python 3 (for gateway build script)
|
||||
- Docker & Docker Compose
|
||||
- PostgreSQL 16+
|
||||
- RabbitMQ 3+
|
||||
|
||||
**Required CLI Tools**
|
||||
```bash
|
||||
# Hot Chocolate Fusion CLI
|
||||
dotnet tool install -g HotChocolate.Fusion.CommandLine
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Local Development
|
||||
|
||||
1. **Clone the repository**
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd FictionArchive
|
||||
```
|
||||
|
||||
2. **Start infrastructure** (PostgreSQL, RabbitMQ)
|
||||
```bash
|
||||
docker compose up -d postgres rabbitmq
|
||||
```
|
||||
|
||||
3. **Build and run backend**
|
||||
```bash
|
||||
dotnet restore
|
||||
dotnet build FictionArchive.sln
|
||||
|
||||
# Start services (in separate terminals or use a process manager)
|
||||
dotnet run --project FictionArchive.Service.NovelService
|
||||
dotnet run --project FictionArchive.Service.UserService
|
||||
dotnet run --project FictionArchive.Service.TranslationService
|
||||
dotnet run --project FictionArchive.Service.FileService
|
||||
dotnet run --project FictionArchive.Service.SchedulerService
|
||||
dotnet run --project FictionArchive.Service.AuthenticationService
|
||||
|
||||
# Start the gateway (builds fusion schema automatically)
|
||||
dotnet run --project FictionArchive.API
|
||||
```
|
||||
|
||||
4. **Build and run frontend**
|
||||
```bash
|
||||
cd fictionarchive-web
|
||||
npm install
|
||||
npm run codegen # Generate GraphQL types
|
||||
npm run dev # Start dev server at http://localhost:5173
|
||||
```
|
||||
|
||||
### Docker Deployment
|
||||
|
||||
1. **Create environment file**
|
||||
```bash
|
||||
cp .env.example .env
|
||||
# Edit .env with your configuration
|
||||
```
|
||||
|
||||
2. **Start all services**
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Create a `.env` file in the project root:
|
||||
|
||||
```bash
|
||||
# PostgreSQL
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=your-secure-password
|
||||
|
||||
# RabbitMQ
|
||||
RABBITMQ_USER=guest
|
||||
RABBITMQ_PASSWORD=your-secure-password
|
||||
|
||||
# External Services
|
||||
NOVELPIA_USERNAME=your-username
|
||||
NOVELPIA_PASSWORD=your-password
|
||||
DEEPL_API_KEY=your-api-key
|
||||
|
||||
# S3 Storage
|
||||
S3_ENDPOINT=https://s3.example.com
|
||||
S3_BUCKET=fictionarchive
|
||||
S3_ACCESS_KEY=your-access-key
|
||||
S3_SECRET_KEY=your-secret-key
|
||||
|
||||
# OIDC Authentication
|
||||
OIDC_AUTHORITY=https://auth.example.com/application/o/fiction-archive/
|
||||
OIDC_CLIENT_ID=your-client-id
|
||||
```
|
||||
|
||||
### Frontend Environment
|
||||
|
||||
Create `fictionarchive-web/.env.local`:
|
||||
|
||||
```bash
|
||||
VITE_GRAPHQL_URI=http://localhost:5234/graphql/
|
||||
VITE_OIDC_AUTHORITY=https://auth.example.com/application/o/fiction-archive/
|
||||
VITE_OIDC_CLIENT_ID=your-client-id
|
||||
VITE_OIDC_REDIRECT_URI=http://localhost:5173/
|
||||
VITE_OIDC_POST_LOGOUT_REDIRECT_URI=http://localhost:5173/
|
||||
```
|
||||
|
||||
## Building the GraphQL Gateway
|
||||
|
||||
The API gateway uses Hot Chocolate Fusion to compose schemas from all subgraphs. The gateway schema is rebuilt automatically when building the API project.
|
||||
|
||||
**Manual rebuild:**
|
||||
```bash
|
||||
cd FictionArchive.API
|
||||
python build_gateway.py
|
||||
```
|
||||
|
||||
**Skip specific services** by adding them to `FictionArchive.API/gateway_skip.txt`:
|
||||
```
|
||||
FictionArchive.Service.NovelService.Tests
|
||||
```
|
||||
|
||||
## CI/CD
|
||||
|
||||
The project uses Gitea Actions with the following workflows:
|
||||
|
||||
| Workflow | Trigger | Description |
|
||||
|----------|---------|-------------|
|
||||
| `build.yml` | Push/PR to master | CI checks - builds and tests |
|
||||
| `build-subgraphs.yml` | Service changes on master | Builds subgraph `.fsp` packages |
|
||||
| `build-gateway.yml` | Gateway changes or subgraph builds | Composes gateway and builds Docker image |
|
||||
| `release.yml` | Tag `v*.*.*` | Builds and pushes all Docker images |
|
||||
|
||||
### Release Process
|
||||
|
||||
```bash
|
||||
git tag v1.0.0
|
||||
git push origin v1.0.0
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
FictionArchive/
|
||||
├── FictionArchive.sln
|
||||
├── FictionArchive.Common/ # Shared enums and extensions
|
||||
├── FictionArchive.Service.Shared/ # Shared infrastructure (EventBus, DB)
|
||||
├── FictionArchive.API/ # GraphQL Fusion Gateway
|
||||
├── FictionArchive.Service.NovelService/
|
||||
├── FictionArchive.Service.UserService/
|
||||
├── FictionArchive.Service.TranslationService/
|
||||
├── FictionArchive.Service.FileService/
|
||||
├── FictionArchive.Service.SchedulerService/
|
||||
├── FictionArchive.Service.AuthenticationService/
|
||||
├── FictionArchive.Service.NovelService.Tests/
|
||||
├── fictionarchive-web/ # React frontend
|
||||
├── docker-compose.yml
|
||||
└── .gitea/workflows/ # CI/CD workflows
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
dotnet test FictionArchive.sln
|
||||
|
||||
# Run specific test project
|
||||
dotnet test FictionArchive.Service.NovelService.Tests
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
- [ARCHITECTURE.md](ARCHITECTURE.md) - Detailed architecture documentation
|
||||
- [AGENTS.md](AGENTS.md) - Development guidelines and coding standards
|
||||
Reference in New Issue
Block a user