Files
NianAIGC/backend/internal/application/application.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)
}