diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index 321a0666bb..1ca87c1d1e 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -2660,6 +2660,9 @@ "repo.licenses.key_revoked": "License key revoked.", "repo.licenses.master_key_created": "Master License Key Created", "repo.licenses.master_key_created_copy": "This is your organization master key with unlimited access to all update channels. Copy it now — it will not be shown again.", + "repo.licenses.regenerate_master_key": "Regenerate", + "repo.licenses.regenerate_master_key_help": "Deactivates the current master key and generates a new one. The new key will be shown once.", + "repo.licenses.master_key_regenerated": "Master key regenerated. Copy the new key below — it will not be shown again.", "repo.licenses.update_feeds": "Update Feed URLs", "repo.licenses.edit_key": "Edit License Key", "repo.licenses.licensee_name": "Licensee Name", diff --git a/routers/web/repo/licenses.go b/routers/web/repo/licenses.go index 7f31859bd2..5182ef3257 100644 --- a/routers/web/repo/licenses.go +++ b/routers/web/repo/licenses.go @@ -90,6 +90,10 @@ func Licenses(ctx *context.Context) { } } + // Always load the master key for display (prefix + status). + masterKey, _ := licenses.GetMasterKey(ctx, ownerID) + ctx.Data["MasterKey"] = masterKey + pkgs, err := licenses.ListLicensePackages(ctx, ownerID) if err != nil { ctx.ServerError("ListLicensePackages", err) @@ -203,6 +207,54 @@ func LicensesCreatePackage(ctx *context.Context) { ctx.Redirect(ctx.Repo.RepoLink + "/licenses") } +// LicensesRegenerateMasterKey handles POST to regenerate the master key. +func LicensesRegenerateMasterKey(ctx *context.Context) { + ownerID := ctx.Repo.Repository.OwnerID + + // Deactivate the old master key. + oldKey, _ := licenses.GetMasterKey(ctx, ownerID) + if oldKey != nil { + oldKey.IsActive = false + _ = licenses.UpdateLicenseKey(ctx, oldKey) + } + + // Find the master package. + pkgs, err := licenses.ListLicensePackages(ctx, ownerID) + if err != nil { + ctx.ServerError("ListLicensePackages", err) + return + } + var masterPkg *licenses.LicensePackage + for _, pkg := range pkgs { + if pkg.Name == licenses.MasterPackageName { + masterPkg = pkg + break + } + } + if masterPkg == nil { + ctx.Flash.Error("Master package not found") + ctx.Redirect(ctx.Repo.RepoLink + "/licenses") + return + } + + // Create a new master key. + newKey := &licenses.LicenseKey{ + PackageID: masterPkg.ID, + OwnerID: ownerID, + IsInternal: true, + IsActive: true, + } + rawKey, err := licenses.CreateLicenseKey(ctx, newKey) + if err != nil { + ctx.ServerError("CreateLicenseKey", err) + return + } + + ctx.Flash.Success(ctx.Tr("repo.licenses.master_key_regenerated")) + ctx.Data["NewMasterKey"] = rawKey + Licenses(ctx) +} + // LicensesGenerateKey handles POST to generate a new key from a package. func LicensesGenerateKey(ctx *context.Context) { packageID, _ := strconv.ParseInt(ctx.FormString("package_id"), 10, 64) diff --git a/routers/web/web.go b/routers/web/web.go index 19d71398c5..4a8ecc18ae 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1549,6 +1549,7 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) { m.Post("/packages/{id}/archive", repo.LicensesArchivePackage) m.Post("/packages/{id}/unarchive", repo.LicensesUnarchivePackage) m.Post("/keys/generate", repo.LicensesGenerateKey) + m.Post("/master-key/regenerate", repo.LicensesRegenerateMasterKey) m.Get("/keys/{id}/edit", repo.LicensesEditKey) m.Post("/keys/{id}/edit", repo.LicensesEditKeyPost) m.Post("/keys/{id}/revoke", repo.LicensesRevokeKey) diff --git a/templates/repo/licenses.tmpl b/templates/repo/licenses.tmpl index e67c132935..aebd76106c 100644 --- a/templates/repo/licenses.tmpl +++ b/templates/repo/licenses.tmpl @@ -25,12 +25,32 @@ {{end}} + {{/* ── Master Key Info ── */}} + {{if and .MasterKey .IsRepoAdmin}} +
+
+ {{ctx.Locale.Tr "repo.licenses.master_label"}} + {{.MasterKey.KeyPrefix}} + {{if .MasterKey.IsActive}} + {{ctx.Locale.Tr "repo.licenses.active"}} + {{else}} + {{ctx.Locale.Tr "repo.licenses.inactive"}} + {{end}} +
+
+ {{.CsrfTokenHtml}} + +
+
+ {{end}} + {{/* ── License Packages ── */}} -

{{svg "octicon-key" 16}} {{ctx.Locale.Tr "repo.licenses.packages"}} {{if .IsRepoAdmin}} - {{svg "octicon-plus" 14}} {{ctx.Locale.Tr "repo.licenses.new_package"}} + {{end}}

@@ -94,60 +114,6 @@ {{end}}
- {{/* ── Create New License Package (form panel, toggled by header button) ── */}} - {{if .IsRepoAdmin}} -
-
- {{.CsrfTokenHtml}} -
-
- - -
-
- - -
-
-
-
- - -

0 = {{ctx.Locale.Tr "repo.licenses.lifetime"}}

-
-
- - -

0 = {{ctx.Locale.Tr "repo.licenses.unlimited"}}

-
-
- - -

{{ctx.Locale.Tr "repo.licenses.domain_lock_hours_help"}}

-
-
- {{template "shared/combolist" dict "Name" "allowed_channels" "Title" (ctx.Locale.Tr "repo.licenses.channels") "Items" $.ChannelItems "SelectedValues" "" "EmptyText" (ctx.Locale.Tr "repo.licenses.all_channels") "Icon" "octicon-tag"}} -

{{ctx.Locale.Tr "repo.licenses.channels_help"}}

-
-
-
- - -

{{ctx.Locale.Tr "repo.licenses.repo_scope_help"}}

-
- -
-
-
- {{end}} - {{/* ── Issued Keys ── */}} {{if or .LicenseKeys .SearchQuery}}

@@ -183,7 +149,7 @@
{{if .KeyRaw}}{{.KeyRaw}}{{else}}{{.KeyPrefix}}{{end}} - {{if .KeyRaw}}{{end}} + {{if .IsInternal}} {{ctx.Locale.Tr "repo.licenses.master_label"}}{{end}}
@@ -309,6 +275,60 @@ +{{/* ── Create New License Package Modal ── */}} +{{if .IsRepoAdmin}} + +{{end}} + {{/* ── Delete Package Confirmation Modal ── */}}