140d9f7f25
Implement a comprehensive setup wizard to improve onboarding and configuration experience for both personal and server deployments. Key features: - Interactive wizard with profile selection (personal/server/custom) - Quick setup mode with sensible defaults - First-run detection with helpful welcome message - Directory configuration with validation - Server OAuth/JWT configuration with auto-generation - Environment file creation for server deployments - Template generators for systemd service and env files New commands: - opal setup # Interactive wizard - opal setup --quick # Quick setup with defaults - opal setup --profile # Use specific profile - opal setup --show-systemd # Show systemd template - opal setup --show-env # Show environment file template Implementation: - internal/wizard/prompts.go: Reusable prompt utilities - internal/wizard/profiles.go: Profile definitions and templates - cmd/setup.go: Main setup command implementation - cmd/root.go: First-run detection and welcome message - internal/engine/config.go: ConfigExists() and IsFirstRun() helpers User experience: - On first run, shows welcome message suggesting 'opal setup' - Non-intrusive - creates defaults automatically if skipped - Wizard guides through all configuration options - Server setup includes OAuth/JWT configuration - Environment file created with proper permissions (0600) - Clear next steps displayed after completion
119 lines
2.5 KiB
Go
119 lines
2.5 KiB
Go
package wizard
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
// Profile represents a configuration profile
|
|
type Profile struct {
|
|
Name string
|
|
Description string
|
|
ConfigDir string
|
|
DataDir string
|
|
IsServer bool
|
|
}
|
|
|
|
// GetProfiles returns available setup profiles
|
|
func GetProfiles() []Profile {
|
|
home, _ := os.UserHomeDir()
|
|
|
|
return []Profile{
|
|
{
|
|
Name: "Personal",
|
|
Description: "Single user, default locations",
|
|
ConfigDir: filepath.Join(home, ".config", "opal"),
|
|
DataDir: filepath.Join(home, ".local", "share", "opal"),
|
|
IsServer: false,
|
|
},
|
|
{
|
|
Name: "Server",
|
|
Description: "Production deployment, system paths",
|
|
ConfigDir: "/etc/opal",
|
|
DataDir: "/var/lib/opal",
|
|
IsServer: true,
|
|
},
|
|
{
|
|
Name: "Custom",
|
|
Description: "Configure all options manually",
|
|
ConfigDir: "",
|
|
DataDir: "",
|
|
IsServer: false,
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetProfileChoices returns profile names for display
|
|
func GetProfileChoices() []string {
|
|
profiles := GetProfiles()
|
|
choices := make([]string, len(profiles))
|
|
for i, p := range profiles {
|
|
choices[i] = fmt.Sprintf("%s - %s", p.Name, p.Description)
|
|
}
|
|
return choices
|
|
}
|
|
|
|
// SystemdServiceTemplate returns a systemd service file template
|
|
func SystemdServiceTemplate(configDir, dataDir string) string {
|
|
return fmt.Sprintf(`[Unit]
|
|
Description=Opal Task API Server
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=opal
|
|
Group=opal
|
|
WorkingDirectory=%s
|
|
EnvironmentFile=%s/opal.env
|
|
ExecStart=/usr/local/bin/opal server start --addr :8080
|
|
Restart=always
|
|
RestartSec=5
|
|
|
|
# Security hardening
|
|
NoNewPrivileges=true
|
|
PrivateTmp=true
|
|
ProtectSystem=strict
|
|
ProtectHome=true
|
|
ReadWritePaths=%s
|
|
ReadOnlyPaths=%s
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
`, dataDir, configDir, dataDir, configDir)
|
|
}
|
|
|
|
// EnvFileTemplate returns an environment file template
|
|
func EnvFileTemplate(oauthIssuer, oauthClientID, oauthClientSecret, oauthRedirectURI, jwtSecret string) string {
|
|
return fmt.Sprintf(`# Opal Server Configuration
|
|
# Generated by opal setup wizard
|
|
|
|
# Server
|
|
SERVER_ADDR=:8080
|
|
|
|
# Directory Configuration (optional - defaults to /etc/opal and /var/lib/opal)
|
|
# OPAL_CONFIG_DIR=/etc/opal
|
|
# OPAL_DATA_DIR=/var/lib/opal
|
|
|
|
# OAuth Configuration
|
|
OAUTH_ENABLED=true
|
|
OAUTH_ISSUER=%s
|
|
OAUTH_CLIENT_ID=%s
|
|
OAUTH_CLIENT_SECRET=%s
|
|
OAUTH_REDIRECT_URI=%s
|
|
|
|
# JWT Configuration
|
|
JWT_SECRET=%s
|
|
JWT_EXPIRY=3600
|
|
|
|
# Refresh Token Configuration
|
|
REFRESH_TOKEN_EXPIRY=604800
|
|
`,
|
|
oauthIssuer,
|
|
oauthClientID,
|
|
oauthClientSecret,
|
|
oauthRedirectURI,
|
|
jwtSecret,
|
|
)
|
|
}
|