Compare commits

..

1 Commits

Author SHA1 Message Date
Stavros
ae71280f95 feat: optimize vite chunks 2026-02-17 13:24:16 +02:00
6 changed files with 221 additions and 221 deletions

View File

@@ -2,171 +2,220 @@
# The base URL where the app is hosted. # The base URL where the app is hosted.
TINYAUTH_APPURL= TINYAUTH_APPURL=
# The directory where resources are stored. # The directory where resources are stored.
TINYAUTH_RESOURCESDIR="./resources" TINYAUTH_RESOURCESDIR="./resources"
# The path to the database file. # The path to the database file.
TINYAUTH_DATABASEPATH="./tinyauth.db" TINYAUTH_DATABASEPATH="./tinyauth.db"
# Disable analytics. # Disable analytics.
TINYAUTH_DISABLEANALYTICS=false TINYAUTH_DISABLEANALYTICS=false
# Disable resources server. # Disable resources server.
TINYAUTH_DISABLERESOURCES=false TINYAUTH_DISABLERESOURCES=false
# server config
# The port on which the server listens. # The port on which the server listens.
TINYAUTH_SERVER_PORT=3000 TINYAUTH_SERVER_PORT=3000
# The address on which the server listens. # The address on which the server listens.
TINYAUTH_SERVER_ADDRESS="0.0.0.0" TINYAUTH_SERVER_ADDRESS="0.0.0.0"
# The path to the Unix socket. # The path to the Unix socket.
TINYAUTH_SERVER_SOCKETPATH= TINYAUTH_SERVER_SOCKETPATH=
# auth config
# List of allowed IPs or CIDR ranges. # List of allowed IPs or CIDR ranges.
TINYAUTH_AUTH_IP_ALLOW= TINYAUTH_AUTH_IP_ALLOW=
# List of blocked IPs or CIDR ranges. # List of blocked IPs or CIDR ranges.
TINYAUTH_AUTH_IP_BLOCK= TINYAUTH_AUTH_IP_BLOCK=
# Comma-separated list of users (username:hashed_password). # Comma-separated list of users (username:hashed_password).
TINYAUTH_AUTH_USERS= TINYAUTH_AUTH_USERS=
# Path to the users file. # Path to the users file.
TINYAUTH_AUTH_USERSFILE= TINYAUTH_AUTH_USERSFILE=
# Enable secure cookies. # Enable secure cookies.
TINYAUTH_AUTH_SECURECOOKIE=false TINYAUTH_AUTH_SECURECOOKIE=false
# Session expiry time in seconds. # Session expiry time in seconds.
TINYAUTH_AUTH_SESSIONEXPIRY=86400 TINYAUTH_AUTH_SESSIONEXPIRY=86400
# Maximum session lifetime in seconds. # Maximum session lifetime in seconds.
TINYAUTH_AUTH_SESSIONMAXLIFETIME=0 TINYAUTH_AUTH_SESSIONMAXLIFETIME=0
# Login timeout in seconds. # Login timeout in seconds.
TINYAUTH_AUTH_LOGINTIMEOUT=300 TINYAUTH_AUTH_LOGINTIMEOUT=300
# Maximum login retries. # Maximum login retries.
TINYAUTH_AUTH_LOGINMAXRETRIES=3 TINYAUTH_AUTH_LOGINMAXRETRIES=3
# Comma-separated list of trusted proxy addresses. # Comma-separated list of trusted proxy addresses.
TINYAUTH_AUTH_TRUSTEDPROXIES= TINYAUTH_AUTH_TRUSTEDPROXIES=
# apps config
# The domain of the app. # The domain of the app.
TINYAUTH_APPS_name_CONFIG_DOMAIN= TINYAUTH_APPS_name_CONFIG_DOMAIN=
# Comma-separated list of allowed users. # Comma-separated list of allowed users.
TINYAUTH_APPS_name_USERS_ALLOW= TINYAUTH_APPS_name_USERS_ALLOW=
# Comma-separated list of blocked users. # Comma-separated list of blocked users.
TINYAUTH_APPS_name_USERS_BLOCK= TINYAUTH_APPS_name_USERS_BLOCK=
# Comma-separated list of allowed OAuth groups. # Comma-separated list of allowed OAuth groups.
TINYAUTH_APPS_name_OAUTH_WHITELIST= TINYAUTH_APPS_name_OAUTH_WHITELIST=
# Comma-separated list of required OAuth groups. # Comma-separated list of required OAuth groups.
TINYAUTH_APPS_name_OAUTH_GROUPS= TINYAUTH_APPS_name_OAUTH_GROUPS=
# List of allowed IPs or CIDR ranges. # List of allowed IPs or CIDR ranges.
TINYAUTH_APPS_name_IP_ALLOW= TINYAUTH_APPS_name_IP_ALLOW=
# List of blocked IPs or CIDR ranges. # List of blocked IPs or CIDR ranges.
TINYAUTH_APPS_name_IP_BLOCK= TINYAUTH_APPS_name_IP_BLOCK=
# List of IPs or CIDR ranges that bypass authentication. # List of IPs or CIDR ranges that bypass authentication.
TINYAUTH_APPS_name_IP_BYPASS= TINYAUTH_APPS_name_IP_BYPASS=
# Custom headers to add to the response. # Custom headers to add to the response.
TINYAUTH_APPS_name_RESPONSE_HEADERS= TINYAUTH_APPS_name_RESPONSE_HEADERS=
# Basic auth username. # Basic auth username.
TINYAUTH_APPS_name_RESPONSE_BASICAUTH_USERNAME= TINYAUTH_APPS_name_RESPONSE_BASICAUTH_USERNAME=
# Basic auth password. # Basic auth password.
TINYAUTH_APPS_name_RESPONSE_BASICAUTH_PASSWORD= TINYAUTH_APPS_name_RESPONSE_BASICAUTH_PASSWORD=
# Path to the file containing the basic auth password. # Path to the file containing the basic auth password.
TINYAUTH_APPS_name_RESPONSE_BASICAUTH_PASSWORDFILE= TINYAUTH_APPS_name_RESPONSE_BASICAUTH_PASSWORDFILE=
# Comma-separated list of allowed paths. # Comma-separated list of allowed paths.
TINYAUTH_APPS_name_PATH_ALLOW= TINYAUTH_APPS_name_PATH_ALLOW=
# Comma-separated list of blocked paths. # Comma-separated list of blocked paths.
TINYAUTH_APPS_name_PATH_BLOCK= TINYAUTH_APPS_name_PATH_BLOCK=
# Comma-separated list of required LDAP groups. # Comma-separated list of required LDAP groups.
TINYAUTH_APPS_name_LDAP_GROUPS= TINYAUTH_APPS_name_LDAP_GROUPS=
# oauth config
# Comma-separated list of allowed OAuth domains. # Comma-separated list of allowed OAuth domains.
TINYAUTH_OAUTH_WHITELIST= TINYAUTH_OAUTH_WHITELIST=
# The OAuth provider to use for automatic redirection. # The OAuth provider to use for automatic redirection.
TINYAUTH_OAUTH_AUTOREDIRECT= TINYAUTH_OAUTH_AUTOREDIRECT=
# OAuth client ID. # OAuth client ID.
TINYAUTH_OAUTH_PROVIDERS_name_CLIENTID= TINYAUTH_OAUTH_PROVIDERS_name_CLIENTID=
# OAuth client secret. # OAuth client secret.
TINYAUTH_OAUTH_PROVIDERS_name_CLIENTSECRET= TINYAUTH_OAUTH_PROVIDERS_name_CLIENTSECRET=
# Path to the file containing the OAuth client secret. # Path to the file containing the OAuth client secret.
TINYAUTH_OAUTH_PROVIDERS_name_CLIENTSECRETFILE= TINYAUTH_OAUTH_PROVIDERS_name_CLIENTSECRETFILE=
# OAuth scopes. # OAuth scopes.
TINYAUTH_OAUTH_PROVIDERS_name_SCOPES= TINYAUTH_OAUTH_PROVIDERS_name_SCOPES=
# OAuth redirect URL. # OAuth redirect URL.
TINYAUTH_OAUTH_PROVIDERS_name_REDIRECTURL= TINYAUTH_OAUTH_PROVIDERS_name_REDIRECTURL=
# OAuth authorization URL. # OAuth authorization URL.
TINYAUTH_OAUTH_PROVIDERS_name_AUTHURL= TINYAUTH_OAUTH_PROVIDERS_name_AUTHURL=
# OAuth token URL. # OAuth token URL.
TINYAUTH_OAUTH_PROVIDERS_name_TOKENURL= TINYAUTH_OAUTH_PROVIDERS_name_TOKENURL=
# OAuth userinfo URL. # OAuth userinfo URL.
TINYAUTH_OAUTH_PROVIDERS_name_USERINFOURL= TINYAUTH_OAUTH_PROVIDERS_name_USERINFOURL=
# Allow insecure OAuth connections. # Allow insecure OAuth connections.
TINYAUTH_OAUTH_PROVIDERS_name_INSECURE=false TINYAUTH_OAUTH_PROVIDERS_name_INSECURE=false
# Provider name in UI. # Provider name in UI.
TINYAUTH_OAUTH_PROVIDERS_name_NAME= TINYAUTH_OAUTH_PROVIDERS_name_NAME=
# oidc config
# Path to the private key file. # Path to the private key file.
TINYAUTH_OIDC_PRIVATEKEYPATH="./tinyauth_oidc_key" TINYAUTH_OIDC_PRIVATEKEYPATH="./tinyauth_oidc_key"
# Path to the public key file. # Path to the public key file.
TINYAUTH_OIDC_PUBLICKEYPATH="./tinyauth_oidc_key.pub" TINYAUTH_OIDC_PUBLICKEYPATH="./tinyauth_oidc_key.pub"
# OIDC client ID. # OIDC client ID.
TINYAUTH_OIDC_CLIENTS_name_CLIENTID= TINYAUTH_OIDC_CLIENTS_name_CLIENTID=
# OIDC client secret. # OIDC client secret.
TINYAUTH_OIDC_CLIENTS_name_CLIENTSECRET= TINYAUTH_OIDC_CLIENTS_name_CLIENTSECRET=
# Path to the file containing the OIDC client secret. # Path to the file containing the OIDC client secret.
TINYAUTH_OIDC_CLIENTS_name_CLIENTSECRETFILE= TINYAUTH_OIDC_CLIENTS_name_CLIENTSECRETFILE=
# List of trusted redirect URIs. # List of trusted redirect URIs.
TINYAUTH_OIDC_CLIENTS_name_TRUSTEDREDIRECTURIS= TINYAUTH_OIDC_CLIENTS_name_TRUSTEDREDIRECTURIS=
# Client name in UI. # Client name in UI.
TINYAUTH_OIDC_CLIENTS_name_NAME= TINYAUTH_OIDC_CLIENTS_name_NAME=
# ui config
# The title of the UI. # The title of the UI.
TINYAUTH_UI_TITLE="Tinyauth" TINYAUTH_UI_TITLE="Tinyauth"
# Message displayed on the forgot password page. # Message displayed on the forgot password page.
TINYAUTH_UI_FORGOTPASSWORDMESSAGE="You can change your password by changing the configuration." TINYAUTH_UI_FORGOTPASSWORDMESSAGE="You can change your password by changing the configuration."
# Path to the background image. # Path to the background image.
TINYAUTH_UI_BACKGROUNDIMAGE="/background.jpg" TINYAUTH_UI_BACKGROUNDIMAGE="/background.jpg"
# Disable UI warnings. # Disable UI warnings.
TINYAUTH_UI_DISABLEWARNINGS=false TINYAUTH_UI_DISABLEWARNINGS=false
# ldap config
# LDAP server address. # LDAP server address.
TINYAUTH_LDAP_ADDRESS= TINYAUTH_LDAP_ADDRESS=
# Bind DN for LDAP authentication. # Bind DN for LDAP authentication.
TINYAUTH_LDAP_BINDDN= TINYAUTH_LDAP_BINDDN=
# Bind password for LDAP authentication. # Bind password for LDAP authentication.
TINYAUTH_LDAP_BINDPASSWORD= TINYAUTH_LDAP_BINDPASSWORD=
# Base DN for LDAP searches. # Base DN for LDAP searches.
TINYAUTH_LDAP_BASEDN= TINYAUTH_LDAP_BASEDN=
# Allow insecure LDAP connections. # Allow insecure LDAP connections.
TINYAUTH_LDAP_INSECURE=false TINYAUTH_LDAP_INSECURE=false
# LDAP search filter. # LDAP search filter.
TINYAUTH_LDAP_SEARCHFILTER="(uid=%s)" TINYAUTH_LDAP_SEARCHFILTER="(uid=%s)"
# Certificate for mTLS authentication. # Certificate for mTLS authentication.
TINYAUTH_LDAP_AUTHCERT= TINYAUTH_LDAP_AUTHCERT=
# Certificate key for mTLS authentication. # Certificate key for mTLS authentication.
TINYAUTH_LDAP_AUTHKEY= TINYAUTH_LDAP_AUTHKEY=
# Cache duration for LDAP group membership in seconds. # Cache duration for LDAP group membership in seconds.
TINYAUTH_LDAP_GROUPCACHETTL=900 TINYAUTH_LDAP_GROUPCACHETTL=900
# log config
# Log level (trace, debug, info, warn, error). # Log level (trace, debug, info, warn, error).
TINYAUTH_LOG_LEVEL="info" TINYAUTH_LOG_LEVEL="info"
# Enable JSON formatted logs. # Enable JSON formatted logs.
TINYAUTH_LOG_JSON=false TINYAUTH_LOG_JSON=false
# Enable this log stream. # Enable this log stream.
TINYAUTH_LOG_STREAMS_HTTP_ENABLED=true TINYAUTH_LOG_STREAMS_HTTP_ENABLED=true
# Log level for this stream. Use global if empty. # Log level for this stream. Use global if empty.
TINYAUTH_LOG_STREAMS_HTTP_LEVEL= TINYAUTH_LOG_STREAMS_HTTP_LEVEL=
# Enable this log stream. # Enable this log stream.
TINYAUTH_LOG_STREAMS_APP_ENABLED=true TINYAUTH_LOG_STREAMS_APP_ENABLED=true
# Log level for this stream. Use global if empty. # Log level for this stream. Use global if empty.
TINYAUTH_LOG_STREAMS_APP_LEVEL= TINYAUTH_LOG_STREAMS_APP_LEVEL=
# Enable this log stream. # Enable this log stream.
TINYAUTH_LOG_STREAMS_AUDIT_ENABLED=false TINYAUTH_LOG_STREAMS_AUDIT_ENABLED=false
# Log level for this stream. Use global if empty. # Log level for this stream. Use global if empty.
TINYAUTH_LOG_STREAMS_AUDIT_LEVEL= TINYAUTH_LOG_STREAMS_AUDIT_LEVEL=

3
.gitignore vendored
View File

@@ -42,6 +42,3 @@ __debug_*
# traefik data # traefik data
/traefik /traefik
# generated markdown (for docs)
/config.gen.md

102
config.example.yaml Normal file
View File

@@ -0,0 +1,102 @@
# Tinyauth Example Configuration
# The base URL where Tinyauth is accessible
appUrl: "https://auth.example.com"
# Directory for static resources
resourcesDir: "./resources"
# Path to SQLite database file
databasePath: "./tinyauth.db"
# Disable usage analytics
disableAnalytics: false
# Disable static resource serving
disableResources: false
# Disable UI warning messages
disableUIWarnings: false
# Logging Configuration
log:
# Log level: trace, debug, info, warn, error
level: "info"
json: false
streams:
app:
enabled: true
level: "warn"
http:
enabled: true
level: "debug"
audit:
enabled: false
level: "info"
# Server Configuration
server:
# Port to listen on
port: 3000
# Interface to bind to (0.0.0.0 for all interfaces)
address: "0.0.0.0"
# Unix socket path (optional, overrides port/address if set)
socketPath: ""
# Comma-separated list of trusted proxy IPs/CIDRs
trustedProxies: ""
# Authentication Configuration
auth:
# Format: username:bcrypt_hash (use bcrypt to generate hash)
users: "admin:$2a$10$example_bcrypt_hash_here"
# Path to external users file (optional)
usersFile: ""
# Enable secure cookies (requires HTTPS)
secureCookie: false
# Session expiry in seconds (3600 = 1 hour)
sessionExpiry: 3600
# Session maximum lifetime in seconds (0 = unlimited)
sessionMaxLifetime: 0
# Login timeout in seconds (300 = 5 minutes)
loginTimeout: 300
# Maximum login retries before lockout
loginMaxRetries: 3
# OAuth Configuration
oauth:
# Regex pattern for allowed email addresses (e.g., /@example\.com$/)
whitelist: ""
# Provider ID to auto-redirect to (skips login page)
autoRedirect: ""
# OAuth Provider Configuration (replace myprovider with your provider name)
providers:
myprovider:
clientId: "your_client_id_here"
clientSecret: "your_client_secret_here"
authUrl: "https://provider.example.com/oauth/authorize"
tokenUrl: "https://provider.example.com/oauth/token"
userInfoUrl: "https://provider.example.com/oauth/userinfo"
redirectUrl: "https://auth.example.com/api/oauth/callback/myprovider"
scopes: "openid email profile"
name: "My OAuth Provider"
# Allow insecure connections (self-signed certificates)
insecure: false
# UI Customization
ui:
# Custom title for login page
title: "Tinyauth"
# Message shown on forgot password page
forgotPasswordMessage: "Contact your administrator to reset your password"
# Background image URL for login page
backgroundImage: ""
# LDAP Configuration (optional)
ldap:
# LDAP server address
address: "ldap://ldap.example.com:389"
# DN for binding to LDAP server
bindDn: "cn=readonly,dc=example,dc=com"
# Password for bind DN
bindPassword: "your_bind_password"
# Base DN for user searches
baseDn: "dc=example,dc=com"
# Search filter (%s will be replaced with username)
searchFilter: "(&(uid=%s)(memberOf=cn=users,ou=groups,dc=example,dc=com))"
# Allow insecure LDAP connections
insecure: false

View File

@@ -2,37 +2,10 @@ package main
import ( import (
"log/slog" "log/slog"
"reflect"
) )
func main() { func main() {
slog.Info("generating example env file") slog.Info("generating example env file")
generateExampleEnv() generateExampleEnv()
slog.Info("generating config reference markdown file")
generateMarkdown()
}
func walkAndBuild[T any](parent reflect.Type, parentValue reflect.Value,
parentPath string, entries *[]T,
buildEntry func(child reflect.StructField, childValue reflect.Value, parentPath string, entries *[]T),
buildMap func(child reflect.StructField, parentPath string, entries *[]T),
buildChildPath func(parentPath string, childName string) string,
) {
for i := 0; i < parent.NumField(); i++ {
field := parent.Field(i)
fieldType := field.Type
fieldValue := parentValue.Field(i)
switch fieldType.Kind() {
case reflect.Struct:
childPath := buildChildPath(parentPath, field.Name)
walkAndBuild[T](fieldType, fieldValue, childPath, entries, buildEntry, buildMap, buildChildPath)
case reflect.Map:
buildMap(field, parentPath, entries)
case reflect.Bool, reflect.String, reflect.Slice, reflect.Int:
buildEntry(field, fieldValue, parentPath, entries)
default:
slog.Info("unknown type", "type", fieldType.Kind())
}
}
} }

View File

@@ -2,9 +2,7 @@ package main
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"io/fs"
"log/slog" "log/slog"
"os" "os"
"reflect" "reflect"
@@ -13,7 +11,7 @@ import (
"github.com/steveiliop56/tinyauth/internal/config" "github.com/steveiliop56/tinyauth/internal/config"
) )
type EnvEntry struct { type Path struct {
Name string Name string
Description string Description string
Value any Value any
@@ -21,17 +19,17 @@ type EnvEntry struct {
func generateExampleEnv() { func generateExampleEnv() {
cfg := config.NewDefaultConfiguration() cfg := config.NewDefaultConfiguration()
entries := make([]EnvEntry, 0) paths := make([]Path, 0)
root := reflect.TypeOf(cfg).Elem() root := reflect.TypeOf(cfg).Elem()
rootValue := reflect.ValueOf(cfg).Elem() rootValue := reflect.ValueOf(cfg).Elem()
rootPath := "TINYAUTH_" rootPath := "TINYAUTH_"
walkAndBuild(root, rootValue, rootPath, &entries, buildEnvEntry, buildEnvMapEntry, buildEnvChildPath) buildPaths(root, rootValue, rootPath, &paths)
compiled := compileEnv(entries) compiled := compileEnv(paths)
err := os.Remove(".env.example") err := os.Remove(".env.example")
if err != nil && !errors.Is(err, fs.ErrNotExist) { if err != nil {
slog.Error("failed to remove example env file", "error", err) slog.Error("failed to remove example env file", "error", err)
os.Exit(1) os.Exit(1)
} }
@@ -43,88 +41,96 @@ func generateExampleEnv() {
} }
} }
func buildEnvEntry(child reflect.StructField, childValue reflect.Value, parentPath string, entries *[]EnvEntry) { func buildPaths(parent reflect.Type, parentValue reflect.Value, parentPath string, paths *[]Path) {
desc := child.Tag.Get("description") for i := 0; i < parent.NumField(); i++ {
tag := child.Tag.Get("yaml") field := parent.Field(i)
fieldType := field.Type
fieldValue := parentValue.Field(i)
switch fieldType.Kind() {
case reflect.Struct:
childPath := parentPath + strings.ToUpper(field.Name) + "_"
buildPaths(fieldType, fieldValue, childPath, paths)
case reflect.Map:
buildMapPaths(field, parentPath, paths)
case reflect.Bool, reflect.String, reflect.Slice, reflect.Int:
buildPath(field, fieldValue, parentPath, paths)
default:
slog.Info("unknown type", "type", fieldType.Kind())
}
}
}
if tag == "-" { func buildPath(field reflect.StructField, fieldValue reflect.Value, parent string, paths *[]Path) {
desc := field.Tag.Get("description")
yamlTag := field.Tag.Get("yaml")
// probably internal logic, should be skipped
if yamlTag == "-" {
return return
} }
value := childValue.Interface() defaultValue := fieldValue.Interface()
entry := EnvEntry{ path := Path{
Name: parentPath + strings.ToUpper(child.Name), Name: parent + strings.ToUpper(field.Name),
Description: desc, Description: desc,
} }
switch childValue.Kind() { switch fieldValue.Kind() {
case reflect.Slice: case reflect.Slice:
sl, ok := value.([]string) sl, ok := defaultValue.([]string)
if !ok { if !ok {
slog.Error("invalid default value", "value", value) slog.Error("invalid default value", "value", defaultValue)
return return
} }
entry.Value = strings.Join(sl, ",") path.Value = strings.Join(sl, ",")
case reflect.String: case reflect.String:
st, ok := value.(string) st, ok := defaultValue.(string)
if !ok { if !ok {
slog.Error("invalid default value", "value", value) slog.Error("invalid default value", "value", defaultValue)
return return
} }
// good idea to escape strings probably
if st != "" { if st != "" {
entry.Value = fmt.Sprintf(`"%s"`, st) path.Value = fmt.Sprintf(`"%s"`, st)
} else { } else {
entry.Value = "" path.Value = ""
} }
default: default:
entry.Value = value path.Value = defaultValue
} }
*entries = append(*entries, entry) *paths = append(*paths, path)
} }
func buildEnvMapEntry(child reflect.StructField, parentPath string, entries *[]EnvEntry) { func buildMapPaths(field reflect.StructField, parentPath string, paths *[]Path) {
fieldType := child.Type fieldType := field.Type
if fieldType.Key().Kind() != reflect.String { if fieldType.Key().Kind() != reflect.String {
slog.Info("unsupported map key type", "type", fieldType.Key().Kind()) slog.Info("unsupported map key type", "type", fieldType.Key().Kind())
return return
} }
mapPath := parentPath + strings.ToUpper(child.Name) + "_name_" mapPath := parentPath + strings.ToUpper(field.Name) + "_name_"
valueType := fieldType.Elem() valueType := fieldType.Elem()
if valueType.Kind() == reflect.Struct { if valueType.Kind() == reflect.Struct {
zeroValue := reflect.New(valueType).Elem() zeroValue := reflect.New(valueType).Elem()
walkAndBuild(valueType, zeroValue, mapPath, entries, buildEnvEntry, buildEnvMapEntry, buildEnvChildPath) buildPaths(valueType, zeroValue, mapPath, paths)
} }
} }
func buildEnvChildPath(parent string, child string) string { func compileEnv(paths []Path) []byte {
return parent + strings.ToUpper(child) + "_"
}
func compileEnv(entries []EnvEntry) []byte {
buffer := bytes.Buffer{} buffer := bytes.Buffer{}
buffer.WriteString("# Tinyauth example configuration\n\n") buffer.WriteString("# Tinyauth example configuration\n\n")
previousSection := "" for _, path := range paths {
for _, entry := range entries {
if strings.Count(entry.Name, "_") > 1 {
section := strings.Split(strings.TrimPrefix(entry.Name, "TINYAUTH_"), "_")[0]
if section != previousSection {
buffer.WriteString("\n# " + strings.ToLower(section) + " config\n\n")
previousSection = section
}
}
buffer.WriteString("# ") buffer.WriteString("# ")
buffer.WriteString(entry.Description) buffer.WriteString(path.Description)
buffer.WriteString("\n") buffer.WriteString("\n")
buffer.WriteString(entry.Name) buffer.WriteString(path.Name)
buffer.WriteString("=") buffer.WriteString("=")
fmt.Fprintf(&buffer, "%v", entry.Value) fmt.Fprintf(&buffer, "%v", path.Value)
buffer.WriteString("\n") buffer.WriteString("\n\n")
} }
return buffer.Bytes() return buffer.Bytes()

View File

@@ -1,127 +0,0 @@
package main
import (
"bytes"
"errors"
"fmt"
"io/fs"
"log/slog"
"os"
"reflect"
"strings"
"github.com/steveiliop56/tinyauth/internal/config"
)
type MarkdownEntry struct {
Env string
Flag string
Description string
Default any
}
func generateMarkdown() {
cfg := config.NewDefaultConfiguration()
entries := make([]MarkdownEntry, 0)
root := reflect.TypeOf(cfg).Elem()
rootValue := reflect.ValueOf(cfg).Elem()
rootPath := "tinyauth."
walkAndBuild(root, rootValue, rootPath, &entries, buildMdEntry, buildMdMapEntry, buildMdChildPath)
compiled := compileMd(entries)
err := os.Remove("config.gen.md")
if err != nil && !errors.Is(err, fs.ErrNotExist) {
slog.Error("failed to remove example env file", "error", err)
os.Exit(1)
}
err = os.WriteFile("config.gen.md", compiled, 0644)
if err != nil {
slog.Error("failed to write example env file", "error", err)
os.Exit(1)
}
}
func buildMdEntry(child reflect.StructField, childValue reflect.Value, parentPath string, entries *[]MarkdownEntry) {
desc := child.Tag.Get("description")
tag := child.Tag.Get("yaml")
if tag == "-" {
return
}
value := childValue.Interface()
entry := MarkdownEntry{
Env: strings.ToUpper(strings.ReplaceAll(parentPath, ".", "_")) + strings.ToUpper(child.Name),
Flag: fmt.Sprintf("--%s%s", strings.TrimPrefix(parentPath, "tinyauth."), tag),
Description: desc,
}
switch childValue.Kind() {
case reflect.Slice:
sl, ok := value.([]string)
if !ok {
slog.Error("invalid default value", "value", value)
return
}
entry.Default = fmt.Sprintf("`%s`", strings.Join(sl, ","))
default:
entry.Default = fmt.Sprintf("`%v`", value)
}
*entries = append(*entries, entry)
}
func buildMdMapEntry(child reflect.StructField, parentPath string, entries *[]MarkdownEntry) {
fieldType := child.Type
if fieldType.Key().Kind() != reflect.String {
slog.Info("unsupported map key type", "type", fieldType.Key().Kind())
return
}
tag := child.Tag.Get("yaml")
if tag == "-" {
return
}
mapPath := parentPath + tag + ".[name]."
valueType := fieldType.Elem()
if valueType.Kind() == reflect.Struct {
zeroValue := reflect.New(valueType).Elem()
walkAndBuild(valueType, zeroValue, mapPath, entries, buildMdEntry, buildMdMapEntry, buildMdChildPath)
}
}
func buildMdChildPath(parent string, child string) string {
return parent + strings.ToLower(child) + "."
}
func compileMd(entries []MarkdownEntry) []byte {
buffer := bytes.Buffer{}
buffer.WriteString("# Tinyauth configuration reference\n\n")
buffer.WriteString("| Environment | Flag | Description | Default |\n")
buffer.WriteString("| - | - | - | - |\n")
previousSection := ""
for _, entry := range entries {
if strings.Count(entry.Env, "_") > 1 {
section := strings.Split(strings.TrimPrefix(entry.Env, "TINYAUTH_"), "_")[0]
if section != previousSection {
buffer.WriteString("\n## " + strings.ToLower(section) + "\n\n")
buffer.WriteString("| Environment | Flag | Description | Default |\n")
buffer.WriteString("| - | - | - | - |\n")
previousSection = section
}
}
fmt.Fprintf(&buffer, "| `%s` | `%s` | %s | %s |\n", entry.Env, entry.Flag, entry.Description, entry.Default)
}
return buffer.Bytes()
}