2026-08-11 19:57:21 +08:00
2026-08-11 19:57:21 +08:00
2026-08-11 19:57:21 +08:00
2026-08-11 19:57:21 +08:00
2026-08-06 11:11:11 +08:00
2026-08-06 11:11:11 +08:00
2026-08-06 11:11:11 +08:00
2026-08-06 11:11:11 +08:00
2026-08-06 11:11:11 +08:00
2026-08-06 11:11:11 +08:00
2026-08-11 19:57:21 +08:00
2026-08-06 11:11:11 +08:00
2026-08-06 11:11:11 +08:00
2026-08-06 11:11:11 +08:00

Dashboard - 多人副屏系统

License: MIT


项目简介

多用户副屏系统,支持日历、天气、闹钟、番茄钟、倒计时等功能。每个用户拥有完全隔离的数据与独立的显示端/控制端:

  • 显示端 (/) — 大屏/触屏设备展示信息
  • 控制端 (/control) — 完整配置面板
  • Root 管理员 (id=0) — 首个自动创建的账户,拥有全部功能与用户管理权限

功能

  • 多用户:Root 管理员可注册/编辑/删除用户;所有用户数据完全隔离,普通用户无管理权限
  • 两种注册方式:管理员代注册(正式账户,可代配 TOTP/Passkey);用户自助申请(管理员确认后生成临时账户)
  • 临时账户:服务器持有临时 TOTP 密钥,管理员仅可见实时验证码;用户登录后被强制引导配置自己的登录方式,配置完成即正式生效
  • 日历:支持订阅 iCalendar (.ics) 链接及本地文件上传,自动同步日程
  • 天气:基于和风天气 (QWeather) API,含逐小时/分钟级预报
  • 闹钟:多组闹钟设定,自动弹出提醒,支持自定义铃声
  • 番茄钟:定时提醒,自动继续,断网重连自动恢复
  • 倒计时:可自定义时长,到时提醒
  • 自定义展示页:支持多组自定义 HTML 模板,一键切换

🔐 安全特性

支持双因素认证 (MFA),适合公网部署:

方式 说明
Passkey (WebAuthn) 指纹、面容、安全密钥等设备内置生物/硬件认证
TOTP Google Authenticator / Microsoft Authenticator 等标准应用
一次性恢复码 8 个恢复码,每个仅可使用一次
临时密码 首次启动为 Root 自动生成 6 位随机密码,显示在控制台

登录流程:

首次启动(无数据库)→ 自动创建 Root 管理员 (id=0),控制台打印临时密码
     ↓
访问 /control → 输入 6 位数字(自动识别 TOTP / 临时账户 TOTP / Root 临时密码)
     ↓
登录成功 → 签发 access token(前端持有)+ refresh token(httpOnly cookie)
     ↓
Root 临时密码 → 强制跳转 /mfa-setup 配置 MFA
正式用户 TOTP/Passkey → 直接进入控制面板
临时账户 TOTP → 强制跳转 /mfa-setup 配置登录方式,配置完成后临时 TOTP 立即失效

会话管理:

  • 控制端「设置 → 踢掉当前账号的所有设备」:所有登录该账号的设备(控制端 + 显示端,含当前设备)立即下线,所有登录用户可用
  • Root「用户管理 → 踢掉所有用户」:所有用户的控制端与显示端全部下线(含 Root 自身),需二次验证

用户管理(仅 Root):

  • 管理员代注册:填写信息 → 正式账户,可随后代配置 TOTP(生成密钥+二维码)或 Passkey(注册仪式在当前浏览器完成)
  • 用户自助申请:用户在 /register-request 填写信息 → 管理员确认 → 生成临时账户(服务器持有临时 TOTP 密钥,管理员仅看到实时验证码)
  • 注册/编辑备注/删除用户均需二次验证管理员身份(Passkey 优先,无 Passkey 时用 TOTP)
  • 删除用户会级联删除其全部数据并使其会话立即失效
  • 用户 ID 对用户透明(仅在备注默认为 ID 时可见一次),普通用户只能看到自己的数据

安全防护:

  • 登录频率限制(渐进式封禁:5次→5分钟、10次→15分钟、15次→24小时)
  • CSRF 防护(Origin/Referer 校验)
  • 敏感操作二次验证(用户管理、MFA 配置需重新确认身份)
  • 双 Token 认证:短期 access token(Authorization: Bearer,15 分钟)+ 长期 refresh token(httpOnly cookie,30 天滑动续期、每次刷新轮换)
  • Access token 快照会话版本:删除用户 / 踢设备 / 踢所有用户后立即使全部 access token 失效
  • 显示端 SSE 通过一次性短时 ticket 鉴权(EventSource 无法带自定义 header)
  • 内容安全策略(CSP)限制脚本/样式来源
  • 铃声上传仅允许音频格式+MIME 校验
  • 第三方日历数据 HTML 转义防止 XSS

快速开始

开发环境

# 1. 安装依赖
pip install -r requirements.txt

# 2. 直接运行(调试模式)
python start.py

# 3. 访问
#    显示端: http://localhost:5000
#    控制端: http://localhost:5000/control

生产部署 (推荐 Nginx + Certbot)

客户端 (HTTPS)
    │
    ▼ 443
Nginx (SSL termination + Certbot)
    │  proxy_pass http://127.0.0.1:5000
    │
    ▼ 5000 (HTTP)
Flask (127.0.0.1:5000)
    │
    ▼
data.db (SQLite)

1. Nginx 配置

server {
    listen 443 ssl;
    server_name your-domain.com;

    ssl_certificate     /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    # 可选:限制上传大小
    client_max_body_size 5M;
}

# HTTP → HTTPS 强制跳转
server {
    listen 80;
    server_name your-domain.com;
    return 301 https://$host$request_uri;
}

2. 启动 Flask

# 生产模式启动
FLASK_HTTPS=yes python start.py

# 可选:固定 SECRET_KEY(保持跨重启会话有效)
export FLASK_SECRET_KEY="$(openssl rand -hex 32)"

3. Certbot 申请证书

sudo certbot --nginx -d your-domain.com


Docker 部署

快速启动

# 构建并启动(后台运行,首次会自动从 Gitee clone 代码)
docker compose up -d

# 查看启动日志(获取临时密码)
docker compose logs app

# 停止
docker compose down

访问 http://你的IP:5000,日志中会打印临时密码。

更新代码

# 方法一:进入容器内 git pull(推荐)
docker compose exec app git pull
docker compose restart app

# 方法二:重建镜像(较慢)
docker compose down
docker compose build --no-cache
docker compose up -d

环境变量配置

编辑 docker-compose.yml 的 environment 段:

environment:
  - FLASK_BIND=0.0.0.0
  - FLASK_SECRET_KEY=your-secret-here    # 可选:固定密钥
  - FLASK_HTTPS=yes                      # 如果前面有 HTTPS 反代

持久化数据

  • 数据库:./data/ 目录挂载到 /app/data/,数据库文件 data.db 自动在此创建
  • 项目代码:命名卷 app_code 挂载到 /app,首次自动初始化为镜像内克隆的代码,之后 git pull 的修改永久保留数据库文件 data.db 会自动在该目录下创建,重启/升级后数据保留。
# 数据文件位置
ls -la data/
# data.db  ← 数据库

搭配 Nginx 反代(生产)

使用宿主机上的 Nginx(或额外 Nginx 容器)做 SSL 终结。配置与前文 Nginx 部分相同,只需将 proxy_pass 指向 Docker 宿主机 IP:5000:

location / {
    proxy_pass http://宿主机IP:5000;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $host;
}

⚠️ 注意:容器内 Flask 绑定 0.0.0.0:5000,宿主机可直接访问 localhost:5000。如需限制仅 Nginx 访问,在 docker-compose.yml 中将 ports 改为 127.0.0.1:5000:5000。


环境变量

变量 默认值 说明
FLASK_HTTPS 空 设为 yes/true 启用 refresh token cookie 的 Secure 属性(Nginx 反代时启用)
FLASK_SECRET_KEY 自动随机生成 保留兼容;认证已由 access/refresh 双 token 承载,不再依赖 Flask session
FLASK_BIND 127.0.0.1 监听地址,Docker 部署需设为 0.0.0.0
FLASK_DB_PATH ./data.db 数据库文件路径,Docker 挂载数据卷时需同步修改
FLASK_DEBUG 空 设为 true 开启 Flask 调试模式(生产环境禁用)

文件结构

├── run.py                  # 入口(端口 5001,仅开发)
├── start.py                # 入口(端口 5000,推荐生产使用)
├── requirements.txt
├── backend/
│   ├── app.py              # Flask 应用工厂 + 安全配置
│   ├── auth.py             # 双 Token 认证(TOTP/Passkey/恢复码/临时密码)
│   ├── webauthn_mgr.py     # WebAuthn (Passkey) 注册与认证
│   ├── jwt_util.py         # Ed25519 JWT 签名(和风天气)
│   ├── models.py           # SQLite 模型、schema 版本迁移、token 管理
│   ├── routes.py           # 全部 API 路由 + CSRF 防护 + SSE ticket
│   └── *_mgr.py            # 各功能模块管理器
└── frontend/
    ├── templates/           # HTML 模板
    └── static/
        ├── css/             # 样式
        ├── js/              # JavaScript(纯原生,无框架)
        │   ├── token.js     # 双 Token 客户端(access 存储/刷新/401 重试/SSE ticket)
        │   ├── control.js   # 控制端逻辑
        │   └── display.js   # 显示端逻辑(账户时区渲染)
        └── ringtones/       # 自定义铃声

数据库迁移(版本化,升级无需重写数据)

数据库内置 meta 表记录 schema 版本(程序内 DB_VERSION,当前为 1)。每次启动时比对:

情况 行为
数据库版本 == 程序版本 直接使用,跳过重建
无版本字段(旧版数据库)或 数据库版本 < 程序版本 重建数据库并迁移(旧版单用户数据自动归属 Root (id=0),新增字段一律用 DEFAULT 填充,camera_config 等已删除的表自动丢弃)
数据库版本 > 程序版本 报错并终止启动(数据库比程序新,需先升级程序)

旧版 4 位 PIN 不会被迁移。运行测试:python3 tests/test_multi_user.py。

注意

  • Passkey (WebAuthn) 需要 HTTPS(localhost 除外)
  • 旧版 4 位 PIN 已弃用,数据库迁移时旧 PIN 不会被迁移

License

MIT

S
Description
No description provided
Readme MIT
606 KiB
Languages
Python 53.3%
JavaScript 23.5%
HTML 14.6%
CSS 5.8%
Swift 2.6%
Other 0.2%