# yaml-language-server: $schema=https://schema.zeabur.app/template.json
apiVersion: zeabur.com/v1
kind: Template
metadata:
    name: PaperPhone-Plus
spec:
    description: A WeChat-style, end-to-end encrypted IM app built with React + Rust (Axum), featuring stateless ECDH E2EE, WebRTC video/voice calls, Cloudflare R2 file storage, 8-language i18n, and iOS PWA support — fully self-hostable.
    icon: https://raw.githubusercontent.com/619dev/Paperphone-plus/main/client/public/icons/icon-512.png
    variables:
        - key: PUBLIC_DOMAIN
          type: DOMAIN
          name: Client Domain
          description: The public domain for the PaperPhone web client (e.g. paperphone.example.com).
        - key: JWT_SECRET
          type: STRING
          name: JWT Secret
          description: A long random string used to sign authentication tokens. Change this before going to production.
        - key: R2_ACCOUNT_ID
          type: STRING
          name: Cloudflare R2 Account ID
          description: Your Cloudflare account ID (found in Cloudflare Dashboard → right sidebar).
        - key: R2_ACCESS_KEY_ID
          type: STRING
          name: R2 Access Key ID
          description: R2 API token access key (R2 → Manage API tokens → Create token).
        - key: R2_SECRET_ACCESS_KEY
          type: STRING
          name: R2 Secret Access Key
          description: R2 API token secret key paired with the access key above.
        - key: R2_BUCKET
          type: STRING
          name: R2 Bucket Name
          description: Name of the R2 bucket to store uploaded files (e.g. paperphone).
        - key: R2_PUBLIC_URL
          type: STRING
          name: R2 Public URL (optional)
          description: Public base URL for the bucket, e.g. https://pub-xxx.r2.dev or your custom domain. If set, files are served directly from R2/CDN. Leave blank to proxy files through the server.
        - key: MYSQL_ROOT_PASSWORD
          type: STRING
          name: MySQL Root Password
          description: Password for the MySQL root user. Set this yourself — it will NOT be auto-generated.
        - key: CF_CALLS_APP_ID
          type: STRING
          name: Cloudflare Calls App ID (optional)
          description: Required only for cross-network WebRTC video/voice calls. Obtain from Cloudflare Dashboard → Calls → your App. Leave blank to use STUN only (LAN calls work without this).
        - key: CF_CALLS_APP_SECRET
          type: STRING
          name: Cloudflare Calls App Secret (optional)
          description: The Bearer token / App Secret paired with CF_CALLS_APP_ID above. Leave blank if you are not using Cloudflare TURN.
        - key: VAPID_PUBLIC_KEY
          type: STRING
          name: VAPID Public Key (optional)
          description: 'Web Push VAPID public key for push notifications. Generate with: npx web-push generate-vapid-keys. Leave blank to disable push.'
        - key: VAPID_PRIVATE_KEY
          type: STRING
          name: VAPID Private Key (optional)
          description: Web Push VAPID private key paired with the public key above.
        - key: VAPID_SUBJECT
          type: STRING
          name: VAPID Subject (optional)
          description: Contact email for VAPID, e.g. mailto:admin@yoursite.com.
        - key: ONESIGNAL_APP_ID
          type: STRING
          name: OneSignal App ID (optional)
          description: For native Android/iOS push via Median.co. Get from OneSignal Dashboard → Settings → Keys & IDs.
        - key: ONESIGNAL_REST_KEY
          type: STRING
          name: OneSignal REST API Key (optional)
          description: OneSignal REST API key paired with the App ID above.
        - key: TELEGRAM_BOT_TOKEN
          type: STRING
          name: Telegram Bot Token (optional)
          description: Token from @BotFather for proxying Telegram sticker packs. Leave blank to disable sticker packs.
        - key: STICKER_PACKS
          type: STRING
          name: Sticker Packs (optional)
          description: Comma-separated list of Telegram sticker pack names with labels, in the format name:label (e.g. pack_name:My Pack,other_pack:Other Pack). No limit on quantity. Leave blank to use the 8 built-in defaults.
    tags:
        - Chat
        - Security
        - Communication
    readme: "# PaperPhone Plus\n\nA **WeChat-style**, end-to-end encrypted instant messaging application.\nRewritten with **React 19 + TypeScript + Vite** frontend and **Rust (Axum)** backend\nfor maximum performance and type safety.\n\n## Security\n- **Stateless ECDH + XSalsa20-Poly1305** — per-message ephemeral keys, forward secrecy without session state\n- **Zero-knowledge server** — only ciphertext is stored; private keys never leave the device\n- **Four-tier key persistence** — memory → localStorage → sessionStorage → IndexedDB, survives WebView restarts\n\n## Features\n- \U0001F4F9 **Video & Voice Calls** — WebRTC P2P (1:1) + Mesh (group up to 6)\n- \U0001F465 **Group Chat** — up to 2000 members, plain-text messaging, Do Not Disturb mode, member management\n- \U0001F310 **Moments** — social feed with text + up to 9 photos or 1 video (≤ 10 min), likes, comments, tag-based visibility\n- \U0001F464 **User Profile** — contact profile page with Moments feed, bidirectional privacy controls (hide their / hide mine)\n- \U0001F4F0 **Timeline** — Xiaohongshu-style public feed with masonry layout, up to 50 images/videos + 2000-char text, anonymous posting, likes & comments\n- ⏱️ **Auto-Delete Messages** — 5 tiers: never / 1 day / 3 days / 1 week / 1 month\n- \U0001F514 **Push Notifications** — Web Push (VAPID) + OneSignal dual-channel\n- \U0001F310 **Multi-language** — Chinese, English, Japanese, Korean, French, German, Russian, Spanish\n- \U0001F4F1 **iOS PWA** — \"Add to Home Screen\" via Safari, no enterprise cert needed\n- \U0001F4AC Rich messaging: text, images, video, document files (PDF/DOCX/XLSX etc. with type icons), voice, 200+ emoji (8 categories), Telegram sticker packs, delivery receipts, typing indicators\n- \U0001F5C2️ **Cloudflare R2** file storage (images, voice & video)\n- \U0001F3F7️ **Friend Tags** — tag-based contact filtering & Moments visibility control\n- \U0001F3AD **Telegram Sticker Packs** — dynamic sticker packs with configurable pack list, unlimited quantity\n- \U0001F511 **Two-Factor Auth (2FA)** — Google Authenticator TOTP, 8 recovery codes\n- \U0001F4F7 **QR Code Scan & Share** — scan QR to add friends or join groups; group QR codes with configurable expiry (1 week / 1 month / 3 months)\n\n## After Deployment\n1. Open the domain assigned to the **client** service.\n2. Register an account — cryptographic keys are generated locally on your device.\n3. (Optional) Configure Cloudflare R2 for image/voice/video uploads — set `R2_*` variables on the **server** service.\n4. (Optional) Configure Cloudflare TURN for cross-network video calls — set `CF_CALLS_APP_ID` and `CF_CALLS_APP_SECRET`.\n5. (Optional) Enable push notifications — set `VAPID_*` variables for Web Push.\n6. (Optional) Enable Telegram sticker packs — set `TELEGRAM_BOT_TOKEN` and optionally `STICKER_PACKS`.\n\n## Docker Hub Images\n- `facilisvelox/paperphone-plus-client:latest`\n- `facilisvelox/paperphone-plus-server:latest`"
    services:
        - name: MySQL
          icon: https://raw.githubusercontent.com/zeabur/service-icons/main/marketplace/mysql.svg
          template: PREBUILT
          spec:
            id: mysql
            source:
                image: mysql:8.0
            ports:
                - id: database
                  port: 3306
                  type: TCP
            volumes:
                - id: data
                  dir: /var/lib/mysql
            instructions:
                - title: MySQL Host
                  content: ${PORT_FORWARDED_HOSTNAME}
                - title: MySQL Port
                  content: ${DATABASE_PORT_FORWARDED_PORT}
                - title: MySQL User
                  content: ${MYSQL_USER}
                - title: MySQL Password
                  content: ${MYSQL_PASSWORD}
                - title: MySQL Database
                  content: ${MYSQL_DATABASE}
            env:
                MYSQL_DATABASE:
                    default: paperphone
                    expose: true
                MYSQL_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
                MYSQL_PASSWORD:
                    default: ${MYSQL_ROOT_PASSWORD}
                    expose: true
                MYSQL_PORT:
                    default: "3306"
                    expose: true
                MYSQL_ROOT_HOST:
                    default: '%'
                MYSQL_ROOT_PASSWORD:
                    default: ${MYSQL_ROOT_PASSWORD}
                    expose: true
                MYSQL_USER:
                    default: paperphone
                    expose: true
        - name: Redis
          icon: https://raw.githubusercontent.com/zeabur/service-icons/main/marketplace/redis.svg
          template: PREBUILT
          spec:
            id: redis
            source:
                image: redis:7-alpine
                command:
                    - redis-server
                    - --save
                    - "60"
                    - "1"
            ports:
                - id: database
                  port: 6379
                  type: TCP
            volumes:
                - id: data
                  dir: /data
            instructions:
                - title: Redis Connection String
                  content: redis://${PORT_FORWARDED_HOSTNAME}:${DATABASE_PORT_FORWARDED_PORT}
            env:
                REDIS_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
                REDIS_PORT:
                    default: "6379"
                    expose: true
                REDIS_URI:
                    default: redis://${REDIS_HOST}:${REDIS_PORT}
                    expose: true
        - name: server
          icon: https://raw.githubusercontent.com/619dev/Paperphone-plus/main/client/public/icons/icon-512.png
          dependencies:
            - MySQL
            - Redis
          template: PREBUILT
          spec:
            id: server
            source:
                image: facilisvelox/paperphone-plus-server:latest
            ports:
                - id: web
                  port: 3000
                  type: HTTP
            volumes:
                - id: uploads
                  dir: /app/uploads
            env:
                CF_CALLS_APP_ID:
                    default: ${CF_CALLS_APP_ID}
                CF_CALLS_APP_SECRET:
                    default: ${CF_CALLS_APP_SECRET}
                DB_HOST:
                    default: ${MYSQL_HOST}
                DB_NAME:
                    default: ${MYSQL_DATABASE}
                DB_PASS:
                    default: ${MYSQL_PASSWORD}
                DB_PORT:
                    default: ${MYSQL_PORT}
                DB_USER:
                    default: paperphone
                JWT_SECRET:
                    default: ${JWT_SECRET}
                ONESIGNAL_APP_ID:
                    default: ${ONESIGNAL_APP_ID}
                ONESIGNAL_REST_KEY:
                    default: ${ONESIGNAL_REST_KEY}
                PORT:
                    default: "3000"
                R2_ACCESS_KEY_ID:
                    default: ${R2_ACCESS_KEY_ID}
                R2_ACCOUNT_ID:
                    default: ${R2_ACCOUNT_ID}
                R2_BUCKET:
                    default: ${R2_BUCKET}
                R2_PUBLIC_URL:
                    default: ${R2_PUBLIC_URL}
                R2_SECRET_ACCESS_KEY:
                    default: ${R2_SECRET_ACCESS_KEY}
                REDIS_HOST:
                    default: ${REDIS_HOST}
                REDIS_PORT:
                    default: ${REDIS_PORT}
                SERVER_URL:
                    default: ${ZEABUR_WEB_URL}
                    expose: true
                STICKER_PACKS:
                    default: ${STICKER_PACKS}
                TELEGRAM_BOT_TOKEN:
                    default: ${TELEGRAM_BOT_TOKEN}
                UPLOAD_DIR:
                    default: /app/uploads
                VAPID_PRIVATE_KEY:
                    default: ${VAPID_PRIVATE_KEY}
                VAPID_PUBLIC_KEY:
                    default: ${VAPID_PUBLIC_KEY}
                VAPID_SUBJECT:
                    default: ${VAPID_SUBJECT}
        - name: client
          icon: https://raw.githubusercontent.com/619dev/Paperphone-plus/main/client/public/icons/icon-512.png
          dependencies:
            - server
          template: PREBUILT
          spec:
            id: client
            source:
                image: facilisvelox/paperphone-plus-client:latest
            ports:
                - id: web
                  port: 80
                  type: HTTP
            env:
                SERVER_URL:
                    default: ${SERVER_URL}
          domainKey: PUBLIC_DOMAIN
localization:
    zh-CN:
        description: 微信风格的端对端加密即时通讯 App（React + Rust），支持无状态 ECDH 加密、WebRTC 视频/语音通话、 Cloudflare R2 文件存储、8 种语言与 iOS PWA 部署，完全可自建。
        readme: "# PaperPhone Plus\n\n**微信风格**的端对端加密即时通讯应用。\n使用 **React 19 + TypeScript + Vite** 前端和 **Rust (Axum)** 后端重写，\n获得极致性能和类型安全。\n\n## 安全模型\n- **无状态 ECDH + XSalsa20-Poly1305** — 逐消息临时密钥，前向保密无需会话状态同步\n- **零知识服务器** — 仅存储密文，私钥仅在设备本地，永不上传\n- **四层密钥持久化** — 内存 → localStorage → sessionStorage → IndexedDB，Android WebView 重启后仍可恢复\n\n## 功能亮点\n- \U0001F4F9 **视频/语音通话** — WebRTC P2P（1:1）+ Mesh 多人（≤6 人）\n- \U0001F465 **群聊** — 最多 2000 人群组，纯文本消息，免打扰模式，成员管理\n- \U0001F310 **朋友圈** — 文字 + 最多 9 张图或 1 个视频（≤10 分钟），点赞、评论、标签可见性控制\n- \U0001F464 **个人资料** — 联系人资料页（朋友圈动态 +「不看此人朋友圈」与「不让他看我的朋友圈」双向隐私控制）\n- \U0001F4F0 **时间线** — 小红书风格公开发帖区，双列瀑布流布局，最多50个图片/视频+2000字文字，支持匿名发帖、点赞、评论\n- ⏱️ **消息自动删除** — 5 档可选（永不/1天/3天/1周/1月）\n- \U0001F514 **消息推送** — Web Push (VAPID) + OneSignal 双通道\n- \U0001F310 **多语言** — 中文、英文、日语、韩语、法语、德语、俄语、西班牙语\n- \U0001F4F1 **iOS 永久免签** — Safari「添加到主屏幕」，无需企业证书\n- \U0001F4AC 富文本消息、图片、视频、文档文件（PDF/DOCX/XLSX 等带类型图标）、语音、Emoji（200+，8 分类）、Telegram 贴纸包、送达回执、打字状态\n- \U0001F5C2️ **Cloudflare R2** 对象存储（图片、语音与视频）\n- \U0001F3F7️ **好友标签** — 标签分类筛选通讯录 + 朋友圈可见性控制\n- \U0001F3AD **Telegram 贴纸包** — 动态贴纸包管理，可自定义包列表，数量无上限\n- \U0001F511 **两步验证 (2FA)** — Google Authenticator TOTP，8 个恢复码\n- \U0001F4F7 **扫码加好友/入群** — 扫一扫二维码添加好友、加入群聊，群二维码可设置有效期（1周/1月/3月）\n\n## 部署后操作\n1. 打开分配给 **client** 服务的域名。\n2. 注册账号 — 密钥在本地设备生成。\n3. （可选）在 **server** 服务设置 `R2_*` 变量，启用图片/语音/视频上传功能。\n4. （可选）配置 Cloudflare TURN 以支持跨公网视频通话。\n5. （可选）设置 `VAPID_*` 变量启用浏览器推送通知。\n6. （可选）设置 `TELEGRAM_BOT_TOKEN` 和 `STICKER_PACKS` 启用 Telegram 贴纸包功能。"
