Files
MangaReader/ios-app/Sources/Config
renato97 abef30252c feat: Migrate to dedicated manga.cbcren.online subdomain
🎯 Changes:
- Moved from gitea.cbcren.online to manga.cbcren.online
- Updated Caddy proxy configuration with SSL auto-cert
- Updated iOS app APIConfig to use HTTPS
- Changed port from 3001 to standard HTTPS (443)
- No interference with Gitea or other services

🔧 Technical Details:
- DNS: manga.cbcren.online → 194.163.191.200
- Proxy: Caddy with automatic HTTPS
- Backend: 172.17.0.1:3001 (Docker gateway)
- SSL: Automatic Let's Encrypt certificate

 Tested:
- Health check: https://manga.cbcren.online/api/health ✓
- Storage stats: https://manga.cbcren.online/api/storage/stats ✓
- HTTPS redirect working correctly ✓

🤖 Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-04 16:52:10 +01:00
..

API Configuration for MangaReader iOS App

Overview

This directory contains the API configuration for connecting the iOS app to the VPS backend. The configuration is centralized in APIConfig.swift and includes all necessary settings for API communication.

Files

  • APIConfig.swift: Main configuration file with all API settings, endpoints, and helper methods
  • APIConfigExample.swift: Comprehensive usage examples and demonstrations
  • README.md (this file): Documentation and usage guide

Current Configuration

Server Connection

  • Server URL: https://gitea.cbcren.online
  • Port: 3001
  • Full Base URL: https://gitea.cbcren.online:3001
  • API Version: v1
  • API Base Path: https://gitea.cbcren.online:3001/api/v1

Timeouts

  • Default Request Timeout: 30.0 seconds (for regular API calls)
  • Resource Download Timeout: 300.0 seconds (5 minutes, for large downloads)

Retry Policy

  • Max Retries: 3 attempts
  • Base Retry Delay: 1.0 second (with exponential backoff)

Cache Configuration

  • Max Memory Usage: 100 cached responses
  • Cache Expiry: 300.0 seconds (5 minutes)

Usage

Basic URL Construction

// Method 1: Use the helper function
let url = APIConfig.url(for: "manga/popular")
// Result: "https://gitea.cbcren.online:3001/manga/popular"

// Method 2: Get a URL object
if let urlObj = APIConfig.urlObject(for: "manga/popular") {
    var request = URLRequest(url: urlObj)
    // Make request...
}

// Method 3: Use predefined endpoints
let endpoint = APIConfig.Endpoints.download(mangaSlug: "one-piece", chapterNumber: 1089)
// Result: "https://gitea.cbcren.online:3001/api/v1/download/one-piece/1089"

URLSession Configuration

let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = APIConfig.defaultTimeout
configuration.timeoutIntervalForResource = APIConfig.downloadTimeout
let session = URLSession(configuration: configuration)

URLRequest with Headers

var request = URLRequest(url: url)
request.timeoutInterval = APIConfig.defaultTimeout

// Add common headers
for (key, value) in APIConfig.commonHeaders {
    request.setValue(value, forHTTPHeaderField: key)
}

// Add authentication if needed
if let token = authToken {
    let authHeaders = APIConfig.authHeader(token: token)
    for (key, value) in authHeaders {
        request.setValue(value, forHTTPHeaderField: key)
    }
}

Available Endpoints

Download Endpoints

// Request chapter download
APIConfig.Endpoints.download(mangaSlug: "one-piece", chapterNumber: 1089)

// Check if chapter is downloaded
APIConfig.Endpoints.checkDownloaded(mangaSlug: "one-piece", chapterNumber: 1089)

// List all downloaded chapters for a manga
APIConfig.Endpoints.listChapters(mangaSlug: "one-piece")

// Get specific image from chapter
APIConfig.Endpoints.getImage(mangaSlug: "one-piece", chapterNumber: 1089, pageIndex: 0)

// Delete a chapter
APIConfig.Endpoints.deleteChapter(mangaSlug: "one-piece", chapterNumber: 1089)

Server Endpoints

// Get storage statistics
APIConfig.Endpoints.storageStats()

// Health check
APIConfig.Endpoints.health()

Environment Configuration

The configuration includes presets for different environments:

Development

APIConfig.development
// - serverURL: "http://192.168.1.100"
// - port: 3001
// - timeout: 60.0s
// - logging: true

Staging

APIConfig.staging
// - serverURL: "https://staging.cbcren.online"
// - port: 3001
// - timeout: 30.0s
// - logging: true

Production (Current)

APIConfig.production
// - serverURL: "https://gitea.cbcren.online"
// - port: 3001
// - timeout: 30.0s
// - logging: false

Testing (Debug Only)

#if DEBUG
APIConfig.testing
// - serverURL: "http://localhost:3001"
// - port: 3001
// - timeout: 5.0s
// - logging: true
#endif

Changing the Server URL

To change the API server URL, modify the serverURL property in APIConfig.swift:

// In APIConfig.swift, line 37
static let serverURL = "https://gitea.cbcren.online" // Change this

For environment-specific URLs, use compile-time conditionals:

#if DEBUG
static let serverURL = "http://192.168.1.100"  // Local development
#else
static let serverURL = "https://gitea.cbcren.online"  // Production
#endif

Error Codes

The API defines specific error codes for different scenarios:

APIConfig.ErrorCodes.chapterNotFound           // 40401
APIConfig.ErrorCodes.chapterAlreadyDownloaded  // 40901
APIConfig.ErrorCodes.storageLimitExceeded      // 50701
APIConfig.ErrorCodes.invalidImageFormat        // 42201
APIConfig.ErrorCodes.downloadFailed            // 50001

Validation

The configuration includes a validation method:

if APIConfig.isValid {
    print("Configuration is valid")
} else {
    print("Configuration is invalid")
}

This checks:

  • Server URL is not empty
  • Port is in valid range (1-65535)
  • Timeout values are positive
  • Retry count is non-negative

Debug Support

In debug builds, you can print the current configuration:

#if DEBUG
APIConfig.printConfiguration()
#endif

This outputs:

=== API Configuration ===
Server URL: https://gitea.cbcren.online
Port: 3001
Base URL: https://gitea.cbcren.online:3001
API Version: v1
Default Timeout: 30.0s
Download Timeout: 300.0s
Max Retries: 3
Logging Enabled: false
Cache Enabled: true
=========================

Best Practices

  1. Always use predefined endpoints when available instead of manually constructing URLs
  2. Use appropriate timeouts - defaultTimeout for regular calls, downloadTimeout for large downloads
  3. Validate configuration on app startup
  4. Use the helper methods (url(), urlObject()) for URL construction
  5. Include common headers in all requests
  6. Handle specific error codes defined in APIConfig.ErrorCodes
  7. Enable logging only in debug builds for security

Example: Making an API Call

func fetchPopularManga() async throws -> [Manga] {
    // Construct URL
    guard let url = APIConfig.urlObject(for: "manga/popular") else {
        throw APIError.invalidURL
    }

    // Create request
    var request = URLRequest(url: url)
    request.timeoutInterval = APIConfig.defaultTimeout

    // Add headers
    for (key, value) in APIConfig.commonHeaders {
        request.setValue(value, forHTTPHeaderField: key)
    }

    // Make request
    let (data, response) = try await URLSession.shared.data(for: request)

    // Validate response
    guard let httpResponse = response as? HTTPURLResponse,
          httpResponse.statusCode == 200 else {
        throw APIError.requestFailed
    }

    // Decode response
    let mangas = try JSONDecoder().decode([Manga].self, from: data)
    return mangas
}

Example: Downloading with Retry

func downloadChapterWithRetry(
    mangaSlug: String,
    chapterNumber: Int
) async throws -> Data {
    let endpoint = APIConfig.Endpoints.download(
        mangaSlug: mangaSlug,
        chapterNumber: chapterNumber
    )

    return try await fetchWithRetry(endpoint: endpoint, retryCount: 0)
}

func fetchWithRetry(endpoint: String, retryCount: Int) async throws -> Data {
    guard let url = URL(string: endpoint),
          retryCount < APIConfig.maxRetries else {
        throw APIError.retryLimitExceeded
    }

    var request = URLRequest(url: url)
    request.timeoutInterval = APIConfig.downloadTimeout

    do {
        let (data, response) = try await URLSession.shared.data(for: request)

        if let httpResponse = response as? HTTPURLResponse,
           httpResponse.statusCode == 200 {
            return data
        } else {
            throw APIError.requestFailed
        }
    } catch {
        // Calculate exponential backoff delay
        let delay = APIConfig.baseRetryDelay * pow(2.0, Double(retryCount))
        try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))

        return try await fetchWithRetry(endpoint: endpoint, retryCount: retryCount + 1)
    }
}

Troubleshooting

Connection Issues

  1. Verify server URL: Check that serverURL is correct and accessible
  2. Check port: Ensure port matches the backend server configuration
  3. Test connectivity: Use the health endpoint: APIConfig.Endpoints.health()
  4. Enable logging: Set loggingEnabled = true to see request details

Timeout Issues

  1. For regular API calls: Use APIConfig.defaultTimeout (30 seconds)
  2. For large downloads: Use APIConfig.downloadTimeout (300 seconds)
  3. Slow networks: Increase timeout values if needed

SSL Certificate Issues

If using HTTPS with a self-signed certificate:

  1. Add the certificate to the app's bundle
  2. Configure URLSession to trust the certificate
  3. Or use HTTP for development (not recommended for production)

Migration Notes

When migrating from the old configuration:

  1. Replace hardcoded URLs with APIConfig.url(for:) or predefined endpoints
  2. Use APIConfig.commonHeaders instead of manually setting headers
  3. Replace hardcoded timeouts with APIConfig.defaultTimeout or APIConfig.downloadTimeout
  4. Add validation on app startup with APIConfig.isValid
  5. Use specific error codes from APIConfig.ErrorCodes

Additional Resources

  • See APIConfigExample.swift for more comprehensive examples
  • Check the backend API documentation for available endpoints
  • Review the iOS app's Services directory for integration examples