From 8856b5a7a84fc058df5abc49c251245fbc21e25d Mon Sep 17 00:00:00 2001 From: ms Date: Thu, 4 Dec 2025 13:13:52 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=91=E5=B8=83=20Glean?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/glean/0.1.4/data.yml | 53 +++++++++++ apps/glean/0.1.4/docker-compose.yml | 130 ++++++++++++++++++++++++++ apps/glean/0.1.4/envs/default.env | 2 + apps/glean/0.1.4/envs/global.env | 2 + apps/glean/0.1.4/scripts/init.sh | 17 ++++ apps/glean/0.1.4/scripts/uninstall.sh | 10 ++ apps/glean/0.1.4/scripts/upgrade.sh | 17 ++++ apps/glean/README.md | 75 +++++++++++++++ apps/glean/data.yml | 14 +++ apps/glean/logo.png | Bin 0 -> 11652 bytes 10 files changed, 320 insertions(+) create mode 100644 apps/glean/0.1.4/data.yml create mode 100644 apps/glean/0.1.4/docker-compose.yml create mode 100644 apps/glean/0.1.4/envs/default.env create mode 100644 apps/glean/0.1.4/envs/global.env create mode 100644 apps/glean/0.1.4/scripts/init.sh create mode 100644 apps/glean/0.1.4/scripts/uninstall.sh create mode 100644 apps/glean/0.1.4/scripts/upgrade.sh create mode 100644 apps/glean/README.md create mode 100644 apps/glean/data.yml create mode 100644 apps/glean/logo.png diff --git a/apps/glean/0.1.4/data.yml b/apps/glean/0.1.4/data.yml new file mode 100644 index 000000000..699f4dd3d --- /dev/null +++ b/apps/glean/0.1.4/data.yml @@ -0,0 +1,53 @@ +additionalProperties: + formFields: + - default: "/home/glean" + edit: true + envKey: GLEAN_ROOT_PATH + labelZh: 数据持久化路径 + labelEn: Data persistence path + required: true + type: text + - default: 8080 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelZh: WebUI 端口 + labelEn: WebUI port + required: true + rule: paramPort + type: number + - default: 3001 + edit: true + envKey: PANEL_APP_PORT_ADMIN + labelZh: WebUI 后台端口 + labelEn: WebUI Admin Port + required: true + rule: paramPort + type: number + - default: "admin" + edit: true + envKey: ADMIN_USERNAME + labelZh: 管理员用户名 + labelEn: Admin username + required: true + type: text + - default: "" + edit: true + envKey: ADMIN_PASSWORD + labelZh: 管理员密码 + labelEn: Admin password + required: true + type: password + - default: "" + edit: true + envKey: SECRET_KEY + labelZh: 签名密钥 + labelEn: Signature key + required: true + type: password + - default: "glean" + edit: true + envKey: POSTGRES_PASSWORD + labelZh: 数据库密码 + labelEn: Postgres password + required: true + type: text diff --git a/apps/glean/0.1.4/docker-compose.yml b/apps/glean/0.1.4/docker-compose.yml new file mode 100644 index 000000000..322f0ba6b --- /dev/null +++ b/apps/glean/0.1.4/docker-compose.yml @@ -0,0 +1,130 @@ +networks: + 1panel-network: + external: true + glean-network: + driver: bridge + +services: + glean-postgres: + image: postgres:16-alpine + container_name: ${CONTAINER_NAME}-postgres + restart: unless-stopped + volumes: + - ${GLEAN_ROOT_PATH}/postgres:/var/lib/postgresql/data + networks: + - glean-network + env_file: + - ${GLOBAL_ENV_FILE:-/etc/1panel/envs/global.env} + - ${ENV_FILE:-/etc/1panel/envs/default.env} + healthcheck: + test: [ "CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-glean}" ] + interval: 10s + timeout: 5s + retries: 5 + environment: + POSTGRES_DB: ${POSTGRES_DB:-glean} + POSTGRES_USER: ${POSTGRES_USER:-glean} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-glean} + + glean-redis: + image: redis:7-alpine + container_name: ${CONTAINER_NAME}-redis + command: redis-server --appendonly yes + restart: unless-stopped + volumes: + - ${GLEAN_ROOT_PATH}/redis:/data + networks: + - glean-network + env_file: + - ${GLOBAL_ENV_FILE:-/etc/1panel/envs/global.env} + - ${ENV_FILE:-/etc/1panel/envs/default.env} + healthcheck: + test: [ "CMD", "redis-cli", "ping" ] + interval: 10s + timeout: 5s + retries: 5 + + glean-backend: + depends_on: + glean-postgres: + condition: service_healthy + glean-redis: + condition: service_healthy + image: ghcr.io/leslieleung/glean-backend:0.1.4 + container_name: ${CONTAINER_NAME} + labels: + createdBy: "Apps" + environment: + DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-glean}:${POSTGRES_PASSWORD:-glean}@postgres:5432/${POSTGRES_DB:-glean} + REDIS_URL: redis://redis:6379/0 + SECRET_KEY: ${SECRET_KEY:-change-me-in-production-use-a-long-random-string} + CORS_ORIGINS: '["http://localhost", "http://localhost:3000", "http://localhost:3001"]' + DEBUG: ${DEBUG:-false} + RUN_MIGRATIONS: "true" + CREATE_ADMIN: ${CREATE_ADMIN:-true} + ADMIN_USERNAME: ${ADMIN_USERNAME:-admin} + ADMIN_PASSWORD: ${ADMIN_PASSWORD:-} + ADMIN_ROLE: ${ADMIN_ROLE:-super_admin} + restart: unless-stopped + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:8000/api/health" ] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + networks: + - glean-network + env_file: + - ${GLOBAL_ENV_FILE:-/etc/1panel/envs/global.env} + - ${ENV_FILE:-/etc/1panel/envs/default.env} + command: [ "uv", "run", "--no-sync", "uvicorn", "glean_api.main:app", "--host", "0.0.0.0", "--port", "8000" ] + + glean-worker: + depends_on: + glean-postgres: + condition: service_healthy + glean-redis: + condition: service_healthy + glean-backend: + condition: service_healthy + image: ghcr.io/leslieleung/glean-backend:0.1.4 + container_name: ${CONTAINER_NAME}-worker + environment: + DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-glean}:${POSTGRES_PASSWORD:-glean}@postgres:5432/${POSTGRES_DB:-glean} + REDIS_URL: redis://redis:6379/0 + SECRET_KEY: ${SECRET_KEY:-} + networks: + - glean-network + env_file: + - ${GLOBAL_ENV_FILE:-/etc/1panel/envs/global.env} + - ${ENV_FILE:-/etc/1panel/envs/default.env} + restart: unless-stopped + command: [ "uv", "run", "--no-sync", "arq", "glean_worker.main.WorkerSettings" ] + + glean-web: + depends_on: + - glean-backend + image: ghcr.io/leslieleung/glean-web:0.1.4 + container_name: ${CONTAINER_NAME}-web + ports: + - "${PANEL_APP_PORT_HTTP:-80}:80" + networks: + - glean-network + restart: unless-stopped + env_file: + - ${GLOBAL_ENV_FILE:-/etc/1panel/envs/global.env} + - ${ENV_FILE:-/etc/1panel/envs/default.env} + + glean-admin: + depends_on: + - glean-backend + image: ghcr.io/leslieleung/glean-admin:0.1.4 + container_name: ${CONTAINER_NAME}-admin + ports: + - "${PANEL_APP_PORT_ADMIN:-3001}:80" + networks: + - glean-network + restart: unless-stopped + env_file: + - ${GLOBAL_ENV_FILE:-/etc/1panel/envs/global.env} + - ${ENV_FILE:-/etc/1panel/envs/default.env} diff --git a/apps/glean/0.1.4/envs/default.env b/apps/glean/0.1.4/envs/default.env new file mode 100644 index 000000000..cd05f46e6 --- /dev/null +++ b/apps/glean/0.1.4/envs/default.env @@ -0,0 +1,2 @@ +# copyright© 2024 XinJiang Ms Studio +ENV_FILE=.env diff --git a/apps/glean/0.1.4/envs/global.env b/apps/glean/0.1.4/envs/global.env new file mode 100644 index 000000000..e10989fe4 --- /dev/null +++ b/apps/glean/0.1.4/envs/global.env @@ -0,0 +1,2 @@ +# copyright© 2024 XinJiang Ms Studio +TZ=Asia/Shanghai diff --git a/apps/glean/0.1.4/scripts/init.sh b/apps/glean/0.1.4/scripts/init.sh new file mode 100644 index 000000000..07fb8c3fe --- /dev/null +++ b/apps/glean/0.1.4/scripts/init.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +if [ -f .env ]; then + source .env + + # setup-1 add default values + CURRENT_DIR=$(pwd) + sed -i '/^ENV_FILE=/d' .env + sed -i '/^GLOBAL_ENV_FILE=/d' .env + echo "ENV_FILE=${CURRENT_DIR}/.env" >> .env + echo "GLOBAL_ENV_FILE=${CURRENT_DIR}/envs/global.env" >> .env + + echo "Check Finish." + +else + echo "Error: .env file not found." +fi diff --git a/apps/glean/0.1.4/scripts/uninstall.sh b/apps/glean/0.1.4/scripts/uninstall.sh new file mode 100644 index 000000000..c86c4fbca --- /dev/null +++ b/apps/glean/0.1.4/scripts/uninstall.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +if [ -f .env ]; then + source .env + + echo "Check Finish." + +else + echo "Error: .env file not found." +fi diff --git a/apps/glean/0.1.4/scripts/upgrade.sh b/apps/glean/0.1.4/scripts/upgrade.sh new file mode 100644 index 000000000..07fb8c3fe --- /dev/null +++ b/apps/glean/0.1.4/scripts/upgrade.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +if [ -f .env ]; then + source .env + + # setup-1 add default values + CURRENT_DIR=$(pwd) + sed -i '/^ENV_FILE=/d' .env + sed -i '/^GLOBAL_ENV_FILE=/d' .env + echo "ENV_FILE=${CURRENT_DIR}/.env" >> .env + echo "GLOBAL_ENV_FILE=${CURRENT_DIR}/envs/global.env" >> .env + + echo "Check Finish." + +else + echo "Error: .env file not found." +fi diff --git a/apps/glean/README.md b/apps/glean/README.md new file mode 100644 index 000000000..a18778de4 --- /dev/null +++ b/apps/glean/README.md @@ -0,0 +1,75 @@ +# Glean 拾灵 + +简短的介绍 + +![Glean 拾灵](https://file.lifebus.top/imgs/glean_cover.png) + +![](https://img.shields.io/badge/%E6%96%B0%E7%96%86%E8%90%8C%E6%A3%AE%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E5%B7%A5%E4%BD%9C%E5%AE%A4-%E6%8F%90%E4%BE%9B%E6%8A%80%E6%9C%AF%E6%94%AF%E6%8C%81-blue) + +## 核心功能 + +### 📰 RSS 订阅 + +订阅和管理 RSS/Atom 源,支持 OPML 导入导出 + +### 📚 智能阅读 + +简洁的阅读体验,支持内容过滤 + +### 🔖 稍后阅读 + +保存文章以便稍后阅读,支持自动清理 + +### 📁 文件夹与标签 + +多层级文件夹和标签组织内容 + +### ⭐ 收藏系统 + +收藏订阅文章或外部链接 + +### 🔧 后台同步 + +每 15 分钟自动更新订阅源 + +### 🔒 自托管 + +Docker 部署,完全掌控数据 + +### 🎨 现代界面 + +美观的暖色深色主题响应式界面 + +### 👨‍💼 管理后台 + +用户管理和系统监控 + +## 规划中的功能(WIP) + +### 🧠 智能推荐 + +基于 AI 的偏好学习和文章评分 + +### ⚙️ 规则引擎 + +支持 Jinja2 风格条件的自动化处理 + +### 🤖 AI 功能 + +摘要生成、自动打标、关键词提取(BYOK) + +### 📄 完整内容获取 + +为仅提供摘要的 RSS 源获取完整正文 + +### 🔌 Chrome 扩展 + +浏览器一键收藏 + +### 📱 移动端 PWA + +适配移动设备的渐进式 Web 应用 + +--- + +![Ms Studio](https://file.lifebus.top/imgs/ms_blank_001.png) diff --git a/apps/glean/data.yml b/apps/glean/data.yml new file mode 100644 index 000000000..1a576bc6b --- /dev/null +++ b/apps/glean/data.yml @@ -0,0 +1,14 @@ +additionalProperties: + key: glean + name: Glean 拾灵 + tags: + - WebSite + - Local + shortDescZh: 一个自托管的 RSS 阅读器和个人知识管理工具。 + shortDescEn: A self-hosted RSS reader and personal knowledge management tool. + type: website + crossVersionUpdate: true + limit: 0 + website: https://github.com/LeslieLeung/glean/ + github: https://github.com/LeslieLeung/glean/ + document: https://github.com/LeslieLeung/glean/ diff --git a/apps/glean/logo.png b/apps/glean/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2512e2b9c51ec76297a259267e8856d202b6695e GIT binary patch literal 11652 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 literal 0 HcmV?d00001