- API: return 500 on GetCustomFieldsByOwner failure instead of silently
swallowing the error
- resolveExtensionMetadata: add DownloadGating/KeyPrefix to metadata
struct instead of mutating the caller's cfg pointer (side effect)
- resolveExtensionMetadata: add Description custom field mapping
- Composer: use meta.PHPMinimum instead of bypassing the cascade
- Web form: flash error on custom field save failure instead of silent log
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add `custom_fields` map to IssueTemplate struct so templates can specify
default values (e.g. `Priority: Medium`). On new issue form, org-level
issue-scoped fields appear in the sidebar with template defaults pre-selected.
NewIssuePost saves the values after issue creation. The API create issue
endpoint also accepts `custom_fields` by name.
Closes#493
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
API endpoints:
- GET/POST/DELETE /api/v1/orgs/{org}/custom-fields — org field definitions
- GET/PUT /api/v1/repos/{owner}/{repo}/metadata — repo-scoped field values
- GET/PUT /api/v1/repos/{owner}/{repo}/issues/{index}/custom-fields — issue values
All endpoints use field names as keys (not IDs) for ergonomic access.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The em dash in the TODO comment was encoded as invalid UTF-8, causing
a Go compiler error. Replace with ASCII hyphen.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment out custom-fields API routes in api.go that referenced handler
functions from the deleted routers/api/v1/repo/custom_field.go. Remove
the unreferenced modules/structs/custom_field.go and the duplicate
v1_25/v323 migration (superseded by v1_27/v343).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The old API file references non-existent struct names and fields.
Will be rebuilt properly after the web UI is complete.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SECURITY: Add verifyPackageOwnership/verifyKeyOwnership checks to
all API handlers that accept ID parameters. Prevents cross-org
access where an admin of org A could modify org B's license data.
FIX: RepoScope validation now properly parses JSON arrays using
json.Unmarshal instead of strings.Contains. The old approach matched
substrings (repo ID "2" matched inside "12"). Now uses typed int64
comparison.
FIX: Add {{$.CsrfTokenHtml}} to both delete confirmation modal
forms (package and key) in repo and org templates. Without CSRF
tokens, the form-fetch-action POST requests would be rejected.
FIX: HTML-escape release notes in WordPress changelog to prevent
XSS via malicious release note content reaching WP admin dashboards.
FIX: Parse AllowedChannels JSON format before comma-split fallback
to avoid garbage values from splitting JSON arrays by comma.
FIX: Add missing third return value (false) on error path in
validateUpdateKey to prevent compile error.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SECURITY: ValidateLicenseKeyForRepo() now checks the package's
RepoScope field against the requesting repo ID. A package scoped
to repo A will reject keys when accessed from repo B's update feed.
Update server and download gating both use the new function.
Master/internal keys bypass repo scope checks.
RepoScope supports: "all" (any repo), single repo ID string,
or JSON array of repo IDs like ["1","5","12"].
Also adds POST /license-keys/{id}/revoke API endpoint that was
missing from the API but existed in web handlers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Migration v340: sync all missing columns (key_raw, payment_ref,
last_heartbeat_unix, is_archived, licensing_enabled, download_gating,
support_url, and all extension metadata fields).
Package archiving (#384): add IsArchived field with archive/unarchive
handlers and collapsible "Archived Packages" section in templates.
Existing keys from archived packages continue to work.
Expanded delete permissions (#385): org owners and site admins can
permanently delete packages and keys (previously site admin only).
Search (#392): server-side search across key_prefix, key_raw,
licensee_name, licensee_email, domain_restriction, and payment_ref
via ?q= query parameter on both repo and org licenses pages.
Sortable tables (#390): Fomantic UI sortable class on keys table
with new Domain column showing DomainRestriction per key.
Download gating (#347): three modes — none, prerelease-only, and
all downloads. CheckDownloadGating() intercepts both release
attachment and git archive download handlers.
Support URL (#393): configurable SupportURL field on
UpdateStreamConfig for wiki or external site links.
Changelog XML (#343): ServeChangelogXML endpoint at /changelog.xml
generates Joomla-compatible changelog from release notes. Parses
Keep-a-Changelog markdown sections into <security>, <fix>,
<addition>, <change>, <remove>, <note> XML elements.
API renew (#387): POST /license-keys/{id}/renew endpoint extends
key expiration by package duration.
Closes#384, #385, #386, #387, #389, #390, #392, #393
Refs #343, #346, #347
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- chore: remove stale mustNotBeArchived FIXME (CanEnableEditor no longer exists)
- fix(routes): remove dead /cherry-pick/{sha} route — replaced by /_cherrypick/
- fix(feed): use full ref name instead of ShortName for file feed revision
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- fix(templates): add required validation to issue dropdown fields
- refactor(ts): remove redundant `handled` field from MarkdownHandleIndentionResult
- refactor(go): rename HasOrgOrUserVisible to IsOwnerVisibleToDoer for clarity
Refs #311, #334
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Add key editing, domain enforcement, purchase webhooks, public
validation API, channels multiselect, Joomla downloadkey element,
licensing feature toggle, unified update system, release tag
enforcement, heartbeat tracking, and improved settings UX.
Phase 1: Full key display with AbsoluteShort dates, master package
protection (hide edit/delete in UI, reject in handlers).
Phase 2: Key edit page with template, handlers, and routes for both
repo and org levels. Master keys redirect away.
Phase 3: Domain restriction checking against CSV allowlist,
MaxSites enforcement via CountUniqueDomainsByKey and
IsDomainKnownForKey, dlid query param support for Joomla.
Phase 4: Purchase webhook (POST /license-keys/purchase) with
PaymentRef idempotency. Public validation endpoint
(POST /license-keys/validate) outside auth middleware.
PATCH /license-keys/{id} for API key editing.
Phase 5: Channels multiselect using org UpdateStreamConfig streams
rendered as checkboxes, stored as JSON arrays.
Additional: downloadkey XML element, LicensingEnabled toggle on
UpdateStreamConfig, Dolibarr endpoint unified with key validation,
release tag suffix enforcement, LastHeartbeatUnix field with
TouchHeartbeat, and cleaned-up settings pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Same bug as keys — packages were created with is_active=false
causing all key validation to reject even valid keys.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Keys were created with is_active=false (DB default) because the API
handler didn't explicitly set IsActive. This caused all key validation
to fail.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix two compilation errors in license key API routes:
- reqRepoAdmin() does not exist, use reqAdmin() instead
- m.Delete(handler) needs route pattern: m.Delete("", handler)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace ctx.Bind/ctx.APIErrorValidation (which don't exist on
APIContext) with web.GetForm() pattern used by all other API handlers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add REST API for managing license packages and keys:
- GET/POST /api/v1/repos/{owner}/{repo}/license-packages
- GET/POST /api/v1/repos/{owner}/{repo}/license-keys
- DELETE /api/v1/repos/{owner}/{repo}/license-keys/{id}
- GET /api/v1/repos/{owner}/{repo}/license-keys/{id}/usage
API structs for create/edit/response, with raw key only returned on
creation. Requires repo admin permissions.
Ref #239
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rebrand the built-in actions bot user from upstream Gitea naming to
MokoGitea branding:
- Name: gitea-actions → mokogitea-actions
- FullName: Gitea Actions → MokoGitea Actions
- Email: teabot@gitea.io → mokogitea-actions[bot]@mokoconsulting.tech
Add backward-compatible name recognition so all three bot name variants
(mokogitea-actions, gitea-actions, github-actions) with optional [bot]
suffix resolve to the same system user.
Add WhitelistActionsUser, MergeWhitelistActionsUser, and
ForcePushAllowlistActionsUser toggles to branch protection rules,
allowing CI/CD workflows to push to protected branches when explicitly
enabled. Previously the actions bot (virtual user ID -2) could never be
added to whitelist because updateUserWhitelist() only validates real
database users.
Closes#233
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The API endpoint POST /releases/{id}/assets bypasses CreateRelease
and UpdateRelease, so checksums were not generated for API uploads.
Added GenerateReleaseChecksums call after successful asset upload.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename the Go module path from code.gitea.io/gitea to
git.mokoconsulting.tech/MokoConsulting/MokoGitea across the entire
codebase.
Scope:
- go.mod module declaration
- 2,235 Go source files (import paths)
- Dockerfile WORKDIR and COPY paths
- Swagger API templates
- golangci.yml linter config
External dependencies (code.gitea.io/gitea-vet, code.gitea.io/sdk/gitea,
gitea.com/gitea/act, etc.) are intentionally NOT renamed — they are
separate upstream modules.
Closes#132
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR hardens artifact URL signing by encoding signature inputs in an
unambiguous binary payload before computing the HMAC.
What it changes:
- replace direct concatenation-style signing inputs with explicit
payload builders
- encode string fields with a length prefix before appending their bytes
- encode integer fields as fixed-width binary values instead of decimal
text
- apply the same hardening to both:
- Actions Artifact V4 signing in `routers/api/actions/artifactsv4.go`
- artifact download signing in `routers/api/v1/repo/action.go`
- add regression tests that verify distinct field combinations produce
distinct payloads and signatures
Why:
The previous signing logic built HMAC inputs by appending multiple
fields without a strongly structured representation. That kind of
construction can create ambiguity at field boundaries, where different
parameter combinations may serialize into the same byte stream for
signing.
This change removes that ambiguity by constructing a deterministic
payload format with explicit boundaries between fields.
Backport #37707
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
backport #37118
This PR closes remaining `public-only` token gaps in the API by making
the restriction apply consistently across repository, organization,
activity, notification, and authenticated `/api/v1/user/...` routes.
Previously, `public-only` tokens were still able to:
- receive private results from some list/search/self endpoints,
- access repository data through ID-based lookups,
- and reach several authenticated self routes that should remain
unavailable for public-only access.
This change treats `public-only` as a cross-cutting visibility boundary:
- list/search endpoints now filter private resources consistently,
- repository lookups enforce the same restriction even when addressed
indirectly,
- and self routes that inherently expose or mutate private account state
now reject `public-only` tokens.
---
Generated by a coding agent with Codex 5.2
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Fixes nil pointer dereference in BulkSetIssueAssignees — the issue's
Repo field must be loaded before UpdateAssignees calls CanAssignTo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add four new API endpoints under /repos/{owner}/{repo}/issues/bulk/ for
performing batch operations across multiple issues in a single request.
Each endpoint returns a partial-failure result with per-issue success/failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Restore original modules/badge/badge.go (upstream) and adapt our
presets to use GenerateBadge() and the existing Badge type.
- Rename Generate → GenerateRepoBadge to avoid conflict
- Add FormatRepoBadgeSVG for SVG rendering
- Add RenderFlat/RenderFlatSquare methods on Badge
- Fix API handler to use new function names
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The project API endpoints (ListProjects, CreateProject, etc.) were
added to the router but their handler implementations don't exist.
Comment out until implementations are available.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix all 9 remaining files with CurrentRefSubURL field name
- Fix unused 'attempt' variable in action.go
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix ToActionWorkflowRun calls in routers and services/actions
- Fix PrepareToStartRunWithConcurrency 3-value return + type mismatch
- Fix PrepareToStartJobWithConcurrency 3-value return in run.go
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace 9 more files still using gitea.com/gitea/runner/act/model
with github.com/nektos/act/pkg/model (resolved via replace directive)
- Fix ToActionWorkflowRun call: args were (ctx, run, nil) but
signature is (ctx, repo, run)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Repositories with names starting with "." are now treated as system
repositories that are always private and cannot be made public. This is
enforced at every code path: API create, web create, migrate, template
create, push-to-create, API edit, web settings, and public access
settings. On creation paths, privacy is silently forced. On edit paths,
a clear error is returned.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add organization-scoped branch protection rules that cascade to all
repos within the org. Repo-level rules take precedence; org rules
serve as the fallback when no repo rule matches a branch.
- New table: org_protected_branch (migration v332)
- OrgProtectedBranch model with full CRUD operations
- API endpoints: GET/POST/PATCH/DELETE /api/v1/orgs/{org}/branch_protections
- Inheritance via GetFirstMatchProtectedBranchRule() fallback
- InheritedFrom field added to BranchProtection API response
- Org rules use team-based whitelists (no per-user IDs at org level)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Issue
Closes#37217
The error string was getting lost while returning due to `ctx.JSON()`
which cannot serialize the `error` object.
## Fix
Use `ctx.APIError()` to return proper error messages back to the client.
## Summary
This PR adds support for updating pull mirror authentication via the
repository edit API and UI.
It introduces new mirror authentication fields in _EditRepoOption_,
updates the API logic to safely handle partial credential updates, and
fixes the web settings flow so that the existing remote username is
preserved when only the password is changed.
### What changed
- added _auth_username_, _auth_password_, and _auth_token_ to
EditRepoOption
- updated the repository edit API to apply mirror auth changes via
_updateMirror_
- preserved existing username/password when only part of the auth
payload is provided
- used oauth2 as the default username when _auth_token_ is provided
- kept stored mirror URLs sanitized in DB and API responses
- updated Swagger schema for the new API fields
- added API integration tests for password-only and token-only updates
- added a web settings test to ensure username preservation on partial
updates
## Why
Some use cases require automated synchronization of pull mirrors, for
example in CI/CD pipelines or integrations with external systems.
At the same time, many organizations enforce security policies that
require periodic token rotation (e.g., monthly).
Currently, mirror credentials can only be updated via the UI, which
makes automation difficult.
## This change enables:
- automated token rotation
- avoiding manual updates via the UI
- easier integration with secret management systems
## Testing
- added integration coverage for mirror auth updates via _PATCH
/api/v1/repos/{owner}/{repo}_
- added web settings tests for password-only updates preserving the
existing username
## Result
Ability to automate auth update
<img width="2400" height="1245" alt="1"
src="https://github.com/user-attachments/assets/67fd5cca-9cb3-4536-b0e2-4d09b8ebff0f"
/>
<img width="962" height="932" alt="image"
src="https://github.com/user-attachments/assets/5d548f5d-aadf-4807-ba52-9c29df93a4cc"
/>
Generative AI was used to help with making this PR.
##
1. Rename CompareInfo.MergeBase to CompareBase, it is not merge base
2. Remove unused template variables `ctx.Data["Username"]` and
`ctx.Data["Reponame"]`
3. Decouple some template variable accesses, use typed struct
---------
Co-authored-by: Nicolas <bircni@icloud.com>
Add a build-time conversion step that transforms the existing Swagger
2.0 spec into an OpenAPI 3.0 spec. The OAS3 spec is served alongside the
existing Swagger 2.0 spec, enabling API clients that require OAS3 to
generate code directly from Gitea's API.
This is not to be an answer to how gitea handles OAS3 long term,
but a way to use what we have to move a step forward.
---------
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Avoid per-item DB queries in ListRuns, ListJobs, and ListActionTasks by
batch-loading trigger users, repositories, and task attributes before
the conversion loop. Remove ReferencesGitRepo from the /actions route
group since no task/run endpoints use it.
Added tests for these endpoints as well.
---------
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
The `Repository` struct in `services/context/repo.go` embedded
`access_model.Permission` anonymously, causing all permission methods to
be promoted directly onto `Repository`. This made it unclear at call
sites whether a method belonged to `Repository` itself or to its
embedded `Permission`.
### Changes
- **`services/context/repo.go`**: Replace anonymous
`access_model.Permission` with named field `Permission
access_model.Permission`
- **49 files** updated to route permission method calls through the
named field:
```go
// Before
ctx.Repo.IsAdmin()
ctx.Repo.CanWrite(unit.TypeCode)
ctx.Repo.CanReadIssuesOrPulls(isPull)
slices.ContainsFunc(unitTypes, ctx.Repo.CanWrite)
// After
ctx.Repo.Permission.IsAdmin()
ctx.Repo.Permission.CanWrite(unit.TypeCode)
ctx.Repo.Permission.CanReadIssuesOrPulls(isPull)
slices.ContainsFunc(unitTypes, ctx.Repo.Permission.CanWrite)
```
Methods defined directly on `*Repository` (`CanWriteToBranch`,
`CanCreateBranch`, etc.) are unchanged.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: wxiaoguang <2114189+wxiaoguang@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Nicolas <bircni@icloud.com>