diff options
author | Daniel Baumann <daniel@debian.org> | 2024-10-18 20:33:49 +0200 |
---|---|---|
committer | Daniel Baumann <daniel@debian.org> | 2024-12-12 23:57:56 +0100 |
commit | e68b9d00a6e05b3a941f63ffb696f91e554ac5ec (patch) | |
tree | 97775d6c13b0f416af55314eb6a89ef792474615 /routers/api/v1/misc | |
parent | Initial commit. (diff) | |
download | forgejo-e68b9d00a6e05b3a941f63ffb696f91e554ac5ec.tar.xz forgejo-e68b9d00a6e05b3a941f63ffb696f91e554ac5ec.zip |
Adding upstream version 9.0.3.
Signed-off-by: Daniel Baumann <daniel@debian.org>
Diffstat (limited to '')
-rw-r--r-- | routers/api/v1/misc/gitignore.go | 56 | ||||
-rw-r--r-- | routers/api/v1/misc/label_templates.go | 60 | ||||
-rw-r--r-- | routers/api/v1/misc/licenses.go | 76 | ||||
-rw-r--r-- | routers/api/v1/misc/markup.go | 110 | ||||
-rw-r--r-- | routers/api/v1/misc/markup_test.go | 184 | ||||
-rw-r--r-- | routers/api/v1/misc/nodeinfo.go | 80 | ||||
-rw-r--r-- | routers/api/v1/misc/signing.go | 63 | ||||
-rw-r--r-- | routers/api/v1/misc/version.go | 25 |
8 files changed, 654 insertions, 0 deletions
diff --git a/routers/api/v1/misc/gitignore.go b/routers/api/v1/misc/gitignore.go new file mode 100644 index 0000000..dffd771 --- /dev/null +++ b/routers/api/v1/misc/gitignore.go @@ -0,0 +1,56 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package misc + +import ( + "net/http" + + "code.gitea.io/gitea/modules/options" + repo_module "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" +) + +// Shows a list of all Gitignore templates +func ListGitignoresTemplates(ctx *context.APIContext) { + // swagger:operation GET /gitignore/templates miscellaneous listGitignoresTemplates + // --- + // summary: Returns a list of all gitignore templates + // produces: + // - application/json + // responses: + // "200": + // "$ref": "#/responses/GitignoreTemplateList" + ctx.JSON(http.StatusOK, repo_module.Gitignores) +} + +// SHows information about a gitignore template +func GetGitignoreTemplateInfo(ctx *context.APIContext) { + // swagger:operation GET /gitignore/templates/{name} miscellaneous getGitignoreTemplateInfo + // --- + // summary: Returns information about a gitignore template + // produces: + // - application/json + // parameters: + // - name: name + // in: path + // description: name of the template + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/GitignoreTemplateInfo" + // "404": + // "$ref": "#/responses/notFound" + name := util.PathJoinRelX(ctx.Params("name")) + + text, err := options.Gitignore(name) + if err != nil { + ctx.NotFound() + return + } + + ctx.JSON(http.StatusOK, &structs.GitignoreTemplateInfo{Name: name, Source: string(text)}) +} diff --git a/routers/api/v1/misc/label_templates.go b/routers/api/v1/misc/label_templates.go new file mode 100644 index 0000000..cc11f37 --- /dev/null +++ b/routers/api/v1/misc/label_templates.go @@ -0,0 +1,60 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package misc + +import ( + "net/http" + + repo_module "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/convert" +) + +// Shows a list of all Label templates +func ListLabelTemplates(ctx *context.APIContext) { + // swagger:operation GET /label/templates miscellaneous listLabelTemplates + // --- + // summary: Returns a list of all label templates + // produces: + // - application/json + // responses: + // "200": + // "$ref": "#/responses/LabelTemplateList" + result := make([]string, len(repo_module.LabelTemplateFiles)) + for i := range repo_module.LabelTemplateFiles { + result[i] = repo_module.LabelTemplateFiles[i].DisplayName + } + + ctx.JSON(http.StatusOK, result) +} + +// Shows all labels in a template +func GetLabelTemplate(ctx *context.APIContext) { + // swagger:operation GET /label/templates/{name} miscellaneous getLabelTemplateInfo + // --- + // summary: Returns all labels in a template + // produces: + // - application/json + // parameters: + // - name: name + // in: path + // description: name of the template + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/LabelTemplateInfo" + // "404": + // "$ref": "#/responses/notFound" + name := util.PathJoinRelX(ctx.Params("name")) + + labels, err := repo_module.LoadTemplateLabelsByDisplayName(name) + if err != nil { + ctx.NotFound() + return + } + + ctx.JSON(http.StatusOK, convert.ToLabelTemplateList(labels)) +} diff --git a/routers/api/v1/misc/licenses.go b/routers/api/v1/misc/licenses.go new file mode 100644 index 0000000..2a980f5 --- /dev/null +++ b/routers/api/v1/misc/licenses.go @@ -0,0 +1,76 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package misc + +import ( + "fmt" + "net/http" + "net/url" + + "code.gitea.io/gitea/modules/options" + repo_module "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" +) + +// Returns a list of all License templates +func ListLicenseTemplates(ctx *context.APIContext) { + // swagger:operation GET /licenses miscellaneous listLicenseTemplates + // --- + // summary: Returns a list of all license templates + // produces: + // - application/json + // responses: + // "200": + // "$ref": "#/responses/LicenseTemplateList" + response := make([]api.LicensesTemplateListEntry, len(repo_module.Licenses)) + for i, license := range repo_module.Licenses { + response[i] = api.LicensesTemplateListEntry{ + Key: license, + Name: license, + URL: fmt.Sprintf("%sapi/v1/licenses/%s", setting.AppURL, url.PathEscape(license)), + } + } + ctx.JSON(http.StatusOK, response) +} + +// Returns information about a gitignore template +func GetLicenseTemplateInfo(ctx *context.APIContext) { + // swagger:operation GET /licenses/{name} miscellaneous getLicenseTemplateInfo + // --- + // summary: Returns information about a license template + // produces: + // - application/json + // parameters: + // - name: name + // in: path + // description: name of the license + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/LicenseTemplateInfo" + // "404": + // "$ref": "#/responses/notFound" + name := util.PathJoinRelX(ctx.Params("name")) + + text, err := options.License(name) + if err != nil { + ctx.NotFound() + return + } + + response := api.LicenseTemplateInfo{ + Key: name, + Name: name, + URL: fmt.Sprintf("%sapi/v1/licenses/%s", setting.AppURL, url.PathEscape(name)), + Body: string(text), + // This is for combatibilty with the GitHub API. This Text is for some reason added to each License response. + Implementation: "Create a text file (typically named LICENSE or LICENSE.txt) in the root of your source code and copy the text of the license into the file", + } + + ctx.JSON(http.StatusOK, response) +} diff --git a/routers/api/v1/misc/markup.go b/routers/api/v1/misc/markup.go new file mode 100644 index 0000000..9699c79 --- /dev/null +++ b/routers/api/v1/misc/markup.go @@ -0,0 +1,110 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package misc + +import ( + "net/http" + + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/markup/markdown" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/common" + "code.gitea.io/gitea/services/context" +) + +// Markup render markup document to HTML +func Markup(ctx *context.APIContext) { + // swagger:operation POST /markup miscellaneous renderMarkup + // --- + // summary: Render a markup document as HTML + // parameters: + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/MarkupOption" + // consumes: + // - application/json + // produces: + // - text/html + // responses: + // "200": + // "$ref": "#/responses/MarkupRender" + // "422": + // "$ref": "#/responses/validationError" + + form := web.GetForm(ctx).(*api.MarkupOption) + + if ctx.HasAPIError() { + ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg()) + return + } + + common.RenderMarkup(ctx.Base, ctx.Repo, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki) +} + +// Markdown render markdown document to HTML +func Markdown(ctx *context.APIContext) { + // swagger:operation POST /markdown miscellaneous renderMarkdown + // --- + // summary: Render a markdown document as HTML + // parameters: + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/MarkdownOption" + // consumes: + // - application/json + // produces: + // - text/html + // responses: + // "200": + // "$ref": "#/responses/MarkdownRender" + // "422": + // "$ref": "#/responses/validationError" + + form := web.GetForm(ctx).(*api.MarkdownOption) + + if ctx.HasAPIError() { + ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg()) + return + } + + mode := "markdown" + if form.Mode == "comment" || form.Mode == "gfm" { + mode = form.Mode + } + + common.RenderMarkup(ctx.Base, ctx.Repo, mode, form.Text, form.Context, "", form.Wiki) +} + +// MarkdownRaw render raw markdown HTML +func MarkdownRaw(ctx *context.APIContext) { + // swagger:operation POST /markdown/raw miscellaneous renderMarkdownRaw + // --- + // summary: Render raw markdown as HTML + // parameters: + // - name: body + // in: body + // description: Request body to render + // required: true + // schema: + // type: string + // consumes: + // - text/plain + // produces: + // - text/html + // responses: + // "200": + // "$ref": "#/responses/MarkdownRender" + // "422": + // "$ref": "#/responses/validationError" + defer ctx.Req.Body.Close() + if err := markdown.RenderRaw(&markup.RenderContext{ + Ctx: ctx, + }, ctx.Req.Body, ctx.Resp); err != nil { + ctx.InternalServerError(err) + return + } +} diff --git a/routers/api/v1/misc/markup_test.go b/routers/api/v1/misc/markup_test.go new file mode 100644 index 0000000..5236fd0 --- /dev/null +++ b/routers/api/v1/misc/markup_test.go @@ -0,0 +1,184 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package misc + +import ( + go_context "context" + "io" + "net/http" + "strings" + "testing" + + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/contexttest" + + "github.com/stretchr/testify/assert" +) + +const ( + AppURL = "http://localhost:3000/" + Repo = "gogits/gogs" + FullURL = AppURL + Repo + "/" +) + +func testRenderMarkup(t *testing.T, mode, filePath, text, responseBody string, responseCode int) { + setting.AppURL = AppURL + options := api.MarkupOption{ + Mode: mode, + Text: text, + Context: Repo, + Wiki: true, + FilePath: filePath, + } + ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markup") + web.SetForm(ctx, &options) + Markup(ctx) + assert.Equal(t, responseBody, resp.Body.String()) + assert.Equal(t, responseCode, resp.Code) + resp.Body.Reset() +} + +func testRenderMarkdown(t *testing.T, mode, text, responseBody string, responseCode int) { + setting.AppURL = AppURL + options := api.MarkdownOption{ + Mode: mode, + Text: text, + Context: Repo, + Wiki: true, + } + ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markdown") + web.SetForm(ctx, &options) + Markdown(ctx) + assert.Equal(t, responseBody, resp.Body.String()) + assert.Equal(t, responseCode, resp.Code) + resp.Body.Reset() +} + +func TestAPI_RenderGFM(t *testing.T) { + markup.Init(&markup.ProcessorHelper{ + IsUsernameMentionable: func(ctx go_context.Context, username string) bool { + return username == "r-lyeh" + }, + }) + + testCasesCommon := []string{ + // dear imgui wiki markdown extract: special wiki syntax + `Wiki! Enjoy :) +- [[Links, Language bindings, Engine bindings|Links]] +- [[Tips]] +- Bezier widget (by @r-lyeh) https://github.com/ocornut/imgui/issues/786`, + // rendered + `<p>Wiki! Enjoy :)</p> +<ul> +<li><a href="` + FullURL + `wiki/Links" rel="nofollow">Links, Language bindings, Engine bindings</a></li> +<li><a href="` + FullURL + `wiki/Tips" rel="nofollow">Tips</a></li> +<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="https://github.com/ocornut/imgui/issues/786" rel="nofollow">https://github.com/ocornut/imgui/issues/786</a></li> +</ul> +`, + // Guard wiki sidebar: special syntax + `[[Guardfile-DSL / Configuring-Guard|Guardfile-DSL---Configuring-Guard]]`, + // rendered + `<p><a href="` + FullURL + `wiki/Guardfile-DSL---Configuring-Guard" rel="nofollow">Guardfile-DSL / Configuring-Guard</a></p> +`, + // special syntax + `[[Name|Link]]`, + // rendered + `<p><a href="` + FullURL + `wiki/Link" rel="nofollow">Name</a></p> +`, + // empty + ``, + // rendered + ``, + } + + testCasesDocument := []string{ + // wine-staging wiki home extract: special wiki syntax, images + `## What is Wine Staging? +**Wine Staging** on website [wine-staging.com](http://wine-staging.com). + +## Quick Links +Here are some links to the most important topics. You can find the full list of pages at the sidebar. + +[[Configuration]] +[[images/icon-bug.png]] +`, + // rendered + `<h2 id="user-content-what-is-wine-staging">What is Wine Staging?</h2> +<p><strong>Wine Staging</strong> on website <a href="http://wine-staging.com" rel="nofollow">wine-staging.com</a>.</p> +<h2 id="user-content-quick-links">Quick Links</h2> +<p>Here are some links to the most important topics. You can find the full list of pages at the sidebar.</p> +<p><a href="` + FullURL + `wiki/Configuration" rel="nofollow">Configuration</a> +<a href="` + FullURL + `wiki/raw/images/icon-bug.png" rel="nofollow"><img src="` + FullURL + `wiki/raw/images/icon-bug.png" title="icon-bug.png" alt="images/icon-bug.png"/></a></p> +`, + } + + for i := 0; i < len(testCasesCommon); i += 2 { + text := testCasesCommon[i] + response := testCasesCommon[i+1] + testRenderMarkdown(t, "gfm", text, response, http.StatusOK) + testRenderMarkup(t, "gfm", "", text, response, http.StatusOK) + testRenderMarkdown(t, "comment", text, response, http.StatusOK) + testRenderMarkup(t, "comment", "", text, response, http.StatusOK) + testRenderMarkup(t, "file", "path/test.md", text, response, http.StatusOK) + } + + for i := 0; i < len(testCasesDocument); i += 2 { + text := testCasesDocument[i] + response := testCasesDocument[i+1] + testRenderMarkdown(t, "gfm", text, response, http.StatusOK) + testRenderMarkup(t, "gfm", "", text, response, http.StatusOK) + testRenderMarkup(t, "file", "path/test.md", text, response, http.StatusOK) + } + + testRenderMarkup(t, "file", "path/test.unknown", "## Test", "Unsupported render extension: .unknown\n", http.StatusUnprocessableEntity) + testRenderMarkup(t, "unknown", "", "## Test", "Unknown mode: unknown\n", http.StatusUnprocessableEntity) +} + +var simpleCases = []string{ + // Guard wiki sidebar: special syntax + `[[Guardfile-DSL / Configuring-Guard|Guardfile-DSL---Configuring-Guard]]`, + // rendered + `<p>[[Guardfile-DSL / Configuring-Guard|Guardfile-DSL---Configuring-Guard]]</p> +`, + // special syntax + `[[Name|Link]]`, + // rendered + `<p>[[Name|Link]]</p> +`, + // empty + ``, + // rendered + ``, +} + +func TestAPI_RenderSimple(t *testing.T) { + setting.AppURL = AppURL + options := api.MarkdownOption{ + Mode: "markdown", + Text: "", + Context: Repo, + } + ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markdown") + for i := 0; i < len(simpleCases); i += 2 { + options.Text = simpleCases[i] + web.SetForm(ctx, &options) + Markdown(ctx) + assert.Equal(t, simpleCases[i+1], resp.Body.String()) + resp.Body.Reset() + } +} + +func TestAPI_RenderRaw(t *testing.T) { + setting.AppURL = AppURL + ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markdown") + for i := 0; i < len(simpleCases); i += 2 { + ctx.Req.Body = io.NopCloser(strings.NewReader(simpleCases[i])) + MarkdownRaw(ctx) + assert.Equal(t, simpleCases[i+1], resp.Body.String()) + resp.Body.Reset() + } +} diff --git a/routers/api/v1/misc/nodeinfo.go b/routers/api/v1/misc/nodeinfo.go new file mode 100644 index 0000000..9c2a0db --- /dev/null +++ b/routers/api/v1/misc/nodeinfo.go @@ -0,0 +1,80 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package misc + +import ( + "net/http" + "time" + + issues_model "code.gitea.io/gitea/models/issues" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" +) + +const cacheKeyNodeInfoUsage = "API_NodeInfoUsage" + +// NodeInfo returns the NodeInfo for the Gitea instance to allow for federation +func NodeInfo(ctx *context.APIContext) { + // swagger:operation GET /nodeinfo miscellaneous getNodeInfo + // --- + // summary: Returns the nodeinfo of the Gitea application + // produces: + // - application/json + // responses: + // "200": + // "$ref": "#/responses/NodeInfo" + + nodeInfoUsage := structs.NodeInfoUsage{} + if setting.Federation.ShareUserStatistics { + var cached bool + nodeInfoUsage, cached = ctx.Cache.Get(cacheKeyNodeInfoUsage).(structs.NodeInfoUsage) + + if !cached { + usersTotal := int(user_model.CountUsers(ctx, nil)) + now := time.Now() + timeOneMonthAgo := now.AddDate(0, -1, 0).Unix() + timeHaveYearAgo := now.AddDate(0, -6, 0).Unix() + usersActiveMonth := int(user_model.CountUsers(ctx, &user_model.CountUserFilter{LastLoginSince: &timeOneMonthAgo})) + usersActiveHalfyear := int(user_model.CountUsers(ctx, &user_model.CountUserFilter{LastLoginSince: &timeHaveYearAgo})) + + allIssues, _ := issues_model.CountIssues(ctx, &issues_model.IssuesOptions{}) + allComments, _ := issues_model.CountComments(ctx, &issues_model.FindCommentsOptions{}) + + nodeInfoUsage = structs.NodeInfoUsage{ + Users: structs.NodeInfoUsageUsers{ + Total: usersTotal, + ActiveMonth: usersActiveMonth, + ActiveHalfyear: usersActiveHalfyear, + }, + LocalPosts: int(allIssues), + LocalComments: int(allComments), + } + + if err := ctx.Cache.Put(cacheKeyNodeInfoUsage, nodeInfoUsage, 180); err != nil { + ctx.InternalServerError(err) + return + } + } + } + + nodeInfo := &structs.NodeInfo{ + Version: "2.1", + Software: structs.NodeInfoSoftware{ + Name: "forgejo", + Version: setting.AppVer, + Repository: "https://codeberg.org/forgejo/forgejo.git", + Homepage: "https://forgejo.org/", + }, + Protocols: []string{"activitypub"}, + Services: structs.NodeInfoServices{ + Inbound: []string{}, + Outbound: []string{"rss2.0"}, + }, + OpenRegistrations: setting.Service.ShowRegistrationButton, + Usage: nodeInfoUsage, + } + ctx.JSON(http.StatusOK, nodeInfo) +} diff --git a/routers/api/v1/misc/signing.go b/routers/api/v1/misc/signing.go new file mode 100644 index 0000000..24a46c1 --- /dev/null +++ b/routers/api/v1/misc/signing.go @@ -0,0 +1,63 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package misc + +import ( + "fmt" + "net/http" + + asymkey_service "code.gitea.io/gitea/services/asymkey" + "code.gitea.io/gitea/services/context" +) + +// SigningKey returns the public key of the default signing key if it exists +func SigningKey(ctx *context.APIContext) { + // swagger:operation GET /signing-key.gpg miscellaneous getSigningKey + // --- + // summary: Get default signing-key.gpg + // produces: + // - text/plain + // responses: + // "200": + // description: "GPG armored public key" + // schema: + // type: string + + // swagger:operation GET /repos/{owner}/{repo}/signing-key.gpg repository repoSigningKey + // --- + // summary: Get signing-key.gpg for given repository + // produces: + // - text/plain + // 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": + // description: "GPG armored public key" + // schema: + // type: string + + path := "" + if ctx.Repo != nil && ctx.Repo.Repository != nil { + path = ctx.Repo.Repository.RepoPath() + } + + content, err := asymkey_service.PublicSigningKey(ctx, path) + if err != nil { + ctx.Error(http.StatusInternalServerError, "gpg export", err) + return + } + _, err = ctx.Write([]byte(content)) + if err != nil { + ctx.Error(http.StatusInternalServerError, "gpg export", fmt.Errorf("Error writing key content %w", err)) + } +} diff --git a/routers/api/v1/misc/version.go b/routers/api/v1/misc/version.go new file mode 100644 index 0000000..e3b43a0 --- /dev/null +++ b/routers/api/v1/misc/version.go @@ -0,0 +1,25 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package misc + +import ( + "net/http" + + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" +) + +// Version shows the version of the Gitea server +func Version(ctx *context.APIContext) { + // swagger:operation GET /version miscellaneous getVersion + // --- + // summary: Returns the version of the Gitea application + // produces: + // - application/json + // responses: + // "200": + // "$ref": "#/responses/ServerVersion" + ctx.JSON(http.StatusOK, &structs.ServerVersion{Version: setting.AppVer}) +} |