@@ -48,14 +48,18 @@ func (LicenseKey) TableName() string {
|
||||
return "license_key"
|
||||
}
|
||||
|
||||
// GenerateKeyString creates a random license key in MOKO-XXXX-XXXX-XXXX-XXXX format.
|
||||
func GenerateKeyString() (string, error) {
|
||||
// GenerateKeyString creates a random license key in PREFIX-XXXX-XXXX-XXXX-XXXX format.
|
||||
// If prefix is empty, defaults to "MOKO".
|
||||
func GenerateKeyString(prefix string) (string, error) {
|
||||
if prefix == "" {
|
||||
prefix = "MOKO"
|
||||
}
|
||||
b := make([]byte, 16)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return "", err
|
||||
}
|
||||
hex := strings.ToUpper(hex.EncodeToString(b))
|
||||
return fmt.Sprintf("MOKO-%s-%s-%s-%s", hex[0:4], hex[4:8], hex[8:12], hex[12:16]), nil
|
||||
h := strings.ToUpper(hex.EncodeToString(b))
|
||||
return fmt.Sprintf("%s-%s-%s-%s-%s", prefix, h[0:4], h[4:8], h[8:12], h[12:16]), nil
|
||||
}
|
||||
|
||||
// HashKey returns the SHA-256 hash of a raw key string.
|
||||
@@ -65,8 +69,14 @@ func HashKey(rawKey string) string {
|
||||
}
|
||||
|
||||
// CreateLicenseKey generates a new key, stores it in plaintext and hashed, and returns the raw key.
|
||||
// The prefix is looked up from the org's update stream config.
|
||||
func CreateLicenseKey(ctx context.Context, key *LicenseKey) (rawKey string, err error) {
|
||||
rawKey, err = GenerateKeyString()
|
||||
prefix := ""
|
||||
cfg := GetEffectiveConfig(ctx, key.OwnerID, 0)
|
||||
if cfg != nil && cfg.KeyPrefix != "" {
|
||||
prefix = cfg.KeyPrefix
|
||||
}
|
||||
rawKey, err = GenerateKeyString(prefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("GenerateKeyString: %w", err)
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ type UpdateStreamConfig struct {
|
||||
FeedVisibility string `xorm:"VARCHAR(20) NOT NULL DEFAULT 'public'"` // public, no-download, hidden
|
||||
DownloadGating string `xorm:"VARCHAR(20) NOT NULL DEFAULT 'none'"` // none, all, prerelease
|
||||
SupportURL string `xorm:"TEXT"` // wiki or external support page URL
|
||||
KeyPrefix string `xorm:"VARCHAR(20)"` // org-specific license key prefix (e.g. "ACME")
|
||||
// Extension metadata — used in update feed generation.
|
||||
ExtensionName string `xorm:"TEXT"` // element identifier (e.g. pkg_mokowaas, com_mokowaas)
|
||||
DisplayName string `xorm:"TEXT"` // human-readable name (e.g. "Package - MokoWaaS")
|
||||
|
||||
@@ -49,6 +49,7 @@ type updateStreamConfig340 struct {
|
||||
InfoURL string `xorm:"TEXT"`
|
||||
TargetVersion string `xorm:"TEXT"`
|
||||
PHPMinimum string `xorm:"VARCHAR(20)"`
|
||||
KeyPrefix string `xorm:"VARCHAR(20)"`
|
||||
}
|
||||
|
||||
func (updateStreamConfig340) TableName() string {
|
||||
|
||||
@@ -2677,6 +2677,7 @@
|
||||
"repo.licenses.feed_joomla_updates": "Joomla updates.xml",
|
||||
"repo.licenses.feed_dolibarr_updates": "Dolibarr JSON",
|
||||
"repo.licenses.feed_wordpress_updates": "WordPress (PUC JSON)",
|
||||
"repo.licenses.open_feed": "Open in new tab",
|
||||
"repo.licenses.feed_changelog_xml": "Changelog XML (Joomla)",
|
||||
"repo.licenses.master_label": "Master",
|
||||
"repo.licenses.unlimited": "unlimited",
|
||||
@@ -2908,6 +2909,8 @@
|
||||
"org.settings.streams_tag_help": "When licensing is active, release tags with prerelease suffixes must match one of the streams above (e.g. v1.0.0-rc1 matches the -rc stream).",
|
||||
"org.settings.custom_streams": "Custom Stream Definitions (JSON)",
|
||||
"org.settings.custom_streams_help": "JSON array of stream objects. Each needs: name, suffix, description. Example: [{\"name\":\"lts\",\"suffix\":\"-lts\",\"description\":\"Long-term support\"}]",
|
||||
"org.settings.key_prefix": "License Key Prefix",
|
||||
"org.settings.key_prefix_help": "Custom prefix for license keys generated in this org (e.g. ACME, CLIENT). Leave empty for default (MOKO). Max 20 chars, auto-uppercased.",
|
||||
"org.settings.parent_org": "Parent Organization",
|
||||
"org.settings.parent_org_none": "(none — top-level organization)",
|
||||
"org.settings.parent_org_help": "Set a parent org for enterprise hierarchy. Child orgs inherit license packages and master keys from parent orgs.",
|
||||
|
||||
@@ -5,6 +5,7 @@ package org
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/licenses"
|
||||
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/templates"
|
||||
@@ -47,6 +48,7 @@ func SettingsUpdateStreamsPost(ctx *context.Context) {
|
||||
FeedVisibility: ctx.FormString("feed_visibility"),
|
||||
DownloadGating: ctx.FormString("download_gating"),
|
||||
SupportURL: ctx.FormString("support_url"),
|
||||
KeyPrefix: strings.ToUpper(strings.TrimSpace(ctx.FormString("key_prefix"))),
|
||||
ExtensionName: ctx.FormString("extension_name"),
|
||||
DisplayName: ctx.FormString("display_name"),
|
||||
Description: ctx.FormString("feed_description"),
|
||||
|
||||
@@ -25,14 +25,16 @@
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<h4 class="ui top attached header">
|
||||
{{svg "octicon-key" 16}} {{ctx.Locale.Tr "repo.licenses.packages"}}
|
||||
<details id="new-package-details">
|
||||
<h4 class="ui top attached header tw-flex tw-items-center tw-justify-between">
|
||||
<span>{{svg "octicon-key" 16}} {{ctx.Locale.Tr "repo.licenses.packages"}}</span>
|
||||
{{if .IsRepoAdmin}}
|
||||
<summary class="ui primary small button">{{svg "octicon-plus" 14}} {{ctx.Locale.Tr "repo.licenses.new_package"}}</summary>
|
||||
{{end}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
{{if .IsRepoAdmin}}
|
||||
<details class="tw-mb-4">
|
||||
<summary class="ui primary button">{{svg "octicon-plus" 14}} {{ctx.Locale.Tr "repo.licenses.new_package"}}</summary>
|
||||
<div class="tw-mt-4">
|
||||
<div class="tw-mb-4">
|
||||
<form class="ui form" method="post" action="{{$.Org.HomeLink}}/-/licenses/packages">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="two fields">
|
||||
@@ -80,9 +82,9 @@
|
||||
</div>
|
||||
<button class="ui primary button" type="submit">{{ctx.Locale.Tr "repo.licenses.create_package"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
{{if .LicensePackages}}
|
||||
<table class="ui compact table">
|
||||
<thead>
|
||||
|
||||
@@ -37,6 +37,12 @@
|
||||
<p class="help">{{ctx.Locale.Tr "org.settings.feed_visibility_help"}}</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "org.settings.key_prefix"}}</label>
|
||||
<input name="key_prefix" value="{{.StreamConfig.KeyPrefix}}" placeholder="MOKO" maxlength="20">
|
||||
<p class="help">{{ctx.Locale.Tr "org.settings.key_prefix_help"}}</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "org.settings.download_gating"}}</label>
|
||||
<select name="download_gating" class="ui dropdown">
|
||||
|
||||
@@ -26,8 +26,12 @@
|
||||
{{end}}
|
||||
|
||||
{{/* ── License Packages ── */}}
|
||||
<h4 class="ui top attached header">
|
||||
{{svg "octicon-key" 16}} {{ctx.Locale.Tr "repo.licenses.packages"}}
|
||||
<details id="new-package-details">
|
||||
<h4 class="ui top attached header tw-flex tw-items-center tw-justify-between">
|
||||
<span>{{svg "octicon-key" 16}} {{ctx.Locale.Tr "repo.licenses.packages"}}</span>
|
||||
{{if .IsRepoAdmin}}
|
||||
<summary class="ui primary small button">{{svg "octicon-plus" 14}} {{ctx.Locale.Tr "repo.licenses.new_package"}}</summary>
|
||||
{{end}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
{{if .LicensePackages}}
|
||||
@@ -90,12 +94,9 @@
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{/* ── Create New License Package ── */}}
|
||||
{{/* ── Create New License Package (form panel, toggled by header button) ── */}}
|
||||
{{if .IsRepoAdmin}}
|
||||
<div class="tw-mt-4">
|
||||
<details>
|
||||
<summary class="ui primary button">{{svg "octicon-plus" 14}} {{ctx.Locale.Tr "repo.licenses.new_package"}}</summary>
|
||||
<div class="ui segment tw-mt-2">
|
||||
<div class="ui attached segment">
|
||||
<form class="ui form" method="post" action="{{.RepoLink}}/licenses/packages">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="two fields">
|
||||
@@ -143,9 +144,8 @@
|
||||
</div>
|
||||
<button class="ui primary button" type="submit">{{ctx.Locale.Tr "repo.licenses.create_package"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</details>
|
||||
{{end}}
|
||||
|
||||
{{/* ── Issued Keys ── */}}
|
||||
@@ -272,6 +272,7 @@
|
||||
<div class="ui action input tw-w-full">
|
||||
<input class="js-feed-url-joomla" type="text" readonly value="{{.Repository.HTMLURL ctx}}/updates.xml" onclick="this.select()">
|
||||
<button class="ui button" data-clipboard-target=".js-feed-url-joomla" data-tooltip-content="{{ctx.Locale.Tr "copy_url"}}">{{svg "octicon-copy" 14}}</button>
|
||||
<a class="ui button" href="{{.Repository.HTMLURL ctx}}/updates.xml" target="_blank" data-tooltip-content="{{ctx.Locale.Tr "repo.licenses.open_feed"}}">{{svg "octicon-link-external" 14}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -281,6 +282,7 @@
|
||||
<div class="ui action input tw-w-full">
|
||||
<input class="js-feed-url-dolibarr" type="text" readonly value="{{.Repository.HTMLURL ctx}}/updates/dolibarr.json" onclick="this.select()">
|
||||
<button class="ui button" data-clipboard-target=".js-feed-url-dolibarr" data-tooltip-content="{{ctx.Locale.Tr "copy_url"}}">{{svg "octicon-copy" 14}}</button>
|
||||
<a class="ui button" href="{{.Repository.HTMLURL ctx}}/updates/dolibarr.json" target="_blank" data-tooltip-content="{{ctx.Locale.Tr "repo.licenses.open_feed"}}">{{svg "octicon-link-external" 14}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -290,6 +292,7 @@
|
||||
<div class="ui action input tw-w-full">
|
||||
<input class="js-feed-url-wordpress" type="text" readonly value="{{.Repository.HTMLURL ctx}}/updates/wordpress.json" onclick="this.select()">
|
||||
<button class="ui button" data-clipboard-target=".js-feed-url-wordpress" data-tooltip-content="{{ctx.Locale.Tr "copy_url"}}">{{svg "octicon-copy" 14}}</button>
|
||||
<a class="ui button" href="{{.Repository.HTMLURL ctx}}/updates/wordpress.json" target="_blank" data-tooltip-content="{{ctx.Locale.Tr "repo.licenses.open_feed"}}">{{svg "octicon-link-external" 14}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -298,6 +301,7 @@
|
||||
<div class="ui action input tw-w-full">
|
||||
<input class="js-feed-url-changelog" type="text" readonly value="{{.Repository.HTMLURL ctx}}/changelog.xml" onclick="this.select()">
|
||||
<button class="ui button" data-clipboard-target=".js-feed-url-changelog" data-tooltip-content="{{ctx.Locale.Tr "copy_url"}}">{{svg "octicon-copy" 14}}</button>
|
||||
<a class="ui button" href="{{.Repository.HTMLURL ctx}}/changelog.xml" target="_blank" data-tooltip-content="{{ctx.Locale.Tr "repo.licenses.open_feed"}}">{{svg "octicon-link-external" 14}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user