diff --git a/.env.docker.example b/.env.docker.example deleted file mode 100644 index 546ddb1..0000000 --- a/.env.docker.example +++ /dev/null @@ -1,28 +0,0 @@ -# Copy to .env (next to docker-compose.yml), edit, then: -# docker compose up -d --build - -# Domain nginx binds (server_name) and acme.sh issues the cert for. Must resolve -# to this host with ports 80 + 443 reachable for the Let's Encrypt http-01 check. -# Use "localhost" for local testing — then ACME is skipped and a self-signed cert -# is used (443 still works, browser will warn). -DOMAIN=vividai.run - -# Email for the Let's Encrypt / ACME account (renewal notices). -ACME_EMAIL=admin@example.com - -# Host ports nginx listens on (80 = ACME challenge + redirect, 443 = TLS). -HTTP_PORT=80 -HTTPS_PORT=443 - -# Session cookie Secure flag — keep true when serving over HTTPS (the default). -COOKIE_SECURE=true - -APP_TITLE=image2api - -# --- Postgres --- -POSTGRES_PASSWORD=change-me-postgres - -# --- S3 / object storage (RustFS) --- -S3_BUCKET=vivid-ai -S3_ACCESS_KEY=vividai -S3_SECRET_KEY=change-me-s3-secret diff --git a/README.en.md b/README.en.md index 6b51285..0d7c1a7 100644 --- a/README.en.md +++ b/README.en.md @@ -1,5 +1,7 @@
+Vivid AI +

image2api

**Multi-provider AI image / video generation gateway — one OpenAI-compatible API, seven platforms aggregated, a ready-to-run operations system** @@ -14,7 +16,7 @@ [![Vue 3](https://img.shields.io/badge/Vue-3-42b883?logo=vuedotjs&logoColor=white)](https://vuejs.org) [![Docker](https://img.shields.io/badge/Docker-ready-2496ED?logo=docker&logoColor=white)](#-deployment) [![OpenAI Compatible](https://img.shields.io/badge/OpenAI-compatible-412991?logo=openai&logoColor=white)](#-openai-compatible-api) -[![HTTPS](https://img.shields.io/badge/HTTPS-acme.sh%20auto--issue-success)](#option-1-docker-one-command-recommended) +[![HTTPS](https://img.shields.io/badge/HTTPS-your--proxy-lightgrey)](#-deployment) [![Providers](https://img.shields.io/badge/providers-7-orange)](#-supported-models--providers) [![Self-hosted](https://img.shields.io/badge/self--hosted-yes-success)](#-deployment) [![License](https://img.shields.io/badge/license-MIT-blue)](#-license) @@ -51,7 +53,7 @@ It's more than an API proxy: it ships with **credit billing, CDK top-ups, referr > 💡 Both frontend and backend are **fully open-source** (MIT) — Go + Vue 3, free to fork and self-host. -**At a glance** 🔌 OpenAI-compatible · 🤖 7 platforms, 10+ models · 🔁 auto failover / token keep-alive · 💳 credits + agent pricing · 🎨 generation frontend + admin console · 🐳 one-command deploy + auto HTTPS +**At a glance** 🔌 OpenAI-compatible · 🤖 7 platforms, 10+ models · 🔁 auto failover / token keep-alive · 💳 credits + agent pricing · 🎨 generation frontend + admin console · 🐳 one-command deploy (bring your own TLS proxy) ## 🖼️ Screenshots @@ -100,7 +102,7 @@ It's more than an API proxy: it ships with **credit billing, CDK top-ups, referr - Overview dashboard (trends / DAU / top failures / top spenders) - Model management (normal + agent price) · account management (bulk import / dedup / quota) · **concurrency groups** · **order management** (filter / search / paginate) · site-wide logs · user management (set as agent / assign concurrency group / view cumulative top-up) · CDK · showcase · **announcements** · site config (incl. epay) -**🧰 Engineering highlights**: tls-client (Chrome JA3/JA4 fingerprint) reliably passes Cloudflare · media stored in S3/RustFS, served through an authenticated proxy with retention cleanup · self-healing maintenance loop (quota recovery / credential refresh / orphan-job cleanup with refunds) · one-command Docker deploy with acme.sh auto HTTPS. +**🧰 Engineering highlights**: tls-client (Chrome JA3/JA4 fingerprint) reliably passes Cloudflare · media stored in S3/RustFS, served through an authenticated proxy with retention cleanup · self-healing maintenance loop (quota recovery / credential refresh / orphan-job cleanup with refunds) · one-command Docker deploy (TLS via your own reverse proxy). ## 🤖 Supported Models / Providers @@ -139,27 +141,11 @@ Images return OpenAI-style `{ "created": ..., "data": [{ "b64_json": "..." }] }` ## 🚀 Deployment -Both frontend and backend are open-source. Docker one-command is recommended; you can also build from source with **Go 1.26+**. +> Domain + HTTPS are handled by your own reverse proxy (this project issues no certificates). -> Prerequisite: a domain A-record pointing to this host, with **ports 80 / 443 open to the internet** (required for Let's Encrypt verification). +**Docker (recommended)**: `docker compose up -d --build` brings up PostgreSQL + Redis + RustFS + backend + frontend (nginx serving **HTTP on container port 2000**); point your reverse proxy at `http://:2000` (port via `WEB_PORT`; set passwords / keys / `CORS_ORIGINS` in a `.env` beside the compose file, `COOKIE_SECURE=true` when your proxy serves HTTPS). -### Option 1: Docker, one command (recommended) - -Requires Docker + Docker Compose. A single command brings up Postgres + Redis + RustFS + backend + frontend, and **auto-issues / renews the HTTPS certificate** (built-in acme.sh). - -```bash -cp .env.docker.example .env # fill in DOMAIN / ACME_EMAIL / POSTGRES_PASSWORD / S3_SECRET_KEY -sh install.sh # = docker compose up -d --build -``` - -Open `https:///`; watch cert progress with `docker compose logs -f acme`. With `DOMAIN=localhost` a self-signed cert is used (local testing). - -
-Option 2: Manual install — bring your own PostgreSQL / Redis / RustFS / Nginx (click to expand) - -
- -Provide your own **PostgreSQL · Redis · RustFS (or any S3) · Nginx**, **Go 1.26+** for the backend and **Node 18+** for the frontend. +Or **build from source** — bring your own **PostgreSQL · Redis · RustFS (or any S3) · reverse proxy**: ```bash # 1. Create an empty database (the backend auto-migrates on start) @@ -216,7 +202,7 @@ server { |---|---| | Backend | Go · gin · gorm (PostgreSQL) · go-redis · tls-client (Chrome fingerprint) | | Frontend | Vue 3 · Vue Router · Vite · Tailwind CSS v4 | -| Infrastructure | PostgreSQL · Redis · RustFS (S3-compatible) · Nginx · acme.sh | +| Infrastructure | PostgreSQL · Redis · RustFS (S3-compatible) · Nginx | ## 📦 Repository Layout @@ -256,12 +242,10 @@ frontend/ Frontend source (Vue 3 + Vite) │ ├── layouts/ Public / admin layouts │ ├── utils/ Utility functions │ └── api.js · auth.js … API client, auth, theme, credits, etc. -├── Dockerfile Nginx static hosting + cert watcher +├── Dockerfile Nginx static hosting (HTTP :2000) + API proxy └── default.conf.template Nginx site template (reverse proxy + caching) -docker-compose.yml Docker orchestration (Postgres / Redis / RustFS / backend / frontend / acme) -install.sh One-command deploy script (= docker compose up -d --build) -.env.docker.example Deployment env-var template +docker-compose.yml Docker orchestration (Postgres / Redis / RustFS / backend / frontend) ``` ## 🗺️ Roadmap diff --git a/README.md b/README.md index ba99219..de1a678 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@
+Vivid AI +

image2api

**多供应商 AI 生图 / 生视频网关 —— 一套 OpenAI 兼容 API,聚合七大平台,开箱即用的运营系统** @@ -14,7 +16,7 @@ [![Vue 3](https://img.shields.io/badge/Vue-3-42b883?logo=vuedotjs&logoColor=white)](https://vuejs.org) [![Docker](https://img.shields.io/badge/Docker-ready-2496ED?logo=docker&logoColor=white)](#-部署) [![OpenAI Compatible](https://img.shields.io/badge/OpenAI-compatible-412991?logo=openai&logoColor=white)](#-openai-兼容-api) -[![HTTPS](https://img.shields.io/badge/HTTPS-acme.sh%20自动签发-success)](#方式一docker-一键推荐) +[![HTTPS](https://img.shields.io/badge/HTTPS-反代自理-lightgrey)](#-部署) [![Providers](https://img.shields.io/badge/供应商-7%20平台-orange)](#-支持的模型--供应商) [![Self-hosted](https://img.shields.io/badge/self--hosted-yes-success)](#-部署) [![License](https://img.shields.io/badge/license-MIT-blue)](#-license) @@ -51,7 +53,7 @@ > 💡 前后端**完全开源**(MIT),Go + Vue 3,可自由二开 / 自部署。 -**一句话亮点** 🔌 OpenAI 兼容 · 🤖 7 平台十余模型 · 🔁 自动换号 / Token 保活 · 💳 积分 + 在线充值(易支付)+ 代理价 · 🧩 并发分组 · 🎨 画图前端 + 管理后台 · 🐳 一键部署 + 自动 HTTPS +**一句话亮点** 🔌 OpenAI 兼容 · 🤖 7 平台十余模型 · 🔁 自动换号 / Token 保活 · 💳 积分 + 在线充值(易支付)+ 代理价 · 🧩 并发分组 · 🎨 画图前端 + 管理后台 · 🐳 一键部署(TLS 反代自理) ## 🖼️ 界面预览 @@ -107,7 +109,7 @@ - 概览看板(趋势 / DAU / 失败 Top / 消费榜) - 模型管理(普通价 + 代理价) · 账号管理(批量导入 / 去重 / 额度) · **并发分组** · **订单管理**(筛选 / 搜索 / 分页) · 全站日志 · 用户管理(设为代理 / 分配并发组 / 看累计充值) · CDK · 展示位 · **站点公告** · 站点配置(含易支付) -**🧰 工程亮点**:tls-client(Chrome JA3/JA4 指纹)稳定穿透 Cloudflare · 媒体存 S3/RustFS 经鉴权代理分发 + 保留期清理 · 自愈式维护轮询(恢复额度 / 刷新凭据 / 清理僵死任务并退款) · 一条命令 Docker 部署 + acme.sh 自动 HTTPS。 +**🧰 工程亮点**:tls-client(Chrome JA3/JA4 指纹)稳定穿透 Cloudflare · 媒体存 S3/RustFS 经鉴权代理分发 + 保留期清理 · 自愈式维护轮询(恢复额度 / 刷新凭据 / 清理僵死任务并退款) · 一条命令 Docker 部署(TLS 交给你的反代)。 ## 🤖 支持的模型 / 供应商 @@ -147,27 +149,11 @@ curl https://你的域名/v1/images/edits \ ## 🚀 部署 -前后端均开源。推荐 Docker 一键;也可用 **Go 1.26+** 从源码构建。 +> 域名 + HTTPS 由你自己的反向代理处理(本项目不内置证书签发)。 -> 前置:域名 A 记录指向本机,**80 / 443 对公网开放**(Let's Encrypt 验证需要)。 +**Docker(推荐)**:`docker compose up -d --build` 一条命令拉起 PostgreSQL + Redis + RustFS + 后端 + 前端(nginx **HTTP 监听容器 2000 端口**),把你的反向代理指到 `http://<本机>:2000`(端口用 `WEB_PORT` 改;要自定义密码 / 密钥 / `CORS_ORIGINS` 就在 compose 同目录建 `.env` 配,反代走 HTTPS 时设 `COOKIE_SECURE=true`)。 -### 方式一:Docker 一键(推荐) - -需要 Docker + Docker Compose。一条命令拉起 Postgres + Redis + RustFS + 后端 + 前端,并**自动签发 / 续期 HTTPS 证书**(内置 acme.sh)。 - -```bash -cp .env.docker.example .env # 填 DOMAIN / ACME_EMAIL / POSTGRES_PASSWORD / S3_SECRET_KEY -sh install.sh # = docker compose up -d --build -``` - -打开 `https://<你的域名>/`;证书进度 `docker compose logs -f acme`。`DOMAIN=localhost` 时用自签证书(本地测试)。 - -
-方式二:手动安装 — 自建 PostgreSQL / Redis / RustFS / Nginx(点击展开) - -
- -自备 **PostgreSQL · Redis · RustFS(或任意 S3)· Nginx**,后端用 **Go 1.26+**,前端用 **Node 18+**。 +也可**从源码手动构建**,自备 **PostgreSQL · Redis · RustFS(或任意 S3)· 反向代理**: ```bash # 1. 创建空库(后端启动自动建表) @@ -216,15 +202,13 @@ server { > 完整环境变量见 `backend/.env.example`。 -
- ## 🧱 技术栈 | 层 | 技术 | |---|---| | 后端 | Go · gin · gorm(PostgreSQL)· go-redis · tls-client(Chrome 指纹) | | 前端 | Vue 3 · Vue Router · Vite · Tailwind CSS v4 | -| 基础设施 | PostgreSQL · Redis · RustFS(S3 兼容)· Nginx · acme.sh | +| 基础设施 | PostgreSQL · Redis · RustFS(S3 兼容)· Nginx | ## 📦 仓库结构 @@ -264,12 +248,11 @@ frontend/ 前端源码(Vue 3 + Vite) │ ├── layouts/ 公共 / 后台布局 │ ├── utils/ 工具函数 │ └── api.js · auth.js … 接口封装、鉴权、主题、积分等 -├── Dockerfile Nginx 静态托管 + 证书监听 +├── Dockerfile Nginx 静态托管(HTTP :2000)+ API 反代 └── default.conf.template Nginx 站点模板(反代 + 缓存策略) -docker-compose.yml Docker 编排(Postgres / Redis / RustFS / 后端 / 前端 / acme) -install.sh 一键部署脚本(= docker compose up -d --build) -.env.docker.example 部署环境变量模板 +docker-compose.yml Docker 编排(Postgres / Redis / RustFS / 后端 / 前端) +.env.example → backend/.env 后端环境变量模板 ``` ## 🗺️ Roadmap diff --git a/docker-compose.yml b/docker-compose.yml index 32a42a0..e063a48 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,9 +1,10 @@ # image2api — one-command stack: PostgreSQL + Redis + RustFS (S3) + backend -# (pre-built binary, closed-source — backend/bin/api ships with the repo) + -# frontend/nginx (open-source, built here). -# 1) cp .env.docker.example .env # 填 DOMAIN / ACME_EMAIL / 密码 / 密钥 -# 2) docker compose up -d --build (或 sh install.sh) -# Then open https:/// (nginx serves the SPA, proxies the API, TLS via acme.sh). +# (pre-built binary) + frontend/nginx (built here, HTTP only on port 2000). +# TLS + domain are NOT handled here — put your own reverse proxy in front of the +# web container (host port ${WEB_PORT:-2000}) and terminate TLS there. +# 1) cp .env.docker.example .env # 填 密码 / 密钥 / CORS_ORIGINS +# 2) docker compose up -d --build +# Then point your reverse proxy at http://:${WEB_PORT:-2000}. services: postgres: image: postgres:16-alpine @@ -77,8 +78,10 @@ services: RUSTFS_BUCKET: ${S3_BUCKET:-vivid-ai} RUSTFS_ACCESS_KEY: ${S3_ACCESS_KEY:-vividai} RUSTFS_SECRET_KEY: ${S3_SECRET_KEY:-vividai-secret-change-me} - CORS_ORIGINS: https://${DOMAIN:-localhost} - COOKIE_SECURE: ${COOKIE_SECURE:-true} + # Your site origin(s), comma-separated (the URL your reverse proxy serves). + CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:2000} + # Keep true only if your reverse proxy serves HTTPS; false for plain HTTP. + COOKIE_SECURE: ${COOKIE_SECURE:-false} volumes: - generated:/app/data/generated depends_on: @@ -98,68 +101,16 @@ services: web: build: context: ./frontend - environment: - DOMAIN: ${DOMAIN:-localhost} - # Only substitute ${DOMAIN} in the nginx template — leave $host/$uri/etc. - NGINX_ENVSUBST_FILTER: DOMAIN + # nginx serves the SPA + proxies the API on container port 2000 (HTTP only). + # Point your own reverse proxy at host:${WEB_PORT:-2000}. ports: - - "${HTTP_PORT:-80}:80" - - "${HTTPS_PORT:-443}:443" - volumes: - - certs:/etc/nginx/certs - - acme_webroot:/var/www/acme + - "${WEB_PORT:-2000}:2000" depends_on: - backend restart: unless-stopped - # acme.sh — issues + auto-renews the Let's Encrypt cert via http-01 (webroot - # shared with nginx) into the shared certs volume. nginx's cert-watch reloads - # it. Requires DOMAIN to resolve to this host and ports 80/443 reachable. - acme: - image: neilpang/acme.sh:latest - depends_on: - - web - environment: - DOMAIN: ${DOMAIN:-localhost} - ACME_EMAIL: ${ACME_EMAIL:-admin@example.com} - # Persist account + cert state here (a volume on /acme.sh would shadow the - # installed binary, so use a separate config-home). - LE_CONFIG_HOME: /acme-data - volumes: - - certs:/etc/nginx/certs - - acme_webroot:/var/www/acme - - acmedata:/acme-data - entrypoint: ["/bin/sh", "-c"] - command: - - | - set -e - D="$${DOMAIN:-localhost}" - # Skip ACME for non-public domains (localhost / bare IP) — the self-signed - # bootstrap cert keeps 443 working for local testing. - case "$$D" in - localhost|127.0.0.1|"") echo "acme: DOMAIN=$$D not public, skipping issuance"; exec tail -f /dev/null ;; - esac - mkdir -p /etc/nginx/certs/live/$$D /var/www/acme /acme-data - acme.sh --set-default-ca --server letsencrypt - acme.sh --register-account -m "$${ACME_EMAIL}" || true - # Retry the first issue until nginx :80 is reachable for the http-01 check. - i=0 - while [ $$i -lt 10 ]; do - if acme.sh --issue -d "$$D" -w /var/www/acme --keylength ec-256; then break; fi - i=$$((i+1)); echo "acme: issue attempt $$i failed, retry in 30s"; sleep 30 - done - acme.sh --install-cert -d "$$D" --ecc \ - --key-file /etc/nginx/certs/live/$$D/privkey.pem \ - --fullchain-file /etc/nginx/certs/live/$$D/fullchain.pem || true - # daemon: renew checks daily; renewals auto-reinstall to the paths above. - while true; do sleep 12h; acme.sh --cron || true; done - restart: unless-stopped - volumes: pgdata: redisdata: rustfsdata: generated: - certs: - acme_webroot: - acmedata: diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 43263fd..e5392f2 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 # Frontend is open-source: built from source inside the image, then served by -# nginx which also reverse-proxies the API and terminates TLS (certs from the -# acme.sh sidecar via a shared volume). +# nginx (HTTP only on :2000) which also reverse-proxies the API. TLS + domain are +# handled by the user's own reverse proxy in front of this container. # ---- build stage ---- FROM node:22-alpine AS build WORKDIR /app @@ -12,12 +12,7 @@ RUN npm run build # ---- serve stage ---- FROM nginx:1.27-alpine -# openssl: self-signed bootstrap cert. The nginx image substitutes ${DOMAIN} in -# /etc/nginx/templates/*.template at startup (limited to DOMAIN via the filter). -RUN apk add --no-cache openssl COPY --from=build /app/dist /usr/share/nginx/html -COPY default.conf.template /etc/nginx/templates/default.conf.template -COPY docker-entrypoint.d/10-selfsigned.sh /docker-entrypoint.d/10-selfsigned.sh -COPY docker-entrypoint.d/30-cert-watch.sh /docker-entrypoint.d/30-cert-watch.sh -RUN chmod +x /docker-entrypoint.d/10-selfsigned.sh /docker-entrypoint.d/30-cert-watch.sh -EXPOSE 80 443 +# Plain config (no envsubst / no ${DOMAIN}) — copied straight into conf.d. +COPY default.conf.template /etc/nginx/conf.d/default.conf +EXPOSE 2000 diff --git a/frontend/default.conf.template b/frontend/default.conf.template index 397523f..c5e7a55 100644 --- a/frontend/default.conf.template +++ b/frontend/default.conf.template @@ -1,43 +1,16 @@ -# nginx for the docker stack. ${DOMAIN} is filled at container start (envsubst, -# limited to DOMAIN via NGINX_ENVSUBST_FILTER). Port 80 serves the ACME -# http-01 challenge and redirects everything else to HTTPS; port 443 serves the -# SPA + reverse-proxies the API. Certs come from the shared volume, issued/renewed -# by the acme.sh sidecar (a self-signed cert bootstraps 443 before the real one). - -# ---- 80: ACME challenge + redirect to HTTPS ---- +# nginx for the docker stack — HTTP only on :2000. TLS + domain are handled by +# the user's OWN reverse proxy placed in front of this container; this just serves +# the SPA and reverse-proxies the API to the backend. server { - listen 80; - listen [::]:80; - server_name ${DOMAIN}; - - # acme.sh writes http-01 challenge files here (shared volume). - location ^~ /.well-known/acme-challenge/ { - root /var/www/acme; - default_type "text/plain"; - } - location / { - return 301 https://$host$request_uri; - } -} - -# ---- 443: the app ---- -server { - listen 443 ssl; - listen [::]:443 ssl; - http2 on; - server_name ${DOMAIN}; - - ssl_certificate /etc/nginx/certs/live/${DOMAIN}/fullchain.pem; - ssl_certificate_key /etc/nginx/certs/live/${DOMAIN}/privkey.pem; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_session_cache shared:SSL:10m; + listen 2000; + listen [::]:2000; + server_name _; root /usr/share/nginx/html; index index.html; client_max_body_size 50m; - # Proxy headers (inherited by all proxy_pass locations below). proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -61,12 +34,10 @@ server { } # ---- API / media / health -> backend ---- - # 动态 API no-store,防止 CDN 缓存 GET 响应(如 managed-models)→ 改价不生效。 location ^~ /admin/api/ { proxy_pass http://backend:6666; add_header Cache-Control "no-store" always; } location ^~ /images/ { proxy_pass http://backend:6666; } location = /health { proxy_pass http://backend:6666; } - - # /v1 is per-API-key authenticated — must NOT be cached by any CDN/proxy. + # /v1 is per-API-key authenticated — never cache. location ^~ /v1/ { proxy_pass http://backend:6666; add_header Cache-Control "no-store" always; diff --git a/frontend/docker-entrypoint.d/10-selfsigned.sh b/frontend/docker-entrypoint.d/10-selfsigned.sh deleted file mode 100644 index 4e2540e..0000000 --- a/frontend/docker-entrypoint.d/10-selfsigned.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh -# Bootstrap a self-signed cert so nginx's 443 server block can start BEFORE -# acme.sh has issued the real certificate. acme.sh later overwrites these files -# in the shared volume; 30-cert-watch.sh reloads nginx when that happens. -set -e -D="${DOMAIN:-localhost}" -CERT_DIR="/etc/nginx/certs/live/$D" -mkdir -p "$CERT_DIR" /var/www/acme -if [ ! -s "$CERT_DIR/fullchain.pem" ] || [ ! -s "$CERT_DIR/privkey.pem" ]; then - echo "nginx: generating self-signed bootstrap cert for $D" - openssl req -x509 -newkey rsa:2048 -nodes -days 3650 \ - -keyout "$CERT_DIR/privkey.pem" -out "$CERT_DIR/fullchain.pem" \ - -subj "/CN=$D" >/dev/null 2>&1 -fi diff --git a/frontend/docker-entrypoint.d/30-cert-watch.sh b/frontend/docker-entrypoint.d/30-cert-watch.sh deleted file mode 100644 index ebed017..0000000 --- a/frontend/docker-entrypoint.d/30-cert-watch.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -# Reload nginx whenever the certificate file changes — i.e. once acme.sh has -# issued/renewed the real cert into the shared volume, nginx picks it up within -# ~a minute without a container restart. Runs in the background so it doesn't -# block startup. -D="${DOMAIN:-localhost}" -CERT="/etc/nginx/certs/live/$D/fullchain.pem" -( - last="" - while true; do - sleep 60 - cur="$(stat -c %Y "$CERT" 2>/dev/null || echo '')" - if [ -n "$cur" ] && [ "$cur" != "$last" ]; then - # Skip the very first observation (the self-signed bootstrap); only reload - # on a genuine change (acme.sh overwrote the cert). - [ -n "$last" ] && nginx -s reload 2>/dev/null || true - last="$cur" - fi - done -) & diff --git a/frontend/src/views/DocsView.vue b/frontend/src/views/DocsView.vue index 844c361..ca6126c 100644 --- a/frontend/src/views/DocsView.vue +++ b/frontend/src/views/DocsView.vue @@ -78,6 +78,18 @@ const sizeTable = [ { ratio: '1:3 · 竖', k1: '512x1536', k2: '768x2304', k4: '1280x3840' }, ] +// ---- 视频 size → 比例 × 分辨率(720p / 1080p)---- +// 视频按「短边」判档:短边 <1080 → 720p,≥1080 → 1080p;宽高比映射比例。 +const videoSizeTable = [ + { ratio: '16:9 · 横', p720: '1280x720', p1080: '1920x1080' }, + { ratio: '9:16 · 竖', p720: '720x1280', p1080: '1080x1920' }, + { ratio: '1:1 · 方', p720: '720x720', p1080: '1080x1080' }, + { ratio: '4:3 · 横', p720: '960x720', p1080: '1440x1080' }, + { ratio: '3:4 · 竖', p720: '720x960', p1080: '1080x1440' }, + { ratio: '3:2 · 横', p720: '1080x720', p1080: '1620x1080' }, + { ratio: '2:3 · 竖', p720: '720x1080', p1080: '1080x1620' }, +] + // ---- examples (built in script so refs resolve correctly) ---- const examples = computed(() => [ { @@ -331,10 +343,10 @@ async function copy(text) {
-

分辨率对照表 · size 该传什么

+

图像分辨率对照表 · size 该传什么

左边选比例,上面选分辨率档,交叉格里就是 size 要传的值(直接复制)。 - 没有 quality 参数,分辨率只看 size 的长边。 + 没有 quality 参数,图像分辨率只看 size长边。 档位必须是该模型支持的(见上方「可用模型」的分辨率列),不支持会自动回退到该模型最低档。

@@ -359,6 +371,33 @@ async function copy(text) { 例:想要 2K 的 16:9 横图"size": "2048x1152"。 留空 size = 默认 1:1 · 2K

+ + +

视频分辨率对照表 · size 该传什么

+

+ 视频用 720p / 1080p 两档,只看 size短边(短边 ≥1080 = 1080p,否则 720p)。 + 档位必须是该视频模型支持的(如 grok-video 仅 720p),不支持会被拒。 +

+
+ + + + + + + + + + + + + +
比例720p1080p
{{ row.ratio }}{{ row.p720 }}{{ row.p1080 }}
+
+

+ 例:想要 720p 的 16:9 横版视频"size": "1280x720"; + 竖版 9:16 → "720x1280"。 +

diff --git a/install.sh b/install.sh deleted file mode 100644 index 9723684..0000000 --- a/install.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env sh -# image2api — one-command install (Docker). Run from the repo root: -# sh install.sh -# Brings up Postgres + Redis + RustFS + backend + frontend, and auto-issues a -# Let's Encrypt HTTPS certificate via the built-in acme.sh service. -set -e -cd "$(dirname "$0")" - -# --- docker present? --- -if ! command -v docker >/dev/null 2>&1; then - echo "ERROR: 未安装 Docker。请先安装 Docker + Docker Compose。" - exit 1 -fi - -# --- env file --- -if [ ! -f .env ]; then - echo "==> 生成 .env(从 .env.docker.example),请按提示编辑后重跑" - cp .env.docker.example .env - echo - echo " 必填:DOMAIN(你的域名)、ACME_EMAIL(证书邮箱)、POSTGRES_PASSWORD、S3_SECRET_KEY" - echo " 编辑好后再次执行: sh install.sh" - exit 0 -fi - -# --- backend binary (closed-source, shipped prebuilt) --- -if [ ! -f backend/bin/api ]; then - echo "ERROR: 缺少 backend/bin/api(后端二进制)。" - echo " 请从 Releases 下载 linux/amd64 的 api 放到 backend/bin/api 后重试。" - exit 1 -fi -chmod +x backend/bin/api 2>/dev/null || true - -# --- up --- -echo "==> docker compose up -d --build" -docker compose up -d --build - -DOMAIN_VAL="$(grep -E '^DOMAIN=' .env | head -1 | cut -d= -f2-)" -echo -echo "完成 ✅ 打开 https://${DOMAIN_VAL:-<你的域名>}/" -echo "证书签发进度: docker compose logs -f acme" -echo "后端日志: docker compose logs -f backend" -echo "停止: docker compose down"