Close #24836 ![image](https://github.com/go-gitea/gitea/assets/2114189/95b025d2-f25f-4246-a08a-fe44ecb787a9) ![image](https://github.com/go-gitea/gitea/assets/2114189/c3afe1fa-2a23-420d-a016-3b67dcd04cd5)tags/v1.20.0-rc0
@@ -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()) |
@@ -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 { |
@@ -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) |
@@ -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 | |||
} |
@@ -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 | |||
} |
@@ -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 | |||
} |
@@ -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 | |||
} |
@@ -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)) | |||
} |
@@ -48,6 +48,6 @@ func RestoreRepo(ctx *myCtx.PrivateContext) { | |||
Err: err.Error(), | |||
}) | |||
} else { | |||
ctx.Status(http.StatusOK) | |||
ctx.PlainText(http.StatusOK, "success") | |||
} | |||
} |