Compare commits

..

3 Commits

11 changed files with 82 additions and 308 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

-118
View File
@@ -1,118 +0,0 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package admin
import (
"io"
"net/http"
"os"
"path/filepath"
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/log"
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/setting"
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/templates"
"git.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["HasLogo"] = fileExists(filepath.Join(imgDir, "logo.png"))
ctx.Data["HasFavicon"] = fileExists(filepath.Join(imgDir, "favicon.png"))
ctx.HTML(http.StatusOK, tplBranding)
}
// 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 "logo":
filename = "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()
// Validate file size (max 2MB)
if header.Size > 2*1024*1024 {
ctx.Flash.Error("File too large (max 2MB)")
ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
return
}
// Ensure the custom image directory exists
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
}
// Write the file
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
}
// Also remove SVG override if present (PNG should take priority)
svgPath := filepath.Join(imgDir, filename[:len(filename)-4]+".svg")
if fileExists(svgPath) {
os.Remove(svgPath)
log.Info("Removed SVG override: %s", 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")
}
func fileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
+1 -9
View File
@@ -93,7 +93,7 @@ func home(ctx *context.Context, viewRepositories bool) {
ListOptions: db.ListOptions{Page: 1, PageSize: 25},
}
members, membersIsPublic, err := organization.FindOrgMembers(ctx, opts)
members, _, err := organization.FindOrgMembers(ctx, opts)
if err != nil {
ctx.ServerError("FindOrgMembers", err)
return
@@ -102,14 +102,6 @@ func home(ctx *context.Context, viewRepositories bool) {
const orgOverviewTeamsLimit = 5
ctx.Data["OrgOverviewMembers"] = members
ctx.Data["OrgOverviewTeams"] = ctx.Org.Teams[:min(len(ctx.Org.Teams), orgOverviewTeamsLimit)]
ctx.Data["Members"] = members
ctx.Data["NumMembers"] = len(members)
ctx.Data["Teams"] = ctx.Org.Teams
ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember
ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
ctx.Data["IsPublicMember"] = func(uid int64) bool {
return membersIsPublic[uid]
}
ctx.Data["DisableNewPullMirrors"] = setting.Mirror.DisableNewPull
ctx.Data["ShowMemberAndTeamTab"] = ctx.Org.IsMember || len(members) > 0
-5
View File
@@ -765,11 +765,6 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
m.Get("/settings", admin.ConfigSettings)
})
m.Group("/branding", func() {
m.Get("", admin.Branding)
m.Post("/upload", admin.BrandingUpload)
})
m.Group("/monitor", func() {
m.Get("/stats", admin.MonitorStats)
m.Get("/cron", admin.CronTasks)
-78
View File
@@ -1,78 +0,0 @@
{{template "admin/layout_head" (dict "pageClass" "admin branding")}}
<div class="admin-setting-content">
<h4 class="ui top attached header">
{{svg "octicon-paintbrush" 16}} Branding
</h4>
<div class="ui attached segment">
<p>Upload custom branding images. Changes take effect immediately — no restart required.</p>
<div class="ui three stackable cards">
<!-- Nav Icon -->
<div class="card">
<div class="content">
<div class="header">Nav Icon</div>
<div class="meta">Top-left corner (30x30px recommended)</div>
</div>
<div class="image tw-p-4 tw-text-center" style="background: var(--color-body);">
<img src="{{AssetUrlPrefix}}/img/logo-small.png?v={{ctx.CspScriptNonce}}" style="max-height: 64px;" onerror="this.src='{{AssetUrlPrefix}}/img/logo.png'">
</div>
<div class="extra content">
<form method="post" action="{{AppSubUrl}}/-/admin/branding/upload" enctype="multipart/form-data">
{{.CsrfTokenHtml}}
<input type="hidden" name="type" value="nav-icon">
<div class="ui action input tw-w-full">
<input type="file" name="file" accept="image/png,image/svg+xml" required>
<button type="submit" class="ui primary button">{{svg "octicon-upload" 14}} Upload</button>
</div>
</form>
{{if .HasNavIcon}}<span class="ui green label tw-mt-2">Custom</span>{{else}}<span class="ui grey label tw-mt-2">Default</span>{{end}}
</div>
</div>
<!-- Login Logo -->
<div class="card">
<div class="content">
<div class="header">Login Logo</div>
<div class="meta">Login page and homepage (wide format recommended)</div>
</div>
<div class="image tw-p-4 tw-text-center" style="background: var(--color-body);">
<img src="{{AssetUrlPrefix}}/img/logo.png?v={{ctx.CspScriptNonce}}" style="max-height: 64px;">
</div>
<div class="extra content">
<form method="post" action="{{AppSubUrl}}/-/admin/branding/upload" enctype="multipart/form-data">
{{.CsrfTokenHtml}}
<input type="hidden" name="type" value="logo">
<div class="ui action input tw-w-full">
<input type="file" name="file" accept="image/png,image/svg+xml" required>
<button type="submit" class="ui primary button">{{svg "octicon-upload" 14}} Upload</button>
</div>
</form>
{{if .HasLogo}}<span class="ui green label tw-mt-2">Custom</span>{{else}}<span class="ui grey label tw-mt-2">Default</span>{{end}}
</div>
</div>
<!-- Favicon -->
<div class="card">
<div class="content">
<div class="header">Favicon</div>
<div class="meta">Browser tab icon (256x256px recommended)</div>
</div>
<div class="image tw-p-4 tw-text-center" style="background: var(--color-body);">
<img src="{{AssetUrlPrefix}}/img/favicon.png?v={{ctx.CspScriptNonce}}" style="max-height: 64px;">
</div>
<div class="extra content">
<form method="post" action="{{AppSubUrl}}/-/admin/branding/upload" enctype="multipart/form-data">
{{.CsrfTokenHtml}}
<input type="hidden" name="type" value="favicon">
<div class="ui action input tw-w-full">
<input type="file" name="file" accept="image/png,image/svg+xml,image/x-icon" required>
<button type="submit" class="ui primary button">{{svg "octicon-upload" 14}} Upload</button>
</div>
</form>
{{if .HasFavicon}}<span class="ui green label tw-mt-2">Custom</span>{{else}}<span class="ui grey label tw-mt-2">Default</span>{{end}}
</div>
</div>
</div>
</div>
</div>
{{template "admin/layout_tail" .}}
-3
View File
@@ -84,9 +84,6 @@
</div>
</details>
{{end}}
<a class="{{if .PageIsAdminBranding}}active {{end}}item" href="{{AppSubUrl}}/-/admin/branding">
{{svg "octicon-paintbrush" 16}} Branding
</a>
<details class="item toggleable-item" {{if or .PageIsAdminConfig}}open{{end}}>
<summary>{{ctx.Locale.Tr "admin.config"}}</summary>
<div class="menu">
+1 -1
View File
@@ -2,7 +2,7 @@
<div class="navbar-left">
<!-- the logo -->
<a class="item" id="navbar-logo" href="{{AppSubUrl}}/" aria-label="{{if .IsSigned}}{{ctx.Locale.Tr "dashboard"}}{{else}}{{ctx.Locale.Tr "home_title"}}{{end}}">
<img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo-small.png" alt="{{ctx.Locale.Tr "logo"}}" aria-hidden="true" onerror="this.src='{{AssetUrlPrefix}}/img/logo.png'">
<img width="30" height="30" src="https://mokoconsulting.tech/images/branding/logo.png" alt="{{ctx.Locale.Tr "logo"}}" aria-hidden="true">
</a>
<!-- mobile right menu, it must be here because in mobile view, each item is a flex column, the first item is a full row column -->
+1 -1
View File
@@ -2,7 +2,7 @@
<div role="main" aria-label="{{if .IsSigned}}{{ctx.Locale.Tr "dashboard"}}{{else}}{{ctx.Locale.Tr "home_title"}}{{end}}" class="page-content home">
<div class="tw-mb-8 tw-px-8">
<div class="center">
<img class="logo" width="220" height="220" src="{{AssetUrlPrefix}}/img/logo.png" alt="{{ctx.Locale.Tr "logo"}}">
<img class="logo" width="220" height="220" src="https://mokoconsulting.tech/images/branding/logo.png" alt="{{ctx.Locale.Tr "logo"}}">
<div class="hero">
<h1 class="ui icon header title tw-text-balance">
{{AppName}}
+1 -1
View File
@@ -21,7 +21,7 @@
<div class="ui container tw-flex">
<div class="item tw-flex-1">
<a href="{{AppSubUrl}}/" aria-label="{{ctx.Locale.Tr "home_title"}}">
<img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo-small.png" alt="{{ctx.Locale.Tr "logo"}}" aria-hidden="true">
<img width="30" height="30" src="https://mokoconsulting.tech/images/branding/logo.png" alt="{{ctx.Locale.Tr "logo"}}" aria-hidden="true">
</a>
</div>
<div class="item">
+78 -88
View File
@@ -1,103 +1,93 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later
VERSION: 05.05.00
VERSION: 05.00.00
-->
<updates>
<update>
<name>MokoGitea</name>
<description>MokoGitea dev build.</description>
<description>MokoGitea update</description>
<element>mokogitea</element>
<type>application</type>
<client>site</client>
<version>05.05.00-dev</version>
<creationDate>2026-05-30</creationDate>
<infourl title="MokoGitea">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/development</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/development/mokogitea-05.05.00-dev.zip</downloadurl>
</downloads>
<sha256>4fee9eb03e4b819a63bce2ceb54fdce0d3eb8bf5b31460fcc42e5ecd75cc856e</sha256>
<tags><tag>dev</tag></tags>
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="go" version=".*"/>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea alpha build.</description>
<element>mokogitea</element>
<type>application</type>
<client>site</client>
<version>05.05.00-alpha</version>
<creationDate>2026-05-30</creationDate>
<infourl title="MokoGitea">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/alpha</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/alpha/mokogitea-05.05.00-alpha.zip</downloadurl>
</downloads>
<sha256>4fee9eb03e4b819a63bce2ceb54fdce0d3eb8bf5b31460fcc42e5ecd75cc856e</sha256>
<tags><tag>alpha</tag></tags>
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="go" version=".*"/>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea beta build.</description>
<element>mokogitea</element>
<type>application</type>
<client>site</client>
<version>05.05.00-beta</version>
<creationDate>2026-05-30</creationDate>
<infourl title="MokoGitea">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/beta</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/beta/mokogitea-05.05.00-beta.zip</downloadurl>
</downloads>
<sha256>4fee9eb03e4b819a63bce2ceb54fdce0d3eb8bf5b31460fcc42e5ecd75cc856e</sha256>
<tags><tag>beta</tag></tags>
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="go" version=".*"/>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea rc build.</description>
<element>mokogitea</element>
<type>application</type>
<client>site</client>
<version>05.05.00-rc</version>
<creationDate>2026-05-30</creationDate>
<infourl title="MokoGitea">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/release-candidate</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/release-candidate/mokogitea-05.05.00-rc.zip</downloadurl>
</downloads>
<sha256>4fee9eb03e4b819a63bce2ceb54fdce0d3eb8bf5b31460fcc42e5ecd75cc856e</sha256>
<tags><tag>rc</tag></tags>
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="go" version=".*"/>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea stable build.</description>
<element>mokogitea</element>
<type>application</type>
<client>site</client>
<version>05.05.00</version>
<creationDate>2026-05-30</creationDate>
<infourl title='MokoGitea'>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/stable</infourl>
<downloads>
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/stable/mokogitea-05.05.00.zip</downloadurl>
</downloads>
<sha256>4fee9eb03e4b819a63bce2ceb54fdce0d3eb8bf5b31460fcc42e5ecd75cc856e</sha256>
<version>05.00.00</version>
<client>server</client>
<tags><tag>stable</tag></tags>
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
<infourl title="MokoGitea">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.05.00.00</infourl>
<downloads>
<downloadurl type="full" format="docker">git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.05.00.00</downloadurl>
</downloads>
<sha256></sha256>
<targetplatform name="mokogitea" version="((1\.25\.)|(1\.26\.))" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea update</description>
<element>mokogitea</element>
<type>application</type>
<version>05.00.00</version>
<client>server</client>
<tags><tag>rc</tag></tags>
<infourl title="MokoGitea RC">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.05.00.00</infourl>
<downloads>
<downloadurl type="full" format="docker">git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.05.00.00</downloadurl>
</downloads>
<sha256></sha256>
<targetplatform name="mokogitea" version="((1\.25\.)|(1\.26\.))" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea update</description>
<element>mokogitea</element>
<type>application</type>
<version>05.00.00</version>
<client>server</client>
<tags><tag>beta</tag></tags>
<infourl title="MokoGitea Beta">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.05.00.00</infourl>
<downloads>
<downloadurl type="full" format="docker">git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.05.00.00</downloadurl>
</downloads>
<sha256></sha256>
<targetplatform name="mokogitea" version="((1\.25\.)|(1\.26\.))" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea update</description>
<element>mokogitea</element>
<type>application</type>
<version>05.00.00</version>
<client>server</client>
<tags><tag>alpha</tag></tags>
<infourl title="MokoGitea Alpha">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.05.00.00</infourl>
<downloads>
<downloadurl type="full" format="docker">git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.05.00.00</downloadurl>
</downloads>
<sha256></sha256>
<targetplatform name="mokogitea" version="((1\.25\.)|(1\.26\.))" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea update</description>
<element>mokogitea</element>
<type>application</type>
<version>06.00.00-dev</version>
<client>server</client>
<tags><tag>development</tag></tags>
<infourl title="MokoGitea Dev">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/src/branch/dev</infourl>
<downloads>
<downloadurl type="full" format="docker">git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.06.00.00-dev</downloadurl>
</downloads>
<sha256></sha256>
<targetplatform name="mokogitea" version="((1\.25\.)|(1\.26\.))" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="go" version=".*" />
</update>
</updates>
-4
View File
@@ -11,10 +11,6 @@
width: 240px;
}
.flex-container-nav .ui.menu .item {
text-align: left;
}
/* wide sidebar on the right, used in frontpage */
.flex-container-sidebar {
width: 35%;