Skip to content
ruanbwPublic

About

每一次跳转,皆为裁决。 / Every jump is a verdict.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Janus

每一次跳转,皆为裁决。 Every jump is a verdict.

license go node postgres

中文 · English · 产品介绍页


这是什么

Janus 是一个自托管的多租户短链服务。租户注册后管理自己的域名与短链,系统为每个已激活域名自动签发并续期 HTTPS 证书,把 域名/短码 的访问重定向到目标 URL。

除跳转外还提供:落地页型短链、访问明细与地理统计、租户级访问处置规则引擎、自定义错误页,以及供脚本调用的 Bearer JWT API。

  • 单服务器部署 — Go(Gin + GORM)+ Vue 3 SPA(nginx)+ PostgreSQL 16 + Caddy(on-demand TLS),docker compose up -d 全部跑起来。
  • 多租户隔离 — 域名、短链、规则、落地页在租户之间完全隔离;部署者持有平台管理员角色。
  • 自带证书 — 平台域名配一条泛解析,每个租户自动获得 <slug>.<平台域名>;自有域名激活后由 Caddy 自动签发 Let's Encrypt 证书。
  • 前后端分离 — 前端独立构建为 nginx 镜像,后端镜像内无任何前端文件(ADR-0006)。
Internet → Caddy(80/443, on-demand TLS)
             ├── /            → nginx(前端 SPA)
             └── /api/*, /{code}  → Go 后端 → PostgreSQL 16

仓库结构

cmd/janus/            # 后端入口(加载配置 → 连库 → 初始化脚本 → worker → HTTP)
internal/             # httpapi(routes/handlers)、rules(规则引擎)、store、domain、geo、rbac…
migrations/init.sql   # 唯一 schema 事实来源,启动时自动幂等执行
web/                  # 前端 SPA(Vue 3 + Vite + Tailwind 4)
docker/               # Dockerfile(backend / web 两个目标)+ nginx.conf
docker-compose.yml    # 开发编排(Postgres + Caddy)
docker-compose.prod.yml, Caddyfile.prod  # 生产编排
docs/                 # deploy.md、testing.md、adr/

环境要求

工具 版本
Docker Desktop(含 Compose v2) 较新版本
Go ≥ 1.26
Node.js ≥ 20.19
pnpm ≥ 9(仓库锁定 10.x)
Caddy CLI ≥ 2.x,开发流程第 3 步信任本地 CA 需要(生产不需要)

开发环境基础设施进 Docker(Postgres、Caddy),后端与前端在终端跑,改代码即时生效。

快速开始(开发)

1. 启动基础设施

# 从模板生成 .env,并把必填的 on-demand TLS 密钥直接填进去
# 重定向会覆盖已有的 .env(数据库密码等手工改动会一起丢):重复执行前先备份
sed "s/^JANUS_CADDY_ASK_TOKEN=.*/JANUS_CADDY_ASK_TOKEN=$(openssl rand -hex 32)/" .env.example > .env
docker compose up -d    # postgres:5432、caddy:80/443

2. 本地域名解析

编辑 /etc/hosts(需要管理员 / sudo 权限):

127.0.0.1 app.janus.test
127.0.0.1 alice.janus.test      # 每注册一个租户加一行,hosts 不支持通配符

这些行只服务于浏览器与 Caddy:平台默认子域 <slug>.janus.test 建号即 active,不做 DNS 校验; 自有域名的激活校验走真实 DNS(实现刻意绕开 /etc/hosts),往 hosts 里加行无效,详见 docs/deploy.md 第 5 节。

3. 信任 Caddy 本地 CA

mkdir -p certs
docker compose exec caddy cat /data/caddy/pki/authorities/local/root.crt > certs/caddy-root.pem
sudo caddy trust --ca certs/caddy-root.pem

4. 启动后端

以下命令都在仓库根目录执行(JANUS_MIGRATIONS_DIR 默认是相对路径 migrations,换目录会读不到 init.sql):

# go run 不读 .env(那是 compose 的行为):set -a 把 .env 里的变量注入进程环境
set -a; . ./.env; set +a
JANUS_COOKIE_SECURE=false JANUS_ADDR=:8080 go run ./cmd/janus

这一步的 set -a; . ./.env 不能省:后端的 JANUS_CADDY_ASK_TOKEN 只从进程环境读, 而 Caddy 容器用的是 .env 里那一份。两边不一致时授权端点一律 403(fail-closed), 表现为 Caddy 一张证书都签不出来(https://app.janus.test/healthz 握手失败、租户域名全不可用), 后端日志只有一行 "JANUS_CADDY_ASK_TOKEN 未配置"。

也可以直接跑 ./run_backend.sh(做同样的事,并先 cd 到仓库根)。

5. 启动前端

corepack enable                        # 本机没有 pnpm 时:corepack 会按 packageManager 字段装上 10.x
cd web && pnpm install && pnpm dev     # http://localhost:5173,/api 代理到 :8080

6. 验证

curl -s  http://127.0.0.1:8080/healthz     # → {"status":"ok"}
curl -sk https://app.janus.test/healthz    # 经 Caddy 走通全链路

浏览器打开 http://localhost:5173 注册账号(账号 + 密码 + 域名前缀),注册即生效,不再需要邮箱验证。

Caddy 占用宿主 80/443,与本机其他绑定这两个端口的服务错开启动。

要验证平台管理端,用内置超管账号 superadmin / 123456 登录(启动时幂等创建)。 首次登录会被强制跳 /account 改密码,改完才能访问其它页面。

7. 清空数据库重来

改了 migrations/init.sql 之后要连数据一起重建(项目约定不写增量迁移):

docker compose down -v && docker compose up -d     # -v 删掉 pgdata 卷,数据全清

-v 会连 caddy_data 一起删掉,本地 CA 根证书随之重建:要重做第 3 步的 caddy trust, 否则浏览器会对 https://*.janus.test 报证书不受信任。

配置

全部配置走环境变量,模板与逐项说明见 .env.example,默认值可直接用于开发。常用项:

变量 开发默认值 说明
JANUS_ADDR :8080 HTTP 监听地址
JANUS_PLATFORM_DOMAIN janus.test 平台裸域名,租户默认域名 <slug>.<平台域名>
JANUS_SERVER_PUBLIC_IP 127.0.0.1 自有域名 DNS 激活校验的比对地址
JANUS_DATABASE_URL postgres://janus:janus@localhost:5432/janus?sslmode=disable 后端连接串
JANUS_COOKIE_SECURE false 会话 cookie Secure 标记,http 开发必须 false
JANUS_JWT_SECRET 空 JWT 签名密钥;留空则每次启动随机生成(重启后 token 失效)
JANUS_CADDY_ASK_TOKEN 空(必填) on-demand TLS 授权端点共享密钥,compose 与后端同值注入
JANUS_DNS_RETRY_INTERVAL / JANUS_DNS_MAX_AGE 5m / 72h 域名激活重试间隔 / 最长等待
JANUS_VISIT_RETENTION 2160h 访问记录保留时长(90 天)

.env 含数据库密码等敏感配置,已在 .gitignore 中。

平台管理员是内置账号 superadmin / 123456,启动时幂等创建,首次登录强制改密;不需要配置项。

生产部署

完整步骤与上线检查清单见 docs/deploy.md。最小流程:

cp .env.example .env && chmod 600 .env

# 下面几项留空会让 docker compose 直接报错退出(必填),先在 .env 里填好:
#   JANUS_PLATFORM_DOMAIN     裸平台域名,如 example.com
#   JANUS_SERVER_PUBLIC_IP    服务器公网 IP
#   JANUS_DB_PASSWORD         别留模板里的 janus
#   JANUS_TRUSTED_PROXY_CIDRS 172.16.0.0/12(Docker 默认网桥段)
#   JANUS_CADDY_ASK_TOKEN     openssl rand -hex 32
# 另外建议同时设 JANUS_JWT_SECRET(openssl rand -hex 32);超管是内置的 superadmin / 123456,无需配置

# Caddyfile.prod 的站点地址不支持环境变量:把 example.com 换成你的平台域名(与 .env 的 JANUS_PLATFORM_DOMAIN 一致)。
# -i.bak 在 macOS(BSD sed)与 Linux(GNU sed)下都可用,故随后删掉备份文件。
sed -i.bak 's/example\.com/your-domain.com/g' Caddyfile.prod && rm Caddyfile.prod.bak
docker compose -f docker-compose.prod.yml up -d --build

前置条件是 DNS:平台裸域名与泛解析 *.<平台域名> 均指向服务器公网 IP。首次启动自动执行初始化脚本、创建免费档种子、初始化超管;证书按需签发。

用预构建镜像代替源码构建(可选)

每个 v* 标签会产出两个镜像(ghcr.io/ruanbw/janus-backend / ghcr.io/ruanbw/janus-web)。 不想在服务器上装 Go 与 Node 时,叠加覆盖文件、加 --no-build 即可:

JANUS_VERSION=1.0.0 docker compose \
  -f docker-compose.prod.yml -f docker-compose.images.yml up -d --no-build

--no-build 不能省(两份文件合并后 service 上仍有 build:,否则会先构建再打标签)。 一个已知限制:注册页的域名提示是构建期烘进前端的,发布镜像用中性默认值;登录后的平台域名由后端下发。 要让注册页显示你自己的域名,需从源码构建。

首次上线后还有三件事不在这一段里:超管首登(superadmin / 123456,登录后强制改密,见 docs/deploy.md 第 3 节)、升级与回滚、备份(尤其 caddy_data 卷,丢了要重签全部证书)——分别见 docs/deploy.md 第 3、8、9 节。

开发约定

  • 提交前:后端 gofmt -l internal cmd、go vet ./...、定向 go test -count=1 ./internal/<pkg>/;前端 pnpm type-check + pnpm build + pnpm check:ui。
  • 测试即规格:需求以 Given-When-Then 测试沉淀,日常只跑被改动包的定向用例,不跑 go test ./...。详见 docs/testing.md。
  • 数据库:migrations/init.sql 是唯一 schema 事实来源,不写增量迁移;改表直接改脚本并 DROP 开发库重建。
  • 新增受保护路由:必须同步 internal/rbac/rbac.go 的 policyText,漏了会被 403。
  • 架构取舍:写 docs/adr/NNNN-*.md(背景 → 权衡 → 后果)。

文档

文档 内容
docs/deploy.md 生产部署指南与上线检查清单
docs/testing.md 测试流程与分层验证
web/README.md 前端技术栈、页面清单、请求约定
web/UI_KIT.md 前端组件契约与三层设计令牌
docs/adr/ 架构决策记录

许可证

GNU Affero General Public License v3.0(AGPL-3.0),全文见 LICENSE。

Copyright (C) 2026 ruanbw and Janus contributors
SPDX-License-Identifier: AGPL-3.0-only

可自由使用、修改、再分发,包括闭源商用;但通过网络向用户提供本程序功能(部署成在线服务)时,必须向这些用户提供对应源码。商业授权 / 闭源分发需求请单独联系作者。

About

每一次跳转,皆为裁决。 / Every jump is a verdict.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages