mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-08-19 08:26:23 -07:00
Rework command line staff management commands and password handling, update logging initialization options, and bump version to 4.2.0 in preparation for upcoming release
This commit is contained in:
parent
305b557e41
commit
d923c73000
10 changed files with 307 additions and 129 deletions
217
cmd/gochan/commandline.go
Normal file
217
cmd/gochan/commandline.go
Normal file
|
@ -0,0 +1,217 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
"github.com/rs/zerolog"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func getPassword() (string, error) {
|
||||
var password string
|
||||
fd := int(os.Stdin.Fd())
|
||||
state, err := term.MakeRaw(fd)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer term.Restore(fd, state)
|
||||
|
||||
for {
|
||||
input := make([]byte, 1)
|
||||
if _, err := syscall.Read(int(fd), input); err != nil {
|
||||
term.Restore(fd, state)
|
||||
return "", err
|
||||
}
|
||||
if input[0] == '\n' || input[0] == '\r' {
|
||||
term.Restore(fd, state)
|
||||
fmt.Println()
|
||||
break
|
||||
}
|
||||
if input[0] == 127 || input[0] == 8 {
|
||||
if len(password) > 0 {
|
||||
password = password[:len(password)-1]
|
||||
fmt.Print("\b \b")
|
||||
}
|
||||
} else if input[0] == 3 {
|
||||
term.Restore(fd, state)
|
||||
fmt.Println("\nAborted.")
|
||||
os.Exit(1)
|
||||
} else {
|
||||
password += string(input[0])
|
||||
fmt.Print("*")
|
||||
}
|
||||
}
|
||||
return password, nil
|
||||
}
|
||||
|
||||
func printInfoAndLog(msg string, infoEv ...*zerolog.Event) {
|
||||
fmt.Println(msg)
|
||||
if len(infoEv) > 0 {
|
||||
infoEv[0].Msg(msg)
|
||||
} else {
|
||||
gcutil.LogInfo().Str("source", "commandLine").Msg(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func fatalAndLog(msg string, err error, fatalEv *zerolog.Event) {
|
||||
fmt.Fprintln(os.Stderr, msg, err)
|
||||
fatalEv.Err(err).Caller(1).Msg(msg)
|
||||
}
|
||||
|
||||
func parseCommandLine() {
|
||||
var newstaff string
|
||||
var delstaff string
|
||||
// var rebuild string
|
||||
var password string
|
||||
var rank int
|
||||
var err error
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
cmd := os.Args[1]
|
||||
var fatalEv *zerolog.Event
|
||||
var systemCritical *config.SystemCriticalConfig
|
||||
if cmd == "newstaff" || cmd == "delstaff" || cmd == "rebuild" {
|
||||
if err = config.InitConfig(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error initializing config:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
systemCritical = config.GetSystemCriticalConfig()
|
||||
if err = gcutil.InitLogs(systemCritical.LogDir, &gcutil.LogOptions{FileOnly: true}); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error initializing logs:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fatalEv = gcutil.LogFatal()
|
||||
defer fatalEv.Discard()
|
||||
|
||||
initDB(fatalEv)
|
||||
}
|
||||
|
||||
switch cmd {
|
||||
case "version":
|
||||
fmt.Println(config.GochanVersion)
|
||||
return
|
||||
case "help", "-h", "--help":
|
||||
fmt.Println("Usage: gochan [command] [options]")
|
||||
fmt.Println("Commands:")
|
||||
fmt.Println(" version Show the version of gochan")
|
||||
fmt.Println(" help Show this help message")
|
||||
fmt.Println(" newstaff Create a new staff account")
|
||||
fmt.Println(" delstaff Delete a staff account")
|
||||
fmt.Println(" rebuild Rebuild the specified components")
|
||||
fmt.Println("Run 'gochan [command] --help' for more information on a command.")
|
||||
case "newstaff":
|
||||
flagSet := flag.NewFlagSet("newstaff", flag.ExitOnError)
|
||||
flagSet.StringVar(&newstaff, "username", "", "Username for the new staff account")
|
||||
flagSet.StringVar(&password, "password", "", "Password for the new staff account")
|
||||
flagSet.IntVar(&rank, "rank", 0, "Rank for the new staff account")
|
||||
flagSet.Parse(os.Args[2:])
|
||||
if newstaff == "" || rank <= 0 {
|
||||
fmt.Fprintln(os.Stderr, "Error: -username and -rank are required")
|
||||
flagSet.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if password == "" {
|
||||
fmt.Print("Enter password for new staff account: ")
|
||||
password, err = getPassword()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error getting password:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if password == "" {
|
||||
fmt.Fprintln(os.Stderr, "Error: Password cannot be empty")
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Print("Confirm password: ")
|
||||
confirm, err := getPassword()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error getting password confirmation:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if password != confirm {
|
||||
fmt.Fprintln(os.Stderr, "Error: Passwords do not match")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
staff, err := gcsql.NewStaff(newstaff, password, rank)
|
||||
if err != nil {
|
||||
fatalAndLog("Error creating new staff account:", err, fatalEv.Str("source", "commandLine").Str("username", newstaff))
|
||||
}
|
||||
printInfoAndLog("New staff account created successfully")
|
||||
gcutil.LogInfo().
|
||||
Str("source", "commandLine").
|
||||
Str("username", newstaff).
|
||||
Msg("New staff account created")
|
||||
fmt.Printf("New staff account %q created with rank %s\n", newstaff, staff.RankTitle())
|
||||
case "delstaff":
|
||||
var force bool
|
||||
flagSet := flag.NewFlagSet("delstaff", flag.ExitOnError)
|
||||
flagSet.StringVar(&delstaff, "username", "", "Username of the staff account to delete")
|
||||
flagSet.BoolVar(&force, "force", false, "Force deletion without confirmation")
|
||||
flagSet.Parse(os.Args[2:])
|
||||
if delstaff == "" {
|
||||
fmt.Fprintln(os.Stderr, "Error: -username is required")
|
||||
flagSet.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
if !force {
|
||||
fmt.Printf("Are you sure you want to delete the staff account %q? [y/N]: ", delstaff)
|
||||
var answer string
|
||||
fmt.Scanln(&answer)
|
||||
answer = strings.ToLower(answer)
|
||||
if answer != "y" && answer != "yes" {
|
||||
fmt.Println("Not deleting.")
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = gcsql.DeactivateStaff(delstaff); err != nil {
|
||||
fatalAndLog("Error deleting staff account:", err, fatalEv.Str("source", "commandLine").Str("username", delstaff))
|
||||
}
|
||||
printInfoAndLog("Staff account deleted successfully", gcutil.LogInfo().Str("source", "commandLine").Str("username", delstaff))
|
||||
case "rebuild":
|
||||
flagSet := flag.NewFlagSet("rebuild", flag.ExitOnError)
|
||||
var rebuildAll bool
|
||||
var rebuildBoards bool
|
||||
var rebuildFront bool
|
||||
var rebuildJS bool
|
||||
flagSet.BoolVar(&rebuildBoards, "boards", false, "Rebuild boards and threads")
|
||||
flagSet.BoolVar(&rebuildFront, "front", false, "Rebuild front page")
|
||||
flagSet.BoolVar(&rebuildJS, "js", false, "Rebuild consts.js")
|
||||
flagSet.BoolVar(&rebuildAll, "all", false, "Rebuild all components (overrides other flags)")
|
||||
flagSet.Parse(os.Args[2:])
|
||||
var rebuildFlag int
|
||||
if rebuildAll {
|
||||
rebuildFlag = buildAll
|
||||
}
|
||||
if rebuildBoards {
|
||||
rebuildFlag |= buildBoards
|
||||
}
|
||||
if rebuildFront {
|
||||
rebuildFlag |= buildFront
|
||||
}
|
||||
if rebuildJS {
|
||||
rebuildFlag |= buildJS
|
||||
}
|
||||
if rebuildFlag == 0 {
|
||||
fmt.Fprintln(os.Stderr, "Error: At least one rebuild option is required")
|
||||
flagSet.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
startupRebuild(rebuildFlag, fatalEv)
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "Unknown command:", cmd)
|
||||
fmt.Println("Run 'gochan help' for a list of commands.")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -1,11 +1,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/building"
|
||||
|
@ -33,6 +30,10 @@ func cleanup() {
|
|||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) > 1 {
|
||||
parseCommandLine()
|
||||
return
|
||||
}
|
||||
gcutil.LogInfo().Str("version", config.GochanVersion).Msg("Starting gochan")
|
||||
fatalEv := gcutil.LogFatal()
|
||||
defer func() {
|
||||
|
@ -48,7 +49,11 @@ func main() {
|
|||
|
||||
uid, gid := config.GetUser()
|
||||
systemCritical := config.GetSystemCriticalConfig()
|
||||
if err = gcutil.InitLogs(systemCritical.LogDir, systemCritical.LogLevel(), uid, gid); err != nil {
|
||||
if err = gcutil.InitLogs(systemCritical.LogDir, &gcutil.LogOptions{
|
||||
LogLevel: systemCritical.LogLevel(),
|
||||
UID: uid,
|
||||
GID: gid,
|
||||
}); err != nil {
|
||||
fatalEv.Err(err).Caller().
|
||||
Str("logDir", systemCritical.LogDir).
|
||||
Int("uid", uid).
|
||||
|
@ -70,25 +75,8 @@ func main() {
|
|||
|
||||
events.TriggerEvent("startup")
|
||||
|
||||
if err = gcsql.ConnectToDB(&systemCritical.SQLConfig); err != nil {
|
||||
fatalEv.Err(err).Msg("Failed to connect to the database")
|
||||
}
|
||||
events.TriggerEvent("db-connected")
|
||||
gcutil.LogInfo().
|
||||
Str("DBtype", systemCritical.DBtype).
|
||||
Str("DBhost", systemCritical.DBhost).
|
||||
Msg("Connected to database")
|
||||
initDB(fatalEv)
|
||||
|
||||
if err = gcsql.CheckAndInitializeDatabase(systemCritical.DBtype); err != nil {
|
||||
cleanup()
|
||||
gcutil.LogFatal().Err(err).Msg("Failed to initialize the database")
|
||||
}
|
||||
events.TriggerEvent("db-initialized")
|
||||
if err = gcsql.ResetViews(); err != nil {
|
||||
fatalEv.Err(err).Caller().Msg("Failed resetting SQL views")
|
||||
}
|
||||
|
||||
parseCommandLine(fatalEv)
|
||||
serverutil.InitMinifier()
|
||||
siteCfg := config.GetSiteConfig()
|
||||
if err = geoip.SetupGeoIP(siteCfg.GeoIPType, siteCfg.GeoIPOptions); err != nil {
|
||||
|
@ -122,71 +110,24 @@ func main() {
|
|||
<-sc
|
||||
}
|
||||
|
||||
func parseCommandLine(fatalEv *zerolog.Event) {
|
||||
var newstaff string
|
||||
var delstaff string
|
||||
var rebuild string
|
||||
var rank int
|
||||
var err error
|
||||
flag.StringVar(&newstaff, "newstaff", "", "<newusername>:<newpassword>")
|
||||
flag.StringVar(&delstaff, "delstaff", "", "<username>")
|
||||
flag.StringVar(&rebuild, "rebuild", "", "accepted values are boards,front,js, or all")
|
||||
flag.IntVar(&rank, "rank", 0, "New staff member rank, to be used with -newstaff or -delstaff")
|
||||
flag.Parse()
|
||||
func initDB(fatalEv *zerolog.Event) {
|
||||
systemCritical := config.GetSystemCriticalConfig()
|
||||
if err := gcsql.ConnectToDB(&systemCritical.SQLConfig); err != nil {
|
||||
fatalEv.Err(err).Msg("Failed to connect to the database")
|
||||
}
|
||||
events.TriggerEvent("db-connected")
|
||||
gcutil.LogInfo().
|
||||
Str("DBtype", systemCritical.DBtype).
|
||||
Str("DBhost", systemCritical.DBhost).
|
||||
Msg("Connected to database")
|
||||
|
||||
rebuildFlag := buildNone
|
||||
switch rebuild {
|
||||
case "boards":
|
||||
rebuildFlag = buildBoards
|
||||
case "front":
|
||||
rebuildFlag = buildFront
|
||||
case "js":
|
||||
rebuildFlag = buildJS
|
||||
case "all":
|
||||
rebuildFlag = buildAll
|
||||
if err := gcsql.CheckAndInitializeDatabase(systemCritical.DBtype); err != nil {
|
||||
cleanup()
|
||||
gcutil.LogFatal().Err(err).Msg("Failed to initialize the database")
|
||||
}
|
||||
if rebuildFlag > 0 {
|
||||
startupRebuild(rebuildFlag, fatalEv)
|
||||
}
|
||||
|
||||
if newstaff != "" {
|
||||
arr := strings.Split(newstaff, ":")
|
||||
if len(arr) < 2 || delstaff != "" {
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
if _, err = gcsql.NewStaff(arr[0], arr[1], rank); err != nil {
|
||||
fatalEv.Err(err).Caller().
|
||||
Str("source", "commandLine").
|
||||
Str("username", arr[0]).
|
||||
Msg("Failed creating new staff account")
|
||||
}
|
||||
gcutil.LogInfo().
|
||||
Str("source", "commandLine").
|
||||
Str("username", arr[0]).
|
||||
Msg("New staff account created")
|
||||
os.Exit(0)
|
||||
}
|
||||
if delstaff != "" {
|
||||
if newstaff != "" {
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Are you sure you want to delete the staff account %q? [y/N]: ", delstaff)
|
||||
|
||||
var answer string
|
||||
fmt.Scanln(&answer)
|
||||
answer = strings.ToLower(answer)
|
||||
if answer == "y" || answer == "yes" {
|
||||
if err = gcsql.DeactivateStaff(delstaff); err != nil {
|
||||
fatalEv.Err(err).Caller().
|
||||
Str("source", "commandLine").
|
||||
Str("username", delstaff).
|
||||
Msg("Unable to delete staff account")
|
||||
}
|
||||
gcutil.LogInfo().Str("newStaff", delstaff).Msg("Staff account deleted")
|
||||
} else {
|
||||
fmt.Println("Not deleting.")
|
||||
}
|
||||
events.TriggerEvent("db-initialized")
|
||||
if err := gcsql.ResetViews(); err != nil {
|
||||
fatalEv.Err(err).Caller().Msg("Failed resetting SQL views")
|
||||
}
|
||||
events.TriggerEvent("db-views-reset")
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/building"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gctemplates"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
"github.com/gochan-org/gochan/pkg/server/serverutil"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
@ -23,44 +20,37 @@ func startupRebuild(buildFlag int, fatalEv *zerolog.Event) {
|
|||
var err error
|
||||
serverutil.InitMinifier()
|
||||
if err = gctemplates.InitTemplates(); err != nil {
|
||||
fatalEv.Err(err).Caller().
|
||||
Str("building", "initialization").
|
||||
Msg("Unable to initialize templates")
|
||||
fatalAndLog("Unable to initialize templates:", err, fatalEv.Str("building", "initialization"))
|
||||
}
|
||||
|
||||
if buildFlag&buildBoards > 0 {
|
||||
gcsql.ResetBoardSectionArrays()
|
||||
if err = building.BuildBoardListJSON(); err != nil {
|
||||
fatalEv.Err(err).Caller().
|
||||
Str("building", "sections").
|
||||
Msg("Unable to build section array")
|
||||
if err = gcsql.ResetBoardSectionArrays(); err != nil {
|
||||
fatalAndLog("Unable to reset board section arrays:", err, fatalEv.Str("building", "reset"))
|
||||
}
|
||||
|
||||
if err = building.BuildBoards(true); err != nil {
|
||||
fatalEv.Err(err).Caller().
|
||||
Str("building", "boards").
|
||||
Msg("Unable to build boards")
|
||||
if err = building.BuildBoardListJSON(); err != nil {
|
||||
fatalAndLog("Unable to build board list JSON:", err, fatalEv.Str("building", "boardListJSON"))
|
||||
}
|
||||
gcutil.LogInfo().Msg("Boards built successfully")
|
||||
printInfoAndLog("Board list JSON built successfully")
|
||||
|
||||
if err = building.BuildBoards(true); err != nil {
|
||||
fatalAndLog("Unable to build boards:", err, fatalEv.Str("building", "boards"))
|
||||
}
|
||||
printInfoAndLog("Boards built successfully")
|
||||
}
|
||||
|
||||
if buildFlag&buildJS > 0 {
|
||||
if err = building.BuildJS(); err != nil {
|
||||
fatalEv.Err(err).Caller().
|
||||
Str("building", "js").
|
||||
Msg("Unable to build consts.js")
|
||||
fatalAndLog("Unable to build consts.js:", err, fatalEv.Str("building", "js"))
|
||||
}
|
||||
gcutil.LogInfo().Msg("consts.js built successfully")
|
||||
printInfoAndLog("consts.js built successfully")
|
||||
}
|
||||
|
||||
if buildFlag&buildFront > 0 {
|
||||
if err = building.BuildFrontPage(); err != nil {
|
||||
fatalEv.Err(err).Caller().
|
||||
Str("building", "front").
|
||||
Msg("Unable to build front page")
|
||||
fatalAndLog("Unable to build front page:", err, fatalEv.Str("building", "front"))
|
||||
}
|
||||
gcutil.LogInfo().Msg("Front page built successfully")
|
||||
printInfoAndLog("Front page built successfully")
|
||||
}
|
||||
gcutil.LogInfo().Msg("Finished building without errors, exiting.")
|
||||
os.Exit(0)
|
||||
printInfoAndLog("Finished building without errors, exiting.")
|
||||
}
|
||||
|
|
3
go.mod
3
go.mod
|
@ -29,6 +29,7 @@ require (
|
|||
golang.org/x/crypto v0.37.0
|
||||
golang.org/x/image v0.26.0
|
||||
golang.org/x/net v0.39.0
|
||||
golang.org/x/term v0.32.0
|
||||
layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf
|
||||
layeh.com/gopher-luar v1.0.11
|
||||
)
|
||||
|
@ -50,7 +51,7 @@ require (
|
|||
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
||||
github.com/tdewolff/test v1.0.11 // indirect
|
||||
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
|
6
go.sum
6
go.sum
|
@ -259,8 +259,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
|
@ -270,6 +270,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
|||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
<h1>404: File not found</h1>
|
||||
<img src="/error/lol 404.gif" alt="lol 404">
|
||||
<p>The requested file could not be found on this server.</p>
|
||||
<hr/>Site powered by <a href="https://github.com/gochan-org/gochan" target="_blank">Gochan</a> v4.1.0
|
||||
<hr/>Site powered by <a href="https://github.com/gochan-org/gochan" target="_blank">Gochan</a> v4.2.0
|
||||
</body>
|
||||
</html>
|
|
@ -7,6 +7,6 @@
|
|||
<h1>Error 500: Internal Server error</h1>
|
||||
<img src="/error/server500.gif" alt="server burning">
|
||||
<p>The server encountered an error while trying to serve the page, and we apologize for the inconvenience. The <a href="https://en.wikipedia.org/wiki/Idiot">system administrator</a> will try to fix things as soon they get around to it, whenever that is. Hopefully soon.</p>
|
||||
<hr/>Site powered by <a href="https://github.com/gochan-org/gochan" target="_blank">Gochan</a> v4.1.0
|
||||
<hr/>Site powered by <a href="https://github.com/gochan-org/gochan" target="_blank">Gochan</a> v4.2.0
|
||||
</body>
|
||||
</html>
|
|
@ -7,6 +7,6 @@
|
|||
<h1>Error 502: Bad gateway</h1>
|
||||
<img src="/error/server500.gif" alt="server burning">
|
||||
<p>The server encountered an error while trying to serve the page, and we apologize for the inconvenience. The <a href="https://en.wikipedia.org/wiki/Idiot">system administrator</a> will try to fix things as soon they get around to it, whenever that is. Hopefully soon.</p>
|
||||
<hr/>Site powered by <a href="https://github.com/gochan-org/gochan" target="_blank">Gochan</a> v4.1.0
|
||||
<hr/>Site powered by <a href="https://github.com/gochan-org/gochan" target="_blank">Gochan</a> v4.2.0
|
||||
</body>
|
||||
</html>
|
|
@ -30,7 +30,7 @@ const (
|
|||
DefaultSQLMaxConns = 10
|
||||
DefaultSQLConnMaxLifetimeMin = 3
|
||||
|
||||
GochanVersion = "4.1.0"
|
||||
GochanVersion = "4.2.0"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -100,7 +100,7 @@ func init() {
|
|||
})).With().Timestamp().Logger()
|
||||
}
|
||||
|
||||
func initLog(logPath string, level zerolog.Level) (err error) {
|
||||
func initLog(logPath string, level zerolog.Level, noConsole bool) (err error) {
|
||||
if logFile != nil {
|
||||
// log already initialized
|
||||
if err = logFile.Close(); err != nil {
|
||||
|
@ -114,10 +114,14 @@ func initLog(logPath string, level zerolog.Level) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
writer := zerolog.MultiLevelWriter(logFile, zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
|
||||
w.NoColor = !RunningInTerminal()
|
||||
}))
|
||||
|
||||
var writer zerolog.LevelWriter
|
||||
if noConsole {
|
||||
writer = zerolog.MultiLevelWriter(logFile)
|
||||
} else {
|
||||
writer = zerolog.MultiLevelWriter(logFile, zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
|
||||
w.NoColor = !RunningInTerminal()
|
||||
}))
|
||||
}
|
||||
logger = zerolog.New(writer).With().Timestamp().Logger().Level(level)
|
||||
|
||||
return nil
|
||||
|
@ -138,18 +142,41 @@ func initAccessLog(logPath string) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func InitLogs(logDir string, level zerolog.Level, uid int, gid int) (err error) {
|
||||
if err = initLog(path.Join(logDir, "gochan.log"), level); err != nil {
|
||||
type LogOptions struct {
|
||||
// LogLevel is the zerolog level to use for the log file.
|
||||
LogLevel zerolog.Level
|
||||
// UID is the user ID to set for the log file. If not set or 0, the current
|
||||
// user ID will be used.
|
||||
UID int
|
||||
// GID is the group ID to set for the log file. If not set or 0, the current
|
||||
// group ID will be used.
|
||||
GID int
|
||||
// FileOnly is true if the log file should be used only, and not the console
|
||||
FileOnly bool
|
||||
}
|
||||
|
||||
func InitLogs(logDir string, options *LogOptions) (err error) {
|
||||
if options == nil {
|
||||
options = &LogOptions{}
|
||||
}
|
||||
|
||||
if err = initLog(path.Join(logDir, "gochan.log"), options.LogLevel, options.FileOnly); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = logFile.Chown(uid, gid); err != nil {
|
||||
return err
|
||||
if options.UID > 0 && options.GID > 0 {
|
||||
if err = logFile.Chown(options.UID, options.GID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = initAccessLog(path.Join(logDir, "gochan_access.log")); err != nil {
|
||||
return err
|
||||
}
|
||||
return accessFile.Chown(uid, gid)
|
||||
|
||||
if options.UID > 0 && options.GID > 0 {
|
||||
return accessFile.Chown(options.UID, options.GID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Logger() *zerolog.Logger {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue