Files
Jonathan Miller 9a5720e8ad
Branch Policy Check / Verify merge target (pull_request) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
PR RC Release / Build RC Release (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Branch Cleanup / Delete merged branch (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
chore: rename Go module from git. to code.mokoconsulting.tech (#336)
Full namespace migration: update the Go module path and all import
statements from git.mokoconsulting.tech to code.mokoconsulting.tech.
Also updates all URL references in templates, workflows, configs,
tests, and documentation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-31 10:28:25 -05:00

206 lines
5.8 KiB
Go

// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package admin
import (
"io"
"net/http"
"os"
"path/filepath"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/log"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/setting"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/templates"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/services/context"
)
const tplBranding templates.TplName = "admin/branding"
// brandingImageDir returns the path to the custom branding images directory.
func brandingImageDir() string {
return filepath.Join(setting.CustomPath, "public", "assets", "img")
}
// Branding shows the admin branding page.
func Branding(ctx *context.Context) {
ctx.Data["Title"] = "Branding"
ctx.Data["PageIsAdminBranding"] = true
imgDir := brandingImageDir()
ctx.Data["HasNavIcon"] = fileExists(filepath.Join(imgDir, "logo-small.png"))
ctx.Data["HasLoginLogo"] = fileExists(filepath.Join(imgDir, "login-logo.png"))
ctx.Data["HasFavicon"] = fileExists(filepath.Join(imgDir, "favicon.png"))
ctx.Data["MetaDescription"] = setting.UI.Meta.Description
ctx.Data["MetaAuthor"] = setting.UI.Meta.Author
ctx.Data["HelpURL"] = setting.HelpURL
ctx.Data["SupportURL"] = setting.SupportURL
ctx.HTML(http.StatusOK, tplBranding)
}
// BrandingSettings handles the text branding form submission.
func BrandingSettings(ctx *context.Context) {
appName := ctx.FormString("app_name")
description := ctx.FormString("description")
helpURL := ctx.FormString("help_url")
supportURL := ctx.FormString("support_url")
author := ctx.FormString("author")
// Update in-memory settings
if appName != "" {
setting.AppName = appName
}
if description != "" {
setting.UI.Meta.Description = description
}
setting.HelpURL = helpURL
setting.SupportURL = supportURL
if author != "" {
setting.UI.Meta.Author = author
}
// Persist to app.ini
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
if err != nil {
ctx.Flash.Error("Failed to load config: " + err.Error())
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
return
}
if appName != "" {
cfg.Section("").Key("APP_NAME").SetValue(appName)
}
if description != "" {
cfg.Section("ui.meta").Key("DESCRIPTION").SetValue(description)
}
cfg.Section("").Key("HELP_URL").SetValue(helpURL)
cfg.Section("").Key("SUPPORT_URL").SetValue(supportURL)
if author != "" {
cfg.Section("ui.meta").Key("AUTHOR").SetValue(author)
}
if err := cfg.SaveTo(setting.CustomConf); err != nil {
ctx.Flash.Error("Failed to save config: " + err.Error())
log.Error("SaveTo %s: %v", setting.CustomConf, err)
} else {
ctx.Flash.Success("Branding settings saved")
log.Info("Branding settings updated: AppName=%s, Author=%s", appName, author)
}
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
}
// BrandingUpload handles branding image uploads.
func BrandingUpload(ctx *context.Context) {
imageType := ctx.FormString("type")
if imageType == "" {
ctx.Flash.Error("No image type specified")
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
return
}
var filename string
switch imageType {
case "nav-icon":
filename = "logo-small.png"
case "login-logo":
filename = "login-logo.png"
case "favicon":
filename = "favicon.png"
default:
ctx.Flash.Error("Invalid image type: " + imageType)
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
return
}
file, header, err := ctx.Req.FormFile("file")
if err != nil {
ctx.Flash.Error("Upload failed: " + err.Error())
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
return
}
defer file.Close()
if header.Size > 2*1024*1024 {
ctx.Flash.Error("File too large (max 2MB)")
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
return
}
imgDir := brandingImageDir()
if err := os.MkdirAll(imgDir, 0o755); err != nil {
ctx.Flash.Error("Failed to create image directory")
log.Error("MkdirAll %s: %v", imgDir, err)
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
return
}
destPath := filepath.Join(imgDir, filename)
dest, err := os.Create(destPath)
if err != nil {
ctx.Flash.Error("Failed to save image")
log.Error("Create %s: %v", destPath, err)
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
return
}
defer dest.Close()
if _, err := io.Copy(dest, file); err != nil {
ctx.Flash.Error("Failed to write image")
log.Error("Copy to %s: %v", destPath, err)
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
return
}
// Remove SVG override if present
svgPath := filepath.Join(imgDir, filename[:len(filename)-4]+".svg")
if fileExists(svgPath) {
os.Remove(svgPath)
}
ctx.Flash.Success("Branding image updated: " + imageType)
log.Info("Branding image uploaded: %s (%d bytes)", filename, header.Size)
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
}
// BrandingReset removes a custom branding image, reverting to the built-in default.
func BrandingReset(ctx *context.Context) {
imageType := ctx.FormString("type")
var filename string
switch imageType {
case "nav-icon":
filename = "logo-small.png"
case "login-logo":
filename = "login-logo.png"
case "favicon":
filename = "favicon.png"
default:
ctx.Flash.Error("Invalid image type")
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
return
}
path := filepath.Join(brandingImageDir(), filename)
if fileExists(path) {
if err := os.Remove(path); err != nil {
ctx.Flash.Error("Failed to remove custom image")
log.Error("Remove %s: %v", path, err)
} else {
ctx.Flash.Success("Reset to default: " + imageType)
log.Info("Branding reset to default: %s", filename)
}
} else {
ctx.Flash.Info("Already using default: " + imageType)
}
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
}
func fileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}