diff --git a/modules/context/access_log.go b/modules/context/access_log.go index 9b649a6a0..373574ba1 100644 --- a/modules/context/access_log.go +++ b/modules/context/access_log.go @@ -92,7 +92,7 @@ func AccessLogger() func(http.Handler) http.Handler { RequestID: &requestID, }) if err != nil { - log.Error("Could not set up chi access logger: %v", err.Error()) + log.Error("Could not execute access logger template: %v", err.Error()) } logger.Info("%s", buf.String()) diff --git a/modules/context/response.go b/modules/context/response.go index ca52ea137..8708d77da 100644 --- a/modules/context/response.go +++ b/modules/context/response.go @@ -13,6 +13,7 @@ type ResponseWriter interface { http.Flusher Status() int Before(func(ResponseWriter)) + Size() int // used by access logger template } var _ ResponseWriter = &Response{} @@ -45,6 +46,10 @@ func (r *Response) Write(bs []byte) (int, error) { return size, nil } +func (r *Response) Size() int { + return r.written +} + // WriteHeader write status code func (r *Response) WriteHeader(statusCode int) { if !r.beforeExecuted { diff --git a/modules/graceful/manager_unix.go b/modules/graceful/manager_unix.go index d89f6fc72..f949abfd2 100644 --- a/modules/graceful/manager_unix.go +++ b/modules/graceful/manager_unix.go @@ -244,6 +244,10 @@ func (g *Manager) DoGracefulRestart() { log.Error("Error whilst forking from PID: %d : %v", os.Getpid(), err) } } + // doFork calls RestartProcess which starts a new Gitea process, so this parent process needs to exit + // Otherwise some resources (eg: leveldb lock) will be held by this parent process and the new process will fail to start + log.Info("PID: %d. Shutting down after forking ...", os.Getpid()) + g.doShutdown() } else { log.Info("PID: %d. Not set restartable. Shutting down...", os.Getpid()) g.notify(stoppingMsg) diff --git a/modules/graceful/restart_unix.go b/modules/graceful/restart_unix.go index a7c2b8288..98d5c5cc2 100644 --- a/modules/graceful/restart_unix.go +++ b/modules/graceful/restart_unix.go @@ -109,5 +109,7 @@ func RestartProcess() (int, error) { if err != nil { return 0, err } - return process.Pid, nil + processPid := process.Pid + _ = process.Release() // no wait, so release + return processPid, nil } diff --git a/modules/private/actions.go b/modules/private/actions.go index be24e16d3..c924dac2c 100644 --- a/modules/private/actions.go +++ b/modules/private/actions.go @@ -9,7 +9,6 @@ import ( "code.gitea.io/gitea/modules/setting" ) -// Email structure holds a data for sending general emails type GenerateTokenRequest struct { Scope string } diff --git a/modules/private/manager.go b/modules/private/manager.go index 3448f2e34..a974c3ed0 100644 --- a/modules/private/manager.go +++ b/modules/private/manager.go @@ -19,14 +19,14 @@ import ( func Shutdown(ctx context.Context) ResponseExtra { reqURL := setting.LocalURL + "api/internal/manager/shutdown" req := newInternalRequest(ctx, reqURL, "POST") - return requestJSONUserMsg(req, "Shutting down") + 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 requestJSONUserMsg(req, "Restarting") + return requestJSONClientMsg(req, "Restarting") } // FlushOptions represents the options for the flush call @@ -42,35 +42,35 @@ func FlushQueues(ctx context.Context, timeout time.Duration, nonBlocking bool) R if timeout > 0 { req.SetReadWriteTimeout(timeout + 10*time.Second) } - return requestJSONUserMsg(req, "Flushed") + 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 requestJSONUserMsg(req, "Logging Paused") + 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 requestJSONUserMsg(req, "Logging Restarted") + 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 requestJSONUserMsg(req, "Logging Restarted") + 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 requestJSONUserMsg(req, "Log SQL setting set") + return requestJSONClientMsg(req, "Log SQL setting set") } // LoggerOptions represents the options for the add logger call @@ -90,14 +90,14 @@ func AddLogger(ctx context.Context, logger, writer, mode string, config map[stri Mode: mode, Config: config, }) - return requestJSONUserMsg(req, "Added") + 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 requestJSONUserMsg(req, "Removed") + return requestJSONClientMsg(req, "Removed") } // Processes return the current processes from this gitea instance @@ -108,6 +108,6 @@ func Processes(ctx context.Context, out io.Writer, flat, noSystem, stacktraces, callback := func(resp *http.Response, extra *ResponseExtra) { _, extra.Error = io.Copy(out, resp.Body) } - _, extra := requestJSONResp(req, &callback) + _, extra := requestJSONResp(req, &responseCallback{callback}) return extra } diff --git a/modules/private/request.go b/modules/private/request.go index 3eb8c92c1..d3f99381a 100644 --- a/modules/private/request.go +++ b/modules/private/request.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "net/http" - "unicode" "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/json" @@ -25,7 +24,9 @@ type ResponseExtra struct { Error error } -type responseCallback func(resp *http.Response, extra *ResponseExtra) +type responseCallback struct { + Callback func(resp *http.Response, extra *ResponseExtra) +} func (re *ResponseExtra) HasError() bool { return re.Error != nil @@ -43,7 +44,7 @@ func (re responseError) Error() string { return fmt.Sprintf("internal API error response, status=%d, err=%s", re.statusCode, re.errorString) } -// requestJSONUserMsg sends a request to the gitea server and then parses the response. +// 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. // @@ -89,10 +90,10 @@ func requestJSONResp[T any](req *httplib.Request, res *T) (ret *T, extra Respons } respText.Text = string(bs) return res, extra - } else if callback, ok := v.(*responseCallback); ok { + } else if cb, ok := v.(*responseCallback); ok { // pass the response to callback, and let the callback update the ResponseExtra extra.StatusCode = resp.StatusCode - (*callback)(resp, &extra) + 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 @@ -114,22 +115,13 @@ func requestJSONResp[T any](req *httplib.Request, res *T) (ret *T, extra Respons return res, extra } -// requestJSONUserMsg sends a request to the gitea server and then parses the response as private.Response -// If the request succeeds, the successMsg will be used as part of ResponseExtra.UserMsg. -func requestJSONUserMsg(req *httplib.Request, successMsg string) ResponseExtra { - resp, extra := requestJSONResp(req, &Response{}) +// 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 } - if resp.UserMsg == "" { - extra.UserMsg = successMsg // if UserMsg is empty, then use successMsg as userMsg - } else if successMsg != "" { - // else, now UserMsg is not empty, if successMsg is not empty, then append successMsg to UserMsg - if unicode.IsPunct(rune(extra.UserMsg[len(extra.UserMsg)-1])) { - extra.UserMsg = extra.UserMsg + " " + successMsg - } else { - extra.UserMsg = extra.UserMsg + ". " + successMsg - } - } + extra.UserMsg = clientSuccessMsg return extra } diff --git a/modules/private/restore_repo.go b/modules/private/restore_repo.go index 34d0f5d48..496209d3c 100644 --- a/modules/private/restore_repo.go +++ b/modules/private/restore_repo.go @@ -32,5 +32,5 @@ func RestoreRepo(ctx context.Context, repoDir, ownerName, repoName string, units Validation: validation, }) req.SetTimeout(3*time.Second, 0) // since the request will spend much time, don't timeout - return requestJSONUserMsg(req, fmt.Sprintf("Restore repo %s/%s successfully", ownerName, repoName)) + return requestJSONClientMsg(req, fmt.Sprintf("Restore repo %s/%s successfully", ownerName, repoName)) } diff --git a/routers/private/restore_repo.go b/routers/private/restore_repo.go index 97ac9a3c5..7efc22a3d 100644 --- a/routers/private/restore_repo.go +++ b/routers/private/restore_repo.go @@ -48,6 +48,6 @@ func RestoreRepo(ctx *myCtx.PrivateContext) { Err: err.Error(), }) } else { - ctx.Status(http.StatusOK) + ctx.PlainText(http.StatusOK, "success") } }