142 lines
3.8 KiB
Go
142 lines
3.8 KiB
Go
// Package application composes the Go backend foundation Modules.
|
|
package application
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"os"
|
|
|
|
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/httpapi"
|
|
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/identity"
|
|
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/postgres"
|
|
)
|
|
|
|
type Options struct {
|
|
Context context.Context
|
|
Getenv postgres.Getenv
|
|
ReadFile postgres.ReadFile
|
|
// AuthorizationLoader is an optional Identity persistence Adapter used by
|
|
// composition tests and alternate runtime backends. Production defaults to
|
|
// the PostgreSQL Store opened below.
|
|
AuthorizationLoader identity.AuthorizationSnapshotLoader
|
|
// CredentialAuthenticator is the narrow Password Login persistence seam.
|
|
// Production defaults to the same PostgreSQL Store used for authorization.
|
|
CredentialAuthenticator identity.CredentialAuthenticator
|
|
}
|
|
|
|
type App struct {
|
|
handler http.Handler
|
|
db *postgres.Module
|
|
}
|
|
|
|
func New(options Options) (*App, error) {
|
|
ctx := options.Context
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
getenv := options.Getenv
|
|
if getenv == nil {
|
|
getenv = os.Getenv
|
|
}
|
|
readFile := options.ReadFile
|
|
if readFile == nil {
|
|
readFile = os.ReadFile
|
|
}
|
|
|
|
authConfig, err := ParseAuthConfig(getenv)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
config, err := postgres.ParseConfig(getenv, readFile)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
database, err := postgres.Open(ctx, config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
closeOnError := true
|
|
defer func() {
|
|
if closeOnError {
|
|
database.Close()
|
|
}
|
|
}()
|
|
readiness := databaseReadiness{config: config, store: database.Store}
|
|
var resolver httpapi.SessionResolver
|
|
if authConfig.Configured {
|
|
loader := options.AuthorizationLoader
|
|
if loader == nil {
|
|
loader = database.Store
|
|
}
|
|
resolver = identity.NewResolver(loader, authConfig.SessionSecret, "platform", nil)
|
|
}
|
|
authMe, err := httpapi.NewAuthMeHandler(httpapi.AuthState{
|
|
Required: authConfig.Required, Configured: authConfig.Configured,
|
|
}, resolver)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var passwordIssuer httpapi.PasswordSessionIssuer
|
|
if authConfig.Configured {
|
|
authenticator := options.CredentialAuthenticator
|
|
if authenticator == nil {
|
|
authenticator = database.Store
|
|
}
|
|
passwordIssuer = identity.NewPasswordLogin(authenticator, nil)
|
|
}
|
|
cookieSecure := getenv("ZHINIAN_AUTH_COOKIE_SECURE")
|
|
publicBaseURL := firstAuthEnv(getenv, "NEXT_PUBLIC_APP_URL", "ZHINIAN_PUBLIC_BASE_URL")
|
|
authPassword, err := httpapi.NewAuthPasswordHandler(httpapi.PasswordAuthConfig{
|
|
Configured: authConfig.Configured,
|
|
SessionSecret: authConfig.SessionSecret,
|
|
CookieSecure: cookieSecure,
|
|
PublicBaseURL: publicBaseURL,
|
|
}, passwordIssuer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
authLogout := httpapi.NewAuthLogoutHandler(httpapi.LogoutConfig{
|
|
CookieSecure: cookieSecure,
|
|
PublicBaseURL: publicBaseURL,
|
|
})
|
|
foundation := httpapi.NewHandler(readiness)
|
|
mux := http.NewServeMux()
|
|
mux.Handle("/api/auth/me", authMe)
|
|
mux.Handle("/api/auth/password", authPassword)
|
|
mux.Handle("/api/auth/logout", authLogout)
|
|
mux.Handle("/", foundation)
|
|
closeOnError = false
|
|
return &App{
|
|
db: database,
|
|
handler: mux,
|
|
}, nil
|
|
}
|
|
|
|
func (app *App) Handler() http.Handler {
|
|
return app.handler
|
|
}
|
|
|
|
func (app *App) Close() {
|
|
if app.db != nil {
|
|
app.db.Close()
|
|
}
|
|
}
|
|
|
|
type databaseReadiness struct {
|
|
config postgres.Config
|
|
store *postgres.Store
|
|
}
|
|
|
|
func (adapter databaseReadiness) Status() httpapi.DatabaseStatus {
|
|
configured := adapter.config.Backend == postgres.BackendLocal || adapter.config.DatabaseURL != ""
|
|
return httpapi.DatabaseStatus{
|
|
Backend: string(adapter.config.Backend),
|
|
Configured: configured,
|
|
}
|
|
}
|
|
|
|
func (adapter databaseReadiness) Ready(ctx context.Context) (httpapi.DatabaseStatus, error) {
|
|
status := adapter.Status()
|
|
return status, adapter.store.Readiness(ctx)
|
|
}
|