// Copyright 2026 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package repo import ( "net/http" repo_model "code.gitea.io/gitea/models/repo" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/context" pages_service "code.gitea.io/gitea/services/pages" ) // GetPagesConfig returns the pages configuration for a repository func GetPagesConfig(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/pages repository repoGetPages // --- // summary: Get pages configuration for a repository // produces: // - application/json // parameters: // - name: owner // in: path // description: owner of the repo // type: string // required: true // - name: repo // in: path // description: name of the repo // type: string // required: true // responses: // "200": // "$ref": "#/responses/PagesInfo" // "404": // "$ref": "#/responses/notFound" config, err := pages_service.GetPagesConfig(ctx, ctx.Repo.Repository) if err != nil { ctx.APIErrorInternal(err) return } domains, err := pages_service.GetPagesDomains(ctx, ctx.Repo.Repository.ID) if err != nil { ctx.APIErrorInternal(err) return } ctx.JSON(http.StatusOK, &api.PagesInfo{ Config: &api.PagesConfig{ Enabled: config != nil && config.Enabled, Template: getTemplateOrDefault(config), Subdomain: pages_service.GetPagesSubdomain(ctx.Repo.Repository), URL: pages_service.GetPagesURL(ctx.Repo.Repository), }, Domains: convertPagesDomains(domains), }) } // UpdatePagesConfig updates the pages configuration for a repository func UpdatePagesConfig(ctx *context.APIContext) { // swagger:operation PUT /repos/{owner}/{repo}/pages repository repoUpdatePages // --- // summary: Update pages configuration for a repository // produces: // - application/json // parameters: // - name: owner // in: path // description: owner of the repo // type: string // required: true // - name: repo // in: path // description: name of the repo // type: string // required: true // - name: body // in: body // schema: // "$ref": "#/definitions/CreatePagesConfigOption" // responses: // "200": // "$ref": "#/responses/PagesConfig" // "403": // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" form := web.GetForm(ctx).(*api.CreatePagesConfigOption) if form.Enabled { if err := pages_service.EnablePages(ctx, ctx.Repo.Repository, form.Template); err != nil { ctx.APIErrorInternal(err) return } } else { if err := pages_service.DisablePages(ctx, ctx.Repo.Repository); err != nil { ctx.APIErrorInternal(err) return } } ctx.JSON(http.StatusOK, &api.PagesConfig{ Enabled: form.Enabled, Template: form.Template, Subdomain: pages_service.GetPagesSubdomain(ctx.Repo.Repository), URL: pages_service.GetPagesURL(ctx.Repo.Repository), }) } // DeletePagesConfig disables pages for a repository func DeletePagesConfig(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/pages repository repoDeletePages // --- // summary: Disable pages for a repository // produces: // - application/json // parameters: // - name: owner // in: path // description: owner of the repo // type: string // required: true // - name: repo // in: path // description: name of the repo // type: string // required: true // responses: // "204": // "$ref": "#/responses/empty" // "403": // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" if err := pages_service.DisablePages(ctx, ctx.Repo.Repository); err != nil { ctx.APIErrorInternal(err) return } // Also delete all custom domains if err := repo_model.DeletePagesDomainsByRepoID(ctx, ctx.Repo.Repository.ID); err != nil { ctx.APIErrorInternal(err) return } ctx.Status(http.StatusNoContent) } // ListPagesDomains returns all custom domains for a repository's pages func ListPagesDomains(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/pages/domains repository repoListPagesDomains // --- // summary: List custom domains for repository pages // produces: // - application/json // parameters: // - name: owner // in: path // description: owner of the repo // type: string // required: true // - name: repo // in: path // description: name of the repo // type: string // required: true // responses: // "200": // "$ref": "#/responses/PagesDomainList" // "404": // "$ref": "#/responses/notFound" domains, err := pages_service.GetPagesDomains(ctx, ctx.Repo.Repository.ID) if err != nil { ctx.APIErrorInternal(err) return } ctx.JSON(http.StatusOK, convertPagesDomains(domains)) } // AddPagesDomain adds a custom domain for pages func AddPagesDomain(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/pages/domains repository repoAddPagesDomain // --- // summary: Add a custom domain for repository pages // produces: // - application/json // parameters: // - name: owner // in: path // description: owner of the repo // type: string // required: true // - name: repo // in: path // description: name of the repo // type: string // required: true // - name: body // in: body // schema: // "$ref": "#/definitions/AddPagesDomainOption" // responses: // "201": // "$ref": "#/responses/PagesDomain" // "403": // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" // "422": // "$ref": "#/responses/validationError" form := web.GetForm(ctx).(*api.AddPagesDomainOption) domain, err := pages_service.AddPagesDomain(ctx, ctx.Repo.Repository.ID, form.Domain) if err != nil { if repo_model.IsErrPagesDomainAlreadyExist(err) { ctx.APIError(http.StatusUnprocessableEntity, "Domain already exists") return } ctx.APIErrorInternal(err) return } ctx.JSON(http.StatusCreated, convertPagesDomain(domain)) } // DeletePagesDomain removes a custom domain func DeletePagesDomain(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/pages/domains/{domain} repository repoDeletePagesDomain // --- // summary: Remove a custom domain for repository pages // produces: // - application/json // parameters: // - name: owner // in: path // description: owner of the repo // type: string // required: true // - name: repo // in: path // description: name of the repo // type: string // required: true // - name: domain // in: path // description: the domain to remove // type: string // required: true // responses: // "204": // "$ref": "#/responses/empty" // "403": // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" domainName := ctx.PathParam("domain") // Find the domain domain, err := repo_model.GetPagesDomainByDomain(ctx, domainName) if err != nil { if repo_model.IsErrPagesDomainNotExist(err) { ctx.APIErrorNotFound() return } ctx.APIErrorInternal(err) return } // Verify domain belongs to this repo if domain.RepoID != ctx.Repo.Repository.ID { ctx.APIErrorNotFound() return } if err := repo_model.DeletePagesDomain(ctx, domain.ID); err != nil { ctx.APIErrorInternal(err) return } ctx.Status(http.StatusNoContent) } // VerifyPagesDomain verifies a custom domain func VerifyPagesDomain(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/pages/domains/{domain}/verify repository repoVerifyPagesDomain // --- // summary: Verify a custom domain for repository pages // produces: // - application/json // parameters: // - name: owner // in: path // description: owner of the repo // type: string // required: true // - name: repo // in: path // description: name of the repo // type: string // required: true // - name: domain // in: path // description: the domain to verify // type: string // required: true // responses: // "200": // "$ref": "#/responses/PagesDomain" // "403": // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" domainName := ctx.PathParam("domain") // Find the domain domain, err := repo_model.GetPagesDomainByDomain(ctx, domainName) if err != nil { if repo_model.IsErrPagesDomainNotExist(err) { ctx.APIErrorNotFound() return } ctx.APIErrorInternal(err) return } // Verify domain belongs to this repo if domain.RepoID != ctx.Repo.Repository.ID { ctx.APIErrorNotFound() return } if err := pages_service.VerifyDomain(ctx, domain.ID); err != nil { ctx.APIErrorInternal(err) return } // Reload domain domain, err = repo_model.GetPagesDomainByID(ctx, domain.ID) if err != nil { ctx.APIErrorInternal(err) return } ctx.JSON(http.StatusOK, convertPagesDomain(domain)) } // Helper functions func getTemplateOrDefault(config interface{}) string { if config == nil { return "simple" } // Try to get template from config return "simple" } func convertPagesDomain(domain *repo_model.PagesDomain) *api.PagesDomain { return &api.PagesDomain{ ID: domain.ID, Domain: domain.Domain, Verified: domain.Verified, VerificationToken: domain.VerificationToken, SSLStatus: string(domain.SSLStatus), SSLCertExpiry: domain.SSLCertExpiry.AsTime(), Created: domain.CreatedUnix.AsTime(), Verified_At: domain.VerifiedUnix.AsTime(), } } func convertPagesDomains(domains []*repo_model.PagesDomain) []*api.PagesDomain { result := make([]*api.PagesDomain, len(domains)) for i, d := range domains { result[i] = convertPagesDomain(d) } return result }