Merge pull request #5477 from okxlin/batch-770/snipo-20260801

feat: add Snipo app
This commit is contained in:
okxlin
2026-08-01 06:03:40 +08:00
committed by GitHub
16 changed files with 1019 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
CONTAINER_NAME=snipo
PANEL_APP_BIND_ADDRESS=127.0.0.1
PANEL_APP_PORT_HTTP=8080
SNIPO_MASTER_PASSWORD=replace-with-a-strong-unique-password
SNIPO_SESSION_SECRET=replace-with-a-random-session-secret
SNIPO_ENCRYPTION_SALT=replace-with-a-random-encryption-salt
SNIPO_SESSION_DURATION=168h
SNIPO_TRUST_PROXY=false
SNIPO_MAX_FILES_PER_SNIPPET=10
SNIPO_ENABLE_PUBLIC_SNIPPETS=true
SNIPO_ENABLE_API_TOKENS=true
SNIPO_ENABLE_BACKUP_RESTORE=true
SNIPO_LOG_LEVEL=info
SNIPO_LOG_FORMAT=json
APP_DATA_DIR=./data
+265
View File
@@ -0,0 +1,265 @@
additionalProperties:
formFields:
- default: 127.0.0.1
edit: true
envKey: PANEL_APP_BIND_ADDRESS
labelEn: Bind Address
labelZh: 绑定地址
label:
en: Bind Address
zh: 绑定地址
zh-Hant: 綁定位址
ja: バインドアドレス
ko: 바인드 주소
ru: Адрес привязки
ms: Alamat ikatan
pt-br: Endereco de vinculacao
required: true
type: text
- default: 8080
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: ""
edit: true
envKey: SNIPO_MASTER_PASSWORD
labelEn: Master Password
labelZh: 主密码
label:
en: Master Password
zh: 主密码
zh-Hant: 主密碼
ja: マスターパスワード
ko: 마스터 비밀번호
ru: Главный пароль
ms: Kata laluan induk
pt-br: Senha mestra
random: true
required: true
rule: paramComplexity
type: password
- default: ""
edit: false
envKey: SNIPO_SESSION_SECRET
labelEn: Session Secret (Auto-generated)
labelZh: 会话密钥(自动生成)
label:
en: Session Secret (Auto-generated)
zh: 会话密钥(自动生成)
zh-Hant: 工作階段密鑰(自動產生)
ja: セッションシークレット(自動生成)
ko: 세션 비밀 키(자동 생성)
ru: Секрет сеанса (создается автоматически)
ms: Rahsia sesi (dijana secara automatik)
pt-br: Segredo de sessao (gerado automaticamente)
random: true
required: true
type: password
- default: ""
edit: false
envKey: SNIPO_ENCRYPTION_SALT
labelEn: Encryption Salt (Auto-generated)
labelZh: 加密盐(自动生成)
label:
en: Encryption Salt (Auto-generated)
zh: 加密盐(自动生成)
zh-Hant: 加密鹽值(自動產生)
ja: 暗号化ソルト(自動生成)
ko: 암호화 솔트(자동 생성)
ru: Соль шифрования (создается автоматически)
ms: Garam penyulitan (dijana secara automatik)
pt-br: Sal de criptografia (gerado automaticamente)
random: true
required: true
type: password
- default: 168h
edit: true
envKey: SNIPO_SESSION_DURATION
labelEn: Session Duration
labelZh: 会话有效期
label:
en: Session Duration
zh: 会话有效期
zh-Hant: 工作階段有效期
ja: セッション有効期間
ko: 세션 유효 기간
ru: Срок действия сеанса
ms: Tempoh sesi
pt-br: Duracao da sessao
required: true
type: text
- default: "false"
edit: true
envKey: SNIPO_TRUST_PROXY
labelEn: Trust Reverse Proxy Headers
labelZh: 信任反向代理请求头
label:
en: Trust Reverse Proxy Headers
zh: 信任反向代理请求头
zh-Hant: 信任反向代理請求標頭
ja: リバースプロキシヘッダーを信頼
ko: 리버스 프록시 헤더 신뢰
ru: Доверять заголовкам обратного прокси
ms: Percayai pengepala proksi songsang
pt-br: Confiar nos cabecalhos do proxy reverso
required: true
type: select
values:
- label: "false"
value: "false"
- label: "true"
value: "true"
- default: 10
edit: true
envKey: SNIPO_MAX_FILES_PER_SNIPPET
labelEn: Maximum Files per Snippet
labelZh: 每个片段的最大文件数
label:
en: Maximum Files per Snippet
zh: 每个片段的最大文件数
zh-Hant: 每個片段的最大檔案數
ja: スニペットごとの最大ファイル数
ko: 코드 조각당 최대 파일 수
ru: Максимум файлов в одном фрагменте
ms: Fail maksimum bagi setiap cebisan
pt-br: Maximo de arquivos por trecho
required: true
type: number
- default: "true"
edit: true
envKey: SNIPO_ENABLE_PUBLIC_SNIPPETS
labelEn: Enable Public Snippets
labelZh: 启用公开片段
label:
en: Enable Public Snippets
zh: 启用公开片段
zh-Hant: 啟用公開片段
ja: 公開スニペットを有効化
ko: 공개 코드 조각 활성화
ru: Включить публичные фрагменты
ms: Dayakan cebisan awam
pt-br: Ativar trechos publicos
required: true
type: select
values:
- label: "true"
value: "true"
- label: "false"
value: "false"
- default: "true"
edit: true
envKey: SNIPO_ENABLE_API_TOKENS
labelEn: Enable API Tokens
labelZh: 启用 API Token
label:
en: Enable API Tokens
zh: 启用 API Token
zh-Hant: 啟用 API Token
ja: API トークンを有効化
ko: API 토큰 활성화
ru: Включить токены API
ms: Dayakan token API
pt-br: Ativar tokens de API
required: true
type: select
values:
- label: "true"
value: "true"
- label: "false"
value: "false"
- default: "true"
edit: true
envKey: SNIPO_ENABLE_BACKUP_RESTORE
labelEn: Enable Backup and Restore
labelZh: 启用备份与恢复
label:
en: Enable Backup and Restore
zh: 启用备份与恢复
zh-Hant: 啟用備份與還原
ja: バックアップと復元を有効化
ko: 백업 및 복원 활성화
ru: Включить резервное копирование и восстановление
ms: Dayakan sandaran dan pemulihan
pt-br: Ativar backup e restauracao
required: true
type: select
values:
- label: "true"
value: "true"
- label: "false"
value: "false"
- default: info
edit: true
envKey: SNIPO_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: info
value: info
- label: warn
value: warn
- label: error
value: error
- label: debug
value: debug
- default: json
edit: true
envKey: SNIPO_LOG_FORMAT
labelEn: Log Format
labelZh: 日志格式
label:
en: Log Format
zh: 日志格式
zh-Hant: 日誌格式
ja: ログ形式
ko: 로그 형식
ru: Формат журнала
ms: Format log
pt-br: Formato de log
required: true
type: select
values:
- label: json
value: json
- label: text
value: text
- 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
+56
View File
@@ -0,0 +1,56 @@
services:
snipo:
image: "ghcr.io/mohamedelashri/snipo:1.7.4@sha256:bd9d1fa2245ad2bcdaaac0e34df3094f2005e7397de3e42c9aeb0ae9ec765e9f"
container_name: ${CONTAINER_NAME}
restart: unless-stopped
user: "1000:1000"
networks:
- 1panel-network
ports:
- "${PANEL_APP_BIND_ADDRESS}:${PANEL_APP_PORT_HTTP}:8080"
environment:
- SNIPO_MASTER_PASSWORD=${SNIPO_MASTER_PASSWORD}
- SNIPO_SESSION_SECRET=${SNIPO_SESSION_SECRET}
- SNIPO_ENCRYPTION_SALT=${SNIPO_ENCRYPTION_SALT}
- SNIPO_SESSION_DURATION=${SNIPO_SESSION_DURATION}
- SNIPO_HOST=0.0.0.0
- SNIPO_PORT=8080
- SNIPO_TRUST_PROXY=${SNIPO_TRUST_PROXY}
- SNIPO_MAX_FILES_PER_SNIPPET=${SNIPO_MAX_FILES_PER_SNIPPET}
- SNIPO_DB_PATH=/data/snipo.db
- SNIPO_DB_MAX_CONNS=1
- SNIPO_DB_BUSY_TIMEOUT=5000
- SNIPO_DB_JOURNAL=WAL
- SNIPO_DB_SYNC=NORMAL
- SNIPO_DB_MMAP_SIZE=67108864
- SNIPO_DB_CACHE_SIZE=-1000
- SNIPO_RATE_LIMIT=100
- SNIPO_RATE_LIMIT_READ=1000
- SNIPO_RATE_LIMIT_WRITE=500
- SNIPO_RATE_LIMIT_ADMIN=100
- SNIPO_ENABLE_PUBLIC_SNIPPETS=${SNIPO_ENABLE_PUBLIC_SNIPPETS}
- SNIPO_ENABLE_API_TOKENS=${SNIPO_ENABLE_API_TOKENS}
- SNIPO_ENABLE_BACKUP_RESTORE=${SNIPO_ENABLE_BACKUP_RESTORE}
- SNIPO_LOG_LEVEL=${SNIPO_LOG_LEVEL}
- SNIPO_LOG_FORMAT=${SNIPO_LOG_FORMAT}
volumes:
- "${APP_DATA_DIR}:/data"
read_only: true
tmpfs:
- /tmp:rw,nosuid,nodev,noexec,size=16m,uid=1000,gid=1000,mode=1777
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "/snipo", "health"]
interval: 30s
timeout: 5s
retries: 8
start_period: 10s
labels:
createdBy: "Apps"
networks:
1panel-network:
external: true
+118
View File
@@ -0,0 +1,118 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
APP_ROOT_DIR="$(dirname "$ROOT_DIR")"
ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}"
fail() {
printf '%s\n' "$1" >&2
exit 1
}
strip_matching_quotes() {
local value="$1"
if [[ ${#value} -ge 2 ]]; then
if [[ "${value:0:1}" == '"' && "${value: -1}" == '"' ]]; then
value="${value:1:${#value}-2}"
elif [[ "${value:0:1}" == "'" && "${value: -1}" == "'" ]]; then
value="${value:1:${#value}-2}"
fi
fi
printf '%s\n' "$value"
}
read_env_value() {
local key="$1"
local value=""
if [[ -f "$ENV_FILE" ]]; then
value="$(grep -E "^${key}=" "$ENV_FILE" | tail -n 1 | cut -d '=' -f 2- || true)"
fi
value="${value%$'\r'}"
strip_matching_quotes "$value"
}
set_env_value() {
local key="$1"
local value="$2"
local temp_file
temp_file="$(mktemp "${ENV_FILE}.tmp.XXXXXX")"
awk -v key="$key" -v value="$value" '
BEGIN { updated = 0 }
$0 ~ ("^" key "=") {
if (!updated) {
print key "=" value
updated = 1
}
next
}
{ print }
END {
if (!updated) print key "=" value
}
' "$ENV_FILE" >"$temp_file"
chmod 600 "$temp_file"
mv -f -- "$temp_file" "$ENV_FILE"
}
path_is_dotenv_safe() {
local value="$1"
case "$value" in
*$'\n'* | *$'\r'* | *\\* | *'$'* | *'#'* | *'"'* | *"'"*) return 1 ;;
*) return 0 ;;
esac
}
[[ -f "$ENV_FILE" ]] || fail "Environment file not found: ${ENV_FILE}"
[[ ! -L "$ENV_FILE" ]] || fail "Environment file must not be a symbolic link"
[[ "$(id -u)" -eq 0 ]] || fail "Snipo init must run as root"
if [[ ${APP_DATA_DIR+x} ]]; then
DATA_DIR_RAW="$APP_DATA_DIR"
else
DATA_DIR_RAW="$(read_env_value APP_DATA_DIR)"
fi
DATA_DIR_RAW="$(strip_matching_quotes "${DATA_DIR_RAW:-./data}")"
[[ -n "$DATA_DIR_RAW" ]] || fail "APP_DATA_DIR must not be empty"
path_is_dotenv_safe "$DATA_DIR_RAW" || fail "APP_DATA_DIR contains unsupported dotenv characters"
if [[ "$DATA_DIR_RAW" = /* ]]; then
[[ ! -L "$DATA_DIR_RAW" ]] || fail "APP_DATA_DIR must not be a symbolic link"
DATA_DIR_ABS="$(realpath -m -- "$DATA_DIR_RAW")"
[[ "$DATA_DIR_ABS" != "/" ]] || fail "APP_DATA_DIR must not be the filesystem root"
else
CONTAINER_NAME_VALUE="$(read_env_value CONTAINER_NAME)"
[[ "$CONTAINER_NAME_VALUE" =~ ^[A-Za-z0-9._-]+$ ]] || \
fail "CONTAINER_NAME contains unsupported characters"
RETAINED_ROOT="${APP_ROOT_DIR}/retained-data"
[[ ! -L "$RETAINED_ROOT" ]] || fail "The retained data root must not be a symbolic link"
RETAINED_INSTANCE_ROOT="${RETAINED_ROOT}/${CONTAINER_NAME_VALUE}"
[[ ! -L "$RETAINED_INSTANCE_ROOT" ]] || fail "The retained instance root must not be a symbolic link"
DATA_DIR_PATH="${RETAINED_INSTANCE_ROOT}/${DATA_DIR_RAW#./}"
[[ ! -L "$DATA_DIR_PATH" ]] || fail "APP_DATA_DIR must not be a symbolic link"
RETAINED_INSTANCE_ABS="$(realpath -m -- "$RETAINED_INSTANCE_ROOT")"
DATA_DIR_ABS="$(realpath -m -- "$DATA_DIR_PATH")"
case "$DATA_DIR_ABS" in
"${RETAINED_INSTANCE_ABS}"/*) ;;
*) fail "Relative APP_DATA_DIR must remain inside the isolated retained-data directory" ;;
esac
set_env_value APP_DATA_DIR "\"${DATA_DIR_ABS}\""
fi
if [[ -e "$DATA_DIR_ABS" && ! -d "$DATA_DIR_ABS" ]]; then
fail "APP_DATA_DIR must be a directory"
fi
if [[ ! -e "$DATA_DIR_ABS" ]]; then
install -d -m 0750 -- "$DATA_DIR_ABS"
chown 1000:1000 -- "$DATA_DIR_ABS"
fi
[[ "$(stat -c '%u:%g' "$DATA_DIR_ABS")" == "1000:1000" ]] || \
fail "APP_DATA_DIR must be owned by UID/GID 1000:1000"
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
# Persistent data is intentionally retained for recovery or reinstallation.
exit 0
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
# Snipo applies embedded SQLite migrations when the new container starts.
exit 0
+51
View File
@@ -0,0 +1,51 @@
# Snipo
## 产品介绍
Snipo 是一个面向单用户的轻量级代码片段管理器,支持在一个自托管实例中组织、搜索和分享多文件代码片段。
## 主要功能
- 多文件代码片段、语法高亮、文件夹、标签、搜索和历史记录
- 公开链接、软删除、过期时间和备份恢复
- 可按权限创建读、写和管理 API Token
- 使用内置 SQLite 数据库,无需外部数据库
## 访问说明
安装时设置主密码,随后通过 `http://<服务器 IP>:<端口>` 访问并登录。Snipo 只有一个共享的主密码和数据库,不提供多用户或租户隔离。默认绑定 `127.0.0.1`;远程访问应通过 1Panel 的 HTTPS 反向代理发布。
## 数据与安全
- 数据库、自动生成的加密盐和备份数据保存在 `APP_DATA_DIR` 中。相对路径会在安装时解析到应用级 `retained-data` 目录并按容器名隔离,卸载实例后仍会保留;绝对路径保持不变。升级或迁移前应备份 SQLite 数据库、WAL/SHM 文件和 `.encryption_salt`。
- 容器以 UID/GID `1000:1000` 运行,根文件系统只读,丢弃全部 Linux capabilities,并只提供一个小型可写 `/tmp`。
- 默认启用公开片段、API Token 和备份恢复功能,可在安装表单中关闭不需要的功能。
- 公开片段可由持有链接的任何人访问。对公网部署时应启用 HTTPS,并避免绕过反向代理直接访问应用端口。
- Snipo 启动时自动执行内置数据库迁移。升级后应检查 `/health` 并验证常用片段的读写和持久化。
## Introduction
Snipo is a lightweight, single-user snippet manager for organizing, searching, and sharing multi-file code snippets in one self-hosted instance.
## Features
- Multi-file snippets, syntax highlighting, folders, tags, search, and history
- Public links, soft deletion, expiration, and backup/restore
- Read, write, and administrative API tokens
- Embedded SQLite storage without an external database
## Usage And Security
- Set the master password during installation, then sign in at `http://<server-ip>:<port>`. Snipo has one shared password and database; it does not provide multi-user isolation.
- The service binds to `127.0.0.1` by default. Publish it remotely through a 1Panel HTTPS reverse proxy.
- `APP_DATA_DIR` stores the SQLite database, generated encryption salt, and backups. Relative paths are resolved into a per-container directory under the app-level `retained-data` directory so uninstalling an instance preserves them; absolute paths remain unchanged.
- The container runs as UID/GID `1000:1000`, uses a read-only root filesystem, drops all Linux capabilities, and receives only a small writable `/tmp`.
- Public snippets are accessible to anyone with their link. Review the public-sharing, API-token, and backup switches before exposing the service.
## References
- Project: <https://github.com/MohamedElashri/snipo>
- Stable release: <https://github.com/MohamedElashri/snipo/releases/tag/v1.7.4>
- Deployment guide: <https://github.com/MohamedElashri/snipo/blob/v1.7.4/docs/deployment.md>
- Security model: <https://github.com/MohamedElashri/snipo/blob/v1.7.4/SECURITY.md>
- License: <https://github.com/MohamedElashri/snipo/blob/v1.7.4/LICENSE> (AGPL-3.0)
+31
View File
@@ -0,0 +1,31 @@
name: Snipo
tags:
- 工具
title: 轻量级单用户代码片段管理器
description: 轻量级单用户代码片段管理器
additionalProperties:
key: snipo
name: Snipo
tags:
- Tool
shortDescZh: 轻量级单用户代码片段管理器
shortDescEn: Lightweight single-user snippet manager
description:
en: Lightweight single-user snippet manager
zh: 轻量级单用户代码片段管理器
zh-Hant: 輕量級單一使用者程式碼片段管理器
ja: 軽量なシングルユーザー向けスニペット管理ツール
ko: 가벼운 단일 사용자 코드 조각 관리자
ru: Легковесный менеджер фрагментов кода для одного пользователя
ms: Pengurus cebisan kod ringan untuk seorang pengguna
pt-br: Gerenciador leve de trechos de codigo para um unico usuario
type: website
crossVersionUpdate: true
limit: 0
recommend: 0
website: https://github.com/MohamedElashri/snipo
github: https://github.com/MohamedElashri/snipo
document: https://github.com/MohamedElashri/snipo/blob/main/docs/deployment.md
architectures:
- amd64
- arm64
+15
View File
@@ -0,0 +1,15 @@
CONTAINER_NAME=snipo
PANEL_APP_BIND_ADDRESS=127.0.0.1
PANEL_APP_PORT_HTTP=8080
SNIPO_MASTER_PASSWORD=replace-with-a-strong-unique-password
SNIPO_SESSION_SECRET=replace-with-a-random-session-secret
SNIPO_ENCRYPTION_SALT=replace-with-a-random-encryption-salt
SNIPO_SESSION_DURATION=168h
SNIPO_TRUST_PROXY=false
SNIPO_MAX_FILES_PER_SNIPPET=10
SNIPO_ENABLE_PUBLIC_SNIPPETS=true
SNIPO_ENABLE_API_TOKENS=true
SNIPO_ENABLE_BACKUP_RESTORE=true
SNIPO_LOG_LEVEL=info
SNIPO_LOG_FORMAT=json
APP_DATA_DIR=./data
+265
View File
@@ -0,0 +1,265 @@
additionalProperties:
formFields:
- default: 127.0.0.1
edit: true
envKey: PANEL_APP_BIND_ADDRESS
labelEn: Bind Address
labelZh: 绑定地址
label:
en: Bind Address
zh: 绑定地址
zh-Hant: 綁定位址
ja: バインドアドレス
ko: 바인드 주소
ru: Адрес привязки
ms: Alamat ikatan
pt-br: Endereco de vinculacao
required: true
type: text
- default: 8080
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: ""
edit: true
envKey: SNIPO_MASTER_PASSWORD
labelEn: Master Password
labelZh: 主密码
label:
en: Master Password
zh: 主密码
zh-Hant: 主密碼
ja: マスターパスワード
ko: 마스터 비밀번호
ru: Главный пароль
ms: Kata laluan induk
pt-br: Senha mestra
random: true
required: true
rule: paramComplexity
type: password
- default: ""
edit: false
envKey: SNIPO_SESSION_SECRET
labelEn: Session Secret (Auto-generated)
labelZh: 会话密钥(自动生成)
label:
en: Session Secret (Auto-generated)
zh: 会话密钥(自动生成)
zh-Hant: 工作階段密鑰(自動產生)
ja: セッションシークレット(自動生成)
ko: 세션 비밀 키(자동 생성)
ru: Секрет сеанса (создается автоматически)
ms: Rahsia sesi (dijana secara automatik)
pt-br: Segredo de sessao (gerado automaticamente)
random: true
required: true
type: password
- default: ""
edit: false
envKey: SNIPO_ENCRYPTION_SALT
labelEn: Encryption Salt (Auto-generated)
labelZh: 加密盐(自动生成)
label:
en: Encryption Salt (Auto-generated)
zh: 加密盐(自动生成)
zh-Hant: 加密鹽值(自動產生)
ja: 暗号化ソルト(自動生成)
ko: 암호화 솔트(자동 생성)
ru: Соль шифрования (создается автоматически)
ms: Garam penyulitan (dijana secara automatik)
pt-br: Sal de criptografia (gerado automaticamente)
random: true
required: true
type: password
- default: 168h
edit: true
envKey: SNIPO_SESSION_DURATION
labelEn: Session Duration
labelZh: 会话有效期
label:
en: Session Duration
zh: 会话有效期
zh-Hant: 工作階段有效期
ja: セッション有効期間
ko: 세션 유효 기간
ru: Срок действия сеанса
ms: Tempoh sesi
pt-br: Duracao da sessao
required: true
type: text
- default: "false"
edit: true
envKey: SNIPO_TRUST_PROXY
labelEn: Trust Reverse Proxy Headers
labelZh: 信任反向代理请求头
label:
en: Trust Reverse Proxy Headers
zh: 信任反向代理请求头
zh-Hant: 信任反向代理請求標頭
ja: リバースプロキシヘッダーを信頼
ko: 리버스 프록시 헤더 신뢰
ru: Доверять заголовкам обратного прокси
ms: Percayai pengepala proksi songsang
pt-br: Confiar nos cabecalhos do proxy reverso
required: true
type: select
values:
- label: "false"
value: "false"
- label: "true"
value: "true"
- default: 10
edit: true
envKey: SNIPO_MAX_FILES_PER_SNIPPET
labelEn: Maximum Files per Snippet
labelZh: 每个片段的最大文件数
label:
en: Maximum Files per Snippet
zh: 每个片段的最大文件数
zh-Hant: 每個片段的最大檔案數
ja: スニペットごとの最大ファイル数
ko: 코드 조각당 최대 파일 수
ru: Максимум файлов в одном фрагменте
ms: Fail maksimum bagi setiap cebisan
pt-br: Maximo de arquivos por trecho
required: true
type: number
- default: "true"
edit: true
envKey: SNIPO_ENABLE_PUBLIC_SNIPPETS
labelEn: Enable Public Snippets
labelZh: 启用公开片段
label:
en: Enable Public Snippets
zh: 启用公开片段
zh-Hant: 啟用公開片段
ja: 公開スニペットを有効化
ko: 공개 코드 조각 활성화
ru: Включить публичные фрагменты
ms: Dayakan cebisan awam
pt-br: Ativar trechos publicos
required: true
type: select
values:
- label: "true"
value: "true"
- label: "false"
value: "false"
- default: "true"
edit: true
envKey: SNIPO_ENABLE_API_TOKENS
labelEn: Enable API Tokens
labelZh: 启用 API Token
label:
en: Enable API Tokens
zh: 启用 API Token
zh-Hant: 啟用 API Token
ja: API トークンを有効化
ko: API 토큰 활성화
ru: Включить токены API
ms: Dayakan token API
pt-br: Ativar tokens de API
required: true
type: select
values:
- label: "true"
value: "true"
- label: "false"
value: "false"
- default: "true"
edit: true
envKey: SNIPO_ENABLE_BACKUP_RESTORE
labelEn: Enable Backup and Restore
labelZh: 启用备份与恢复
label:
en: Enable Backup and Restore
zh: 启用备份与恢复
zh-Hant: 啟用備份與還原
ja: バックアップと復元を有効化
ko: 백업 및 복원 활성화
ru: Включить резервное копирование и восстановление
ms: Dayakan sandaran dan pemulihan
pt-br: Ativar backup e restauracao
required: true
type: select
values:
- label: "true"
value: "true"
- label: "false"
value: "false"
- default: info
edit: true
envKey: SNIPO_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: info
value: info
- label: warn
value: warn
- label: error
value: error
- label: debug
value: debug
- default: json
edit: true
envKey: SNIPO_LOG_FORMAT
labelEn: Log Format
labelZh: 日志格式
label:
en: Log Format
zh: 日志格式
zh-Hant: 日誌格式
ja: ログ形式
ko: 로그 형식
ru: Формат журнала
ms: Format log
pt-br: Formato de log
required: true
type: select
values:
- label: json
value: json
- label: text
value: text
- 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
+56
View File
@@ -0,0 +1,56 @@
services:
snipo:
image: "ghcr.io/mohamedelashri/snipo:latest"
container_name: ${CONTAINER_NAME}
restart: unless-stopped
user: "1000:1000"
networks:
- 1panel-network
ports:
- "${PANEL_APP_BIND_ADDRESS}:${PANEL_APP_PORT_HTTP}:8080"
environment:
- SNIPO_MASTER_PASSWORD=${SNIPO_MASTER_PASSWORD}
- SNIPO_SESSION_SECRET=${SNIPO_SESSION_SECRET}
- SNIPO_ENCRYPTION_SALT=${SNIPO_ENCRYPTION_SALT}
- SNIPO_SESSION_DURATION=${SNIPO_SESSION_DURATION}
- SNIPO_HOST=0.0.0.0
- SNIPO_PORT=8080
- SNIPO_TRUST_PROXY=${SNIPO_TRUST_PROXY}
- SNIPO_MAX_FILES_PER_SNIPPET=${SNIPO_MAX_FILES_PER_SNIPPET}
- SNIPO_DB_PATH=/data/snipo.db
- SNIPO_DB_MAX_CONNS=1
- SNIPO_DB_BUSY_TIMEOUT=5000
- SNIPO_DB_JOURNAL=WAL
- SNIPO_DB_SYNC=NORMAL
- SNIPO_DB_MMAP_SIZE=67108864
- SNIPO_DB_CACHE_SIZE=-1000
- SNIPO_RATE_LIMIT=100
- SNIPO_RATE_LIMIT_READ=1000
- SNIPO_RATE_LIMIT_WRITE=500
- SNIPO_RATE_LIMIT_ADMIN=100
- SNIPO_ENABLE_PUBLIC_SNIPPETS=${SNIPO_ENABLE_PUBLIC_SNIPPETS}
- SNIPO_ENABLE_API_TOKENS=${SNIPO_ENABLE_API_TOKENS}
- SNIPO_ENABLE_BACKUP_RESTORE=${SNIPO_ENABLE_BACKUP_RESTORE}
- SNIPO_LOG_LEVEL=${SNIPO_LOG_LEVEL}
- SNIPO_LOG_FORMAT=${SNIPO_LOG_FORMAT}
volumes:
- "${APP_DATA_DIR}:/data"
read_only: true
tmpfs:
- /tmp:rw,nosuid,nodev,noexec,size=16m,uid=1000,gid=1000,mode=1777
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "/snipo", "health"]
interval: 30s
timeout: 5s
retries: 8
start_period: 10s
labels:
createdBy: "Apps"
networks:
1panel-network:
external: true
+118
View File
@@ -0,0 +1,118 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
APP_ROOT_DIR="$(dirname "$ROOT_DIR")"
ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}"
fail() {
printf '%s\n' "$1" >&2
exit 1
}
strip_matching_quotes() {
local value="$1"
if [[ ${#value} -ge 2 ]]; then
if [[ "${value:0:1}" == '"' && "${value: -1}" == '"' ]]; then
value="${value:1:${#value}-2}"
elif [[ "${value:0:1}" == "'" && "${value: -1}" == "'" ]]; then
value="${value:1:${#value}-2}"
fi
fi
printf '%s\n' "$value"
}
read_env_value() {
local key="$1"
local value=""
if [[ -f "$ENV_FILE" ]]; then
value="$(grep -E "^${key}=" "$ENV_FILE" | tail -n 1 | cut -d '=' -f 2- || true)"
fi
value="${value%$'\r'}"
strip_matching_quotes "$value"
}
set_env_value() {
local key="$1"
local value="$2"
local temp_file
temp_file="$(mktemp "${ENV_FILE}.tmp.XXXXXX")"
awk -v key="$key" -v value="$value" '
BEGIN { updated = 0 }
$0 ~ ("^" key "=") {
if (!updated) {
print key "=" value
updated = 1
}
next
}
{ print }
END {
if (!updated) print key "=" value
}
' "$ENV_FILE" >"$temp_file"
chmod 600 "$temp_file"
mv -f -- "$temp_file" "$ENV_FILE"
}
path_is_dotenv_safe() {
local value="$1"
case "$value" in
*$'\n'* | *$'\r'* | *\\* | *'$'* | *'#'* | *'"'* | *"'"*) return 1 ;;
*) return 0 ;;
esac
}
[[ -f "$ENV_FILE" ]] || fail "Environment file not found: ${ENV_FILE}"
[[ ! -L "$ENV_FILE" ]] || fail "Environment file must not be a symbolic link"
[[ "$(id -u)" -eq 0 ]] || fail "Snipo init must run as root"
if [[ ${APP_DATA_DIR+x} ]]; then
DATA_DIR_RAW="$APP_DATA_DIR"
else
DATA_DIR_RAW="$(read_env_value APP_DATA_DIR)"
fi
DATA_DIR_RAW="$(strip_matching_quotes "${DATA_DIR_RAW:-./data}")"
[[ -n "$DATA_DIR_RAW" ]] || fail "APP_DATA_DIR must not be empty"
path_is_dotenv_safe "$DATA_DIR_RAW" || fail "APP_DATA_DIR contains unsupported dotenv characters"
if [[ "$DATA_DIR_RAW" = /* ]]; then
[[ ! -L "$DATA_DIR_RAW" ]] || fail "APP_DATA_DIR must not be a symbolic link"
DATA_DIR_ABS="$(realpath -m -- "$DATA_DIR_RAW")"
[[ "$DATA_DIR_ABS" != "/" ]] || fail "APP_DATA_DIR must not be the filesystem root"
else
CONTAINER_NAME_VALUE="$(read_env_value CONTAINER_NAME)"
[[ "$CONTAINER_NAME_VALUE" =~ ^[A-Za-z0-9._-]+$ ]] || \
fail "CONTAINER_NAME contains unsupported characters"
RETAINED_ROOT="${APP_ROOT_DIR}/retained-data"
[[ ! -L "$RETAINED_ROOT" ]] || fail "The retained data root must not be a symbolic link"
RETAINED_INSTANCE_ROOT="${RETAINED_ROOT}/${CONTAINER_NAME_VALUE}"
[[ ! -L "$RETAINED_INSTANCE_ROOT" ]] || fail "The retained instance root must not be a symbolic link"
DATA_DIR_PATH="${RETAINED_INSTANCE_ROOT}/${DATA_DIR_RAW#./}"
[[ ! -L "$DATA_DIR_PATH" ]] || fail "APP_DATA_DIR must not be a symbolic link"
RETAINED_INSTANCE_ABS="$(realpath -m -- "$RETAINED_INSTANCE_ROOT")"
DATA_DIR_ABS="$(realpath -m -- "$DATA_DIR_PATH")"
case "$DATA_DIR_ABS" in
"${RETAINED_INSTANCE_ABS}"/*) ;;
*) fail "Relative APP_DATA_DIR must remain inside the isolated retained-data directory" ;;
esac
set_env_value APP_DATA_DIR "\"${DATA_DIR_ABS}\""
fi
if [[ -e "$DATA_DIR_ABS" && ! -d "$DATA_DIR_ABS" ]]; then
fail "APP_DATA_DIR must be a directory"
fi
if [[ ! -e "$DATA_DIR_ABS" ]]; then
install -d -m 0750 -- "$DATA_DIR_ABS"
chown 1000:1000 -- "$DATA_DIR_ABS"
fi
[[ "$(stat -c '%u:%g' "$DATA_DIR_ABS")" == "1000:1000" ]] || \
fail "APP_DATA_DIR must be owned by UID/GID 1000:1000"
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
# Persistent data is intentionally retained for recovery or reinstallation.
exit 0
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
# Snipo applies embedded SQLite migrations when the new container starts.
exit 0
+9
View File
@@ -0,0 +1,9 @@
Snipo logo
Source: https://github.com/MohamedElashri/snipo/blob/c5eab947cce8728c2a431d2ed3426d452d14c3bf/assets/logo/icon-256.png
Source commit: c5eab947cce8728c2a431d2ed3426d452d14c3bf
Source SHA-256: dae9ce2b27caa1c1dbbc27669773d8f9d229a41f5ce2550f08f77c52c7331edb
License: GNU Affero General Public License v3.0 (AGPL-3.0-only)
License text: https://github.com/MohamedElashri/snipo/blob/c5eab947cce8728c2a431d2ed3426d452d14c3bf/LICENSE
The store asset is a proportional rendering of the official project icon.
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB