summaryrefslogtreecommitdiffstats
path: root/modules/private
diff options
context:
space:
mode:
authorDaniel Baumann <daniel@debian.org>2024-10-18 20:33:49 +0200
committerDaniel Baumann <daniel@debian.org>2024-12-12 23:57:56 +0100
commite68b9d00a6e05b3a941f63ffb696f91e554ac5ec (patch)
tree97775d6c13b0f416af55314eb6a89ef792474615 /modules/private
parentInitial commit. (diff)
downloadforgejo-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.go25
-rw-r--r--modules/private/forgejo_actions.go32
-rw-r--r--modules/private/hook.go129
-rw-r--r--modules/private/internal.go96
-rw-r--r--modules/private/key.go30
-rw-r--r--modules/private/mail.go33
-rw-r--r--modules/private/manager.go120
-rw-r--r--modules/private/request.go128
-rw-r--r--modules/private/restore_repo.go36
-rw-r--r--modules/private/serv.go63
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{})
+}