From fff5d8c206eeb7c9cc10397e784608e77891d998 Mon Sep 17 00:00:00 2001 From: Trey t Date: Wed, 26 Nov 2025 21:12:02 -0600 Subject: [PATCH] Integrate GoAdmin into main API with auto-migrations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Admin panel accessible at /admin on the same domain - GoAdmin tables created automatically during startup migrations - Default credentials: admin / admin 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- cmd/api/main.go | 11 ++ internal/database/database.go | 255 ++++++++++++++++++++++++++++++++++ 2 files changed, 266 insertions(+) diff --git a/cmd/api/main.go b/cmd/api/main.go index d9fad17..6a565aa 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -9,9 +9,11 @@ import ( "syscall" "time" + _ "github.com/lib/pq" // PostgreSQL driver for GoAdmin "github.com/rs/zerolog/log" "gorm.io/gorm" + "github.com/treytartt/mycrib-api/internal/admin" "github.com/treytartt/mycrib-api/internal/config" "github.com/treytartt/mycrib-api/internal/database" "github.com/treytartt/mycrib-api/internal/router" @@ -91,6 +93,15 @@ func main() { } r := router.SetupRouter(deps) + // Setup GoAdmin panel at /admin + if db != nil { + if _, err := admin.Setup(r, cfg); err != nil { + log.Warn().Err(err).Msg("Failed to setup admin panel - admin will be unavailable") + } else { + log.Info().Msg("Admin panel available at /admin") + } + } + // Create HTTP server srv := &http.Server{ Addr: fmt.Sprintf(":%d", cfg.Server.Port), diff --git a/internal/database/database.go b/internal/database/database.go index f52de92..861d202 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -151,6 +151,261 @@ func Migrate() error { return fmt.Errorf("failed to run migrations: %w", err) } + // Run GoAdmin migrations + if err := migrateGoAdmin(); err != nil { + return fmt.Errorf("failed to run GoAdmin migrations: %w", err) + } + log.Info().Msg("Database migrations completed successfully") return nil } + +// migrateGoAdmin creates GoAdmin required tables +func migrateGoAdmin() error { + log.Info().Msg("Running GoAdmin migrations...") + + // GoAdmin session table + if err := db.Exec(` + CREATE TABLE IF NOT EXISTS goadmin_session ( + id SERIAL PRIMARY KEY, + sid VARCHAR(50) NOT NULL DEFAULT '', + "values" TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `).Error; err != nil { + return err + } + db.Exec(`CREATE INDEX IF NOT EXISTS idx_goadmin_session_sid ON goadmin_session(sid)`) + + // GoAdmin users table + if err := db.Exec(` + CREATE TABLE IF NOT EXISTS goadmin_users ( + id SERIAL PRIMARY KEY, + username VARCHAR(100) NOT NULL DEFAULT '', + password VARCHAR(100) NOT NULL DEFAULT '', + name VARCHAR(100) NOT NULL DEFAULT '', + avatar VARCHAR(255) DEFAULT '', + remember_token VARCHAR(100) DEFAULT '', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `).Error; err != nil { + return err + } + db.Exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_goadmin_users_username ON goadmin_users(username)`) + + // GoAdmin roles table + if err := db.Exec(` + CREATE TABLE IF NOT EXISTS goadmin_roles ( + id SERIAL PRIMARY KEY, + name VARCHAR(50) NOT NULL DEFAULT '', + slug VARCHAR(50) NOT NULL DEFAULT '', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `).Error; err != nil { + return err + } + db.Exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_goadmin_roles_slug ON goadmin_roles(slug)`) + + // GoAdmin permissions table + if err := db.Exec(` + CREATE TABLE IF NOT EXISTS goadmin_permissions ( + id SERIAL PRIMARY KEY, + name VARCHAR(50) NOT NULL DEFAULT '', + slug VARCHAR(50) NOT NULL DEFAULT '', + http_method VARCHAR(255) DEFAULT '', + http_path TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `).Error; err != nil { + return err + } + db.Exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_goadmin_permissions_slug ON goadmin_permissions(slug)`) + + // GoAdmin role_users table + if err := db.Exec(` + CREATE TABLE IF NOT EXISTS goadmin_role_users ( + id SERIAL PRIMARY KEY, + role_id INT NOT NULL, + user_id INT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `).Error; err != nil { + return err + } + db.Exec(`CREATE INDEX IF NOT EXISTS idx_goadmin_role_users_role_id ON goadmin_role_users(role_id)`) + db.Exec(`CREATE INDEX IF NOT EXISTS idx_goadmin_role_users_user_id ON goadmin_role_users(user_id)`) + + // GoAdmin user_permissions table + if err := db.Exec(` + CREATE TABLE IF NOT EXISTS goadmin_user_permissions ( + id SERIAL PRIMARY KEY, + user_id INT NOT NULL, + permission_id INT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `).Error; err != nil { + return err + } + db.Exec(`CREATE INDEX IF NOT EXISTS idx_goadmin_user_permissions_user_id ON goadmin_user_permissions(user_id)`) + db.Exec(`CREATE INDEX IF NOT EXISTS idx_goadmin_user_permissions_permission_id ON goadmin_user_permissions(permission_id)`) + + // GoAdmin role_permissions table + if err := db.Exec(` + CREATE TABLE IF NOT EXISTS goadmin_role_permissions ( + id SERIAL PRIMARY KEY, + role_id INT NOT NULL, + permission_id INT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `).Error; err != nil { + return err + } + db.Exec(`CREATE INDEX IF NOT EXISTS idx_goadmin_role_permissions_role_id ON goadmin_role_permissions(role_id)`) + db.Exec(`CREATE INDEX IF NOT EXISTS idx_goadmin_role_permissions_permission_id ON goadmin_role_permissions(permission_id)`) + + // GoAdmin menu table + if err := db.Exec(` + CREATE TABLE IF NOT EXISTS goadmin_menu ( + id SERIAL PRIMARY KEY, + parent_id INT NOT NULL DEFAULT 0, + type INT NOT NULL DEFAULT 0, + "order" INT NOT NULL DEFAULT 0, + title VARCHAR(50) NOT NULL DEFAULT '', + icon VARCHAR(50) NOT NULL DEFAULT '', + uri VARCHAR(3000) NOT NULL DEFAULT '', + header VARCHAR(150) DEFAULT '', + plugin_name VARCHAR(150) NOT NULL DEFAULT '', + uuid VARCHAR(150) DEFAULT '', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `).Error; err != nil { + return err + } + db.Exec(`CREATE INDEX IF NOT EXISTS idx_goadmin_menu_parent_id ON goadmin_menu(parent_id)`) + + // GoAdmin role_menu table + if err := db.Exec(` + CREATE TABLE IF NOT EXISTS goadmin_role_menu ( + id SERIAL PRIMARY KEY, + role_id INT NOT NULL, + menu_id INT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `).Error; err != nil { + return err + } + db.Exec(`CREATE INDEX IF NOT EXISTS idx_goadmin_role_menu_role_id ON goadmin_role_menu(role_id)`) + db.Exec(`CREATE INDEX IF NOT EXISTS idx_goadmin_role_menu_menu_id ON goadmin_role_menu(menu_id)`) + + // GoAdmin operation_log table + if err := db.Exec(` + CREATE TABLE IF NOT EXISTS goadmin_operation_log ( + id SERIAL PRIMARY KEY, + user_id INT NOT NULL, + path VARCHAR(255) NOT NULL DEFAULT '', + method VARCHAR(10) NOT NULL DEFAULT '', + ip VARCHAR(15) NOT NULL DEFAULT '', + input TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `).Error; err != nil { + return err + } + db.Exec(`CREATE INDEX IF NOT EXISTS idx_goadmin_operation_log_user_id ON goadmin_operation_log(user_id)`) + + // GoAdmin site table + if err := db.Exec(` + CREATE TABLE IF NOT EXISTS goadmin_site ( + id SERIAL PRIMARY KEY, + key VARCHAR(100) NOT NULL DEFAULT '', + value TEXT NOT NULL, + description VARCHAR(3000) DEFAULT '', + state INT NOT NULL DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `).Error; err != nil { + return err + } + db.Exec(`CREATE INDEX IF NOT EXISTS idx_goadmin_site_key ON goadmin_site(key)`) + + // Seed default admin user (password: admin - bcrypt hash) + db.Exec(` + INSERT INTO goadmin_users (username, password, name, avatar) + VALUES ('admin', '$2a$10$sRv1E1XmGXS5HgU7VK3bNOQRZLGDON0.2xvMlz.bKcIzI3pAF1T3y', 'Administrator', '') + ON CONFLICT DO NOTHING + `) + + // Seed default roles + db.Exec(`INSERT INTO goadmin_roles (name, slug) VALUES ('Administrator', 'administrator') ON CONFLICT DO NOTHING`) + db.Exec(`INSERT INTO goadmin_roles (name, slug) VALUES ('Operator', 'operator') ON CONFLICT DO NOTHING`) + + // Seed default permissions + db.Exec(`INSERT INTO goadmin_permissions (name, slug, http_method, http_path) VALUES ('All permissions', '*', '', '*') ON CONFLICT DO NOTHING`) + db.Exec(`INSERT INTO goadmin_permissions (name, slug, http_method, http_path) VALUES ('Dashboard', 'dashboard', 'GET', '/') ON CONFLICT DO NOTHING`) + + // Assign admin user to administrator role (if not already assigned) + db.Exec(` + INSERT INTO goadmin_role_users (role_id, user_id) + SELECT r.id, u.id FROM goadmin_roles r, goadmin_users u + WHERE r.slug = 'administrator' AND u.username = 'admin' + AND NOT EXISTS ( + SELECT 1 FROM goadmin_role_users ru + WHERE ru.role_id = r.id AND ru.user_id = u.id + ) + `) + + // Assign all permissions to administrator role (if not already assigned) + db.Exec(` + INSERT INTO goadmin_role_permissions (role_id, permission_id) + SELECT r.id, p.id FROM goadmin_roles r, goadmin_permissions p + WHERE r.slug = 'administrator' AND p.slug = '*' + AND NOT EXISTS ( + SELECT 1 FROM goadmin_role_permissions rp + WHERE rp.role_id = r.id AND rp.permission_id = p.id + ) + `) + + // Seed default menu items (only if menu is empty) + var menuCount int64 + db.Raw(`SELECT COUNT(*) FROM goadmin_menu`).Scan(&menuCount) + if menuCount == 0 { + db.Exec(` + INSERT INTO goadmin_menu (parent_id, type, "order", title, icon, uri, plugin_name) VALUES + (0, 1, 1, 'Dashboard', 'fa-bar-chart', '/', ''), + (0, 1, 2, 'Admin', 'fa-tasks', '', ''), + (2, 1, 1, 'Users', 'fa-users', '/info/goadmin_users', ''), + (2, 1, 2, 'Roles', 'fa-user', '/info/goadmin_roles', ''), + (2, 1, 3, 'Permissions', 'fa-ban', '/info/goadmin_permissions', ''), + (2, 1, 4, 'Menu', 'fa-bars', '/menu', ''), + (2, 1, 5, 'Operation Log', 'fa-history', '/info/goadmin_operation_log', ''), + (0, 1, 3, 'MyCrib', 'fa-home', '', ''), + (8, 1, 1, 'Users', 'fa-user', '/info/users', ''), + (8, 1, 2, 'Residences', 'fa-building', '/info/residences', ''), + (8, 1, 3, 'Tasks', 'fa-tasks', '/info/tasks', ''), + (8, 1, 4, 'Contractors', 'fa-wrench', '/info/contractors', ''), + (8, 1, 5, 'Documents', 'fa-file', '/info/documents', ''), + (8, 1, 6, 'Notifications', 'fa-bell', '/info/notifications', '') + `) + + // Assign all menus to administrator role + db.Exec(` + INSERT INTO goadmin_role_menu (role_id, menu_id) + SELECT r.id, m.id FROM goadmin_roles r, goadmin_menu m + WHERE r.slug = 'administrator' + `) + } + + log.Info().Msg("GoAdmin migrations completed") + return nil +}