Artifact 639b58612e0a25ba8b009229cba283950afe5c7c81833fa1d5fba068c0187a6f:
- File
backend/src/swiftflow_backend.gleam
— part of check-in
[1821522f51]
at
2026-10-23 05:51:23
on branch trunk
— RBAC 权限与多仓数据隔离
- 新增用户、角色、权限点、用户角色、用户授权仓库、登录会话、审计日志表(008_rbac) - 预设五个角色(超级管理员、仓库主管、制单/调度员、打包复核员、库内作业员),按角色限制登录终端 - 账号密码登录,PBKDF2-SHA256 加盐存储,令牌只存哈希,会话按配置过期,连续输错锁定 - 首个超级管理员从 SWIFTFLOW_ADMIN_USERNAME / SWIFTFLOW_ADMIN_PASSWORD 创建,仅在用户表为空时生效 - 除健康检查与登录外所有接口需登录并校验权限点 - 订单、采购单、库位、库存、批次码按授权仓库过滤;收货、上架、出库只能在当前作业仓库进行 - 用户管理、角色查询、审计日志接口;同步、库位、上架、收货、出库写操作记录审计 (user: pluto size: 2951)
import access import app.{type Context, Context} import config import database import gleam/erlang/process import gleam/int import logging import mist import poller import router import schema import session import sync_lock import wisp import wisp/wisp_mist pub fn main() { logging.configure() wisp.configure_logger() config.load_dotenv() case config.load() { Error(message) -> halt(message) Ok(cfg) -> boot(cfg) } } fn boot(cfg: config.Config) -> Nil { case database.start(cfg) { Error(database.DbError(message)) -> halt(message) Ok(db) -> case schema.migrate(db) { Error(database.DbError(message)) -> halt(message) Ok(_) -> case session.start( cfg.ql361_username, cfg.ql361_password, cfg.ql361_base_url, cfg.request_gap_ms, ), sync_lock.start() { Error(_), _ -> halt("QL361 会话进程启动失败") _, Error(_) -> halt("同步锁进程启动失败") Ok(ql_session), Ok(lock) -> { let auth_cfg = config.load_auth() bootstrap_admin(db, auth_cfg) let ctx = Context( config: cfg, auth: auth_cfg, db: db, session: ql_session, lock: lock, ) let _ = poller.start(ctx) logging.log( logging.Info, "SwiftFlow 已启动,数据库 " <> driver_name(cfg) <> ",订单轮询 " <> int.to_string(cfg.order_poll_ms / 1000) <> " 秒,请求间隔 " <> int.to_string(cfg.request_gap_ms) <> " 毫秒", ) serve(ctx) } } } } } fn serve(ctx: Context) -> Nil { let secret_key_base = wisp.random_string(64) let handler = fn(req) { router.handle_request(req, ctx) } let assert Ok(_) = wisp_mist.handler(handler, secret_key_base) |> mist.new |> mist.port(ctx.config.http_port) |> mist.start process.sleep_forever() } fn bootstrap_admin(db: database.Database, auth_cfg: config.AuthConfig) -> Nil { case access.bootstrap_admin(db, auth_cfg) { Error(message) -> halt(message) Ok(access.AdminCreated(username)) -> logging.log(logging.Info, "已创建首个超级管理员 " <> username) Ok(access.UsersExist) -> Nil Ok(access.AdminNotConfigured) -> logging.log( logging.Warning, "还没有任何用户,且未配置 SWIFTFLOW_ADMIN_USERNAME / SWIFTFLOW_ADMIN_PASSWORD,暂时无人能登录", ) } } fn driver_name(cfg: config.Config) -> String { case cfg.db_driver { config.Sqlite -> "sqlite" config.Postgres -> "postgres" } } fn halt(message: String) -> Nil { logging.log(logging.Error, message) panic }