wxiaoguang 2023-05-22 09:38:38 +08:00 committed by GitHub
parent 2d3ebe889e
commit ec2a01d1e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 36 additions and 34 deletions

View file

@ -92,7 +92,7 @@ func AccessLogger() func(http.Handler) http.Handler {
RequestID: &requestID, RequestID: &requestID,
}) })
if err != nil { 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()) logger.Info("%s", buf.String())

View file

@ -13,6 +13,7 @@ type ResponseWriter interface {
http.Flusher http.Flusher
Status() int Status() int
Before(func(ResponseWriter)) Before(func(ResponseWriter))
Size() int // used by access logger template
} }
var _ ResponseWriter = &Response{} var _ ResponseWriter = &Response{}
@ -45,6 +46,10 @@ func (r *Response) Write(bs []byte) (int, error) {
return size, nil return size, nil
} }
func (r *Response) Size() int {
return r.written
}
// WriteHeader write status code // WriteHeader write status code
func (r *Response) WriteHeader(statusCode int) { func (r *Response) WriteHeader(statusCode int) {
if !r.beforeExecuted { if !r.beforeExecuted {

View file

@ -244,6 +244,10 @@ func (g *Manager) DoGracefulRestart() {
log.Error("Error whilst forking from PID: %d : %v", os.Getpid(), err) 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 { } else {
log.Info("PID: %d. Not set restartable. Shutting down...", os.Getpid()) log.Info("PID: %d. Not set restartable. Shutting down...", os.Getpid())
g.notify(stoppingMsg) g.notify(stoppingMsg)

View file

@ -109,5 +109,7 @@ func RestartProcess() (int, error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
return process.Pid, nil processPid := process.Pid
_ = process.Release() // no wait, so release
return processPid, nil
} }

View file

@ -9,7 +9,6 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
) )
// Email structure holds a data for sending general emails
type GenerateTokenRequest struct { type GenerateTokenRequest struct {
Scope string Scope string
} }

View file

@ -19,14 +19,14 @@ import (
func Shutdown(ctx context.Context) ResponseExtra { func Shutdown(ctx context.Context) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/manager/shutdown" reqURL := setting.LocalURL + "api/internal/manager/shutdown"
req := newInternalRequest(ctx, reqURL, "POST") req := newInternalRequest(ctx, reqURL, "POST")
return requestJSONUserMsg(req, "Shutting down") return requestJSONClientMsg(req, "Shutting down")
} }
// Restart calls the internal restart function // Restart calls the internal restart function
func Restart(ctx context.Context) ResponseExtra { func Restart(ctx context.Context) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/manager/restart" reqURL := setting.LocalURL + "api/internal/manager/restart"
req := newInternalRequest(ctx, reqURL, "POST") req := newInternalRequest(ctx, reqURL, "POST")
return requestJSONUserMsg(req, "Restarting") return requestJSONClientMsg(req, "Restarting")
} }
// FlushOptions represents the options for the flush call // 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 { if timeout > 0 {
req.SetReadWriteTimeout(timeout + 10*time.Second) req.SetReadWriteTimeout(timeout + 10*time.Second)
} }
return requestJSONUserMsg(req, "Flushed") return requestJSONClientMsg(req, "Flushed")
} }
// PauseLogging pauses logging // PauseLogging pauses logging
func PauseLogging(ctx context.Context) ResponseExtra { func PauseLogging(ctx context.Context) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/manager/pause-logging" reqURL := setting.LocalURL + "api/internal/manager/pause-logging"
req := newInternalRequest(ctx, reqURL, "POST") req := newInternalRequest(ctx, reqURL, "POST")
return requestJSONUserMsg(req, "Logging Paused") return requestJSONClientMsg(req, "Logging Paused")
} }
// ResumeLogging resumes logging // ResumeLogging resumes logging
func ResumeLogging(ctx context.Context) ResponseExtra { func ResumeLogging(ctx context.Context) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/manager/resume-logging" reqURL := setting.LocalURL + "api/internal/manager/resume-logging"
req := newInternalRequest(ctx, reqURL, "POST") req := newInternalRequest(ctx, reqURL, "POST")
return requestJSONUserMsg(req, "Logging Restarted") return requestJSONClientMsg(req, "Logging Restarted")
} }
// ReleaseReopenLogging releases and reopens logging files // ReleaseReopenLogging releases and reopens logging files
func ReleaseReopenLogging(ctx context.Context) ResponseExtra { func ReleaseReopenLogging(ctx context.Context) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/manager/release-and-reopen-logging" reqURL := setting.LocalURL + "api/internal/manager/release-and-reopen-logging"
req := newInternalRequest(ctx, reqURL, "POST") req := newInternalRequest(ctx, reqURL, "POST")
return requestJSONUserMsg(req, "Logging Restarted") return requestJSONClientMsg(req, "Logging Restarted")
} }
// SetLogSQL sets database logging // SetLogSQL sets database logging
func SetLogSQL(ctx context.Context, on bool) ResponseExtra { func SetLogSQL(ctx context.Context, on bool) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/manager/set-log-sql?on=" + strconv.FormatBool(on) reqURL := setting.LocalURL + "api/internal/manager/set-log-sql?on=" + strconv.FormatBool(on)
req := newInternalRequest(ctx, reqURL, "POST") 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 // 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, Mode: mode,
Config: config, Config: config,
}) })
return requestJSONUserMsg(req, "Added") return requestJSONClientMsg(req, "Added")
} }
// RemoveLogger removes a logger // RemoveLogger removes a logger
func RemoveLogger(ctx context.Context, logger, writer string) ResponseExtra { 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)) reqURL := setting.LocalURL + fmt.Sprintf("api/internal/manager/remove-logger/%s/%s", url.PathEscape(logger), url.PathEscape(writer))
req := newInternalRequest(ctx, reqURL, "POST") req := newInternalRequest(ctx, reqURL, "POST")
return requestJSONUserMsg(req, "Removed") return requestJSONClientMsg(req, "Removed")
} }
// Processes return the current processes from this gitea instance // 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) { callback := func(resp *http.Response, extra *ResponseExtra) {
_, extra.Error = io.Copy(out, resp.Body) _, extra.Error = io.Copy(out, resp.Body)
} }
_, extra := requestJSONResp(req, &callback) _, extra := requestJSONResp(req, &responseCallback{callback})
return extra return extra
} }

View file

@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"unicode"
"code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
@ -25,7 +24,9 @@ type ResponseExtra struct {
Error error 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 { func (re *ResponseExtra) HasError() bool {
return re.Error != nil 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) 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, // 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. // 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) respText.Text = string(bs)
return res, extra 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 // pass the response to callback, and let the callback update the ResponseExtra
extra.StatusCode = resp.StatusCode extra.StatusCode = resp.StatusCode
(*callback)(resp, &extra) cb.Callback(resp, &extra)
return nil, extra return nil, extra
} else if err := json.NewDecoder(resp.Body).Decode(res); err != nil { } else if err := json.NewDecoder(resp.Body).Decode(res); err != nil {
// decode the response into the given struct // 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 return res, extra
} }
// requestJSONUserMsg sends a request to the gitea server and then parses the response as private.Response // requestJSONClientMsg sends a request to the gitea server, server only responds text message status=200 with "success" body
// If the request succeeds, the successMsg will be used as part of ResponseExtra.UserMsg. // If the request succeeds (200), the argument clientSuccessMsg will be used as ResponseExtra.UserMsg.
func requestJSONUserMsg(req *httplib.Request, successMsg string) ResponseExtra { func requestJSONClientMsg(req *httplib.Request, clientSuccessMsg string) ResponseExtra {
resp, extra := requestJSONResp(req, &Response{}) _, extra := requestJSONResp(req, &responseText{})
if extra.HasError() { if extra.HasError() {
return extra return extra
} }
if resp.UserMsg == "" { extra.UserMsg = clientSuccessMsg
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
}
}
return extra return extra
} }

View file

@ -32,5 +32,5 @@ func RestoreRepo(ctx context.Context, repoDir, ownerName, repoName string, units
Validation: validation, Validation: validation,
}) })
req.SetTimeout(3*time.Second, 0) // since the request will spend much time, don't timeout 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))
} }

View file

@ -48,6 +48,6 @@ func RestoreRepo(ctx *myCtx.PrivateContext) {
Err: err.Error(), Err: err.Error(),
}) })
} else { } else {
ctx.Status(http.StatusOK) ctx.PlainText(http.StatusOK, "success")
} }
} }