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 /modules/private | |
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-- | modules/private/actions.go | 25 | ||||
-rw-r--r-- | modules/private/forgejo_actions.go | 32 | ||||
-rw-r--r-- | modules/private/hook.go | 129 | ||||
-rw-r--r-- | modules/private/internal.go | 96 | ||||
-rw-r--r-- | modules/private/key.go | 30 | ||||
-rw-r--r-- | modules/private/mail.go | 33 | ||||
-rw-r--r-- | modules/private/manager.go | 120 | ||||
-rw-r--r-- | modules/private/request.go | 128 | ||||
-rw-r--r-- | modules/private/restore_repo.go | 36 | ||||
-rw-r--r-- | modules/private/serv.go | 63 |
10 files changed, 692 insertions, 0 deletions
diff --git a/modules/private/actions.go b/modules/private/actions.go new file mode 100644 index 0000000..311a283 --- /dev/null +++ b/modules/private/actions.go @@ -0,0 +1,25 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package private + +import ( + "context" + + "code.gitea.io/gitea/modules/setting" +) + +type GenerateTokenRequest struct { + Scope string +} + +// GenerateActionsRunnerToken calls the internal GenerateActionsRunnerToken function +func GenerateActionsRunnerToken(ctx context.Context, scope string) (*ResponseText, ResponseExtra) { + reqURL := setting.LocalURL + "api/internal/actions/generate_actions_runner_token" + + req := newInternalRequest(ctx, reqURL, "POST", GenerateTokenRequest{ + Scope: scope, + }) + + return requestJSONResp(req, &ResponseText{}) +} diff --git a/modules/private/forgejo_actions.go b/modules/private/forgejo_actions.go new file mode 100644 index 0000000..133d5e2 --- /dev/null +++ b/modules/private/forgejo_actions.go @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT + +package private + +import ( + "context" + + "code.gitea.io/gitea/modules/setting" +) + +type ActionsRunnerRegisterRequest struct { + Token string + Scope string + Labels []string + Name string + Version string +} + +func ActionsRunnerRegister(ctx context.Context, token, scope string, labels []string, name, version string) (string, ResponseExtra) { + reqURL := setting.LocalURL + "api/internal/actions/register" + + req := newInternalRequest(ctx, reqURL, "POST", ActionsRunnerRegisterRequest{ + Token: token, + Scope: scope, + Labels: labels, + Name: name, + Version: version, + }) + + resp, extra := requestJSONResp(req, &ResponseText{}) + return resp.Text, extra +} diff --git a/modules/private/hook.go b/modules/private/hook.go new file mode 100644 index 0000000..93cbcd4 --- /dev/null +++ b/modules/private/hook.go @@ -0,0 +1,129 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package private + +import ( + "context" + "fmt" + "net/url" + "time" + + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/pushoptions" + "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/setting" +) + +// Git environment variables +const ( + GitAlternativeObjectDirectories = "GIT_ALTERNATE_OBJECT_DIRECTORIES" + GitObjectDirectory = "GIT_OBJECT_DIRECTORY" + GitQuarantinePath = "GIT_QUARANTINE_PATH" +) + +// HookOptions represents the options for the Hook calls +type HookOptions struct { + OldCommitIDs []string + NewCommitIDs []string + RefFullNames []git.RefName + UserID int64 + UserName string + GitObjectDirectory string + GitAlternativeObjectDirectories string + GitQuarantinePath string + GitPushOptions map[string]string + PullRequestID int64 + PushTrigger repository.PushTrigger + DeployKeyID int64 // if the pusher is a DeployKey, then UserID is the repo's org user. + IsWiki bool + ActionPerm int +} + +func (o *HookOptions) GetGitPushOptions() pushoptions.Interface { + return pushoptions.NewFromMap(&o.GitPushOptions) +} + +// SSHLogOption ssh log options +type SSHLogOption struct { + IsError bool + Message string +} + +// HookPostReceiveResult represents an individual result from PostReceive +type HookPostReceiveResult struct { + Results []HookPostReceiveBranchResult + RepoWasEmpty bool + Err string +} + +// HookPostReceiveBranchResult represents an individual branch result from PostReceive +type HookPostReceiveBranchResult struct { + Message bool + Create bool + Branch string + URL string +} + +// HookProcReceiveResult represents an individual result from ProcReceive +type HookProcReceiveResult struct { + Results []HookProcReceiveRefResult + Err string +} + +// HookProcReceiveRefResult represents an individual result from ProcReceive +type HookProcReceiveRefResult struct { + OldOID string + NewOID string + Ref string + OriginalRef git.RefName + IsForcePush bool + IsNotMatched bool + Err string +} + +// HookPreReceive check whether the provided commits are allowed +func HookPreReceive(ctx context.Context, ownerName, repoName string, opts HookOptions) ResponseExtra { + reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/pre-receive/%s/%s", url.PathEscape(ownerName), url.PathEscape(repoName)) + req := newInternalRequest(ctx, reqURL, "POST", opts) + req.SetReadWriteTimeout(time.Duration(60+len(opts.OldCommitIDs)) * time.Second) + _, extra := requestJSONResp(req, &ResponseText{}) + return extra +} + +// HookPostReceive updates services and users +func HookPostReceive(ctx context.Context, ownerName, repoName string, opts HookOptions) (*HookPostReceiveResult, ResponseExtra) { + reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/post-receive/%s/%s", url.PathEscape(ownerName), url.PathEscape(repoName)) + req := newInternalRequest(ctx, reqURL, "POST", opts) + req.SetReadWriteTimeout(time.Duration(60+len(opts.OldCommitIDs)) * time.Second) + return requestJSONResp(req, &HookPostReceiveResult{}) +} + +// HookProcReceive proc-receive hook +func HookProcReceive(ctx context.Context, ownerName, repoName string, opts HookOptions) (*HookProcReceiveResult, ResponseExtra) { + reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/proc-receive/%s/%s", url.PathEscape(ownerName), url.PathEscape(repoName)) + + req := newInternalRequest(ctx, reqURL, "POST", opts) + req.SetReadWriteTimeout(time.Duration(60+len(opts.OldCommitIDs)) * time.Second) + return requestJSONResp(req, &HookProcReceiveResult{}) +} + +// SetDefaultBranch will set the default branch to the provided branch for the provided repository +func SetDefaultBranch(ctx context.Context, ownerName, repoName, branch string) ResponseExtra { + reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/set-default-branch/%s/%s/%s", + url.PathEscape(ownerName), + url.PathEscape(repoName), + url.PathEscape(branch), + ) + req := newInternalRequest(ctx, reqURL, "POST") + _, extra := requestJSONResp(req, &ResponseText{}) + return extra +} + +// SSHLog sends ssh error log response +func SSHLog(ctx context.Context, isErr bool, msg string) error { + reqURL := setting.LocalURL + "api/internal/ssh/log" + req := newInternalRequest(ctx, reqURL, "POST", &SSHLogOption{IsError: isErr, Message: msg}) + _, extra := requestJSONResp(req, &ResponseText{}) + return extra.Error +} diff --git a/modules/private/internal.go b/modules/private/internal.go new file mode 100644 index 0000000..9c330a2 --- /dev/null +++ b/modules/private/internal.go @@ -0,0 +1,96 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package private + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "net/http" + "os" + "strings" + "time" + + "code.gitea.io/gitea/modules/httplib" + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/proxyprotocol" + "code.gitea.io/gitea/modules/setting" +) + +// Response is used for internal request response (for user message and error message) +type Response struct { + Err string `json:"err,omitempty"` // server-side error log message, it won't be exposed to end users + UserMsg string `json:"user_msg,omitempty"` // meaningful error message for end users, it will be shown in git client's output. +} + +func getClientIP() string { + sshConnEnv := strings.TrimSpace(os.Getenv("SSH_CONNECTION")) + if len(sshConnEnv) == 0 { + return "127.0.0.1" + } + return strings.Fields(sshConnEnv)[0] +} + +func newInternalRequest(ctx context.Context, url, method string, body ...any) *httplib.Request { + if setting.InternalToken == "" { + log.Fatal(`The INTERNAL_TOKEN setting is missing from the configuration file: %q. +Ensure you are running in the correct environment or set the correct configuration file with -c.`, setting.CustomConf) + } + + req := httplib.NewRequest(url, method). + SetContext(ctx). + Header("X-Real-IP", getClientIP()). + Header("Authorization", fmt.Sprintf("Bearer %s", setting.InternalToken)). + SetTLSClientConfig(&tls.Config{ + InsecureSkipVerify: true, + ServerName: setting.Domain, + }) + + if setting.Protocol == setting.HTTPUnix { + req.SetTransport(&http.Transport{ + DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { + var d net.Dialer + conn, err := d.DialContext(ctx, "unix", setting.HTTPAddr) + if err != nil { + return conn, err + } + if setting.LocalUseProxyProtocol { + if err = proxyprotocol.WriteLocalHeader(conn); err != nil { + _ = conn.Close() + return nil, err + } + } + return conn, err + }, + }) + } else if setting.LocalUseProxyProtocol { + req.SetTransport(&http.Transport{ + DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { + var d net.Dialer + conn, err := d.DialContext(ctx, network, address) + if err != nil { + return conn, err + } + if err = proxyprotocol.WriteLocalHeader(conn); err != nil { + _ = conn.Close() + return nil, err + } + return conn, err + }, + }) + } + + if len(body) == 1 { + req.Header("Content-Type", "application/json") + jsonBytes, _ := json.Marshal(body[0]) + req.Body(jsonBytes) + } else if len(body) > 1 { + log.Fatal("Too many arguments for newInternalRequest") + } + + req.SetTimeout(10*time.Second, 60*time.Second) + return req +} diff --git a/modules/private/key.go b/modules/private/key.go new file mode 100644 index 0000000..dcd1714 --- /dev/null +++ b/modules/private/key.go @@ -0,0 +1,30 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package private + +import ( + "context" + "fmt" + + "code.gitea.io/gitea/modules/setting" +) + +// UpdatePublicKeyInRepo update public key and if necessary deploy key updates +func UpdatePublicKeyInRepo(ctx context.Context, keyID, repoID int64) error { + // Ask for running deliver hook and test pull request tasks. + reqURL := setting.LocalURL + fmt.Sprintf("api/internal/ssh/%d/update/%d", keyID, repoID) + req := newInternalRequest(ctx, reqURL, "POST") + _, extra := requestJSONResp(req, &ResponseText{}) + return extra.Error +} + +// AuthorizedPublicKeyByContent searches content as prefix (leak e-mail part) +// and returns public key found. +func AuthorizedPublicKeyByContent(ctx context.Context, content string) (*ResponseText, ResponseExtra) { + // Ask for running deliver hook and test pull request tasks. + reqURL := setting.LocalURL + "api/internal/ssh/authorized_keys" + req := newInternalRequest(ctx, reqURL, "POST") + req.Param("content", content) + return requestJSONResp(req, &ResponseText{}) +} diff --git a/modules/private/mail.go b/modules/private/mail.go new file mode 100644 index 0000000..08de5b7 --- /dev/null +++ b/modules/private/mail.go @@ -0,0 +1,33 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package private + +import ( + "context" + + "code.gitea.io/gitea/modules/setting" +) + +// Email structure holds a data for sending general emails +type Email struct { + Subject string + Message string + To []string +} + +// SendEmail calls the internal SendEmail function +// It accepts a list of usernames. +// If DB contains these users it will send the email to them. +// If to list == nil, it's supposed to send emails to every user present in DB +func SendEmail(ctx context.Context, subject, message string, to []string) (*ResponseText, ResponseExtra) { + reqURL := setting.LocalURL + "api/internal/mail/send" + + req := newInternalRequest(ctx, reqURL, "POST", Email{ + Subject: subject, + Message: message, + To: to, + }) + + return requestJSONResp(req, &ResponseText{}) +} diff --git a/modules/private/manager.go b/modules/private/manager.go new file mode 100644 index 0000000..6055e55 --- /dev/null +++ b/modules/private/manager.go @@ -0,0 +1,120 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package private + +import ( + "context" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "time" + + "code.gitea.io/gitea/modules/setting" +) + +// Shutdown calls the internal shutdown function +func Shutdown(ctx context.Context) ResponseExtra { + reqURL := setting.LocalURL + "api/internal/manager/shutdown" + req := newInternalRequest(ctx, reqURL, "POST") + return requestJSONClientMsg(req, "Shutting down") +} + +// Restart calls the internal restart function +func Restart(ctx context.Context) ResponseExtra { + reqURL := setting.LocalURL + "api/internal/manager/restart" + req := newInternalRequest(ctx, reqURL, "POST") + return requestJSONClientMsg(req, "Restarting") +} + +// ReloadTemplates calls the internal reload-templates function +func ReloadTemplates(ctx context.Context) ResponseExtra { + reqURL := setting.LocalURL + "api/internal/manager/reload-templates" + req := newInternalRequest(ctx, reqURL, "POST") + return requestJSONClientMsg(req, "Reloaded") +} + +// FlushOptions represents the options for the flush call +type FlushOptions struct { + Timeout time.Duration + NonBlocking bool +} + +// FlushQueues calls the internal flush-queues function +func FlushQueues(ctx context.Context, timeout time.Duration, nonBlocking bool) ResponseExtra { + reqURL := setting.LocalURL + "api/internal/manager/flush-queues" + req := newInternalRequest(ctx, reqURL, "POST", FlushOptions{Timeout: timeout, NonBlocking: nonBlocking}) + if timeout > 0 { + req.SetReadWriteTimeout(timeout + 10*time.Second) + } + return requestJSONClientMsg(req, "Flushed") +} + +// PauseLogging pauses logging +func PauseLogging(ctx context.Context) ResponseExtra { + reqURL := setting.LocalURL + "api/internal/manager/pause-logging" + req := newInternalRequest(ctx, reqURL, "POST") + return requestJSONClientMsg(req, "Logging Paused") +} + +// ResumeLogging resumes logging +func ResumeLogging(ctx context.Context) ResponseExtra { + reqURL := setting.LocalURL + "api/internal/manager/resume-logging" + req := newInternalRequest(ctx, reqURL, "POST") + return requestJSONClientMsg(req, "Logging Restarted") +} + +// ReleaseReopenLogging releases and reopens logging files +func ReleaseReopenLogging(ctx context.Context) ResponseExtra { + reqURL := setting.LocalURL + "api/internal/manager/release-and-reopen-logging" + req := newInternalRequest(ctx, reqURL, "POST") + return requestJSONClientMsg(req, "Logging Restarted") +} + +// SetLogSQL sets database logging +func SetLogSQL(ctx context.Context, on bool) ResponseExtra { + reqURL := setting.LocalURL + "api/internal/manager/set-log-sql?on=" + strconv.FormatBool(on) + req := newInternalRequest(ctx, reqURL, "POST") + return requestJSONClientMsg(req, "Log SQL setting set") +} + +// LoggerOptions represents the options for the add logger call +type LoggerOptions struct { + Logger string + Writer string + Mode string + Config map[string]any +} + +// AddLogger adds a logger +func AddLogger(ctx context.Context, logger, writer, mode string, config map[string]any) ResponseExtra { + reqURL := setting.LocalURL + "api/internal/manager/add-logger" + req := newInternalRequest(ctx, reqURL, "POST", LoggerOptions{ + Logger: logger, + Writer: writer, + Mode: mode, + Config: config, + }) + return requestJSONClientMsg(req, "Added") +} + +// RemoveLogger removes a logger +func RemoveLogger(ctx context.Context, logger, writer string) ResponseExtra { + reqURL := setting.LocalURL + fmt.Sprintf("api/internal/manager/remove-logger/%s/%s", url.PathEscape(logger), url.PathEscape(writer)) + req := newInternalRequest(ctx, reqURL, "POST") + return requestJSONClientMsg(req, "Removed") +} + +// Processes return the current processes from this gitea instance +func Processes(ctx context.Context, out io.Writer, flat, noSystem, stacktraces, json bool, cancel string) ResponseExtra { + reqURL := setting.LocalURL + fmt.Sprintf("api/internal/manager/processes?flat=%t&no-system=%t&stacktraces=%t&json=%t&cancel-pid=%s", flat, noSystem, stacktraces, json, url.QueryEscape(cancel)) + + req := newInternalRequest(ctx, reqURL, "GET") + callback := func(resp *http.Response, extra *ResponseExtra) { + _, extra.Error = io.Copy(out, resp.Body) + } + _, extra := requestJSONResp(req, &responseCallback{callback}) + return extra +} diff --git a/modules/private/request.go b/modules/private/request.go new file mode 100644 index 0000000..58cd261 --- /dev/null +++ b/modules/private/request.go @@ -0,0 +1,128 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package private + +import ( + "fmt" + "io" + "net/http" + + "code.gitea.io/gitea/modules/httplib" + "code.gitea.io/gitea/modules/json" +) + +// ResponseText is used to get the response as text, instead of parsing it as JSON. +type ResponseText struct { + Text string +} + +// ResponseExtra contains extra information about the response, especially for error responses. +type ResponseExtra struct { + StatusCode int + UserMsg string + Error error +} + +type responseCallback struct { + Callback func(resp *http.Response, extra *ResponseExtra) +} + +func (re *ResponseExtra) HasError() bool { + return re.Error != nil +} + +type responseError struct { + statusCode int + errorString string +} + +func (re responseError) Error() string { + if re.errorString == "" { + return fmt.Sprintf("internal API error response, status=%d", re.statusCode) + } + return fmt.Sprintf("internal API error response, status=%d, err=%s", re.statusCode, re.errorString) +} + +// requestJSONResp sends a request to the gitea server and then parses the response. +// If the status code is not 2xx, or any error occurs, the ResponseExtra.Error field is guaranteed to be non-nil, +// and the ResponseExtra.UserMsg field will be set to a message for the end user. +// Caller should check the ResponseExtra.HasError() first to see whether the request fails. +// +// * If the "res" is a struct pointer, the response will be parsed as JSON +// * If the "res" is ResponseText pointer, the response will be stored as text in it +// * If the "res" is responseCallback pointer, the callback function should set the ResponseExtra fields accordingly +func requestJSONResp[T any](req *httplib.Request, res *T) (ret *T, extra ResponseExtra) { + resp, err := req.Response() + if err != nil { + extra.UserMsg = "Internal Server Connection Error" + extra.Error = fmt.Errorf("unable to contact gitea %q: %w", req.GoString(), err) + return nil, extra + } + defer resp.Body.Close() + + extra.StatusCode = resp.StatusCode + + // if the status code is not 2xx, try to parse the error response + if resp.StatusCode/100 != 2 { + var respErr Response + if err := json.NewDecoder(resp.Body).Decode(&respErr); err != nil { + extra.UserMsg = "Internal Server Error Decoding Failed" + extra.Error = fmt.Errorf("unable to decode error response %q: %w", req.GoString(), err) + return nil, extra + } + extra.UserMsg = respErr.UserMsg + if extra.UserMsg == "" { + extra.UserMsg = "Internal Server Error (no message for end users)" + } + extra.Error = responseError{statusCode: resp.StatusCode, errorString: respErr.Err} + return res, extra + } + + // now, the StatusCode must be 2xx + var v any = res + if respText, ok := v.(*ResponseText); ok { + // get the whole response as a text string + bs, err := io.ReadAll(resp.Body) + if err != nil { + extra.UserMsg = "Internal Server Response Reading Failed" + extra.Error = fmt.Errorf("unable to read response %q: %w", req.GoString(), err) + return nil, extra + } + respText.Text = string(bs) + return res, extra + } else if cb, ok := v.(*responseCallback); ok { + // pass the response to callback, and let the callback update the ResponseExtra + extra.StatusCode = resp.StatusCode + cb.Callback(resp, &extra) + return nil, extra + } else if err := json.NewDecoder(resp.Body).Decode(res); err != nil { + // decode the response into the given struct + extra.UserMsg = "Internal Server Response Decoding Failed" + extra.Error = fmt.Errorf("unable to decode response %q: %w", req.GoString(), err) + return nil, extra + } + + if respMsg, ok := v.(*Response); ok { + // if the "res" is Response structure, try to get the UserMsg from it and update the ResponseExtra + extra.UserMsg = respMsg.UserMsg + if respMsg.Err != "" { + // usually this shouldn't happen, because the StatusCode is 2xx, there should be no error. + // but we still handle the "err" response, in case some people return error messages by status code 200. + extra.Error = responseError{statusCode: resp.StatusCode, errorString: respMsg.Err} + } + } + + return res, extra +} + +// requestJSONClientMsg sends a request to the gitea server, server only responds text message status=200 with "success" body +// If the request succeeds (200), the argument clientSuccessMsg will be used as ResponseExtra.UserMsg. +func requestJSONClientMsg(req *httplib.Request, clientSuccessMsg string) ResponseExtra { + _, extra := requestJSONResp(req, &ResponseText{}) + if extra.HasError() { + return extra + } + extra.UserMsg = clientSuccessMsg + return extra +} diff --git a/modules/private/restore_repo.go b/modules/private/restore_repo.go new file mode 100644 index 0000000..496209d --- /dev/null +++ b/modules/private/restore_repo.go @@ -0,0 +1,36 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package private + +import ( + "context" + "fmt" + "time" + + "code.gitea.io/gitea/modules/setting" +) + +// RestoreParams structure holds a data for restore repository +type RestoreParams struct { + RepoDir string + OwnerName string + RepoName string + Units []string + Validation bool +} + +// RestoreRepo calls the internal RestoreRepo function +func RestoreRepo(ctx context.Context, repoDir, ownerName, repoName string, units []string, validation bool) ResponseExtra { + reqURL := setting.LocalURL + "api/internal/restore_repo" + + req := newInternalRequest(ctx, reqURL, "POST", RestoreParams{ + RepoDir: repoDir, + OwnerName: ownerName, + RepoName: repoName, + Units: units, + Validation: validation, + }) + req.SetTimeout(3*time.Second, 0) // since the request will spend much time, don't timeout + return requestJSONClientMsg(req, fmt.Sprintf("Restore repo %s/%s successfully", ownerName, repoName)) +} diff --git a/modules/private/serv.go b/modules/private/serv.go new file mode 100644 index 0000000..480a446 --- /dev/null +++ b/modules/private/serv.go @@ -0,0 +1,63 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package private + +import ( + "context" + "fmt" + "net/url" + + asymkey_model "code.gitea.io/gitea/models/asymkey" + "code.gitea.io/gitea/models/perm" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" +) + +// KeyAndOwner is the response from ServNoCommand +type KeyAndOwner struct { + Key *asymkey_model.PublicKey `json:"key"` + Owner *user_model.User `json:"user"` +} + +// ServNoCommand returns information about the provided key +func ServNoCommand(ctx context.Context, keyID int64) (*asymkey_model.PublicKey, *user_model.User, error) { + reqURL := setting.LocalURL + fmt.Sprintf("api/internal/serv/none/%d", keyID) + req := newInternalRequest(ctx, reqURL, "GET") + keyAndOwner, extra := requestJSONResp(req, &KeyAndOwner{}) + if extra.HasError() { + return nil, nil, extra.Error + } + return keyAndOwner.Key, keyAndOwner.Owner, nil +} + +// ServCommandResults are the results of a call to the private route serv +type ServCommandResults struct { + IsWiki bool + DeployKeyID int64 + KeyID int64 // public key + KeyName string // this field is ambiguous, it can be the name of DeployKey, or the name of the PublicKey + UserName string + UserEmail string + UserID int64 + OwnerName string + RepoName string + RepoID int64 +} + +// ServCommand preps for a serv call +func ServCommand(ctx context.Context, keyID int64, ownerName, repoName string, mode perm.AccessMode, verbs ...string) (*ServCommandResults, ResponseExtra) { + reqURL := setting.LocalURL + fmt.Sprintf("api/internal/serv/command/%d/%s/%s?mode=%d", + keyID, + url.PathEscape(ownerName), + url.PathEscape(repoName), + mode, + ) + for _, verb := range verbs { + if verb != "" { + reqURL += fmt.Sprintf("&verb=%s", url.QueryEscape(verb)) + } + } + req := newInternalRequest(ctx, reqURL, "GET") + return requestJSONResp(req, &ServCommandResults{}) +} |