# yaml-language-server: $schema=https://schema.zeabur.app/template.json
apiVersion: zeabur.com/v1
kind: Template
metadata:
    name: Sub2API
spec:
    description: AI API gateway that distributes and manages subscription quotas for Claude, OpenAI, Gemini and Grok, with API key issuing, token-level billing and load balancing.
    icon: https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/assets/logo.svg
    variables:
        - key: PUBLIC_DOMAIN
          type: DOMAIN
          name: Domain
          description: Domain of your Sub2API gateway (dashboard and API).
    tags:
        - AI
        - LLM
        - Proxy
    readme: |-
        # Sub2API

        Sub2API is an AI API gateway platform that distributes and manages the quotas of AI product subscriptions. Users reach upstream providers through platform-issued API keys, while the gateway takes care of authentication, billing, load balancing and request forwarding.

        This template deploys the complete stack:

        - **sub2api** — Go backend with the embedded Vue admin dashboard, exposed on your public domain.
        - **postgresql** — PostgreSQL 18 on a persistent volume: upstream accounts, keys, usage logs and billing.
        - **redis** — Redis 8 on a persistent volume: cache, queues and distributed scheduling state.

        ## Getting started

        1. Deploy, then open `https://<your-domain>`.
        2. Log in with the admin email and password shown in the Sub2API service **Instructions** section (`admin@sub2api.local` and the auto-generated `ADMIN_PASSWORD` by default; both also appear in the service's variables).
        3. Add upstream accounts (Claude, OpenAI, Gemini, Grok) under **Accounts**, attach them to groups, and create API keys for your users.
        4. Point clients at the gateway:
           - OpenAI / Codex compatible: `https://<your-domain>/v1`
           - Anthropic / Claude Code compatible: `https://<your-domain>`
           - Gemini compatible: `https://<your-domain>`

        ## Notes

        - Both databases are reachable only over the project's private network: this template disables TCP port forwarding. Enable it from each service's **Networking** tab if you want to connect with a local client.
        - Important service instructions:
          - Connection strings for PostgreSQL and Redis are visible in each service's **Instructions** tab.
          - `/app/data` on the Sub2API service holds `config.yaml` (generated once by `AUTO_SETUP`) and application logs; it must stay mounted for the generated `JWT_SECRET` to survive restarts.
          - Updating the image (`weishaw/sub2api:latest`) keeps the data: the app detects the existing configuration instead of running setup again. You can also upgrade in-place from the dashboard's **Check for Updates** button.
        - 2FA is off by default. To enable it, set `TOTP_ENCRYPTION_KEY` on the Sub2API service to a fixed key of 64 hex characters (`openssl rand -hex 32`); an invalid value prevents the server from starting.
        - `JWT_SECRET` is generated on first boot and persisted in the volume. Set `JWT_SECRET` explicitly (32+ bytes, `openssl rand -hex 32`) if you manage secrets yourself.
        - For production, set `SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP=false` on the Sub2API service so only HTTPS upstream URLs are accepted.
        - `RUN_MODE=simple` disables the SaaS/billing features for private use; in production it additionally requires `SIMPLE_MODE_CONFIRM=true`.

        ## Important notice

        Using this project may violate the terms of service of Anthropic and other upstream providers. Use it in compliance with the laws of your country or region; account bans, service interruptions and data loss are the user's own risk. The upstream project does not grant any commercial authorization.

        Upstream project: https://github.com/Wei-Shaw/sub2api
    services:
        - name: postgresql
          icon: https://raw.githubusercontent.com/zeabur/service-icons/main/marketplace/postgresql.svg
          template: PREBUILT
          spec:
            id: postgresql
            source:
                image: postgres:18
                command:
                    - docker-entrypoint.sh
                    - -c
                    - config_file=/etc/postgresql/postgresql.conf
            ports:
                - id: database
                  port: 5432
                  type: TCP
            volumes:
                - id: data
                  dir: /var/lib/postgresql/18/docker
            instructions:
                - title: Internal connection string
                  content: postgresql://${POSTGRES_USERNAME}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DATABASE}
                - title: PostgreSQL username
                  content: ${POSTGRES_USERNAME}
                - title: PostgreSQL password
                  content: ${POSTGRES_PASSWORD}
                - title: PostgreSQL database
                  content: ${POSTGRES_DATABASE}
                - title: PostgreSQL host (private network)
                  content: ${POSTGRES_HOST}
                - title: PostgreSQL port (private network)
                  content: ${POSTGRES_PORT}
            env:
                PGDATA:
                    default: /var/lib/postgresql/18/docker/pgdata
                POSTGRES_CONNECTION_STRING:
                    default: postgresql://${POSTGRES_USERNAME}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DATABASE}
                    expose: true
                POSTGRES_DATABASE:
                    default: ${POSTGRES_DB}
                    expose: true
                POSTGRES_DB:
                    default: sub2api
                POSTGRES_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
                POSTGRES_PASSWORD:
                    default: ${PASSWORD}
                    expose: true
                POSTGRES_PORT:
                    default: ${DATABASE_PORT}
                    expose: true
                POSTGRES_URI:
                    default: ${POSTGRES_CONNECTION_STRING}
                    expose: true
                POSTGRES_USER:
                    default: sub2api
                POSTGRES_USERNAME:
                    default: ${POSTGRES_USER}
                    expose: true
            configs:
                - path: /etc/postgresql/postgresql.conf
                  template: |
                    # https://github.com/postgres/postgres/blob/master/src/backend/utils/misc/postgresql.conf.sample
                    listen_addresses = '*'
                    max_connections = 256
                    shared_buffers = 256MB
                    dynamic_shared_memory_type = posix
                    max_wal_size = 1GB
                    min_wal_size = 80MB
                    log_timezone = 'UTC'
                    datestyle = 'iso, mdy'
                    timezone = 'UTC'
                    lc_messages = 'en_US.UTF-8'
                    lc_monetary = 'en_US.UTF-8'
                    lc_numeric = 'en_US.UTF-8'
                    lc_time = 'en_US.UTF-8'
                    default_text_search_config = 'pg_catalog.english'
                  permission: null
                  envsubst: null
            portForwarding:
                enabled: false
        - name: redis
          icon: https://raw.githubusercontent.com/zeabur/service-icons/main/marketplace/redis.svg
          template: PREBUILT
          spec:
            id: redis
            source:
                image: redis:8-alpine
                command:
                    - redis-server
                    - /etc/redis/redis.conf
            ports:
                - id: database
                  port: 6379
                  type: TCP
            volumes:
                - id: data
                  dir: /data
            instructions:
                - title: Internal connection string
                  content: redis://:${REDIS_PASSWORD}@${REDIS_HOST}:${REDIS_PORT}
                - title: Redis password
                  content: ${REDIS_PASSWORD}
                - title: Redis host (private network)
                  content: ${REDIS_HOST}
                - title: Redis port (private network)
                  content: ${REDIS_PORT}
            env:
                REDIS_CONNECTION_STRING:
                    default: redis://:${REDIS_PASSWORD}@${REDIS_HOST}:${REDIS_PORT}
                    expose: true
                REDIS_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
                REDIS_PASSWORD:
                    default: ${PASSWORD}
                    expose: true
                REDIS_PORT:
                    default: ${DATABASE_PORT}
                    expose: true
                REDIS_URI:
                    default: ${REDIS_CONNECTION_STRING}
                    expose: true
            configs:
                - path: /etc/redis/redis.conf
                  template: |
                    port 6379
                    bind 0.0.0.0
                    protected-mode no
                    dir /data
                    appendonly yes
                    appendfsync everysec
                    save 60 1
                    maxclients 50000
                    requirepass ${REDIS_PASSWORD}
                  permission: null
                  envsubst: true
            portForwarding:
                enabled: false
        - name: sub2api
          icon: https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/assets/logo.svg
          dependencies:
            - postgresql
            - redis
          template: PREBUILT
          spec:
            id: sub2api
            source:
                image: weishaw/sub2api:latest
            ports:
                - id: web
                  port: 8080
                  type: HTTP
            volumes:
                - id: data
                  dir: /app/data
            instructions:
                - title: Admin email
                  content: ${ADMIN_EMAIL}
                - title: Admin password
                  content: ${ADMIN_PASSWORD}
                - title: Gateway URL (OpenAI / Codex compatible)
                  content: https://${ZEABUR_WEB_DOMAIN}/v1
                - title: Gateway URL (Anthropic / Claude Code compatible)
                  content: https://${ZEABUR_WEB_DOMAIN}
                - title: PostgreSQL connection string
                  content: ${POSTGRES_CONNECTION_STRING}
                - title: Redis connection string
                  content: ${REDIS_CONNECTION_STRING}
            env:
                ADMIN_EMAIL:
                    default: admin@sub2api.local
                ADMIN_PASSWORD:
                    default: ${PASSWORD}
                AUTO_SETUP:
                    default: "true"
                DATABASE_DBNAME:
                    default: ${POSTGRES_DATABASE}
                DATABASE_HOST:
                    default: ${POSTGRES_HOST}
                DATABASE_PASSWORD:
                    default: ${POSTGRES_PASSWORD}
                DATABASE_PORT:
                    default: ${POSTGRES_PORT}
                DATABASE_SSLMODE:
                    default: disable
                DATABASE_USER:
                    default: ${POSTGRES_USERNAME}
                JWT_EXPIRE_HOUR:
                    default: "24"
                REDIS_DB:
                    default: "0"
                REDIS_ENABLE_TLS:
                    default: "false"
                REDIS_HOST:
                    default: ${REDIS_HOST}
                REDIS_PASSWORD:
                    default: ${REDIS_PASSWORD}
                REDIS_PORT:
                    default: ${REDIS_PORT}
                RUN_MODE:
                    default: standard
                SERVER_HOST:
                    default: 0.0.0.0
                SERVER_MODE:
                    default: release
                SERVER_PORT:
                    default: "8080"
                TZ:
                    default: Asia/Shanghai
            healthCheck:
                type: HTTP
                port: web
                http:
                    path: /health
          domainKey: PUBLIC_DOMAIN
localization:
    zh-CN:
        description: 一站式 AI API 中转网关，统一接入 Claude、OpenAI、Gemini、Grok 订阅，支持 API Key 分发、Token 级计费与负载均衡。
        readme: |-
            # Sub2API

            Sub2API 是一个 AI API 网关平台，用于分发和管理 AI 产品订阅的额度。用户通过平台签发的 API Key 访问上游服务，网关负责认证、计费、负载均衡与请求转发。

            本模板会部署完整的一套服务：

            - **sub2api**：Go 后端 + 内置 Vue 管理后台，通过你的域名对外提供服务。
            - **postgresql**：PostgreSQL 18，持久化存储上游账号、密钥、用量与账单数据。
            - **redis**：Redis 8，持久化存储缓存、队列与分布式调度状态。

            ## 开始使用

            1. 部署完成后访问 `https://<你的域名>`。
            2. 使用 Sub2API 服务「操作指南」中显示的邮箱与密码登录（默认邮箱 `admin@sub2api.local`，密码为该服务自动生成的 `ADMIN_PASSWORD`，两者也可在服务的变量列表中查看）。
            3. 在「账号」中添加上游账号（Claude、OpenAI、Gemini、Grok），加入分组，然后为用户创建 API Key。
            4. 客户端接入地址：
               - OpenAI / Codex 兼容：`https://<你的域名>/v1`
               - Anthropic / Claude Code 兼容：`https://<你的域名>`
               - Gemini 兼容：`https://<你的域名>`

            ## 说明

            - 数据库与缓存只能通过项目内网访问：本模板关闭了 TCP 端口转发。若需要用本地客户端连接，可在对应服务的「网络」页面开启。
            - 重要信息位置：
              - PostgreSQL 与 Redis 的连接串可在各自服务的「操作指南」中查看。
              - Sub2API 服务的 `/app/data` 保存着 `AUTO_SETUP` 生成的 `config.yaml` 和应用日志，必须保持挂载，否则生成的 `JWT_SECRET` 会在重启后丢失。
              - 更新镜像（`weishaw/sub2api:latest`）不会丢数据：应用会检测到已有配置，不再执行一次安装流程。也可以直接在后台点击「检查更新」在线升级。
            - 两步验证默认关闭。需要启用时，请在 Sub2API 服务中设置 `TOTP_ENCRYPTION_KEY` 为固定的 64 位 hex 字符串（`openssl rand -hex 32`）；写入非法值会导致服务无法启动。
            - `JWT_SECRET` 会在首次启动时生成并保存在数据卷中。如需自行管理密钥，请显式设置 `JWT_SECRET`（至少 32 字节，`openssl rand -hex 32`）。
            - 生产环境建议把 Sub2API 服务的 `SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP` 设为 `false`，只允许 HTTPS 上游地址。
            - `RUN_MODE=simple` 会隐藏 SaaS 计费相关功能，适合内部自用；在生产环境还需同时设置 `SIMPLE_MODE_CONFIRM=true`。

            ## 重要提醒

            使用本项目可能违反 Anthropic 等上游服务商的用户协议。请遵守所在国家或地区的法律法规使用；账号封禁、服务中断、数据丢失等风险由使用者自行承担。上游项目未授权任何形式的商业运营。

            上游项目：https://github.com/Wei-Shaw/sub2api
