chore: git cache cleanup

This commit is contained in:
GitHub Actions
2026-03-04 18:34:49 +00:00
parent c32cce2a88
commit 27c252600a
2001 changed files with 683185 additions and 0 deletions

295
backend/cmd/seed/main.go Normal file
View File

@@ -0,0 +1,295 @@
package main
import (
"io"
"log"
"os"
"time"
"github.com/Wikid82/charon/backend/internal/logger"
"github.com/Wikid82/charon/backend/internal/util"
"github.com/glebarez/sqlite"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
gormlogger "gorm.io/gorm/logger"
"github.com/Wikid82/charon/backend/internal/models"
)
func logSeedResult(entry *logrus.Entry, result *gorm.DB, errorMessage string, logCreated func(), existsMessage string) {
switch {
case result.Error != nil:
entry.WithError(result.Error).Error(errorMessage)
case result.RowsAffected > 0:
logCreated()
default:
entry.Info(existsMessage)
}
}
func main() {
// Connect to database
// Initialize simple logger to stdout
mw := io.MultiWriter(os.Stdout)
logger.Init(false, mw)
// Configure GORM logger to ignore "record not found" errors
// These are expected during seed operations when checking if records exist
gormLog := gormlogger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
gormlogger.Config{
SlowThreshold: 200 * time.Millisecond,
LogLevel: gormlogger.Warn,
IgnoreRecordNotFoundError: true,
Colorful: false,
},
)
db, err := gorm.Open(sqlite.Open("./data/charon.db"), &gorm.Config{
Logger: gormLog,
})
if err != nil {
logger.Log().WithError(err).Fatal("Failed to connect to database")
}
// Auto migrate
if err := db.AutoMigrate(
&models.User{},
&models.ProxyHost{},
&models.CaddyConfig{},
&models.RemoteServer{},
&models.SSLCertificate{},
&models.AccessList{},
&models.Setting{},
&models.ImportSession{},
); err != nil {
logger.Log().WithError(err).Fatal("Failed to migrate database")
}
logger.Log().Info("✓ Database migrated successfully")
// Seed Remote Servers
remoteServers := []models.RemoteServer{
{
UUID: uuid.NewString(),
Name: "Local Docker Registry",
Provider: "docker",
Host: "localhost",
Port: 5000,
Scheme: "http",
Description: "Local Docker container registry",
Enabled: true,
Reachable: false,
},
{
UUID: uuid.NewString(),
Name: "Development API Server",
Provider: "generic",
Host: "192.168.1.100",
Port: 8080,
Scheme: "http",
Description: "Main development API backend",
Enabled: true,
Reachable: false,
},
{
UUID: uuid.NewString(),
Name: "Staging Web App",
Provider: "vm",
Host: "staging.internal",
Port: 3000,
Scheme: "http",
Description: "Staging environment web application",
Enabled: true,
Reachable: false,
},
{
UUID: uuid.NewString(),
Name: "Database Admin",
Provider: "docker",
Host: "localhost",
Port: 8081,
Scheme: "http",
Description: "PhpMyAdmin or similar DB management tool",
Enabled: false,
Reachable: false,
},
}
for _, server := range remoteServers {
result := db.Where("host = ? AND port = ?", server.Host, server.Port).FirstOrCreate(&server)
logEntry := logger.Log().WithField("server", server.Name)
logSeedResult(
logEntry,
result,
"Failed to seed remote server",
func() {
logEntry.Infof("✓ Created remote server: %s (%s:%d)", server.Name, server.Host, server.Port)
},
"Remote server already exists",
)
}
// Seed Proxy Hosts
proxyHosts := []models.ProxyHost{
{
UUID: uuid.NewString(),
Name: "Development App",
DomainNames: "app.local.dev",
ForwardScheme: "http",
ForwardHost: "localhost",
ForwardPort: 3000,
SSLForced: false,
WebsocketSupport: true,
HSTSEnabled: false,
BlockExploits: true,
Enabled: true,
},
{
UUID: uuid.NewString(),
Name: "API Server",
DomainNames: "api.local.dev",
ForwardScheme: "http",
ForwardHost: "192.168.1.100",
ForwardPort: 8080,
SSLForced: false,
WebsocketSupport: false,
HSTSEnabled: false,
BlockExploits: true,
Enabled: true,
},
{
UUID: uuid.NewString(),
Name: "Docker Registry",
DomainNames: "docker.local.dev",
ForwardScheme: "http",
ForwardHost: "localhost",
ForwardPort: 5000,
SSLForced: false,
WebsocketSupport: false,
HSTSEnabled: false,
BlockExploits: true,
Enabled: false,
},
}
for _, host := range proxyHosts {
result := db.Where("domain_names = ?", host.DomainNames).FirstOrCreate(&host)
logEntry := logger.Log().WithField("host", util.SanitizeForLog(host.DomainNames))
logSeedResult(
logEntry,
result,
"Failed to seed proxy host",
func() {
logEntry.Infof("✓ Created proxy host: %s -> %s://%s:%d", host.DomainNames, host.ForwardScheme, host.ForwardHost, host.ForwardPort)
},
"Proxy host already exists",
)
}
// Seed Settings
settings := []models.Setting{
{
Key: "app_name",
Value: "Charon",
Type: "string",
Category: "general",
},
{
Key: "default_scheme",
Value: "http",
Type: "string",
Category: "general",
},
{
Key: "enable_ssl_by_default",
Value: "false",
Type: "bool",
Category: "security",
},
}
for _, setting := range settings {
result := db.Where("key = ?", setting.Key).FirstOrCreate(&setting)
logEntry := logger.Log().WithField("setting", setting.Key)
logSeedResult(
logEntry,
result,
"Failed to seed setting",
func() {
logEntry.Infof("✓ Created setting: %s = %s", setting.Key, setting.Value)
},
"Setting already exists",
)
}
// Seed default admin user (for future authentication)
defaultAdminEmail := os.Getenv("CHARON_DEFAULT_ADMIN_EMAIL")
if defaultAdminEmail == "" {
defaultAdminEmail = "admin@localhost"
}
defaultAdminPassword := os.Getenv("CHARON_DEFAULT_ADMIN_PASSWORD")
// If a default password is not specified, leave the hashed placeholder (non-loginable)
forceAdmin := os.Getenv("CHARON_FORCE_DEFAULT_ADMIN") == "1"
user := models.User{
UUID: uuid.NewString(),
Email: defaultAdminEmail,
Name: "Administrator",
Role: "admin",
Enabled: true,
}
// If a default password provided, use SetPassword to generate a proper bcrypt hash
if defaultAdminPassword != "" {
if err := user.SetPassword(defaultAdminPassword); err != nil {
logger.Log().WithError(err).Error("Failed to hash default admin password")
}
} else {
// Keep previous behavior: using example hashed password (not valid)
user.PasswordHash = "$2a$10$example_hashed_password"
}
var existing models.User
// Find by email first - use Take instead of First to avoid GORM's "record not found" log
result := db.Where("email = ?", user.Email).Take(&existing)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
// Not found -> create new user
createResult := db.Create(&user)
if createResult.Error != nil {
logger.Log().WithError(createResult.Error).Error("Failed to seed user")
} else if createResult.RowsAffected > 0 {
logger.Log().WithField("user", user.Email).Infof("✓ Created default user: %s", user.Email)
}
} else {
// Unexpected error
logger.Log().WithError(result.Error).Error("Failed to query for existing user")
}
} else {
// Found existing user - optionally update if forced
if forceAdmin {
existing.Email = user.Email
existing.Name = user.Name
existing.Role = user.Role
existing.Enabled = user.Enabled
if defaultAdminPassword != "" {
if err := existing.SetPassword(defaultAdminPassword); err == nil {
db.Save(&existing)
logger.Log().WithField("user", existing.Email).Infof("✓ Updated existing admin user password for: %s", existing.Email)
} else {
logger.Log().WithError(err).Error("Failed to update existing admin password")
}
} else {
db.Save(&existing)
logger.Log().WithField("user", existing.Email).Info("User already exists")
}
} else {
logger.Log().WithField("user", existing.Email).Info("User already exists")
}
}
logger.Log().Info("\n✓ Database seeding completed successfully!")
logger.Log().Info(" You can now start the application and see sample data.")
}

View File

@@ -0,0 +1,38 @@
//go:build ignore
// +build ignore
package main
import (
"os"
"path/filepath"
"testing"
)
func TestSeedMain_CreatesDatabaseFile(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
t.Fatalf("getwd: %v", err)
}
tmp := t.TempDir()
if err := os.Chdir(tmp); err != nil {
t.Fatalf("chdir: %v", err)
}
t.Cleanup(func() { _ = os.Chdir(wd) })
if err := os.MkdirAll("data", 0o755); err != nil {
t.Fatalf("mkdir data: %v", err)
}
main()
dbPath := filepath.Join("data", "charon.db")
info, err := os.Stat(dbPath)
if err != nil {
t.Fatalf("expected db file to exist at %s: %v", dbPath, err)
}
if info.Size() == 0 {
t.Fatalf("expected db file to be non-empty")
}
}

View File

@@ -0,0 +1,201 @@
package main
import (
"errors"
"os"
"path/filepath"
"testing"
"github.com/Wikid82/charon/backend/internal/models"
"github.com/sirupsen/logrus"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func TestSeedMain_Smoke(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
t.Fatalf("getwd: %v", err)
}
tmp := t.TempDir()
err = os.Chdir(tmp)
if err != nil {
t.Fatalf("chdir: %v", err)
}
t.Cleanup(func() { _ = os.Chdir(wd) })
// #nosec G301 -- Test data directory, 0o755 acceptable for test environment
err = os.MkdirAll("data", 0o750)
if err != nil {
t.Fatalf("mkdir data: %v", err)
}
main()
p := filepath.Join("data", "charon.db")
if _, err := os.Stat(p); err != nil {
t.Fatalf("expected db file to exist: %v", err)
}
}
func TestSeedMain_ForceAdminUpdatesExistingUserPassword(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
t.Fatalf("getwd: %v", err)
}
tmp := t.TempDir()
err = os.Chdir(tmp)
if err != nil {
t.Fatalf("chdir: %v", err)
}
t.Cleanup(func() {
_ = os.Chdir(wd)
})
err = os.MkdirAll("data", 0o750)
if err != nil {
t.Fatalf("mkdir data: %v", err)
}
dbPath := filepath.Join("data", "charon.db")
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
if err != nil {
t.Fatalf("open db: %v", err)
}
if err := db.AutoMigrate(&models.User{}); err != nil {
t.Fatalf("automigrate: %v", err)
}
seeded := models.User{
UUID: "existing-user",
Email: "admin@localhost",
Name: "Old Name",
Role: models.RolePassthrough,
Enabled: false,
PasswordHash: "$2a$10$example_hashed_password",
}
if err := db.Create(&seeded).Error; err != nil {
t.Fatalf("create seeded user: %v", err)
}
t.Setenv("CHARON_FORCE_DEFAULT_ADMIN", "1")
t.Setenv("CHARON_DEFAULT_ADMIN_PASSWORD", "new-password")
main()
var updated models.User
if err := db.Where("email = ?", "admin@localhost").First(&updated).Error; err != nil {
t.Fatalf("fetch updated user: %v", err)
}
if updated.PasswordHash == "$2a$10$example_hashed_password" {
t.Fatal("expected password hash to be updated for forced admin")
}
if updated.Role != "admin" {
t.Fatalf("expected role admin, got %q", updated.Role)
}
if !updated.Enabled {
t.Fatal("expected forced admin to be enabled")
}
}
func TestSeedMain_ForceAdminWithoutPasswordUpdatesMetadata(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
t.Fatalf("getwd: %v", err)
}
tmp := t.TempDir()
err = os.Chdir(tmp)
if err != nil {
t.Fatalf("chdir: %v", err)
}
t.Cleanup(func() {
_ = os.Chdir(wd)
})
err = os.MkdirAll("data", 0o750)
if err != nil {
t.Fatalf("mkdir data: %v", err)
}
dbPath := filepath.Join("data", "charon.db")
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
if err != nil {
t.Fatalf("open db: %v", err)
}
if err := db.AutoMigrate(&models.User{}); err != nil {
t.Fatalf("automigrate: %v", err)
}
seeded := models.User{
UUID: "existing-user-no-pass",
Email: "admin@localhost",
Name: "Old Name",
Role: models.RolePassthrough,
Enabled: false,
PasswordHash: "$2a$10$example_hashed_password",
}
if err := db.Create(&seeded).Error; err != nil {
t.Fatalf("create seeded user: %v", err)
}
t.Setenv("CHARON_FORCE_DEFAULT_ADMIN", "1")
t.Setenv("CHARON_DEFAULT_ADMIN_PASSWORD", "")
main()
var updated models.User
if err := db.Where("email = ?", "admin@localhost").First(&updated).Error; err != nil {
t.Fatalf("fetch updated user: %v", err)
}
if updated.Role != "admin" {
t.Fatalf("expected role admin, got %q", updated.Role)
}
if !updated.Enabled {
t.Fatal("expected forced admin to be enabled")
}
if updated.PasswordHash != "$2a$10$example_hashed_password" {
t.Fatal("expected password hash to remain unchanged when no password is provided")
}
}
func TestLogSeedResult_Branches(t *testing.T) {
entry := logrus.New().WithField("component", "seed-test")
t.Run("error branch", func(t *testing.T) {
createdCalled := false
result := &gorm.DB{Error: errors.New("insert failed")}
logSeedResult(entry, result, "error", func() {
createdCalled = true
}, "exists")
if createdCalled {
t.Fatal("created callback should not be called on error")
}
})
t.Run("created branch", func(t *testing.T) {
createdCalled := false
result := &gorm.DB{RowsAffected: 1}
logSeedResult(entry, result, "error", func() {
createdCalled = true
}, "exists")
if !createdCalled {
t.Fatal("created callback should be called when rows are affected")
}
})
t.Run("exists branch", func(t *testing.T) {
createdCalled := false
result := &gorm.DB{RowsAffected: 0}
logSeedResult(entry, result, "error", func() {
createdCalled = true
}, "exists")
if createdCalled {
t.Fatal("created callback should not be called when rows are not affected")
}
})
}