Documentation
¶
Overview ¶
Package install 实现首次安装向导:连接测试、安装执行与状态查询。
站点还没有数据库连接信息时 serve 进入安装模式,只挂本包的路由与 Console 静态资源, 其余一律不装配。安装成功后会把连接串写进工作目录的 install.json(见 config 包), 服务随即重新初始化一次,这一次带着数据库,就是正常的站点。
本包不认识模块清单,也不认识设置模块:迁移、初始管理员与站点设置由调用方经 Provision 注入(见 cmd/lumo/install.go),安装包只负责编排顺序与守住安装的前置条件。
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrAlreadyInstalled = errors.New("站点已完成安装")
ErrAlreadyInstalled 表示站点已经装好了,安装入口一律拒绝。
Functions ¶
func WrapBadRequest ¶
WrapBadRequest 把调用方(注入的 Provision)返回的错误标记成站长可自行修正的, 处理器据此原样回显而不是当成内部故障。
安装阶段的失败几乎都属于这一类:数据库权限不足、参数写错、口令不合规。
Types ¶
type AdminInput ¶
type AdminInput struct {
Username string `` /* 126-byte string literal not displayed */
Email string `json:"email" format:"email" maxLength:"254"`
Password string `json:"password" minLength:"8" maxLength:"128"`
}
AdminInput 是初始管理员账号。
用户名与口令的规则照抄数据库层的约束(migrations/00002_auth.sql 的 CHECK 与 auth/password 的长度限制):向导早一步拦住,比让站长在最后一步收到一条 「违反约束 users_username_format」要好得多。
type ApplyInput ¶
type ApplyInput struct {
Database DatabaseInput `json:"database"`
Site SiteInput `json:"site"`
Admin AdminInput `json:"admin"`
}
ApplyInput 是执行安装所需的全部输入。
type ApplyResult ¶
type ApplyResult struct {
Steps []Step `json:"steps"`
// ExternalURL 是站长填的对外地址,可能为空。
ExternalURL string `json:"externalUrl,omitempty"`
// AdminCreated 为假表示库里已有用户,未创建新的管理员。
AdminCreated bool `json:"adminCreated"`
}
ApplyResult 是安装执行的结果。
type BadRequestError ¶
type BadRequestError struct {
// contains filtered or unexported fields
}
BadRequestError 标记「按提示改输入就能解决」的错误。
处理器靠它把这些消息原样回给向导(400),而把其余错误当内部故障处理(500, 细节只进日志)——数据库口令写错和磁盘写不进去,对站长是两件完全不同的事。
func (*BadRequestError) Error ¶
func (e *BadRequestError) Error() string
func (*BadRequestError) Unwrap ¶
func (e *BadRequestError) Unwrap() error
type Check ¶
type Check struct {
Key string `json:"key"`
Label string `json:"label"`
OK bool `json:"ok"`
Detail string `json:"detail"`
}
Check 是一项安装前置检查的结果。
type DatabaseInfo ¶
type DatabaseInfo struct {
ServerVersion string `json:"serverVersion"`
Database string `json:"database"`
User string `json:"user"`
Encoding string `json:"encoding"`
LatencyMS int64 `json:"latencyMs"`
// ExistingUsers 是目标库里已有的用户数;-1 表示该库还没有 Lumo 的数据表。
// 大于 0 时向导要提醒站长:继续下去是接管一个已有站点,不是新建。
ExistingUsers int64 `json:"existingUsers"`
}
DatabaseInfo 是测试连接的结果。
type DatabaseInput ¶
type DatabaseInput struct {
DSN string `json:"dsn,omitempty" doc:"完整连接串;填了就忽略下面各项"`
Host string `json:"host,omitempty" doc:"主机地址,例如 127.0.0.1"`
Port int `json:"port,omitempty" doc:"端口,默认 5432" minimum:"1" maximum:"65535"`
User string `json:"user,omitempty" doc:"用户名"`
Password string `json:"password,omitempty" doc:"口令"`
Name string `json:"name,omitempty" doc:"数据库名"`
SSLMode string `json:"sslMode,omitempty" doc:"TLS 模式" enum:"disable,prefer,require,verify-ca,verify-full"`
}
DatabaseInput 是数据库连接信息。填了 DSN 就忽略逐项字段。
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler 提供安装向导的三个端点。
func NewHandler ¶
NewHandler 构造 Handler。
func (*Handler) Register ¶
Register 把安装端点挂到根 huma API(planes.API())上。
三平面都不合适:它们的解析凭据中间件要查库,而此刻库还不存在 —— 浏览器只要带着任何旧 Cookie 过来就会被挡成 401,向导第一步都走不到。
三个端点一律无条件注册,包括已经装好的站点:OpenAPI 规范要稳定。若按运行时状态 条件注册,`lumo openapi` 在不同库上会导出不同的规范,进而污染 Console 的生成类型。 「能不能装」放在处理器内部判断。
这些端点没有 CSRF 中间件。跨站表单发不出 application/json,而 huma 只解析 JSON 请求体,所以浏览器侧的跨站提交在这里自然失效;至于能直接访问服务器的攻击者, 他本来就能自己跑一遍安装向导,CSRF 拦不住也不增加风险。
type Options ¶
type Options struct {
// Config 是本次启动的配置(此时必然不含可用 DSN)。
Config config.Config
// DataDir 是已初始化的工作目录绝对路径。
DataDir string
Logger *slog.Logger
Version string
// Provision 由调用方注入。
Provision ProvisionFunc
}
Options 是构造 Service 的依赖。
type Params ¶
type Params struct {
DSN string
Site SiteInput
Admin AdminInput
}
Params 是交给调用方执行安装所需的全部输入。
type ProvisionFunc ¶
type ProvisionFunc func(ctx context.Context, db *database.DB, cfg config.Config, in Params) (ProvisionResult, error)
ProvisionFunc 在目标库上完成安装的实质工作:迁移、内置角色、初始管理员、站点设置。
由 cmd/lumo 注入:它拿得到模块清单与设置模块,而本包刻意不认识它们。
type ProvisionResult ¶
type ProvisionResult struct {
// AdminCreated 为假表示目标库里已有用户,故未创建新的管理员。
AdminCreated bool
}
ProvisionResult 是安装执行的结果。
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service 是安装向导的服务端逻辑。
func (*Service) Apply ¶
func (s *Service) Apply(ctx context.Context, in ApplyInput) (*ApplyResult, error)
Apply 执行安装:连库、迁移、建管理员、写站点设置,最后落盘安装产物。
顺序是有意的:安装产物写在最后一步。中途任何一步失败都不会留下这个文件, 于是下次启动仍然进入安装模式,站长可以改完参数重来。
func (*Service) TestDatabase ¶
func (s *Service) TestDatabase(ctx context.Context, in DatabaseInput) (*DatabaseInfo, error)
TestDatabase 试连目标库并回报它的身份。
type SiteInput ¶
type SiteInput struct {
Title string `json:"title,omitempty" maxLength:"100" doc:"站点名称"`
URL string `json:"url,omitempty" maxLength:"255" doc:"对外访问地址,例如 https://example.com"`
}
SiteInput 是站点信息。