From 90f1e953317171cacda6d6a7e0a1e48d5dc8a1c8 Mon Sep 17 00:00:00 2001 From: okxlin <61420215+okxlin@users.noreply.github.com> Date: Fri, 31 Jul 2026 04:02:21 +0800 Subject: [PATCH] feat: add Bichon --- apps/bichon/1.6.2/.env.sample | 6 ++ apps/bichon/1.6.2/data.yml | 95 +++++++++++++++++++++++++ apps/bichon/1.6.2/data/.gitkeep | 1 + apps/bichon/1.6.2/docker-compose.yml | 24 +++++++ apps/bichon/1.6.2/scripts/init.sh | 60 ++++++++++++++++ apps/bichon/1.6.2/scripts/uninstall.sh | 4 ++ apps/bichon/1.6.2/scripts/upgrade.sh | 5 ++ apps/bichon/README.md | 58 +++++++++++++++ apps/bichon/data.yml | 31 ++++++++ apps/bichon/logo.png | Bin 0 -> 4873 bytes 10 files changed, 284 insertions(+) create mode 100644 apps/bichon/1.6.2/.env.sample create mode 100644 apps/bichon/1.6.2/data.yml create mode 100644 apps/bichon/1.6.2/data/.gitkeep create mode 100644 apps/bichon/1.6.2/docker-compose.yml create mode 100755 apps/bichon/1.6.2/scripts/init.sh create mode 100755 apps/bichon/1.6.2/scripts/uninstall.sh create mode 100755 apps/bichon/1.6.2/scripts/upgrade.sh create mode 100644 apps/bichon/README.md create mode 100644 apps/bichon/data.yml create mode 100644 apps/bichon/logo.png diff --git a/apps/bichon/1.6.2/.env.sample b/apps/bichon/1.6.2/.env.sample new file mode 100644 index 000000000..eb3caad35 --- /dev/null +++ b/apps/bichon/1.6.2/.env.sample @@ -0,0 +1,6 @@ +CONTAINER_NAME=bichon-compose-check +PANEL_APP_PORT_HTTP=15630 +APP_DATA_DIR=./data +BICHON_ENCRYPT_PASSWORD=replace-with-a-long-random-encryption-password +BICHON_PUBLIC_URL= +BICHON_LOG_LEVEL=info diff --git a/apps/bichon/1.6.2/data.yml b/apps/bichon/1.6.2/data.yml new file mode 100644 index 000000000..bc72c36f8 --- /dev/null +++ b/apps/bichon/1.6.2/data.yml @@ -0,0 +1,95 @@ +additionalProperties: + formFields: + - default: 15630 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori data + pt-br: Diretorio de dados + required: true + type: text + - edit: true + envKey: BICHON_ENCRYPT_PASSWORD + labelEn: Encryption Password + labelZh: 加密密码 + label: + en: Encryption Password + zh: 加密密码 + zh-Hant: 加密密碼 + ja: 暗号化パスワード + ko: 암호화 비밀번호 + ru: Пароль шифрования + ms: Kata laluan penyulitan + pt-br: Senha de criptografia + random: true + required: true + rule: paramComplexity + type: password + - default: "" + edit: true + envKey: BICHON_PUBLIC_URL + labelEn: Public URL + labelZh: 公开访问地址 + label: + en: Public URL + zh: 公开访问地址 + zh-Hant: 公開存取位址 + ja: 公開 URL + ko: 공개 URL + ru: Публичный URL + ms: URL awam + pt-br: URL publico + required: false + type: text + - default: info + edit: true + envKey: BICHON_LOG_LEVEL + labelEn: Log Level + labelZh: 日志级别 + label: + en: Log Level + zh: 日志级别 + zh-Hant: 日誌層級 + ja: ログレベル + ko: 로그 수준 + ru: Уровень журнала + ms: Tahap log + pt-br: Nivel de log + required: true + type: select + values: + - label: trace + value: trace + - label: debug + value: debug + - label: info + value: info + - label: warn + value: warn + - label: error + value: error diff --git a/apps/bichon/1.6.2/data/.gitkeep b/apps/bichon/1.6.2/data/.gitkeep new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/apps/bichon/1.6.2/data/.gitkeep @@ -0,0 +1 @@ + diff --git a/apps/bichon/1.6.2/docker-compose.yml b/apps/bichon/1.6.2/docker-compose.yml new file mode 100644 index 000000000..ad8307231 --- /dev/null +++ b/apps/bichon/1.6.2/docker-compose.yml @@ -0,0 +1,24 @@ +services: + bichon: + image: "rustmailer/bichon:1.6.2" + container_name: ${CONTAINER_NAME} + restart: always + user: "1000:1000" + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:15630" + environment: + - BICHON_ROOT_DIR=/data + - BICHON_ENCRYPT_PASSWORD=${BICHON_ENCRYPT_PASSWORD} + - BICHON_PUBLIC_URL=${BICHON_PUBLIC_URL} + - BICHON_LOG_LEVEL=${BICHON_LOG_LEVEL} + - BICHON_ANSI_LOGS=false + volumes: + - "${APP_DATA_DIR}:/data" + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/bichon/1.6.2/scripts/init.sh b/apps/bichon/1.6.2/scripts/init.sh new file mode 100755 index 000000000..7437617b3 --- /dev/null +++ b/apps/bichon/1.6.2/scripts/init.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +read_env_value() { + local key="$1" + local value + [[ -f "$ENV_FILE" ]] || return 0 + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + value="${value%$'\r'}" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +DATA_DIR="${APP_DATA_DIR:-}" +if [[ -z "$DATA_DIR" ]]; then + DATA_DIR="$(read_env_value APP_DATA_DIR)" +fi +DATA_DIR="${DATA_DIR:-./data}" + +if [[ "$DATA_DIR" = /* ]]; then + RESOLVED_DATA_DIR="$(realpath -m -- "$DATA_DIR")" + if [[ -L "$DATA_DIR" ]]; then + printf 'APP_DATA_DIR must not be a symbolic link\n' >&2 + exit 1 + fi + if [[ -e "$RESOLVED_DATA_DIR" ]]; then + [[ -d "$RESOLVED_DATA_DIR" ]] || { + printf 'APP_DATA_DIR must refer to a directory\n' >&2 + exit 1 + } + [[ "$(stat -c '%u:%g' "$RESOLVED_DATA_DIR")" == '1000:1000' ]] || { + printf 'Existing absolute APP_DATA_DIR must be owned by UID/GID 1000:1000\n' >&2 + exit 1 + } + else + install -d -m 0750 -- "$RESOLVED_DATA_DIR" + if [[ "$(id -u)" -eq 0 ]]; then + chown 1000:1000 -- "$RESOLVED_DATA_DIR" + fi + fi +else + RESOLVED_DATA_DIR="$(realpath -m -- "$ROOT_DIR/${DATA_DIR#./}")" + case "$RESOLVED_DATA_DIR" in + "$ROOT_DIR"/*) ;; + *) + printf 'Relative APP_DATA_DIR must remain inside the application version directory\n' >&2 + exit 1 + ;; + esac + install -d -m 0750 -- "$RESOLVED_DATA_DIR" + if [[ "$(id -u)" -eq 0 ]]; then + chown 1000:1000 -- "$RESOLVED_DATA_DIR" + fi +fi diff --git a/apps/bichon/1.6.2/scripts/uninstall.sh b/apps/bichon/1.6.2/scripts/uninstall.sh new file mode 100755 index 000000000..c6af20129 --- /dev/null +++ b/apps/bichon/1.6.2/scripts/uninstall.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +docker-compose down --volumes --remove-orphans diff --git a/apps/bichon/1.6.2/scripts/upgrade.sh b/apps/bichon/1.6.2/scripts/upgrade.sh new file mode 100755 index 000000000..5ec28d784 --- /dev/null +++ b/apps/bichon/1.6.2/scripts/upgrade.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +exec "$SCRIPT_DIR/init.sh" diff --git a/apps/bichon/README.md b/apps/bichon/README.md new file mode 100644 index 000000000..b1b999db7 --- /dev/null +++ b/apps/bichon/README.md @@ -0,0 +1,58 @@ +# Bichon + +## 产品介绍 + +Bichon 是使用 Rust 构建的自托管邮件归档服务,可从多个 IMAP 账户下载邮件,在本地建立全文索引,并通过内置 Web 界面和 REST API 提供统一检索。它是归档工具,不是用于撰写或发送邮件的客户端。 + +## 主要功能 + +- 多账户 IMAP 增量归档与定时同步 +- 邮件、附件和联系人全文检索 +- 内嵌元数据、索引和压缩对象存储,无外部数据库依赖 +- 多用户 RBAC、账户级权限和 API token +- EML、MBOX、Thunderbird 和 PST 导入工具 + +## Features + +- Incremental archiving and scheduled synchronization for multiple IMAP accounts +- Full-text search across messages, attachments, and contacts +- Embedded metadata, index, and compressed object stores with no external database +- Multi-user RBAC, account-level permissions, and API tokens +- Import tools for EML, MBOX, Thunderbird, and PST data + +## Introduction + +Bichon is a self-hosted email archiving service written in Rust. It downloads mail from multiple IMAP accounts, builds local full-text indexes, and exposes a unified WebUI and REST API. It is an archive, not a mail client for composing or sending messages. + +## 访问说明 + +安装后通过 `http://<服务器 IP>:15630` 访问,实际端口以安装表单中的 `PANEL_APP_PORT_HTTP` 为准。首次登录用户名为 `admin`,密码为 `admin@bichon`。请登录后立即在设置页面修改默认密码,并通过 1Panel 网站反向代理启用 HTTPS 后再向不受信任网络开放服务。 + +## 参数说明 + +| 变量 | 说明 | 默认值 | 必填 | +| --- | --- | --- | --- | +| `PANEL_APP_PORT_HTTP` | Web 界面和 API 的 HTTP 端口 | `15630` | 是 | +| `APP_DATA_DIR` | 元数据、全文索引和邮件对象存储目录 | `./data` | 是 | +| `BICHON_ENCRYPT_PASSWORD` | 用于加密保存的 IMAP/OAuth 凭据 | 安装时随机生成 | 是 | +| `BICHON_PUBLIC_URL` | OAuth 回调和文档使用的公开 URL | 空 | 否 | +| `BICHON_LOG_LEVEL` | 服务日志级别 | `info` | 是 | + +## 数据与安全 + +- 使用 Bichon 官方单服务镜像,容器按上游要求以 UID/GID `1000:1000` 运行。 +- `APP_DATA_DIR` 必须位于本地文件系统;上游明确不支持 NFS、CIFS/SMB 等网络文件系统作为数据目录。 +- `BICHON_ENCRYPT_PASSWORD` 用于派生 AES-256-GCM 加密密钥。上游当前不支持直接轮换该口令后重新加密已有凭据,因此应妥善保管,并在恢复备份时使用相同值。 +- 可选 SMTP 接收器、TLS 文件和高级存储拆分未在默认安装中启用,可在确认上游配置与端口策略后手工扩展。 + +## 备份与升级 + +升级或迁移前,应停止 Bichon 并备份 `APP_DATA_DIR` 指向的完整目录以及安装时使用的加密口令。默认 `./data` 位于 1Panel 应用版本目录内,卸载时会随应用目录移除;若需要跨卸载保留归档,请选择独立绝对路径,并预先保证其所有权为 UID/GID `1000:1000`。 + +本应用只打包稳定版 `1.6.2`。截至本批次适配时,上游 `latest` 已指向 `2.0.0-alpha.1` 预发布镜像,因此没有将 `latest` 作为稳定版本交付。 + +## 参考资料 + +- 项目仓库: +- Docker 安装说明: +- API 文档: 安装后访问 `/api-docs` diff --git a/apps/bichon/data.yml b/apps/bichon/data.yml new file mode 100644 index 000000000..d0240d0cd --- /dev/null +++ b/apps/bichon/data.yml @@ -0,0 +1,31 @@ +name: Bichon +tags: + - Tool +title: 自托管邮件归档与全文检索服务 +description: 自托管邮件归档与全文检索服务 +additionalProperties: + key: bichon + name: Bichon + tags: + - Tool + shortDescZh: 自托管邮件归档与全文检索服务 + shortDescEn: A self-hosted email archiving and full-text search service + description: + en: A self-hosted email archiving and full-text search service + zh: 自托管邮件归档与全文检索服务 + zh-Hant: 自託管郵件封存與全文檢索服務 + ja: セルフホスト型のメールアーカイブおよび全文検索サービス + ko: 자체 호스팅 이메일 보관 및 전문 검색 서비스 + ru: Самостоятельно размещаемый сервис архивации и полнотекстового поиска почты + ms: Perkhidmatan arkib dan carian teks penuh e-mel hos sendiri + pt-br: Servico auto-hospedado de arquivamento e busca textual de emails + type: tool + crossVersionUpdate: true + limit: 0 + recommend: 0 + website: https://github.com/rustmailer/bichon + github: https://github.com/rustmailer/bichon + document: https://github.com/rustmailer/bichon#quick-start + architectures: + - amd64 + - arm64 diff --git a/apps/bichon/logo.png b/apps/bichon/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5ef735e5176e1a49a1851a17646f5a7194f9cd08 GIT binary patch literal 4873 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 literal 0 HcmV?d00001