mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-08-17 10:56:24 -07:00
Replace gclog/gzlog with structured logging via zerolog
This commit is contained in:
parent
2ff9b0df83
commit
ee61fcb30f
32 changed files with 921 additions and 726 deletions
2
build.py
2
build.py
|
@ -251,7 +251,7 @@ def build(debugging=False):
|
|||
|
||||
def clean():
|
||||
print("Cleaning up")
|
||||
del_files = ("gochan", "gochan.exe", "gochan-migration", "gochan-migration.exe", "releases/", "pkg/gclog/logtest/")
|
||||
del_files = ("gochan", "gochan.exe", "gochan-migration", "gochan-migration.exe", "releases/")
|
||||
for del_file in del_files:
|
||||
delete(del_file)
|
||||
|
||||
|
|
|
@ -9,7 +9,9 @@ import (
|
|||
"syscall"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/gcplugin"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gctemplates"
|
||||
|
@ -23,40 +25,42 @@ import (
|
|||
|
||||
var (
|
||||
versionStr string
|
||||
stdFatal = gclog.LStdLog | gclog.LFatal
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
gclog.Print(gclog.LStdLog, "Cleaning up")
|
||||
fmt.Println("Cleaning up")
|
||||
//gcsql.ExecSQL("DROP TABLE DBPREFIXsessions")
|
||||
gcsql.Close()
|
||||
gcutil.CloseLog()
|
||||
}()
|
||||
|
||||
gclog.Printf(gclog.LStdLog, "Starting gochan v%s", versionStr)
|
||||
fmt.Printf("Starting gochan v%s\n", versionStr)
|
||||
config.InitConfig(versionStr)
|
||||
|
||||
systemCritical := config.GetSystemCriticalConfig()
|
||||
|
||||
err := gcplugin.LoadPlugins(systemCritical.Plugins)
|
||||
if err != nil {
|
||||
gclog.Println(stdFatal|gclog.LErrorLog, "Failed loading plugins:", err.Error())
|
||||
gcutil.LogFatal().Err(err).Msg("failed loading plugins")
|
||||
}
|
||||
|
||||
if err = gcsql.ConnectToDB(
|
||||
systemCritical.DBhost, systemCritical.DBtype, systemCritical.DBname,
|
||||
systemCritical.DBusername, systemCritical.DBpassword, systemCritical.DBprefix,
|
||||
); err != nil {
|
||||
gclog.Print(stdFatal|gclog.LErrorLog, "Failed to connect to the database: ", err.Error())
|
||||
fmt.Println("Failed to connect to the database:", err.Error())
|
||||
gcutil.LogFatal().Err(err).Msg("Failed to connect to the database")
|
||||
}
|
||||
gclog.Print(gclog.LStdLog, "Connected to database")
|
||||
fmt.Println("Connected to database")
|
||||
gcsql.CheckAndInitializeDatabase(systemCritical.DBtype)
|
||||
parseCommandLine()
|
||||
serverutil.InitMinifier()
|
||||
|
||||
posting.InitCaptcha()
|
||||
if err := gctemplates.InitTemplates(); err != nil {
|
||||
gclog.Printf(stdFatal|gclog.LErrorLog, err.Error())
|
||||
fmt.Println("Failed initializing templates:", err.Error())
|
||||
gcutil.LogFatal().Err(err).Send()
|
||||
}
|
||||
|
||||
sc := make(chan os.Signal, 1)
|
||||
|
@ -99,10 +103,22 @@ func parseCommandLine() {
|
|||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
gclog.Printf(gclog.LStdLog|gclog.LStaffLog, "Creating new staff: %q, with password: %q and rank: %d from command line", arr[0], arr[1], rank)
|
||||
fmt.Printf("Creating new staff: %q, with password: %q and rank: %d from command line", arr[0], arr[1], rank)
|
||||
if err = gcsql.NewStaff(arr[0], arr[1], rank); err != nil {
|
||||
gclog.Print(stdFatal, err.Error())
|
||||
fmt.Printf("Failed creating new staff account for %q: %s\n", arr[0], err.Error())
|
||||
gcutil.LogFatal().
|
||||
Str("staff", "add").
|
||||
Str("source", "commandLine").
|
||||
Str("username", arr[0]).
|
||||
Err(err).
|
||||
Msg("Failed creating new staff account")
|
||||
}
|
||||
gcutil.LogInfo().
|
||||
Str("staff", "add").
|
||||
Str("source", "commandLine").
|
||||
Str("username", arr[0]).
|
||||
Msg("New staff account created")
|
||||
fmt.Printf("New staff account created: %s\n", arr[0])
|
||||
os.Exit(0)
|
||||
}
|
||||
if delstaff != "" {
|
||||
|
@ -110,16 +126,19 @@ func parseCommandLine() {
|
|||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
gclog.Printf(gclog.LStdLog, "Are you sure you want to delete the staff account %q? [y/N]: ", delstaff)
|
||||
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.DeleteStaff(delstaff); err != nil {
|
||||
gclog.Printf(stdFatal, "Error deleting %q: %s", delstaff, err.Error())
|
||||
fmt.Printf("Error deleting %q: %s", delstaff, err.Error())
|
||||
gcutil.LogFatal().Str("staff", "delete").Err(err).Send()
|
||||
}
|
||||
gcutil.LogInfo().Str("newStaff", delstaff).Send()
|
||||
} else {
|
||||
gclog.Print(stdFatal, "Not deleting.")
|
||||
fmt.Println("Not deleting.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
|
||||
"github.com/gochan-org/gochan/pkg/building"
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gctemplates"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
|
@ -32,7 +31,6 @@ type gochanServer struct {
|
|||
}
|
||||
|
||||
func (s gochanServer) serveFile(writer http.ResponseWriter, request *http.Request) {
|
||||
|
||||
systemCritical := config.GetSystemCriticalConfig()
|
||||
siteConfig := config.GetSiteConfig()
|
||||
|
||||
|
@ -67,7 +65,10 @@ func (s gochanServer) serveFile(writer http.ResponseWriter, request *http.Reques
|
|||
|
||||
// serve the requested file
|
||||
fileBytes, _ = ioutil.ReadFile(filePath)
|
||||
gclog.Printf(gclog.LAccessLog, "Success: 200 from %s @ %s", gcutil.GetRealIP(request), request.URL.Path)
|
||||
gcutil.Logger().Info().
|
||||
Str("access", request.URL.Path).
|
||||
Int("status", 200).
|
||||
Str("IP", gcutil.GetRealIP(request)).Send()
|
||||
writer.Write(fileBytes)
|
||||
}
|
||||
|
||||
|
@ -125,19 +126,21 @@ func initServer() {
|
|||
|
||||
listener, err := net.Listen("tcp", systemCritical.ListenIP+":"+strconv.Itoa(systemCritical.Port))
|
||||
if err != nil {
|
||||
gclog.Printf(gclog.LErrorLog|gclog.LStdLog|gclog.LFatal,
|
||||
"Failed listening on %s:%d: %s", systemCritical.ListenIP, systemCritical.Port, err.Error())
|
||||
gcutil.Logger().Fatal().
|
||||
Err(err).
|
||||
Str("ListenIP", systemCritical.ListenIP).
|
||||
Int("Port", systemCritical.Port).Send()
|
||||
fmt.Printf("Failed listening on %s:%d: %s", systemCritical.ListenIP, systemCritical.Port, err.Error())
|
||||
}
|
||||
server = new(gochanServer)
|
||||
server.namespaces = make(map[string]func(http.ResponseWriter, *http.Request))
|
||||
|
||||
// Check if Akismet API key is usable at startup.
|
||||
err = serverutil.CheckAkismetAPIKey(siteConfig.AkismetAPIKey)
|
||||
if err == serverutil.ErrBlankAkismetKey {
|
||||
gclog.Print(gclog.LStdLog, err.Error(), ". Akismet spam protection won't be used.")
|
||||
} else if err != nil {
|
||||
gclog.Print(gclog.LErrorLog|gclog.LStdLog, ". Akismet spam protection will be disabled.")
|
||||
siteConfig.AkismetAPIKey = ""
|
||||
if err != nil && err != serverutil.ErrBlankAkismetKey {
|
||||
gcutil.Logger().Err(err).
|
||||
Msg("Akismet spam protection will be disabled")
|
||||
fmt.Println("Got error when initializing Akismet spam protection, it will be disabled:", err)
|
||||
}
|
||||
|
||||
server.namespaces["banned"] = posting.BanHandler
|
||||
|
@ -160,8 +163,10 @@ func initServer() {
|
|||
}
|
||||
|
||||
if err != nil {
|
||||
gclog.Print(gclog.LErrorLog|gclog.LStdLog|gclog.LFatal,
|
||||
"Error initializing server: ", err.Error())
|
||||
gcutil.Logger().Fatal().
|
||||
Err(err).
|
||||
Msg("Error initializing server")
|
||||
fmt.Println("Error initializing server:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,7 +188,10 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
}
|
||||
|
||||
if action == "" && deleteBtn != "Delete" && reportBtn != "Report" && editBtn != "Edit" && doEdit != "1" {
|
||||
gclog.Printf(gclog.LAccessLog, "Received invalid /util request from %q", request.Host)
|
||||
gcutil.Logger().Info().
|
||||
Str("access", request.URL.Path).
|
||||
Str("IP", gcutil.GetRealIP(request)).
|
||||
Msg("Received invalid /util request")
|
||||
if wantsJSON {
|
||||
serverutil.ServeJSON(writer, map[string]interface{}{"error": "Invalid /util request"})
|
||||
} else {
|
||||
|
@ -206,20 +214,23 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
|
||||
if reportBtn == "Report" {
|
||||
// submitted request appears to be a report
|
||||
err = posting.HandleReport(request)
|
||||
if wantsJSON {
|
||||
serverutil.ServeJSON(writer, map[string]interface{}{
|
||||
"error": err,
|
||||
if err = posting.HandleReport(request); err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("IP", gcutil.GetRealIP(request)).
|
||||
Ints("posts", checkedPosts).
|
||||
Str("board", board).
|
||||
Msg("Error submitting report")
|
||||
serverutil.ServeError(writer, err.Error(), wantsJSON, map[string]interface{}{
|
||||
"posts": checkedPosts,
|
||||
"board": board,
|
||||
})
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Println(gclog.LErrorLog,
|
||||
"Error submitting report:", err.Error()))
|
||||
return
|
||||
}
|
||||
gcutil.LogWarning().
|
||||
Ints("reportedPosts", checkedPosts).
|
||||
Str("board", board).
|
||||
Str("IP", gcutil.GetRealIP(request)).Send()
|
||||
|
||||
redirectTo := request.Referer()
|
||||
if redirectTo == "" {
|
||||
// request doesn't have a referer for some reason, redirect to board
|
||||
|
@ -248,8 +259,9 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
var post gcsql.Post
|
||||
post, err = gcsql.GetSpecificPost(checkedPosts[0], true)
|
||||
if err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Error getting post information: ", err.Error()))
|
||||
gcutil.Logger().Error().
|
||||
Err(err).
|
||||
Msg("Error getting post information")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -265,16 +277,12 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
"post": post,
|
||||
"referrer": request.Referer(),
|
||||
}); err != nil {
|
||||
gclog.Print(gclog.LErrorLog,
|
||||
"Error executing edit post template: ", err.Error())
|
||||
if wantsJSON {
|
||||
serverutil.ServeJSON(writer, map[string]interface{}{
|
||||
"error": "Error executing edit post template",
|
||||
})
|
||||
} else {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Error executing edit post template: ", err.Error()))
|
||||
}
|
||||
gcutil.Logger().Error().
|
||||
Err(err).
|
||||
Str("IP", gcutil.GetRealIP(request)).
|
||||
Msg("Error executing edit post template")
|
||||
|
||||
serverutil.ServeError(writer, "Error executing edit post template: "+err.Error(), wantsJSON, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -283,20 +291,28 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
var password string
|
||||
postid, err := strconv.Atoi(request.FormValue("postid"))
|
||||
if err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Invalid form data: ", err.Error()))
|
||||
gcutil.Logger().Error().
|
||||
Err(err).
|
||||
Str("IP", gcutil.GetRealIP(request)).
|
||||
Msg("Invalid form data")
|
||||
serverutil.ServeErrorPage(writer, "Invalid form data: "+err.Error())
|
||||
return
|
||||
}
|
||||
boardid, err := strconv.Atoi(request.FormValue("boardid"))
|
||||
if err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Invalid form data: ", err.Error()))
|
||||
gcutil.Logger().Error().
|
||||
Err(err).
|
||||
Str("IP", gcutil.GetRealIP(request)).
|
||||
Msg("Invalid form data")
|
||||
serverutil.ServeErrorPage(writer, "Invalid form data: "+err.Error())
|
||||
return
|
||||
}
|
||||
password, err = gcsql.GetPostPassword(postid)
|
||||
if err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Invalid form data: ", err.Error()))
|
||||
gcutil.Logger().Error().
|
||||
Err(err).
|
||||
Str("IP", gcutil.GetRealIP(request)).
|
||||
Msg("Invalid form data")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -308,15 +324,21 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
|
||||
var board gcsql.Board
|
||||
if err = board.PopulateData(boardid); err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Invalid form data: ", err.Error()))
|
||||
serverutil.ServeErrorPage(writer, "Invalid form data: "+err.Error())
|
||||
gcutil.Logger().Error().
|
||||
Err(err).
|
||||
Str("IP", gcutil.GetRealIP(request)).
|
||||
Msg("Invalid form data")
|
||||
return
|
||||
}
|
||||
|
||||
if err = gcsql.UpdatePost(postid, request.FormValue("editemail"), request.FormValue("editsubject"),
|
||||
posting.FormatMessage(request.FormValue("editmsg"), board.Dir), request.FormValue("editmsg")); err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Unable to edit post: ", err.Error()))
|
||||
gcutil.Logger().Error().
|
||||
Err(err).
|
||||
Str("IP", gcutil.GetRealIP(request)).
|
||||
Msg("Unable to edit post")
|
||||
serverutil.ServeErrorPage(writer, "Unable to edit post: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -327,7 +349,6 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
} else {
|
||||
http.Redirect(writer, request, "/"+board.Dir+"/res/"+request.FormValue("parentid")+".html#"+strconv.Itoa(postid), http.StatusFound)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -348,59 +369,46 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
post.ID = checkedPostID
|
||||
post.BoardID, err = strconv.Atoi(boardid)
|
||||
if err != nil {
|
||||
gclog.Printf(gclog.LErrorLog, "Invalid board ID in deletion request")
|
||||
if wantsJSON {
|
||||
serverutil.ServeJSON(writer, map[string]interface{}{
|
||||
"error": "invalid boardid string",
|
||||
gcutil.Logger().Error().
|
||||
Err(err).
|
||||
Str("requestType", "deletePost").
|
||||
Str("IP", gcutil.GetRealIP(request)).
|
||||
Str("boardid", boardid).
|
||||
Int("postid", checkedPostID).Send()
|
||||
|
||||
serverutil.ServeError(writer,
|
||||
fmt.Sprintf("Invalid boardid '%s' in post deletion request (got error '%s')", boardid, err),
|
||||
wantsJSON, map[string]interface{}{
|
||||
"boardid": boardid,
|
||||
"postid": checkedPostID,
|
||||
})
|
||||
} else {
|
||||
serverutil.ServeErrorPage(writer,
|
||||
fmt.Sprintf("Invalid boardid '%s' in request (got error '%s')", boardid, err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
post, err = gcsql.GetSpecificPost(post.ID, true)
|
||||
if err == sql.ErrNoRows {
|
||||
if wantsJSON {
|
||||
serverutil.ServeJSON(writer, map[string]interface{}{
|
||||
"error": "Post does not exist",
|
||||
"postid": post.ID,
|
||||
"boardid": post.BoardID,
|
||||
})
|
||||
return
|
||||
} else {
|
||||
serverutil.ServeErrorPage(writer, fmt.Sprintf(
|
||||
"Post #%d has already been deleted or is a post in a deleted thread", post.ID))
|
||||
return
|
||||
}
|
||||
serverutil.ServeError(writer, "Post does not exist", wantsJSON, map[string]interface{}{
|
||||
"postid": post.ID,
|
||||
"boardid": post.BoardID,
|
||||
})
|
||||
} else if err != nil {
|
||||
if wantsJSON {
|
||||
serverutil.ServeJSON(writer, map[string]interface{}{
|
||||
"error": err,
|
||||
"postid": post.ID,
|
||||
"boardid": post.BoardID,
|
||||
})
|
||||
return
|
||||
} else {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Error deleting post: ", err.Error()))
|
||||
return
|
||||
}
|
||||
gcutil.Logger().Error().
|
||||
Str("requestType", "deletePost").
|
||||
Err(err).
|
||||
Int("postid", post.ID).
|
||||
Int("boardid", post.BoardID).
|
||||
Msg("Error deleting post")
|
||||
serverutil.ServeError(writer, "Error deleting post: "+err.Error(), wantsJSON, map[string]interface{}{
|
||||
"postid": post.ID,
|
||||
"boardid": post.BoardID,
|
||||
})
|
||||
}
|
||||
|
||||
if passwordMD5 != post.Password && rank == 0 {
|
||||
if wantsJSON {
|
||||
serverutil.ServeJSON(writer, map[string]interface{}{
|
||||
"error": "incorrect password",
|
||||
"postid": post.ID,
|
||||
"boardid": post.BoardID,
|
||||
})
|
||||
} else {
|
||||
serverutil.ServeErrorPage(writer,
|
||||
fmt.Sprintf("Incorrect password for #%d", post.ID))
|
||||
}
|
||||
serverutil.ServeError(writer, fmt.Sprintf("Incorrect password for #%d", post.ID), wantsJSON, map[string]interface{}{
|
||||
"postid": post.ID,
|
||||
"boardid": post.BoardID,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -408,19 +416,24 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
fileName := post.Filename
|
||||
if fileName != "" && fileName != "deleted" {
|
||||
var files []string
|
||||
var errStr string
|
||||
|
||||
if files, err = post.GetFilePaths(); err != nil {
|
||||
errStr = gclog.Print(gclog.LErrorLog, "Error getting file upload info: ", err.Error())
|
||||
serverutil.ServeError(writer, errStr, wantsJSON, map[string]interface{}{
|
||||
gcutil.Logger().Error().
|
||||
Str("requestType", "deleteFile").
|
||||
Int("postid", post.ID).
|
||||
Err(err).
|
||||
Msg("Error getting file upload info")
|
||||
serverutil.ServeError(writer, "Error getting file upload info: "+err.Error(), wantsJSON, map[string]interface{}{
|
||||
"postid": post.ID,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if err = post.UnlinkUploads(true); err != nil {
|
||||
gclog.Printf(gclog.LErrorLog,
|
||||
"Error unlinking post uploads for #%d: %s", post.ID, err.Error())
|
||||
gcutil.Logger().Error().
|
||||
Str("requestType", "deleteFile").
|
||||
Int("postid", post.ID).
|
||||
Err(err).
|
||||
Msg("Error unlinking post uploads")
|
||||
serverutil.ServeError(writer, err.Error(), wantsJSON, map[string]interface{}{
|
||||
"postid": post.ID,
|
||||
})
|
||||
|
@ -430,8 +443,13 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
for _, filePath := range files {
|
||||
if err = os.Remove(filePath); err != nil {
|
||||
fileBase := path.Base(filePath)
|
||||
errStr = gclog.Printf(gclog.LErrorLog, "Error deleting %s: %s", fileBase, err.Error())
|
||||
serverutil.ServeError(writer, errStr, wantsJSON, map[string]interface{}{
|
||||
gcutil.Logger().Error().
|
||||
Str("requestType", "deleteFile").
|
||||
Int("postid", post.ID).
|
||||
Str("file", filePath).
|
||||
Err(err).
|
||||
Msg("Error unlinking post uploads")
|
||||
serverutil.ServeError(writer, fmt.Sprintf("Error deleting %s: %s", fileBase, err.Error()), wantsJSON, map[string]interface{}{
|
||||
"postid": post.ID,
|
||||
"file": fileBase,
|
||||
})
|
||||
|
@ -453,15 +471,14 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
} else {
|
||||
// delete the post
|
||||
if err = gcsql.DeletePost(post.ID, true); err != nil {
|
||||
if wantsJSON {
|
||||
serverutil.ServeJSON(writer, map[string]interface{}{
|
||||
"error": err,
|
||||
"postid": post.ID,
|
||||
})
|
||||
} else {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Error deleting post: ", err.Error()))
|
||||
}
|
||||
gcutil.Logger().Error().
|
||||
Str("requestType", "deleteFile").
|
||||
Int("postid", post.ID).
|
||||
Err(err).
|
||||
Msg("Error deleting post")
|
||||
serverutil.ServeError(writer, "Error deleting post: "+err.Error(), wantsJSON, map[string]interface{}{
|
||||
"postid": post.ID,
|
||||
})
|
||||
}
|
||||
if post.ParentID == 0 {
|
||||
os.Remove(path.Join(
|
||||
|
@ -472,9 +489,12 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
}
|
||||
building.BuildBoards(false, post.BoardID)
|
||||
}
|
||||
gclog.Printf(gclog.LAccessLog,
|
||||
"Post #%d on boardid %d deleted by %s, file only: %t",
|
||||
post.ID, post.BoardID, post.IP, fileOnly)
|
||||
gcutil.Logger().Info().
|
||||
Str("requestType", "deletePost").
|
||||
Str("IP", post.IP).
|
||||
Int("boardid", post.BoardID).
|
||||
Bool("fileOnly", fileOnly).
|
||||
Msg("Post deleted")
|
||||
if !wantsJSON {
|
||||
http.Redirect(writer, request, request.Referer(), http.StatusFound)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/building"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"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/serverutil"
|
||||
)
|
||||
|
||||
|
@ -15,42 +16,56 @@ const (
|
|||
buildBoards = 1 << (iota - 1)
|
||||
buildFront
|
||||
buildJS
|
||||
buildAll = buildBoards | buildFront | buildJS
|
||||
buildLogFlags = gclog.LErrorLog | gclog.LStdLog | gclog.LFatal
|
||||
buildAll = buildBoards | buildFront | buildJS
|
||||
)
|
||||
|
||||
func startupRebuild(buildFlag int) {
|
||||
var err error
|
||||
serverutil.InitMinifier()
|
||||
if err = gctemplates.InitTemplates(); err != nil {
|
||||
gclog.Print(buildLogFlags, "Error initializing templates: ", err.Error())
|
||||
fmt.Println("Error initializing templates:", err.Error())
|
||||
gcutil.Logger().Fatal().
|
||||
Str("building", "initialization").
|
||||
Err(err).Send()
|
||||
}
|
||||
|
||||
if buildFlag&buildBoards > 0 {
|
||||
gcsql.ResetBoardSectionArrays()
|
||||
if err = building.BuildBoardListJSON(); err != nil {
|
||||
gclog.Print(buildLogFlags, "Error building section array: ", err.Error())
|
||||
fmt.Println("Error building section array:", err.Error())
|
||||
gcutil.Logger().Fatal().
|
||||
Str("building", "sections").
|
||||
Err(err).Send()
|
||||
}
|
||||
|
||||
if err = building.BuildBoards(true); err != nil {
|
||||
gclog.Print(buildLogFlags, "Error building boards: ", err.Error())
|
||||
fmt.Println("Error building boards:", err.Error())
|
||||
gcutil.Logger().Fatal().
|
||||
Str("building", "boards").
|
||||
Err(err).Send()
|
||||
}
|
||||
gclog.Print(gclog.LStdLog, "Boards built successfully")
|
||||
fmt.Println("Boards built successfully")
|
||||
}
|
||||
|
||||
if buildFlag&buildJS > 0 {
|
||||
if err = building.BuildJS(); err != nil {
|
||||
gclog.Print(buildLogFlags, "Error building JS: ", err.Error())
|
||||
fmt.Println("Error building JS:", err.Error())
|
||||
gcutil.Logger().Fatal().
|
||||
Str("building", "js").
|
||||
Err(err).Send()
|
||||
}
|
||||
gclog.Print(gclog.LStdLog, "JavaScript built successfully")
|
||||
fmt.Println("JavaScript built successfully")
|
||||
}
|
||||
|
||||
if buildFlag&buildFront > 0 {
|
||||
if err = building.BuildFrontPage(); err != nil {
|
||||
gclog.Print(buildLogFlags, "Error building front page: ", err.Error())
|
||||
fmt.Println("Error building front page:", err.Error())
|
||||
gcutil.Logger().Fatal().
|
||||
Str("building", "front").
|
||||
Err(err).Send()
|
||||
}
|
||||
gclog.Print(gclog.LStdLog, "Front page built successfully")
|
||||
fmt.Println("Front page built successfully")
|
||||
}
|
||||
gclog.Print(gclog.LStdLog, "Finished building without errors, exiting.")
|
||||
fmt.Println("Finished building without errors, exiting.")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
|
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "gochan.js",
|
||||
"version": "3.0.0",
|
||||
"version": "3.3.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "gochan.js",
|
||||
"version": "3.0.0",
|
||||
"version": "3.3.0",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"jquery": "^3.5.1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "gochan.js",
|
||||
"version": "3.2.0",
|
||||
"version": "3.3.0",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"source": "js/gochan.js",
|
||||
|
|
2
go.mod
2
go.mod
|
@ -7,10 +7,10 @@ require (
|
|||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/frustra/bbcode v0.0.0-20201127003707-6ef347fbe1c8
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/gochan-org/gzlog v1.0.2
|
||||
github.com/lib/pq v1.10.6
|
||||
github.com/mattn/go-sqlite3 v1.14.15
|
||||
github.com/mojocn/base64Captcha v1.3.5
|
||||
github.com/rs/zerolog v1.28.0
|
||||
github.com/tdewolff/minify v2.3.6+incompatible
|
||||
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
||||
github.com/tdewolff/test v1.0.7 // indirect
|
||||
|
|
15
go.sum
15
go.sum
|
@ -3,22 +3,30 @@ github.com/aquilax/tripcode v1.0.0/go.mod h1:Tucn/H6BM/DEmxzj/tnmR7Vs/NV/bgCKo8W
|
|||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/frustra/bbcode v0.0.0-20201127003707-6ef347fbe1c8 h1:sdIsYe6Vv7KIWZWp8KqSeTl+XlF17d+wHCC4lbxFcYs=
|
||||
github.com/frustra/bbcode v0.0.0-20201127003707-6ef347fbe1c8/go.mod h1:0QBxkXxN+o4FyZgLI9FHY/oUizheze3+bNY/kgCKL+4=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/gochan-org/gzlog v1.0.2 h1:nW9jNMP+QlYVXTATtTNMEnMGDGOxLMk4XUYnSe0Pir8=
|
||||
github.com/gochan-org/gzlog v1.0.2/go.mod h1:+gbkGI25c2kj/4Gv4RBXBxhScwTYA8GkWagrmV95Urg=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
|
||||
github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0=
|
||||
github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
|
||||
github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
|
||||
github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
|
||||
github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs=
|
||||
github.com/tdewolff/parse v2.3.4+incompatible h1:x05/cnGwIMf4ceLuDMBOdQ1qGniMoxpP46ghf0Qzh38=
|
||||
|
@ -42,6 +50,9 @@ golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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=
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
<h1>404: File not found</h1>
|
||||
<img src="./lol 404.gif" border="0" alt="">
|
||||
<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> v3.2.0
|
||||
<hr/>Site powered by <a href="https://github.com/gochan-org/gochan" target="_blank">Gochan</a> v3.3.0
|
||||
</body>
|
||||
</html>
|
|
@ -7,6 +7,6 @@
|
|||
<h1>Error 500: Internal Server error</h1>
|
||||
<img src="./server500.gif" border="0" alt="">
|
||||
<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> v3.2.0
|
||||
<hr/>Site powered by <a href="https://github.com/gochan-org/gochan" target="_blank">Gochan</a> v3.3.0
|
||||
</body>
|
||||
</html>
|
|
@ -7,6 +7,6 @@
|
|||
<h1>Error 502: Bad gateway</h1>
|
||||
<img src="./server500.gif" border="0" alt="">
|
||||
<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> v3.2.0
|
||||
<hr/>Site powered by <a href="https://github.com/gochan-org/gochan" target="_blank">Gochan</a> v3.3.0
|
||||
</body>
|
||||
</html>
|
|
@ -10,7 +10,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gctemplates"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
|
@ -45,8 +44,10 @@ func BuildBoardPages(board *gcsql.Board) error {
|
|||
|
||||
// Get all top level posts for the board.
|
||||
if opPosts, err = gcsql.GetTopPosts(board.ID); err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Error getting OP posts for /%s/: %s", board.Dir, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("boardDir", board.Dir).
|
||||
Msg("Failed getting OP posts")
|
||||
return fmt.Errorf("error getting OP posts for /%s/: %s", board.Dir, err.Error())
|
||||
}
|
||||
|
||||
// For each top level post, start building a Thread struct
|
||||
|
@ -57,24 +58,27 @@ func BuildBoardPages(board *gcsql.Board) error {
|
|||
|
||||
var replyCount, err = gcsql.GetReplyCount(op.ID)
|
||||
if err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Error getting replies to /%s/%d: %s",
|
||||
board.Dir, op.ID, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("boardDir", board.Dir).
|
||||
Int("op", op.ID).
|
||||
Msg("Failed getting thread replies")
|
||||
return fmt.Errorf("Error getting replies to /%s/%d: %s", board.Dir, op.ID, err.Error())
|
||||
}
|
||||
thread.NumReplies = replyCount
|
||||
|
||||
fileCount, err := gcsql.GetReplyFileCount(op.ID)
|
||||
if err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Error getting file count to /%s/%d: %s",
|
||||
board.Dir, op.ID, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("boardDir", board.Dir).
|
||||
Int("op", op.ID).
|
||||
Msg("Failed getting file count")
|
||||
return fmt.Errorf("Error getting file count to /%s/%d: %s", board.Dir, op.ID, err.Error())
|
||||
}
|
||||
thread.NumImages = fileCount
|
||||
|
||||
thread.OP = *op
|
||||
|
||||
var numRepliesOnBoardPage int
|
||||
// postCfg := config.getpo
|
||||
postCfg := config.GetBoardConfig("").PostConfig
|
||||
if op.Stickied {
|
||||
// If the thread is stickied, limit replies on the archive page to the
|
||||
|
@ -87,9 +91,11 @@ func BuildBoardPages(board *gcsql.Board) error {
|
|||
|
||||
postsInThread, err = gcsql.GetExistingRepliesLimitedRev(op.ID, numRepliesOnBoardPage)
|
||||
if err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Error getting posts in /%s/%d: %s",
|
||||
board.Dir, op.ID, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("boardDir", board.Dir).
|
||||
Int("op", op.ID).
|
||||
Msg("Failed getting thread posts")
|
||||
return fmt.Errorf("Error getting posts in /%s/%d: %s", board.Dir, op.ID, err.Error())
|
||||
}
|
||||
|
||||
var reversedPosts []gcsql.Post
|
||||
|
@ -134,9 +140,11 @@ func BuildBoardPages(board *gcsql.Board) error {
|
|||
// Open 1.html for writing to the first page.
|
||||
boardPageFile, err = os.OpenFile(path.Join(criticalCfg.DocumentRoot, board.Dir, "1.html"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||
if err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"failed opening /%s/board.html: %s",
|
||||
board.Dir, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("boardDir", board.Dir).
|
||||
Str("page", "board.html").
|
||||
Msg("Failed getting board page")
|
||||
return fmt.Errorf("failed opening /%s/board.html: %s", board.Dir, err.Error())
|
||||
}
|
||||
|
||||
// Render board page template to the file,
|
||||
|
@ -149,8 +157,11 @@ func BuildBoardPages(board *gcsql.Board) error {
|
|||
"board": board,
|
||||
"board_config": config.GetBoardConfig(board.Dir),
|
||||
}, boardPageFile, "text/html"); err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Failed building /%s/: %s", board.Dir, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("boardDir", board.Dir).
|
||||
Str("page", "board.html").
|
||||
Msg("Failed building board")
|
||||
return fmt.Errorf("Failed building /%s/: %s", board.Dir, err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -165,8 +176,11 @@ func BuildBoardPages(board *gcsql.Board) error {
|
|||
|
||||
catalogJSONFile, err := os.OpenFile(path.Join(criticalCfg.DocumentRoot, board.Dir, "catalog.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||
if err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"failed opening /%s/catalog.json: %s", board.Dir, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("subject", "catalog.json").
|
||||
Str("boardDir", board.Dir).
|
||||
Msg("Failed opening catalog.json")
|
||||
return fmt.Errorf("failed opening /%s/catalog.json: %s", board.Dir, err.Error())
|
||||
}
|
||||
defer catalogJSONFile.Close()
|
||||
|
||||
|
@ -178,8 +192,10 @@ func BuildBoardPages(board *gcsql.Board) error {
|
|||
currentPageFilepath = path.Join(criticalCfg.DocumentRoot, board.Dir, pageFilename)
|
||||
currentPageFile, err = os.OpenFile(currentPageFilepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||
if err != nil {
|
||||
gclog.Printf(gclog.LErrorLog,
|
||||
"failed opening /%s/%s: %s", board.Dir, pageFilename, err.Error())
|
||||
gcutil.LogError(err).
|
||||
Str("boardDir", board.Dir).
|
||||
Str("page", pageFilename).
|
||||
Msg("Failed getting board page")
|
||||
continue
|
||||
}
|
||||
defer currentPageFile.Close()
|
||||
|
@ -196,9 +212,10 @@ func BuildBoardPages(board *gcsql.Board) error {
|
|||
gcsql.Post{BoardID: board.ID},
|
||||
},
|
||||
}, currentPageFile, "text/html"); err != nil {
|
||||
err = errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Failed building /%s/ boardpage: %s", board.Dir, err.Error()))
|
||||
return err
|
||||
gcutil.LogError(err).
|
||||
Str("boardDir", board.Dir).
|
||||
Msg("Failed building boardpage")
|
||||
return fmt.Errorf("Failed building /%s/ boardpage: %s", board.Dir, err.Error())
|
||||
}
|
||||
|
||||
// Collect up threads for this page.
|
||||
|
@ -211,12 +228,16 @@ func BuildBoardPages(board *gcsql.Board) error {
|
|||
|
||||
var catalogJSON []byte
|
||||
if catalogJSON, err = json.Marshal(pagesArr); err != nil {
|
||||
return errors.New(gclog.Print(gclog.LErrorLog,
|
||||
"Failed to marshal to JSON: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("boardDir", board.Dir).
|
||||
Msg("Failed to marshal to JSON")
|
||||
return errors.New("failed to marshal to JSON: " + err.Error())
|
||||
}
|
||||
if _, err = catalogJSONFile.Write(catalogJSON); err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Failed writing /%s/catalog.json: %s", board.Dir, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("boardDir", board.Dir).
|
||||
Msg("Failed writing catalog.json")
|
||||
return fmt.Errorf("failed writing /%s/catalog.json: %s", board.Dir, err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -233,8 +254,10 @@ func BuildBoards(verbose bool, which ...int) error {
|
|||
for b, id := range which {
|
||||
boards = append(boards, gcsql.Board{})
|
||||
if err = boards[b].PopulateData(id); err != nil {
|
||||
return errors.New(
|
||||
gclog.Printf(gclog.LErrorLog, "Error getting board information (ID: %d): %s", id, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Int("boardid", id).
|
||||
Msg("Unable to get board information")
|
||||
return fmt.Errorf("Error getting board information (ID: %d): %s", id, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -245,17 +268,19 @@ func BuildBoards(verbose bool, which ...int) error {
|
|||
for b := range boards {
|
||||
board := &boards[b]
|
||||
if err = buildBoard(board, false, true); err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Error building /%s/: %s", board.Dir, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("boardDir", board.Dir).
|
||||
Msg("Failed building board")
|
||||
return fmt.Errorf("Error building /%s/: %s", board.Dir, err.Error())
|
||||
}
|
||||
if verbose {
|
||||
gclog.Printf(gclog.LStdLog, "Built /%s/ successfully", board.Dir)
|
||||
fmt.Printf("Built /%s/ successfully\n", board.Dir)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//BuildCatalog builds the catalog for a board with a given id
|
||||
// BuildCatalog builds the catalog for a board with a given id
|
||||
func BuildCatalog(boardID int) string {
|
||||
err := gctemplates.InitTemplates("catalog")
|
||||
if err != nil {
|
||||
|
@ -264,14 +289,19 @@ func BuildCatalog(boardID int) string {
|
|||
|
||||
var board gcsql.Board
|
||||
if err = board.PopulateData(boardID); err != nil {
|
||||
return gclog.Printf(gclog.LErrorLog, "Error getting board information (ID: %d)", boardID)
|
||||
gcutil.LogError(err).
|
||||
Int("boardid", boardID).
|
||||
Msg("Unable to get board information")
|
||||
return fmt.Sprintf("Error getting board information (ID: %d)", boardID)
|
||||
}
|
||||
criticalCfg := config.GetSystemCriticalConfig()
|
||||
catalogPath := path.Join(criticalCfg.DocumentRoot, board.Dir, "catalog.html")
|
||||
catalogFile, err := os.OpenFile(catalogPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||
if err != nil {
|
||||
return gclog.Printf(gclog.LErrorLog,
|
||||
"Failed opening /%s/catalog.html: %s", board.Dir, err.Error()) + "<br />"
|
||||
gcutil.LogError(err).
|
||||
Str("boardDir", board.Dir).
|
||||
Msg("Failed opening catalog.html")
|
||||
return fmt.Sprintf("Failed opening /%s/catalog.html: %s<br/>", board.Dir, err.Error())
|
||||
}
|
||||
|
||||
threadOPs, err := gcsql.GetTopPosts(boardID)
|
||||
|
@ -281,8 +311,10 @@ func BuildCatalog(boardID int) string {
|
|||
// "deleted_timestamp": nilTimestamp,
|
||||
// }, "ORDER BY bumped ASC")
|
||||
if err != nil {
|
||||
return gclog.Printf(gclog.LErrorLog,
|
||||
"Error building catalog for /%s/: %s", board.Dir, err.Error()) + "<br />"
|
||||
gcutil.LogError(err).
|
||||
Str("building", "catalog").
|
||||
Str("boardDir", board.Dir).Send()
|
||||
return fmt.Sprintf("Error building catalog for /%s/: %s<br/>", board.Dir, err.Error())
|
||||
}
|
||||
|
||||
if err = serverutil.MinifyTemplate(gctemplates.Catalog, map[string]interface{}{
|
||||
|
@ -293,8 +325,10 @@ func BuildCatalog(boardID int) string {
|
|||
"sections": gcsql.AllSections,
|
||||
"threads": threadOPs,
|
||||
}, catalogFile, "text/html"); err != nil {
|
||||
return gclog.Printf(gclog.LErrorLog,
|
||||
"Error building catalog for /%s/: %s", board.Dir, err.Error()) + "<br />"
|
||||
gcutil.LogError(err).
|
||||
Str("building", "catalog").
|
||||
Str("boardDir", board.Dir).Send()
|
||||
return fmt.Sprintf("Error building catalog for /%s/: %s<br/>", board.Dir, err.Error())
|
||||
}
|
||||
return fmt.Sprintf("Built catalog for /%s/ successfully", board.Dir)
|
||||
}
|
||||
|
@ -331,59 +365,49 @@ func buildBoard(board *gcsql.Board, newBoard, force bool) error {
|
|||
thumbInfo, _ := os.Stat(thumbPath)
|
||||
if dirInfo != nil {
|
||||
if !force {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
pathExistsStr, dirPath))
|
||||
gcutil.LogError(os.ErrExist).
|
||||
Str("dirPath", dirPath).Send()
|
||||
return fmt.Errorf(pathExistsStr, dirPath)
|
||||
}
|
||||
if !dirInfo.IsDir() {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
dirIsAFileStr, dirPath))
|
||||
return fmt.Errorf(dirIsAFileStr, dirPath)
|
||||
}
|
||||
} else if err = os.Mkdir(dirPath, 0666); err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
genericErrStr, dirPath, err.Error()))
|
||||
return fmt.Errorf(genericErrStr, dirPath, err.Error())
|
||||
}
|
||||
|
||||
if resInfo != nil {
|
||||
if !force {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
pathExistsStr, resPath))
|
||||
return fmt.Errorf(pathExistsStr, resPath)
|
||||
}
|
||||
if !resInfo.IsDir() {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
dirIsAFileStr, resPath))
|
||||
return fmt.Errorf(dirIsAFileStr, resPath)
|
||||
|
||||
}
|
||||
} else if err = os.Mkdir(resPath, 0666); err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
genericErrStr, resPath, err.Error()))
|
||||
return fmt.Errorf(genericErrStr, resPath, err.Error())
|
||||
}
|
||||
|
||||
if srcInfo != nil {
|
||||
if !force {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
pathExistsStr, srcPath))
|
||||
return fmt.Errorf(pathExistsStr, srcPath)
|
||||
}
|
||||
if !srcInfo.IsDir() {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
dirIsAFileStr, srcPath))
|
||||
return fmt.Errorf(dirIsAFileStr, srcPath)
|
||||
}
|
||||
} else if err = os.Mkdir(srcPath, 0666); err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
genericErrStr, srcPath, err.Error()))
|
||||
return fmt.Errorf(genericErrStr, srcPath, err.Error())
|
||||
}
|
||||
|
||||
if thumbInfo != nil {
|
||||
if !force {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
pathExistsStr, thumbPath))
|
||||
return fmt.Errorf(pathExistsStr, thumbPath)
|
||||
}
|
||||
if !thumbInfo.IsDir() {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
dirIsAFileStr, thumbPath))
|
||||
return fmt.Errorf(dirIsAFileStr, thumbPath)
|
||||
}
|
||||
} else if err = os.Mkdir(thumbPath, 0666); err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
genericErrStr, thumbPath, err.Error()))
|
||||
return fmt.Errorf(genericErrStr, thumbPath, err.Error())
|
||||
}
|
||||
|
||||
BuildBoardPages(board)
|
||||
|
|
|
@ -3,14 +3,15 @@ package building
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"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/serverutil"
|
||||
)
|
||||
|
||||
|
@ -18,16 +19,18 @@ import (
|
|||
func BuildFrontPage() error {
|
||||
err := gctemplates.InitTemplates("front")
|
||||
if err != nil {
|
||||
return errors.New(gclog.Print(gclog.LErrorLog,
|
||||
"Error loading front page template: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("template", "front").Send()
|
||||
return errors.New("Error loading front page template: " + err.Error())
|
||||
}
|
||||
criticalCfg := config.GetSystemCriticalConfig()
|
||||
os.Remove(path.Join(criticalCfg.DocumentRoot, "index.html"))
|
||||
frontFile, err := os.OpenFile(path.Join(criticalCfg.DocumentRoot, "index.html"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||
|
||||
if err != nil {
|
||||
return errors.New(gclog.Print(gclog.LErrorLog,
|
||||
"Failed opening front page for writing: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("building", "front").Send()
|
||||
return errors.New("Failed opening front page for writing: " + err.Error())
|
||||
}
|
||||
defer frontFile.Close()
|
||||
|
||||
|
@ -35,8 +38,9 @@ func BuildFrontPage() error {
|
|||
siteCfg := config.GetSiteConfig()
|
||||
recentPostsArr, err = gcsql.GetRecentPostsGlobal(siteCfg.MaxRecentPosts, !siteCfg.RecentPostsWithNoFile)
|
||||
if err != nil {
|
||||
return errors.New(gclog.Print(gclog.LErrorLog,
|
||||
"Failed loading recent posts: "+err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("building", "recent").Send()
|
||||
return errors.New("Failed loading recent posts: " + err.Error())
|
||||
}
|
||||
|
||||
for b := range gcsql.AllBoards {
|
||||
|
@ -53,8 +57,9 @@ func BuildFrontPage() error {
|
|||
"board_config": config.GetBoardConfig(""),
|
||||
"recent_posts": recentPostsArr,
|
||||
}, frontFile, "text/html"); err != nil {
|
||||
return errors.New(gclog.Print(gclog.LErrorLog,
|
||||
"Failed executing front page template: "+err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("template", "front").Send()
|
||||
return errors.New("Failed executing front page template: " + err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -64,8 +69,9 @@ func BuildBoardListJSON() error {
|
|||
criticalCfg := config.GetSystemCriticalConfig()
|
||||
boardListFile, err := os.OpenFile(path.Join(criticalCfg.DocumentRoot, "boards.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||
if err != nil {
|
||||
return errors.New(
|
||||
gclog.Print(gclog.LErrorLog, "Failed opening boards.json for writing: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("building", "boardsList").Send()
|
||||
return errors.New("Failed opening boards.json for writing: " + err.Error())
|
||||
}
|
||||
defer boardListFile.Close()
|
||||
|
||||
|
@ -87,13 +93,13 @@ func BuildBoardListJSON() error {
|
|||
|
||||
boardJSON, err := json.Marshal(boardsMap)
|
||||
if err != nil {
|
||||
return errors.New(
|
||||
gclog.Print(gclog.LErrorLog, "Failed to create boards.json: ", err.Error()))
|
||||
gcutil.LogError(err).Str("building", "boards.json").Send()
|
||||
return errors.New("Failed to create boards.json: " + err.Error())
|
||||
}
|
||||
|
||||
if _, err = serverutil.MinifyWriter(boardListFile, boardJSON, "application/json"); err != nil {
|
||||
return errors.New(
|
||||
gclog.Print(gclog.LErrorLog, "Failed writing boards.json file: ", err.Error()))
|
||||
gcutil.LogError(err).Str("building", "boards.json").Send()
|
||||
return errors.New("Failed writing boards.json file: " + err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -129,8 +135,8 @@ func BuildJS() error {
|
|||
// build consts.js from template
|
||||
err := gctemplates.InitTemplates("js")
|
||||
if err != nil {
|
||||
return errors.New(gclog.Println(gclog.LErrorLog,
|
||||
"Error loading consts.js template:", err.Error()))
|
||||
gcutil.LogError(err).Str("template", "consts.js").Send()
|
||||
return errors.New("Error loading consts.js template:" + err.Error())
|
||||
}
|
||||
|
||||
boardCfg := config.GetBoardConfig("")
|
||||
|
@ -138,8 +144,10 @@ func BuildJS() error {
|
|||
constsJSPath := path.Join(criticalCfg.DocumentRoot, "js", "consts.js")
|
||||
constsJSFile, err := os.OpenFile(constsJSPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Error opening %q for writing: %s", constsJSPath, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("building", "consts.js").
|
||||
Str("filePath", constsJSPath).Send()
|
||||
return fmt.Errorf("Error opening %q for writing: %s", constsJSPath, err.Error())
|
||||
}
|
||||
defer constsJSFile.Close()
|
||||
|
||||
|
@ -151,8 +159,10 @@ func BuildJS() error {
|
|||
"timezone": criticalCfg.TimeZone,
|
||||
},
|
||||
constsJSFile, "text/javascript"); err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Error building %q: %s", constsJSPath, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("building", "consts.js").
|
||||
Str("filePath", constsJSPath).Send()
|
||||
return fmt.Errorf("Error building %q: %s", constsJSPath, err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,15 +2,15 @@ package building
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"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/serverutil"
|
||||
)
|
||||
|
||||
|
@ -56,8 +56,10 @@ func BuildThreadPages(op *gcsql.Post) error {
|
|||
|
||||
replies, err = gcsql.GetExistingReplies(op.ID)
|
||||
if err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Error building thread %d: %s", op.ID, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("building", "thread").
|
||||
Int("threadid", op.ID).Send()
|
||||
return fmt.Errorf("Error building thread %d: %s", op.ID, err.Error())
|
||||
}
|
||||
criticalCfg := config.GetSystemCriticalConfig()
|
||||
os.Remove(path.Join(criticalCfg.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".html"))
|
||||
|
@ -65,8 +67,11 @@ func BuildThreadPages(op *gcsql.Post) error {
|
|||
threadPageFilepath := path.Join(criticalCfg.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".html")
|
||||
threadPageFile, err = os.OpenFile(threadPageFilepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||
if err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Failed opening /%s/res/%d.html: %s", board.Dir, op.ID, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("building", "thread").
|
||||
Str("boardDir", board.Dir).
|
||||
Int("threadid", op.ID).Send()
|
||||
return fmt.Errorf("Failed opening /%s/res/%d.html: %s", board.Dir, op.ID, err.Error())
|
||||
}
|
||||
|
||||
// render thread page
|
||||
|
@ -79,15 +84,21 @@ func BuildThreadPages(op *gcsql.Post) error {
|
|||
"posts": replies,
|
||||
"op": op,
|
||||
}, threadPageFile, "text/html"); err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Failed building /%s/res/%d threadpage: %s", board.Dir, op.ID, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("building", "thread").
|
||||
Str("boardDir", board.Dir).
|
||||
Int("threadid", op.ID).
|
||||
Msg("Failed building threadpage")
|
||||
return fmt.Errorf("Failed building /%s/res/%d threadpage: %s", board.Dir, op.ID, err.Error())
|
||||
}
|
||||
|
||||
// Put together the thread JSON
|
||||
threadJSONFile, err := os.OpenFile(path.Join(criticalCfg.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||
if err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Failed opening /%s/res/%d.json: %s", board.Dir, op.ID, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("boardDir", board.Dir).
|
||||
Int("threadid", op.ID).Send()
|
||||
return fmt.Errorf("Failed opening /%s/res/%d.json: %s", board.Dir, op.ID, err.Error())
|
||||
}
|
||||
defer threadJSONFile.Close()
|
||||
|
||||
|
@ -100,14 +111,15 @@ func BuildThreadPages(op *gcsql.Post) error {
|
|||
threadMap["posts"] = append(threadMap["posts"], replies...)
|
||||
threadJSON, err := json.Marshal(threadMap)
|
||||
if err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Failed to marshal to JSON: %s", err.Error()))
|
||||
gcutil.LogError(err).Send()
|
||||
return fmt.Errorf("Failed to marshal to JSON: %s", err.Error())
|
||||
}
|
||||
|
||||
if _, err = threadJSONFile.Write(threadJSON); err != nil {
|
||||
return errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Failed writing /%s/res/%d.json: %s", board.Dir, op.ID, err.Error()))
|
||||
}
|
||||
gcutil.LogError(err).
|
||||
Str("boardDir", board.Dir).
|
||||
Int("threadid", op.ID).Send()
|
||||
|
||||
return fmt.Errorf("Failed writing /%s/res/%d.json: %s", board.Dir, op.ID, err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
)
|
||||
|
||||
|
@ -266,11 +265,7 @@ func InitConfig(versionStr string) {
|
|||
}
|
||||
|
||||
cfg.LogDir = gcutil.FindResource(cfg.LogDir, "log", "/var/log/gochan/")
|
||||
if err = gclog.InitLogs(
|
||||
path.Join(cfg.LogDir, "access"),
|
||||
path.Join(cfg.LogDir, "error"),
|
||||
path.Join(cfg.LogDir, "staff"),
|
||||
cfg.DebugMode); err != nil {
|
||||
if err = gcutil.InitLog(path.Join(cfg.LogDir, "gochan.log")); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@ -296,7 +291,9 @@ func InitConfig(versionStr string) {
|
|||
|
||||
if cfg.EnableGeoIP {
|
||||
if _, err = os.Stat(cfg.GeoIPDBlocation); err != nil {
|
||||
gclog.Print(gclog.LErrorLog|gclog.LStdLog, "Unable to find GeoIP file location set in gochan.json, disabling GeoIP")
|
||||
gcutil.LogError(err).
|
||||
Str("location", cfg.GeoIPDBlocation).
|
||||
Msg("Unable to load GeoIP file location set in gochan.json, disabling GeoIP")
|
||||
}
|
||||
cfg.EnableGeoIP = false
|
||||
}
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
package gclog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/gochan-org/gzlog"
|
||||
)
|
||||
|
||||
const (
|
||||
logMaxSize = 1000000 // 1 MB (TODO: make this configurable)
|
||||
logTimeFmt = "2006/01/02 15:04:05 "
|
||||
logFileFlags = os.O_APPEND | os.O_CREATE | os.O_RDWR
|
||||
// LAccessLog should be used for incoming requests
|
||||
LAccessLog = 1 << iota
|
||||
// LErrorLog should be used for internal errors, not HTTP errors like 4xx
|
||||
LErrorLog
|
||||
// LStaffLog is comparable to LAccessLog for staff actions
|
||||
LStaffLog
|
||||
// LStdLog prints directly to standard output
|
||||
LStdLog
|
||||
// LFatal exits gochan after printing (used for fatal initialization errors)
|
||||
LFatal
|
||||
)
|
||||
|
||||
var (
|
||||
accessLog *gzlog.GzLog
|
||||
errorLog *gzlog.GzLog
|
||||
staffLog *gzlog.GzLog
|
||||
debugLog bool
|
||||
)
|
||||
|
||||
func wantStdout(flags int) bool {
|
||||
return debugLog || flags&LStdLog > 0
|
||||
}
|
||||
|
||||
// selectLogs returns an array of GzLogs that we should loop through given
|
||||
// the flags passed to it
|
||||
func selectLogs(flags int) []*gzlog.GzLog {
|
||||
var logs []*gzlog.GzLog
|
||||
if flags&LAccessLog > 0 {
|
||||
logs = append(logs, accessLog)
|
||||
}
|
||||
if flags&LErrorLog > 0 {
|
||||
logs = append(logs, errorLog)
|
||||
}
|
||||
if flags&LStaffLog > 0 {
|
||||
logs = append(logs, staffLog)
|
||||
}
|
||||
return logs
|
||||
}
|
||||
|
||||
// Print is comparable to log.Print but takes binary flags
|
||||
func Print(flags int, v ...interface{}) string {
|
||||
str := fmt.Sprint(v...)
|
||||
logs := selectLogs(flags)
|
||||
for _, l := range logs {
|
||||
l.Print(v...)
|
||||
}
|
||||
if wantStdout(flags) {
|
||||
fmt.Print(v...)
|
||||
fmt.Println()
|
||||
}
|
||||
if flags&LFatal > 0 {
|
||||
os.Exit(1)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// Printf is comparable to log.Logger.Printf but takes binary OR'd flags
|
||||
func Printf(flags int, format string, v ...interface{}) string {
|
||||
str := fmt.Sprintf(format, v...)
|
||||
logs := selectLogs(flags)
|
||||
for _, l := range logs {
|
||||
l.Printf(format, v...)
|
||||
}
|
||||
if wantStdout(flags) {
|
||||
fmt.Printf(format+"\n", v...)
|
||||
}
|
||||
if flags&LFatal > 0 {
|
||||
os.Exit(1)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// Println is comparable to log.Logger.Println but takes binary OR'd flags
|
||||
func Println(flags int, v ...interface{}) string {
|
||||
str := fmt.Sprintln(v...)
|
||||
logs := selectLogs(flags)
|
||||
for _, l := range logs {
|
||||
l.Println(v...)
|
||||
}
|
||||
if wantStdout(flags) {
|
||||
fmt.Println(v...)
|
||||
}
|
||||
if flags&LFatal > 0 {
|
||||
os.Exit(1)
|
||||
}
|
||||
return str[:len(str)-1]
|
||||
}
|
||||
|
||||
// Close closes the log files
|
||||
func Close() {
|
||||
if accessLog != nil {
|
||||
accessLog.Close()
|
||||
}
|
||||
if errorLog != nil {
|
||||
errorLog.Close()
|
||||
}
|
||||
if staffLog != nil {
|
||||
staffLog.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// InitLogs initializes the log files to be used by gochan
|
||||
func InitLogs(accessLogBasePath, errorLogBasePath, staffLogBasePath string, debugMode bool) error {
|
||||
debugLog = debugMode
|
||||
var err error
|
||||
|
||||
if accessLog, err = gzlog.OpenFile(accessLogBasePath, logMaxSize, 0640); err != nil {
|
||||
return err
|
||||
}
|
||||
if errorLog, err = gzlog.OpenFile(errorLogBasePath, logMaxSize, 0640); err != nil {
|
||||
return err
|
||||
}
|
||||
if staffLog, err = gzlog.OpenFile(staffLogBasePath, logMaxSize, 0640); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package gclog
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGochanLog(t *testing.T) {
|
||||
_, err := os.Stat("./logtest")
|
||||
if err != nil {
|
||||
if err = os.Mkdir("./logtest", 0755); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
err = InitLogs("./logtest/access", "./logtest/error", "./logtest/staff", true)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// test Print usage
|
||||
Print(LStdLog, "os.Stdout log", "(Print)")
|
||||
Print(LStdLog|LAccessLog|LErrorLog|LStaffLog, "all logs", "(Print)")
|
||||
Print(LAccessLog, "Access log", "(Print)")
|
||||
Print(LErrorLog, "Error log", "(Print)")
|
||||
Print(LStaffLog, "Staff log", "(Print)")
|
||||
Print(LAccessLog|LErrorLog, "Access and error log", "(Print)")
|
||||
Print(LAccessLog|LStaffLog, "Access and staff log", "(Print)")
|
||||
|
||||
// test Printf usage
|
||||
Printf(LStdLog, "os.Stdout log %q", "(Println)")
|
||||
Printf(LStdLog|LAccessLog|LErrorLog|LStaffLog, "all logs %q", "(Printf)")
|
||||
Printf(LAccessLog, "Access log %q", "(Printf)")
|
||||
Printf(LErrorLog, "Error log %q", "(Printf)")
|
||||
Printf(LStaffLog, "Staff log %q", "(Printf)")
|
||||
Printf(LAccessLog|LErrorLog, "Access and error log %q", "(Printf)")
|
||||
Printf(LAccessLog|LStaffLog, "Access and staff log %q", "(Printf)")
|
||||
|
||||
// test Println usage (proper spacing and no extra newlines)
|
||||
Println(LStdLog, "os.Stdout log", "(Println)")
|
||||
|
||||
t.Logf("%q", Println(LStdLog, "Testing log chaining for errors", "(Println)"))
|
||||
|
||||
Println(LStdLog|LAccessLog|LErrorLog|LStaffLog, "all logs", "(Println)")
|
||||
Println(LAccessLog, "Access log", "(Println)")
|
||||
Println(LErrorLog, "Error log", "(Println)")
|
||||
Println(LStaffLog, "Staff log", "(Println)")
|
||||
Println(LAccessLog|LErrorLog, "Access and error log", "(Println)")
|
||||
// Println(LAccessLog|LStaffLog|LFatal, "Fatal access and staff log", "(Println)")
|
||||
// Println(LAccessLog, "This shouldn't be here", "(Println)")
|
||||
}
|
|
@ -2,7 +2,8 @@ package gcplugin
|
|||
|
||||
import (
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
)
|
||||
|
||||
|
@ -26,11 +27,14 @@ func createLuaLogFunc(which string) lua.LGFunction {
|
|||
}
|
||||
switch which {
|
||||
case "access":
|
||||
gclog.Println(gclog.LAccessLog, args...)
|
||||
gcutil.LogInfo().
|
||||
Interface("pluginAccess", args)
|
||||
case "error":
|
||||
gclog.Println(gclog.LErrorLog, args...)
|
||||
gcutil.LogError(nil).
|
||||
Interface("pluginError", args)
|
||||
case "staff":
|
||||
gclog.Println(gclog.LStaffLog, args...)
|
||||
gcutil.LogInfo().
|
||||
Interface("pluginStaff", args)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
|
|
@ -5,15 +5,15 @@ import (
|
|||
"html/template"
|
||||
"strings"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
x_html "golang.org/x/net/html"
|
||||
)
|
||||
|
||||
//TruncateHTML truncates a template.HTML string to a certain visible character limit and line limit
|
||||
// TruncateHTML truncates a template.HTML string to a certain visible character limit and line limit
|
||||
func truncateHTML(htmlText template.HTML, characterLimit, maxLines int) template.HTML {
|
||||
dom, err := x_html.Parse(strings.NewReader(string(htmlText)))
|
||||
if err != nil {
|
||||
gclog.Println(gclog.LErrorLog, err.Error())
|
||||
gcutil.LogError(err).Send()
|
||||
return template.HTML("Server error truncating HTML, failed to parse html")
|
||||
}
|
||||
truncateHTMLNodes(dom, characterLimit, maxLines)
|
||||
|
@ -70,6 +70,8 @@ func truncateHTMLNodes(node *x_html.Node, charactersLeft, linesLeft int) (charsL
|
|||
charactersLeft -= len(node.Data)
|
||||
return truncateHTMLNodes(node.NextSibling, charactersLeft, linesLeft)
|
||||
}
|
||||
gclog.Println(gclog.LErrorLog, "Did not match any known node type, possible error?: ", node)
|
||||
gcutil.LogError(nil).
|
||||
Interface("node", node).
|
||||
Msg("Did not match any known node type, possible unhandled error?")
|
||||
return charactersLeft, linesLeft
|
||||
}
|
||||
|
|
67
pkg/gcutil/logger.go
Normal file
67
pkg/gcutil/logger.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package gcutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
var (
|
||||
logFile *os.File
|
||||
logger zerolog.Logger
|
||||
)
|
||||
|
||||
type logHook struct{}
|
||||
|
||||
func (lh *logHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
|
||||
if level != zerolog.Disabled && level != zerolog.NoLevel {
|
||||
e.Timestamp()
|
||||
}
|
||||
}
|
||||
|
||||
func InitLog(logPath string) (err error) {
|
||||
if logFile != nil {
|
||||
// log file already initialized, skip
|
||||
return nil
|
||||
}
|
||||
logFile, err = os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger = zerolog.New(logFile).Hook(&logHook{})
|
||||
return nil
|
||||
}
|
||||
|
||||
func Logger() *zerolog.Logger {
|
||||
return &logger
|
||||
}
|
||||
|
||||
func LogInfo() *zerolog.Event {
|
||||
return logger.Info()
|
||||
}
|
||||
|
||||
func LogWarning() *zerolog.Event {
|
||||
return logger.Warn()
|
||||
}
|
||||
|
||||
func LogError(err error) *zerolog.Event {
|
||||
if err != nil {
|
||||
return logger.Err(err).Caller(1)
|
||||
}
|
||||
return logger.Error().Caller(1)
|
||||
}
|
||||
|
||||
func LogFatal() *zerolog.Event {
|
||||
return logger.Fatal().Caller(1)
|
||||
}
|
||||
|
||||
func LogDebug() *zerolog.Event {
|
||||
return logger.Debug().Caller(1)
|
||||
}
|
||||
|
||||
func CloseLog() error {
|
||||
if logFile == nil {
|
||||
return nil
|
||||
}
|
||||
return logFile.Close()
|
||||
}
|
|
@ -17,7 +17,6 @@ import (
|
|||
|
||||
"github.com/gochan-org/gochan/pkg/building"
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gctemplates"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
|
@ -72,7 +71,7 @@ type Action struct {
|
|||
//
|
||||
// IMPORTANT: the writer parameter should only be written to if absolutely necessary (for example,
|
||||
// if a redirect wouldn't work in handler.go) and even then, it should be done sparingly
|
||||
Callback func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) `json:"-"`
|
||||
Callback func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) `json:"-"`
|
||||
}
|
||||
|
||||
var actions = []Action{
|
||||
|
@ -80,7 +79,7 @@ var actions = []Action{
|
|||
ID: "logout",
|
||||
Title: "Logout",
|
||||
Permissions: JanitorPerms,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
if err = gcsql.EndStaffSession(writer, request); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -94,7 +93,7 @@ var actions = []Action{
|
|||
Title: "Log me out everywhere",
|
||||
Permissions: JanitorPerms,
|
||||
JSONoutput: OptionalJSON,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
session, err := request.Cookie("sessiondata")
|
||||
if err != nil {
|
||||
// doesn't have a login session cookie, return with no errors
|
||||
|
@ -107,7 +106,7 @@ var actions = []Action{
|
|||
return "You are not logged in", nil
|
||||
}
|
||||
|
||||
staff, err := gcsql.GetStaffBySession(session.Value)
|
||||
_, err = gcsql.GetStaffBySession(session.Value)
|
||||
if err != nil {
|
||||
// staff session doesn't exist, probably a stale cookie
|
||||
if !wantsJSON {
|
||||
|
@ -120,12 +119,13 @@ var actions = []Action{
|
|||
}
|
||||
numSessions, err := staff.CleanSessions()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
// something went wrong when trying to clean out sessions for this user, return the
|
||||
// number of sessions cleared
|
||||
// something went wrong when trying to clean out sessions for this user
|
||||
return nil, err
|
||||
}
|
||||
serverutil.DeleteCookie(writer, request, "sessiondata")
|
||||
gclog.Printf(gclog.LStaffLog, "Logging %s out of all sessions (%d cleared)", staff.Username, numSessions)
|
||||
gcutil.LogInfo().
|
||||
Str("clearSessions", staff.Username).
|
||||
Int64("cleared", numSessions)
|
||||
if !wantsJSON {
|
||||
http.Redirect(writer, request,
|
||||
config.GetSystemCriticalConfig().WebRoot+"manage",
|
||||
|
@ -139,25 +139,30 @@ var actions = []Action{
|
|||
ID: "cleanup",
|
||||
Title: "Cleanup",
|
||||
Permissions: AdminPerms,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
outputStr := ""
|
||||
if request.FormValue("run") == "Run Cleanup" {
|
||||
outputStr += "Removing deleted posts from the database.<hr />"
|
||||
if err = gcsql.PermanentlyRemoveDeletedPosts(); err != nil {
|
||||
err = errors.New(
|
||||
gclog.Print(gclog.LErrorLog, "Error removing deleted posts from database: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("cleanup", "removeDeletedPosts").
|
||||
Str("action", "cleanup").
|
||||
Str("IP", gcutil.GetRealIP(request))
|
||||
|
||||
err = errors.New("Error removing deleted posts from database: " + err.Error())
|
||||
return outputStr + "<tr><td>" + err.Error() + "</td></tr></table>", err
|
||||
}
|
||||
// TODO: remove orphaned replies and uploads
|
||||
|
||||
outputStr += "Optimizing all tables in database.<hr />"
|
||||
err = gcsql.OptimizeDatabase()
|
||||
if err != nil {
|
||||
err = errors.New(
|
||||
gclog.Print(gclog.LErrorLog, "Error optimizing SQL tables: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("sql", "optimization").
|
||||
Str("action", "cleanup").
|
||||
Str("staff", staff.Username).Send()
|
||||
err = errors.New("Error optimizing SQL tables: " + err.Error())
|
||||
return outputStr + "<tr><td>" + err.Error() + "</td></tr></table>", err
|
||||
}
|
||||
|
||||
outputStr += "Cleanup finished"
|
||||
} else {
|
||||
outputStr += `<form action="/manage?action=cleanup" method="post">` +
|
||||
|
@ -171,7 +176,7 @@ var actions = []Action{
|
|||
Title: "Recent posts",
|
||||
Permissions: JanitorPerms,
|
||||
JSONoutput: OptionalJSON,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
limit := gcutil.HackyStringToInt(request.FormValue("limit"))
|
||||
if limit == 0 {
|
||||
limit = 50
|
||||
|
@ -181,14 +186,14 @@ var actions = []Action{
|
|||
return recentposts, err
|
||||
}
|
||||
manageRecentsBuffer := bytes.NewBufferString("")
|
||||
if err = serverutil.MinifyTemplate(gctemplates.ManageRecentPosts,
|
||||
map[string]interface{}{
|
||||
"recentposts": recentposts,
|
||||
"webroot": config.GetSystemCriticalConfig().WebRoot,
|
||||
},
|
||||
manageRecentsBuffer, "text/html"); err != nil {
|
||||
return "", errors.New(gclog.Print(gclog.LErrorLog,
|
||||
"Error executing ban management page template: "+err.Error()))
|
||||
if err = serverutil.MinifyTemplate(gctemplates.ManageRecentPosts, map[string]interface{}{
|
||||
"recentposts": recentposts,
|
||||
"webroot": config.GetSystemCriticalConfig().WebRoot,
|
||||
}, manageRecentsBuffer, "text/html"); err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "recentposts").Send()
|
||||
return "", errors.New("Error executing ban management page template: " + err.Error())
|
||||
}
|
||||
return manageRecentsBuffer.String(), nil
|
||||
}},
|
||||
|
@ -196,9 +201,14 @@ var actions = []Action{
|
|||
ID: "bans",
|
||||
Title: "Bans",
|
||||
Permissions: ModPerms,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) { //TODO whatever this does idk man
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) { //TODO whatever this does idk man
|
||||
var outputStr string
|
||||
var post gcsql.Post
|
||||
errorEv := gcutil.LogError(nil).
|
||||
Str("action", "bans").
|
||||
Str("staff", staff.Username)
|
||||
defer errorEv.Discard()
|
||||
|
||||
if request.FormValue("do") == "add" {
|
||||
ip := request.FormValue("ip")
|
||||
name := request.FormValue("name")
|
||||
|
@ -216,39 +226,75 @@ var actions = []Action{
|
|||
boards := request.FormValue("boards")
|
||||
reason := html.EscapeString(request.FormValue("reason"))
|
||||
staffNote := html.EscapeString(request.FormValue("staffnote"))
|
||||
currentStaff, _ := getCurrentStaff(request)
|
||||
|
||||
if filename != "" {
|
||||
err = gcsql.CreateFileNameBan(filename, nameIsRegex, currentStaff, permaban, staffNote, boards)
|
||||
err = gcsql.CreateFileNameBan(filename, nameIsRegex, staff.Username, permaban, staffNote, boards)
|
||||
}
|
||||
if err != nil {
|
||||
outputStr += err.Error()
|
||||
err = nil
|
||||
}
|
||||
if name != "" {
|
||||
if err = gcsql.CreateUserNameBan(name, nameIsRegex, currentStaff, permaban, staffNote, boards); err != nil {
|
||||
if err = gcsql.CreateUserNameBan(name, nameIsRegex, staff.Username, permaban, staffNote, boards); err != nil {
|
||||
errorEv.
|
||||
Str("banType", "username").
|
||||
Str("user", name).Send()
|
||||
return "", err
|
||||
}
|
||||
gcutil.LogInfo().
|
||||
Str("action", "bans").
|
||||
Str("staff", staff.Username).
|
||||
Str("banType", "username").
|
||||
Str("user", name).
|
||||
Bool("permaban", permaban).Send()
|
||||
}
|
||||
|
||||
if request.FormValue("fullban") == "on" {
|
||||
err = gcsql.CreateUserBan(ip, false, currentStaff, boards, expires, permaban, staffNote, reason, true, time.Now())
|
||||
err = gcsql.CreateUserBan(ip, false, staff.Username, boards, expires, permaban, staffNote, reason, true, time.Now())
|
||||
if err != nil {
|
||||
errorEv.
|
||||
Str("banType", "ip").
|
||||
Str("banIP", ip).
|
||||
Bool("threadBan", false).
|
||||
Str("bannedFromBoards", boards).Send()
|
||||
return "", err
|
||||
}
|
||||
gcutil.LogInfo().
|
||||
Str("staff", staff.Username).
|
||||
Str("banType", "ip").
|
||||
Str("banIP", ip).
|
||||
Bool("threadBan", true).
|
||||
Str("bannedFromBoards", boards).Send()
|
||||
} else {
|
||||
if request.FormValue("threadban") == "on" {
|
||||
err = gcsql.CreateUserBan(ip, true, currentStaff, boards, expires, permaban, staffNote, reason, true, time.Now())
|
||||
err = gcsql.CreateUserBan(ip, true, staff.Username, boards, expires, permaban, staffNote, reason, true, time.Now())
|
||||
if err != nil {
|
||||
errorEv.
|
||||
Str("banType", "ip").
|
||||
Str("banIP", ip).
|
||||
Bool("threadBan", true).
|
||||
Str("bannedFromBoards", boards).Send()
|
||||
return "", err
|
||||
|
||||
}
|
||||
gcutil.LogInfo().
|
||||
Str("staff", staff.Username).
|
||||
Str("banType", "ip").
|
||||
Str("banIP", ip).
|
||||
Bool("threadBan", true).
|
||||
Str("bannedFromBoards", boards).Send()
|
||||
}
|
||||
if request.FormValue("imageban") == "on" {
|
||||
err = gcsql.CreateFileBan(checksum, currentStaff, permaban, staffNote, boards)
|
||||
err = gcsql.CreateFileBan(checksum, staff.Username, permaban, staffNote, boards)
|
||||
if err != nil {
|
||||
errorEv.
|
||||
Str("banType", "fileBan").
|
||||
Str("checksum", checksum).Send()
|
||||
return "", err
|
||||
}
|
||||
gcutil.LogInfo().
|
||||
Str("staff", staff.Username).
|
||||
Str("banType", "fileBan").
|
||||
Str("checksum", checksum).Send()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -257,6 +303,7 @@ var actions = []Action{
|
|||
var err error
|
||||
post, err = gcsql.GetSpecificPostByString(request.FormValue("postid"), true)
|
||||
if err != nil {
|
||||
errorEv.Err(err).Str("postid", request.FormValue("postid")).Msg("Error getting post")
|
||||
err = errors.New("Error getting post: " + err.Error())
|
||||
return "", err
|
||||
}
|
||||
|
@ -264,20 +311,22 @@ var actions = []Action{
|
|||
|
||||
banlist, err := gcsql.GetAllBans()
|
||||
if err != nil {
|
||||
errorEv.Err(err).Msg("Error getting ban list")
|
||||
err = errors.New("Error getting ban list: " + err.Error())
|
||||
return "", err
|
||||
}
|
||||
manageBansBuffer := bytes.NewBufferString("")
|
||||
|
||||
if err = serverutil.MinifyTemplate(gctemplates.ManageBans,
|
||||
map[string]interface{}{
|
||||
// "systemCritical": config.GetSystemCriticalConfig(),
|
||||
"banlist": banlist,
|
||||
"post": post,
|
||||
},
|
||||
manageBansBuffer, "text/html"); err != nil {
|
||||
return "", errors.New(gclog.Print(gclog.LErrorLog,
|
||||
"Error executing ban management page template: "+err.Error()))
|
||||
if err = serverutil.MinifyTemplate(gctemplates.ManageBans, map[string]interface{}{
|
||||
// "systemCritical": config.GetSystemCriticalConfig(),
|
||||
"banlist": banlist,
|
||||
"post": post,
|
||||
}, manageBansBuffer, "text/html"); err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "bans").
|
||||
Str("template", "manage_bans.html").Send()
|
||||
return "", errors.New("Error executing ban management page template: " + err.Error())
|
||||
}
|
||||
outputStr += manageBansBuffer.String()
|
||||
return outputStr, nil
|
||||
|
@ -287,14 +336,7 @@ var actions = []Action{
|
|||
Title: "IP Search",
|
||||
Permissions: ModPerms,
|
||||
JSONoutput: NoJSON,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
var staff *gcsql.Staff
|
||||
staff, err = getCurrentFullStaff(request)
|
||||
if err != nil {
|
||||
gclog.Printf(gclog.LErrorLog, "Error parsing request: %s", err.Error())
|
||||
return "", err
|
||||
}
|
||||
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
ipQuery := request.Form.Get("ip")
|
||||
limitStr := request.Form.Get("limit")
|
||||
data := map[string]interface{}{
|
||||
|
@ -317,15 +359,23 @@ var actions = []Action{
|
|||
}
|
||||
data["posts"], err = gcsql.GetPostsFromIP(ipQuery, limit, true)
|
||||
if err != nil {
|
||||
return "", errors.New(gclog.Printf(gclog.LErrorLog|gclog.LStaffLog,
|
||||
"Error getting list of posts from %q by staff %s: %s", ipQuery, staff.Username, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "ipsearch").
|
||||
Str("ipQuery", ipQuery).
|
||||
Int("limit", limit).
|
||||
Bool("onlyNotDeleted", true).Send()
|
||||
return "", fmt.Errorf("Error getting list of posts from %q by staff %s: %s", ipQuery, staff.Username, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
manageIpBuffer := bytes.NewBufferString("")
|
||||
if err = serverutil.MinifyTemplate(gctemplates.ManageIPSearch, data, manageIpBuffer, "text/html"); err != nil {
|
||||
return "", errors.New(gclog.Println(gclog.LErrorLog,
|
||||
"Error executing IP search page template:", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "ipsearch").
|
||||
Str("template", "manage_ipsearch.html").Send()
|
||||
return "", errors.New("Error executing IP search page template:" + err.Error())
|
||||
}
|
||||
return manageIpBuffer.String(), nil
|
||||
}},
|
||||
|
@ -334,11 +384,7 @@ var actions = []Action{
|
|||
Title: "Reports",
|
||||
Permissions: ModPerms,
|
||||
JSONoutput: OptionalJSON,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
staff, err := getCurrentFullStaff(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
dismissIDstr := request.FormValue("dismiss")
|
||||
if dismissIDstr != "" {
|
||||
// staff is dismissing a report
|
||||
|
@ -346,21 +392,30 @@ var actions = []Action{
|
|||
block := request.FormValue("block")
|
||||
if block != "" && staff.Rank != 3 {
|
||||
serveError(writer, "permission", "reports", "Only the administrator can block reports", wantsJSON)
|
||||
gclog.Printf(gclog.LStaffLog, "Request by staff %s to block reports to post %d rejected (not an admin)",
|
||||
staff.Username, dismissID,
|
||||
)
|
||||
gcutil.LogWarning().
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "reports").
|
||||
Int("postID", dismissID).
|
||||
Str("rejected", "not an admin").Send()
|
||||
return "", nil
|
||||
}
|
||||
found, err := gcsql.ClearReport(dismissID, staff.ID, block != "" && staff.Rank == 3)
|
||||
if err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "reports").
|
||||
Int("postID", dismissID).Send()
|
||||
return nil, err
|
||||
}
|
||||
if !found {
|
||||
return nil, errors.New("no matching reports")
|
||||
}
|
||||
gclog.Printf(gclog.LStaffLog, "Report id %d cleared by %s, future reports blocked for this post: %t",
|
||||
dismissID, staff.Username, block != "",
|
||||
)
|
||||
gcutil.LogInfo().
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "reports").
|
||||
Int("reportID", dismissID).
|
||||
Bool("blocked", block != "").
|
||||
Msg("Report cleared")
|
||||
}
|
||||
rows, err := gcsql.QuerySQL(`SELECT id,
|
||||
handled_by_staff_id as staff_id,
|
||||
|
@ -408,6 +463,9 @@ var actions = []Action{
|
|||
"staff": staff,
|
||||
}, reportsBuffer, "text/html")
|
||||
if err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "reports").Send()
|
||||
return "", err
|
||||
}
|
||||
output = reportsBuffer.String()
|
||||
|
@ -418,13 +476,7 @@ var actions = []Action{
|
|||
Title: "Staff",
|
||||
Permissions: AdminPerms,
|
||||
JSONoutput: OptionalJSON,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
var currentStaffUsername string
|
||||
currentStaffUsername, err = getCurrentStaff(request)
|
||||
if err != nil {
|
||||
err = errors.New("Error getting current staff username: " + err.Error())
|
||||
return "", err
|
||||
}
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
var outputStr string
|
||||
do := request.FormValue("do")
|
||||
allStaff, err := gcsql.GetAllStaffNopass(true)
|
||||
|
@ -432,8 +484,11 @@ var actions = []Action{
|
|||
return allStaff, err
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.New(gclog.Print(gclog.LErrorLog,
|
||||
"Error getting staff list: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "staff").
|
||||
Msg("Error getting staff list")
|
||||
err = errors.New("Error getting staff list: " + err.Error())
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
@ -444,21 +499,34 @@ var actions = []Action{
|
|||
rankI, _ := strconv.Atoi(rank)
|
||||
if do == "add" {
|
||||
if err = gcsql.NewStaff(username, password, rankI); err != nil {
|
||||
return "", errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Error creating new staff account %q by %q: %s",
|
||||
username, currentStaffUsername, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "staff").
|
||||
Str("newStaff", username).
|
||||
Str("newPass", password).
|
||||
Int("newRank", rankI).
|
||||
Msg("Error creating new staff account")
|
||||
return "", fmt.Errorf("Error creating new staff account %q by %q: %s",
|
||||
username, staff.Username, err.Error())
|
||||
}
|
||||
} else if do == "del" && username != "" {
|
||||
if err = gcsql.DeleteStaff(username); err != nil {
|
||||
return "", errors.New(gclog.Printf(gclog.LErrorLog,
|
||||
"Error deleting staff account %q by %q: %s",
|
||||
username, currentStaffUsername, err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "staff").
|
||||
Str("delStaff", username).
|
||||
Msg("Error deleting staff account")
|
||||
return "", fmt.Errorf("Error deleting staff account %q by %q: %s",
|
||||
username, staff.Username, err.Error())
|
||||
}
|
||||
}
|
||||
allStaff, err = gcsql.GetAllStaffNopass(true)
|
||||
if err != nil {
|
||||
err = errors.New(gclog.Print(gclog.LErrorLog,
|
||||
"Error getting updated staff list: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "staff").
|
||||
Msg("Error getting updated staff list")
|
||||
err = errors.New("Error getting updated staff list: " + err.Error())
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
@ -473,15 +541,16 @@ var actions = []Action{
|
|||
}
|
||||
|
||||
staffBuffer := bytes.NewBufferString("")
|
||||
if err = serverutil.MinifyTemplate(gctemplates.ManageStaff,
|
||||
map[string]interface{}{
|
||||
"allstaff": allStaff,
|
||||
"webroot": config.GetSystemCriticalConfig().WebRoot,
|
||||
"currentUsername": currentStaffUsername,
|
||||
},
|
||||
staffBuffer, "text/html"); err != nil {
|
||||
return "", errors.New(gclog.Print(gclog.LErrorLog,
|
||||
"Error executing staff management page template: ", err.Error()))
|
||||
if err = serverutil.MinifyTemplate(gctemplates.ManageStaff, map[string]interface{}{
|
||||
"allstaff": allStaff,
|
||||
"webroot": config.GetSystemCriticalConfig().WebRoot,
|
||||
"currentUsername": staff.Username,
|
||||
}, staffBuffer, "text/html"); err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "staff").
|
||||
Str("template", "manage_staff.html").Send()
|
||||
return "", errors.New("Error executing staff management page template: " + err.Error())
|
||||
}
|
||||
outputStr += staffBuffer.String()
|
||||
return outputStr, nil
|
||||
|
@ -490,9 +559,9 @@ var actions = []Action{
|
|||
ID: "login",
|
||||
Title: "Login",
|
||||
Permissions: NoPerms,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
systemCritical := config.GetSystemCriticalConfig()
|
||||
if GetStaffRank(request) > 0 {
|
||||
if staff.Rank > 0 {
|
||||
http.Redirect(writer, request, path.Join(systemCritical.WebRoot, "manage"), http.StatusFound)
|
||||
}
|
||||
username := request.FormValue("username")
|
||||
|
@ -505,17 +574,19 @@ var actions = []Action{
|
|||
if username == "" || password == "" {
|
||||
//assume that they haven't logged in
|
||||
manageLoginBuffer := bytes.NewBufferString("")
|
||||
if err = serverutil.MinifyTemplate(gctemplates.ManageLogin,
|
||||
map[string]interface{}{
|
||||
"webroot": config.GetSystemCriticalConfig().WebRoot,
|
||||
"site_config": config.GetSiteConfig(),
|
||||
"sections": gcsql.AllSections,
|
||||
"boards": gcsql.AllBoards,
|
||||
"board_config": config.GetBoardConfig(""),
|
||||
"redirect": redirectAction,
|
||||
}, manageLoginBuffer, "text/html"); err != nil {
|
||||
return "", errors.New(gclog.Print(gclog.LErrorLog,
|
||||
"Error executing staff login page template: ", err.Error()))
|
||||
if err = serverutil.MinifyTemplate(gctemplates.ManageLogin, map[string]interface{}{
|
||||
"webroot": config.GetSystemCriticalConfig().WebRoot,
|
||||
"site_config": config.GetSiteConfig(),
|
||||
"sections": gcsql.AllSections,
|
||||
"boards": gcsql.AllBoards,
|
||||
"board_config": config.GetBoardConfig(""),
|
||||
"redirect": redirectAction,
|
||||
}, manageLoginBuffer, "text/html"); err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "login").
|
||||
Str("template", "manage_login.html").Send()
|
||||
return "", errors.New("Error executing staff login page template: " + err.Error())
|
||||
}
|
||||
output = manageLoginBuffer.String()
|
||||
} else {
|
||||
|
@ -530,7 +601,7 @@ var actions = []Action{
|
|||
Title: "Announcements",
|
||||
Permissions: JanitorPerms,
|
||||
JSONoutput: AlwaysJSON,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
// return an array of announcements and any errors
|
||||
return gcsql.GetAllAccouncements()
|
||||
}},
|
||||
|
@ -538,23 +609,15 @@ var actions = []Action{
|
|||
ID: "staffinfo",
|
||||
Permissions: NoPerms,
|
||||
JSONoutput: AlwaysJSON,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
staff, err := getCurrentFullStaff(request)
|
||||
return staff, err
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
return staff, nil
|
||||
}},
|
||||
{
|
||||
ID: "boards",
|
||||
Title: "Boards",
|
||||
Permissions: AdminPerms,
|
||||
JSONoutput: NoJSON,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
var currentUser string
|
||||
currentUser, err = getCurrentStaff(request)
|
||||
if err != nil {
|
||||
return "", errors.New(gclog.Println(gclog.LErrorLog,
|
||||
"Error parsing current user:", err.Error()))
|
||||
}
|
||||
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
pageBuffer := bytes.NewBufferString("")
|
||||
var board gcsql.Board
|
||||
requestType, boardID, err := boardsRequestType(request)
|
||||
|
@ -572,31 +635,35 @@ var actions = []Action{
|
|||
case "delete":
|
||||
// delete button clicked, delete the board
|
||||
if board, err = gcsql.GetBoardFromID(boardID); err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "boards").
|
||||
Int("deleteBoardID", boardID).Send()
|
||||
return "", err
|
||||
}
|
||||
err = board.Delete()
|
||||
if err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "boards").
|
||||
Str("deleteBoard", board.Dir).Send()
|
||||
return "", err
|
||||
}
|
||||
absPath := board.AbsolutePath()
|
||||
gclog.Printf(gclog.LStaffLog,
|
||||
"Board /%s/ deleted by %s, absolute path: %s\n", board.Dir, currentUser, absPath)
|
||||
err = os.RemoveAll(absPath)
|
||||
gcutil.LogInfo().
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "boards").
|
||||
Str("deleteBoard", board.Dir).Send()
|
||||
err = os.RemoveAll(board.AbsolutePath())
|
||||
case "edit":
|
||||
// edit button clicked, fill the input fields with board data to be edited
|
||||
board, err = gcsql.GetBoardFromID(boardID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
case "modify":
|
||||
// save changes button clicked, apply changes to the board based on the request fields
|
||||
board, err = gcsql.GetBoardFromID(boardID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err = board.ChangeFromRequest(request, true); err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = board.ChangeFromRequest(request, true)
|
||||
case "cancel":
|
||||
// cancel button was clicked
|
||||
fallthrough
|
||||
|
@ -629,8 +696,10 @@ var actions = []Action{
|
|||
"editing": requestType == "edit",
|
||||
"board": board,
|
||||
}, pageBuffer, "text/html"); err != nil {
|
||||
gclog.Printf(gclog.LErrorLog|gclog.LStaffLog,
|
||||
"Error executing manage boards template: %q", err.Error())
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "boards").
|
||||
Str("template", "manage_boards.html").Send()
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
@ -641,7 +710,7 @@ var actions = []Action{
|
|||
Title: "Board sections",
|
||||
Permissions: AdminPerms,
|
||||
JSONoutput: NoJSON,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
section := &gcsql.BoardSection{}
|
||||
editID := request.Form.Get("edit")
|
||||
updateID := request.Form.Get("updatesection")
|
||||
|
@ -731,7 +800,7 @@ var actions = []Action{
|
|||
Title: "Rebuild front page",
|
||||
Permissions: AdminPerms,
|
||||
JSONoutput: OptionalJSON,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
if err = gctemplates.InitTemplates(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -748,7 +817,7 @@ var actions = []Action{
|
|||
Title: "Rebuild everything",
|
||||
Permissions: AdminPerms,
|
||||
JSONoutput: OptionalJSON,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
gctemplates.InitTemplates()
|
||||
gcsql.ResetBoardSectionArrays()
|
||||
buildErr := &ErrStaffAction{
|
||||
|
@ -757,8 +826,7 @@ var actions = []Action{
|
|||
}
|
||||
buildMap := map[string]string{}
|
||||
if err = building.BuildFrontPage(); err != nil {
|
||||
buildErr.Message = gclog.Println(gclog.LErrorLog,
|
||||
"Error building front page:", err.Error())
|
||||
buildErr.Message = "Error building front page: " + err.Error()
|
||||
if wantsJSON {
|
||||
return buildErr, buildErr
|
||||
}
|
||||
|
@ -767,8 +835,7 @@ var actions = []Action{
|
|||
buildMap["front"] = "Built front page successfully"
|
||||
|
||||
if err = building.BuildBoardListJSON(); err != nil {
|
||||
buildErr.Message = gclog.Println(gclog.LErrorLog,
|
||||
"Error building board list:", err.Error())
|
||||
buildErr.Message = "Error building board list: " + err.Error()
|
||||
if wantsJSON {
|
||||
return buildErr, buildErr
|
||||
}
|
||||
|
@ -777,8 +844,7 @@ var actions = []Action{
|
|||
buildMap["boardlist"] = "Built board list successfully"
|
||||
|
||||
if err = building.BuildBoards(false); err != nil {
|
||||
buildErr.Message = gclog.Println(gclog.LErrorLog,
|
||||
"Error building boards:", err.Error())
|
||||
buildErr.Message = "Error building boards: " + err.Error()
|
||||
if wantsJSON {
|
||||
return buildErr, buildErr
|
||||
}
|
||||
|
@ -787,8 +853,7 @@ var actions = []Action{
|
|||
buildMap["boards"] = "Built boards successfully"
|
||||
|
||||
if err = building.BuildJS(); err != nil {
|
||||
buildErr.Message = gclog.Println(gclog.LErrorLog,
|
||||
"Error building consts.js:", err.Error())
|
||||
buildErr.Message = "Error building consts.js: " + err.Error()
|
||||
if wantsJSON {
|
||||
return buildErr, buildErr
|
||||
}
|
||||
|
@ -828,8 +893,11 @@ var actions = []Action{
|
|||
Title: "Rebuild boards",
|
||||
Permissions: AdminPerms,
|
||||
JSONoutput: OptionalJSON,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
if err = gctemplates.InitTemplates(); err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "rebuildboards").Send()
|
||||
return "", err
|
||||
}
|
||||
if wantsJSON {
|
||||
|
@ -844,9 +912,8 @@ var actions = []Action{
|
|||
ID: "reparsehtml",
|
||||
Title: "Reparse HTML",
|
||||
Permissions: AdminPerms,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
var outputStr string
|
||||
|
||||
messages, err := gcsql.GetAllNondeletedMessageRaw()
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -881,7 +948,7 @@ var actions = []Action{
|
|||
Title: "Post info",
|
||||
Permissions: ModPerms,
|
||||
JSONoutput: AlwaysJSON,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
postIDstr := request.FormValue("postid")
|
||||
if postIDstr == "" {
|
||||
return "", errors.New("invalid request (missing postid)")
|
||||
|
@ -927,14 +994,10 @@ var actions = []Action{
|
|||
ID: "wordfilters",
|
||||
Title: "Wordfilters",
|
||||
Permissions: AdminPerms,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
|
||||
managePageBuffer := bytes.NewBufferString("")
|
||||
editIDstr := request.FormValue("edit")
|
||||
deleteIDstr := request.FormValue("delete")
|
||||
var staff *gcsql.Staff
|
||||
if staff, err = getCurrentFullStaff(request); err != nil {
|
||||
return err, err
|
||||
}
|
||||
if deleteIDstr != "" {
|
||||
var result sql.Result
|
||||
if result, err = gcsql.ExecSQL(`DELETE FROM DBPREFIXwordfilters WHERE id = ?`, deleteIDstr); err != nil {
|
||||
|
@ -942,10 +1005,15 @@ var actions = []Action{
|
|||
}
|
||||
if numRows, _ := result.RowsAffected(); numRows < 1 {
|
||||
err = invalidWordfilterID(deleteIDstr)
|
||||
gclog.Println(gclog.LErrorLog|gclog.LStaffLog, err.Error())
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "wordfilters").Send()
|
||||
return err, err
|
||||
}
|
||||
gclog.Printf(gclog.LStaffLog, "%s deleted wordfilter with id #%s", staff.Username, deleteIDstr)
|
||||
gcutil.LogInfo().
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "wordfilters").
|
||||
Str("deletedWordfilterID", deleteIDstr)
|
||||
}
|
||||
|
||||
submitBtn := request.FormValue("dowordfilter")
|
||||
|
@ -1004,7 +1072,13 @@ var actions = []Action{
|
|||
|
||||
err = serverutil.MinifyTemplate(gctemplates.ManageWordfilters,
|
||||
filterMap, managePageBuffer, "text/html")
|
||||
if err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "wordfilters").
|
||||
Str("template", "manage_wordfilters.html").Send()
|
||||
|
||||
}
|
||||
return managePageBuffer.String(), err
|
||||
},
|
||||
},
|
||||
|
|
|
@ -2,11 +2,12 @@ package manage
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/building"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
"github.com/gochan-org/gochan/pkg/serverutil"
|
||||
)
|
||||
|
@ -30,35 +31,42 @@ func serveError(writer http.ResponseWriter, field string, action string, message
|
|||
})
|
||||
}
|
||||
|
||||
func isRequestingJSON(request *http.Request) bool {
|
||||
field := request.Form["json"]
|
||||
return len(field) == 1 && (field[0] == "1" || field[0] == "true")
|
||||
}
|
||||
|
||||
// CallManageFunction is called when a user accesses /manage to use manage tools
|
||||
// or log in to a staff account
|
||||
func CallManageFunction(writer http.ResponseWriter, request *http.Request) {
|
||||
var err error
|
||||
wantsJSON := serverutil.IsRequestingJSON(request)
|
||||
if err = request.ParseForm(); err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Error parsing form data: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("IP", gcutil.GetRealIP(request)).
|
||||
Msg("Error parsing form data")
|
||||
serverutil.ServeError(writer, "Error parsing form data: "+err.Error(), wantsJSON, nil)
|
||||
return
|
||||
}
|
||||
actionID := request.FormValue("action")
|
||||
staffRank := GetStaffRank(request)
|
||||
|
||||
var staff *gcsql.Staff
|
||||
staff, err = getCurrentFullStaff(request)
|
||||
if err == http.ErrNoCookie {
|
||||
staff = &gcsql.Staff{}
|
||||
err = nil
|
||||
} else if err != nil && err != sql.ErrNoRows {
|
||||
gcutil.LogError(err).
|
||||
Str("request", "getCurrentFullStaff").
|
||||
Str("action", actionID).Send()
|
||||
serverutil.ServeError(writer, "Error getting staff info from request: "+err.Error(), wantsJSON, nil)
|
||||
return
|
||||
}
|
||||
if actionID == "" {
|
||||
if staffRank == NoPerms {
|
||||
if staff.Rank == NoPerms {
|
||||
// no action requested and user is not logged in, have them go to login page
|
||||
actionID = "login"
|
||||
} else {
|
||||
actionID = "dashboard"
|
||||
}
|
||||
}
|
||||
wantsJSON := isRequestingJSON(request)
|
||||
|
||||
var managePageBuffer bytes.Buffer
|
||||
action := getAction(actionID, staffRank)
|
||||
action := getAction(actionID, staff.Rank)
|
||||
if action == nil {
|
||||
if wantsJSON {
|
||||
serveError(writer, "notfound", actionID, "action not found", wantsJSON || (action.JSONoutput == AlwaysJSON))
|
||||
|
@ -68,12 +76,14 @@ func CallManageFunction(writer http.ResponseWriter, request *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if staffRank < action.Permissions {
|
||||
if staff.Rank < action.Permissions {
|
||||
writer.WriteHeader(403)
|
||||
staffName, _ := getCurrentStaff(request)
|
||||
gclog.Printf(gclog.LStaffLog,
|
||||
"Rejected request to manage page %s from %s (insufficient permissions)",
|
||||
actionID, staffName)
|
||||
gcutil.LogInfo().
|
||||
Str("staff", "insufficientPerms").
|
||||
Str("IP", gcutil.GetRealIP(request)).
|
||||
Str("username", staffName).
|
||||
Str("action", actionID)
|
||||
serveError(writer, "permission", actionID, "You do not have permission to access this page", wantsJSON || (action.JSONoutput == AlwaysJSON))
|
||||
return
|
||||
}
|
||||
|
@ -87,7 +97,7 @@ func CallManageFunction(writer http.ResponseWriter, request *http.Request) {
|
|||
Message: "Requested mod page does not have a JSON output option",
|
||||
}
|
||||
} else {
|
||||
output, err = action.Callback(writer, request, wantsJSON)
|
||||
output, err = action.Callback(writer, request, staff, wantsJSON)
|
||||
}
|
||||
if err != nil {
|
||||
// writer.WriteHeader(500)
|
||||
|
@ -100,6 +110,8 @@ func CallManageFunction(writer http.ResponseWriter, request *http.Request) {
|
|||
outputJSON, err := gcutil.MarshalJSON(output, true)
|
||||
if err != nil {
|
||||
serveError(writer, "error", actionID, err.Error(), true)
|
||||
gcutil.LogError(err).
|
||||
Str("action", actionID).Send()
|
||||
return
|
||||
}
|
||||
serverutil.MinifyWriter(writer, []byte(outputJSON), "application/json")
|
||||
|
@ -113,14 +125,18 @@ func CallManageFunction(writer http.ResponseWriter, request *http.Request) {
|
|||
headerMap["include_dashboard_link"] = true
|
||||
}
|
||||
if err = building.BuildPageHeader(&managePageBuffer, action.Title, "", headerMap); err != nil {
|
||||
serveError(writer, "error", actionID,
|
||||
gclog.Print(gclog.LErrorLog, "Failed writing page header: ", err.Error()), false)
|
||||
gcutil.LogError(err).
|
||||
Str("action", actionID).
|
||||
Str("staff", "pageHeader").Send()
|
||||
serveError(writer, "error", actionID, "Failed writing page header: "+err.Error(), false)
|
||||
return
|
||||
}
|
||||
managePageBuffer.WriteString("<br />" + fmt.Sprint(output) + "<br /><br />")
|
||||
if err = building.BuildPageFooter(&managePageBuffer); err != nil {
|
||||
serveError(writer, "error", actionID,
|
||||
gclog.Print(gclog.LErrorLog, "Failed writing page footer: ", err.Error()), false)
|
||||
gcutil.LogError(err).
|
||||
Str("action", actionID).
|
||||
Str("staff", "pageFooter").Send()
|
||||
serveError(writer, "error", actionID, "Failed writing page footer: "+err.Error(), false)
|
||||
return
|
||||
}
|
||||
writer.Write(managePageBuffer.Bytes())
|
||||
|
|
|
@ -2,13 +2,12 @@ package manage
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gctemplates"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
|
@ -29,19 +28,33 @@ func createSession(key, username, password string, request *http.Request, writer
|
|||
domain = chopPortNumRegex.Split(domain, -1)[0]
|
||||
|
||||
if !serverutil.ValidReferer(request) {
|
||||
gclog.Print(gclog.LStaffLog, "Rejected login from possible spambot @ "+request.RemoteAddr)
|
||||
gcutil.LogWarning().
|
||||
Str("staff", username).
|
||||
Str("IP", gcutil.GetRealIP(request)).
|
||||
Str("remoteAddr", request.Response.Request.RemoteAddr).
|
||||
Msg("Rejected login from possible spambot")
|
||||
return sOtherError
|
||||
}
|
||||
staff, err := gcsql.GetStaffByName(username)
|
||||
if err != nil {
|
||||
gclog.Print(gclog.LErrorLog, err.Error())
|
||||
if err != sql.ErrNoRows {
|
||||
gcutil.LogError(err).
|
||||
Str("staff", username).
|
||||
Str("IP", gcutil.GetRealIP(request)).
|
||||
Str("remoteAddr", request.RemoteAddr).
|
||||
Msg("Invalid password")
|
||||
}
|
||||
return sInvalidPassword
|
||||
}
|
||||
|
||||
success := bcrypt.CompareHashAndPassword([]byte(staff.PasswordChecksum), []byte(password))
|
||||
if success == bcrypt.ErrMismatchedHashAndPassword {
|
||||
// password mismatch
|
||||
gclog.Print(gclog.LStaffLog, "Failed login (password mismatch) from "+request.RemoteAddr+" at "+time.Now().Format(gcsql.MySQLDatetimeFormat))
|
||||
gcutil.LogError(nil).
|
||||
Str("staff", username).
|
||||
Str("IP", gcutil.GetRealIP(request)).
|
||||
Str("remoteAddr", request.Response.Request.RemoteAddr).
|
||||
Msg("Invalid password")
|
||||
return sInvalidPassword
|
||||
}
|
||||
|
||||
|
@ -61,7 +74,10 @@ func createSession(key, username, password string, request *http.Request, writer
|
|||
})
|
||||
|
||||
if err = gcsql.CreateSession(key, username); err != nil {
|
||||
gclog.Print(gclog.LErrorLog, "Error creating new staff session: ", err.Error())
|
||||
gcutil.LogError(err).
|
||||
Str("staff", username).
|
||||
Str("sessionKey", key).
|
||||
Msg("Error creating new staff session")
|
||||
return sOtherError
|
||||
}
|
||||
|
||||
|
@ -123,15 +139,14 @@ func init() {
|
|||
})
|
||||
}
|
||||
|
||||
func dashboardCallback(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (interface{}, error) {
|
||||
func dashboardCallback(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (interface{}, error) {
|
||||
dashBuffer := bytes.NewBufferString("")
|
||||
announcements, err := gcsql.GetAllAccouncements()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rank := GetStaffRank(request)
|
||||
rankString := ""
|
||||
switch rank {
|
||||
switch staff.Rank {
|
||||
case AdminPerms:
|
||||
rankString = "administrator"
|
||||
case ModPerms:
|
||||
|
@ -140,18 +155,19 @@ func dashboardCallback(writer http.ResponseWriter, request *http.Request, wantsJ
|
|||
rankString = "janitor"
|
||||
}
|
||||
|
||||
availableActions := getAvailableActions(rank, true)
|
||||
if err = serverutil.MinifyTemplate(gctemplates.ManageDashboard,
|
||||
map[string]interface{}{
|
||||
"actions": availableActions,
|
||||
"rank": rank,
|
||||
"rankString": rankString,
|
||||
"announcements": announcements,
|
||||
"boards": gcsql.AllBoards,
|
||||
"webroot": config.GetSystemCriticalConfig().WebRoot,
|
||||
}, dashBuffer, "text/html"); err != nil {
|
||||
gclog.Printf(gclog.LErrorLog|gclog.LStaffLog,
|
||||
"Error executing dashboard template: %q", err.Error())
|
||||
availableActions := getAvailableActions(staff.Rank, true)
|
||||
if err = serverutil.MinifyTemplate(gctemplates.ManageDashboard, map[string]interface{}{
|
||||
"actions": availableActions,
|
||||
"rank": staff.Rank,
|
||||
"rankString": rankString,
|
||||
"announcements": announcements,
|
||||
"boards": gcsql.AllBoards,
|
||||
"webroot": config.GetSystemCriticalConfig().WebRoot,
|
||||
}, dashBuffer, "text/html"); err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("staff", staff.Username).
|
||||
Str("action", "dashboard").
|
||||
Str("template", "manage_dashboard.html").Send()
|
||||
return "", err
|
||||
}
|
||||
return dashBuffer.String(), nil
|
||||
|
@ -169,9 +185,8 @@ func getAvailableActions(rank int, noJSON bool) []Action {
|
|||
return available
|
||||
}
|
||||
|
||||
func getStaffActions(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (interface{}, error) {
|
||||
rank := GetStaffRank(request)
|
||||
availableActions := getAvailableActions(rank, false)
|
||||
func getStaffActions(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (interface{}, error) {
|
||||
availableActions := getAvailableActions(staff.Rank, false)
|
||||
return availableActions, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gctemplates"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
|
@ -26,7 +25,7 @@ const (
|
|||
// BanHandler is used for serving ban pages
|
||||
func BanHandler(writer http.ResponseWriter, request *http.Request) {
|
||||
appealMsg := request.FormValue("appealmsg")
|
||||
// banStatus, err := getBannedStatus(request) TODO refactor to use ipban
|
||||
// banStatus, err := getBannedStatus(request) // TODO refactor to use ipban
|
||||
var banStatus gcsql.BanInfo
|
||||
var err error
|
||||
systemCritical := config.GetSystemCriticalConfig()
|
||||
|
@ -48,8 +47,8 @@ func BanHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
}
|
||||
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Error getting banned status:", err.Error()))
|
||||
gcutil.LogError(err).Msg("Failed getting banned status")
|
||||
serverutil.ServeErrorPage(writer, "Error getting banned status: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -61,8 +60,10 @@ func BanHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
"banBoards": banStatus.Boards,
|
||||
"post": gcsql.Post{},
|
||||
}, writer, "text/html"); err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Error minifying page template: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("template", "banpage").
|
||||
Msg("Failed minifying template")
|
||||
serverutil.ServeErrorPage(writer, "Error minifying page template: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
|
||||
"github.com/gochan-org/gochan/pkg/building"
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gctemplates"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
|
@ -49,7 +48,8 @@ func ServeCaptcha(writer http.ResponseWriter, request *http.Request) {
|
|||
}
|
||||
var err error
|
||||
if err = request.ParseForm(); err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog, "Error parsing request form: ", err.Error()))
|
||||
gcutil.LogError(err).Msg("Failed parsing request form")
|
||||
serverutil.ServeErrorPage(writer, "Error parsing request form: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -105,8 +105,9 @@ func ServeCaptcha(writer http.ResponseWriter, request *http.Request) {
|
|||
}
|
||||
}
|
||||
if err = serverutil.MinifyTemplate(gctemplates.Captcha, captchaStruct, writer, "text/html"); err != nil {
|
||||
fmt.Fprint(writer,
|
||||
gclog.Print(gclog.LErrorLog, "Error executing captcha template: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("template", "captcha").Send()
|
||||
fmt.Fprint(writer, "Error executing captcha template: ", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
|
||||
"github.com/frustra/bbcode"
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -90,10 +90,14 @@ func FormatMessage(message string, boardDir string) template.HTML {
|
|||
var boardIDFound bool
|
||||
|
||||
if boardDir, boardIDFound, err = gcsql.GetBoardFromPostID(postID); err != nil {
|
||||
gclog.Print(gclog.LErrorLog, "Error getting board dir for backlink: ", err.Error())
|
||||
gcutil.LogError(err).
|
||||
Int("postid", postID).
|
||||
Msg("Error getting board dir for backlink")
|
||||
}
|
||||
if linkParent, err = gcsql.GetThreadIDZeroIfTopPost(postID); err != nil {
|
||||
gclog.Print(gclog.LErrorLog, "Error getting post parent for backlink: ", err.Error())
|
||||
gcutil.LogError(err).
|
||||
Int("postid", postID).
|
||||
Msg("Error getting post parent for backlink")
|
||||
}
|
||||
|
||||
// get post board dir
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
"github.com/disintegration/imaging"
|
||||
"github.com/gochan-org/gochan/pkg/building"
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gctemplates"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
|
@ -30,7 +29,6 @@ import (
|
|||
|
||||
const (
|
||||
yearInSeconds = 31536000
|
||||
errStdLogs = gclog.LErrorLog | gclog.LStdLog
|
||||
)
|
||||
|
||||
// MakePost is called when a user accesses /post. Parse form data, then insert and build
|
||||
|
@ -48,14 +46,17 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
http.Redirect(writer, request, systemCritical.WebRoot, http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
post.IP = gcutil.GetRealIP(request)
|
||||
post.ParentID, _ = strconv.Atoi(request.FormValue("threadid"))
|
||||
post.BoardID, _ = strconv.Atoi(request.FormValue("boardid"))
|
||||
var postBoard gcsql.Board
|
||||
postBoard, err := gcsql.GetBoardFromID(post.BoardID)
|
||||
if err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Error getting board info: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Int("boardid", post.BoardID).
|
||||
Str("IP", post.IP).
|
||||
Msg("Error getting board info")
|
||||
serverutil.ServeErrorPage(writer, "Error getting board info: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -88,8 +89,11 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
post.MessageText = strings.Trim(request.FormValue("postmsg"), "\r\n")
|
||||
|
||||
if maxMessageLength, err = gcsql.GetMaxMessageLength(post.BoardID); err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Error getting board info: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Int("boardid", post.BoardID).
|
||||
Str("IP", post.IP).
|
||||
Msg("Error getting board info")
|
||||
serverutil.ServeErrorPage(writer, "Error getting board info: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -99,8 +103,11 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
}
|
||||
|
||||
if post.MessageText, err = ApplyWordFilters(post.MessageText, postBoard.Dir); err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("IP", post.IP).
|
||||
Str("boardDir", postBoard.Dir).
|
||||
Msg("Error applying wordfilters")
|
||||
serverutil.ServeErrorPage(writer, "Error formatting post: "+err.Error())
|
||||
gclog.Print(gclog.LErrorLog, "Error applying wordfilters: ", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -128,7 +135,6 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
MaxAge: yearInSeconds,
|
||||
})
|
||||
|
||||
post.IP = gcutil.GetRealIP(request)
|
||||
post.Timestamp = time.Now()
|
||||
// post.PosterAuthority = getStaffRank(request)
|
||||
post.Bumped = time.Now()
|
||||
|
@ -137,21 +143,31 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
|
||||
//post has no referrer, or has a referrer from a different domain, probably a spambot
|
||||
if !serverutil.ValidReferer(request) {
|
||||
gclog.Print(gclog.LAccessLog, "Rejected post from possible spambot @ "+post.IP)
|
||||
gcutil.LogWarning().
|
||||
Str("spam", "badReferer").
|
||||
Str("IP", post.IP).
|
||||
Msg("Rejected post from possible spambot")
|
||||
return
|
||||
}
|
||||
|
||||
switch serverutil.CheckPostForSpam(post.IP, request.Header["User-Agent"][0], request.Referer(),
|
||||
post.Name, post.Email, post.MessageText) {
|
||||
akismetResult := serverutil.CheckPostForSpam(
|
||||
post.IP, request.Header.Get("User-Agent"), request.Referer(),
|
||||
post.Name, post.Email, post.MessageText,
|
||||
)
|
||||
logEvent := gcutil.LogInfo().
|
||||
Str("User-Agent", request.Header.Get("User-Agent")).
|
||||
Str("IP", post.IP)
|
||||
switch akismetResult {
|
||||
case "discard":
|
||||
logEvent.Str("akismet", "discard").Send()
|
||||
serverutil.ServeErrorPage(writer, "Your post looks like spam.")
|
||||
gclog.Print(gclog.LAccessLog, "Akismet recommended discarding post from: "+post.IP)
|
||||
return
|
||||
case "spam":
|
||||
logEvent.Str("akismet", "spam").Send()
|
||||
serverutil.ServeErrorPage(writer, "Your post looks like spam.")
|
||||
gclog.Print(gclog.LAccessLog, "Akismet suggested post is spam from "+post.IP)
|
||||
return
|
||||
default:
|
||||
logEvent.Discard()
|
||||
}
|
||||
|
||||
postDelay, _ := gcsql.SinceLastPost(post.IP)
|
||||
|
@ -167,8 +183,11 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
|
||||
banStatus, err := getBannedStatus(request)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Error getting banned status: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("IP", post.IP).
|
||||
Fields(gcutil.ParseName(formName)).
|
||||
Msg("Error getting banned status")
|
||||
serverutil.ServeErrorPage(writer, "Error getting banned status: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -184,8 +203,9 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
"ban": banStatus,
|
||||
"banBoards": boards[post.BoardID-1].Dir,
|
||||
}, writer, "text/html"); err != nil {
|
||||
serverutil.ServeErrorPage(writer,
|
||||
gclog.Print(gclog.LErrorLog, "Error minifying page: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("building", "minifier").Send()
|
||||
serverutil.ServeErrorPage(writer, "Error minifying page: "+err.Error())
|
||||
return
|
||||
}
|
||||
writer.Write(banpageBuffer.Bytes())
|
||||
|
@ -217,13 +237,16 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
serverutil.ServeErrorPage(writer, "Post must contain a message if no image is uploaded.")
|
||||
return
|
||||
}
|
||||
gclog.Printf(gclog.LAccessLog,
|
||||
"Receiving post from %s, referred from: %s", post.IP, request.Referer())
|
||||
gcutil.LogInfo().
|
||||
Str("post", "referred").
|
||||
Str("referredFrom", request.Referer()).
|
||||
Str("IP", post.IP)
|
||||
} else {
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
serverutil.ServeErrorPage(writer,
|
||||
gclog.Print(gclog.LErrorLog, "Error while trying to read file: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("upload", "read").Send()
|
||||
serverutil.ServeErrorPage(writer, "Error while trying to read file: "+err.Error())
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
@ -244,6 +267,9 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
var _board = gcsql.Board{}
|
||||
err = _board.PopulateData(gcutil.HackyStringToInt(request.FormValue("boardid")))
|
||||
if err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("IP", post.IP).
|
||||
Str("posting", "updateBoard").Send()
|
||||
serverutil.ServeErrorPage(writer, "Server error: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
@ -252,9 +278,12 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
thumbPath = path.Join(systemCritical.DocumentRoot, boardDir, "thumb", strings.Replace(post.Filename, "."+ext, "t."+thumbExt, -1))
|
||||
catalogThumbPath = path.Join(systemCritical.DocumentRoot, boardDir, "thumb", strings.Replace(post.Filename, "."+ext, "c."+thumbExt, -1))
|
||||
|
||||
if err = ioutil.WriteFile(filePath, data, 0777); err != nil {
|
||||
gclog.Printf(gclog.LErrorLog, "Couldn't write file %q: %s", post.Filename, err.Error())
|
||||
serverutil.ServeErrorPage(writer, `Couldn't write file "`+post.FilenameOriginal+`"`)
|
||||
if err = ioutil.WriteFile(filePath, data, 0644); err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("posting", "upload").
|
||||
Str("IP", post.IP).
|
||||
Str("filename", post.Filename).Send()
|
||||
serverutil.ServeErrorPage(writer, fmt.Sprintf("Couldn't write file %q", post.FilenameOriginal))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -262,32 +291,48 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
post.FileChecksum = fmt.Sprintf("%x", md5.Sum(data))
|
||||
|
||||
if ext == "webm" || ext == "mp4" {
|
||||
gclog.Printf(gclog.LAccessLog, "Receiving post with video: %s from %s, referrer: %s",
|
||||
handler.Filename, post.IP, request.Referer())
|
||||
gcutil.LogInfo().
|
||||
Str("post", "withVideo").
|
||||
Str("IP", post.IP).
|
||||
Str("filename", handler.Filename).
|
||||
Str("referer", request.Referer()).Send()
|
||||
if post.ParentID == 0 {
|
||||
if err := createVideoThumbnail(filePath, thumbPath, boardConfig.ThumbWidth); err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Error creating video thumbnail: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("filePath", filePath).
|
||||
Str("thumbPath", thumbPath).
|
||||
Int("thumbWidth", boardConfig.ThumbWidth).
|
||||
Msg("Error creating video thumbnail")
|
||||
serverutil.ServeErrorPage(writer, "Error creating video thumbnail: "+err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := createVideoThumbnail(filePath, thumbPath, boardConfig.ThumbWidthReply); err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Error creating video thumbnail: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("filePath", filePath).
|
||||
Str("thumbPath", thumbPath).
|
||||
Int("thumbWidth", boardConfig.ThumbWidthReply).
|
||||
Msg("Error creating video thumbnail for reply")
|
||||
serverutil.ServeErrorPage(writer, "Error creating video thumbnail: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := createVideoThumbnail(filePath, catalogThumbPath, boardConfig.ThumbWidthCatalog); err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Error creating video thumbnail: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("filePath", filePath).
|
||||
Str("thumbPath", thumbPath).
|
||||
Int("thumbWidth", boardConfig.ThumbWidthCatalog).
|
||||
Msg("Error creating video thumbnail for catalog")
|
||||
serverutil.ServeErrorPage(writer, "Error creating video thumbnail: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
outputBytes, err := exec.Command("ffprobe", "-v", "quiet", "-show_format", "-show_streams", filePath).CombinedOutput()
|
||||
if err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Error getting video info: ", err.Error()))
|
||||
gcutil.LogError(err).Msg("Error getting video info")
|
||||
|
||||
serverutil.ServeErrorPage(writer, "Error getting video info: "+err.Error())
|
||||
return
|
||||
}
|
||||
if outputBytes != nil {
|
||||
|
@ -318,15 +363,17 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
img, err := imaging.Open(filePath)
|
||||
if err != nil {
|
||||
os.Remove(filePath)
|
||||
gclog.Printf(gclog.LErrorLog, "Couldn't open uploaded file %q: %s", post.Filename, err.Error())
|
||||
gcutil.LogError(err).
|
||||
Str("filePath", filePath).Send()
|
||||
serverutil.ServeErrorPage(writer, "Upload filetype not supported")
|
||||
return
|
||||
}
|
||||
// Get image filesize
|
||||
stat, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Couldn't get image filesize: "+err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("filePath", filePath).Send()
|
||||
serverutil.ServeErrorPage(writer, "Couldn't get image filesize: "+err.Error())
|
||||
return
|
||||
}
|
||||
post.Filesize = int(stat.Size())
|
||||
|
@ -340,8 +387,11 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
}
|
||||
post.ThumbW, post.ThumbH = getThumbnailSize(post.ImageW, post.ImageH, boardDir, thumbType)
|
||||
|
||||
gclog.Printf(gclog.LAccessLog, "Receiving post with image: %q from %s, referrer: %s",
|
||||
handler.Filename, post.IP, request.Referer())
|
||||
gcutil.LogInfo().
|
||||
Str("post", "withFile").
|
||||
Str("IP", post.IP).
|
||||
Str("filename", handler.Filename).
|
||||
Str("referer", request.Referer()).Send()
|
||||
|
||||
if request.FormValue("spoiler") == "on" {
|
||||
// If spoiler is enabled, symlink thumbnail to spoiler image
|
||||
|
@ -364,16 +414,22 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
thumbnail = createImageThumbnail(img, boardDir, "op")
|
||||
catalogThumbnail = createImageThumbnail(img, boardDir, "catalog")
|
||||
if err = imaging.Save(catalogThumbnail, catalogThumbPath); err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Couldn't generate catalog thumbnail: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("thumbPath", catalogThumbPath).
|
||||
Str("IP", post.IP).
|
||||
Msg("Couldn't generate catalog thumbnail")
|
||||
serverutil.ServeErrorPage(writer, "Couldn't generate catalog thumbnail: "+err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
thumbnail = createImageThumbnail(img, boardDir, "reply")
|
||||
}
|
||||
if err = imaging.Save(thumbnail, thumbPath); err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Couldn't save thumbnail: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("thumbPath", thumbPath).
|
||||
Str("IP", post.IP).
|
||||
Msg("Couldn't generate catalog thumbnail")
|
||||
serverutil.ServeErrorPage(writer, "Couldn't save thumbnail: "+err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
@ -381,6 +437,10 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
post.ThumbW = img.Bounds().Max.X
|
||||
post.ThumbH = img.Bounds().Max.Y
|
||||
if err := syscall.Symlink(filePath, thumbPath); err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("thumbPath", thumbPath).
|
||||
Str("IP", post.IP).
|
||||
Msg("Couldn't generate catalog thumbnail")
|
||||
serverutil.ServeErrorPage(writer, "Couldn't create thumbnail: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
@ -388,8 +448,11 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
// Generate catalog thumbnail
|
||||
catalogThumbnail := createImageThumbnail(img, boardDir, "catalog")
|
||||
if err = imaging.Save(catalogThumbnail, catalogThumbPath); err != nil {
|
||||
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
|
||||
"Couldn't generate catalog thumbnail: ", err.Error()))
|
||||
gcutil.LogError(err).
|
||||
Str("thumbPath", catalogThumbPath).
|
||||
Str("IP", post.IP).
|
||||
Msg("Couldn't generate catalog thumbnail")
|
||||
serverutil.ServeErrorPage(writer, "Couldn't generate catalog thumbnail: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -398,13 +461,13 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
}
|
||||
|
||||
if err = gcsql.InsertPost(&post, emailCommand != "sage"); err != nil {
|
||||
gcutil.LogError(err).
|
||||
Str("sql", "postInsertion").Send()
|
||||
if post.Filename != "" {
|
||||
os.Remove(filePath)
|
||||
os.Remove(thumbPath)
|
||||
os.Remove(catalogThumbPath)
|
||||
}
|
||||
serverutil.ServeErrorPage(writer,
|
||||
gclog.Print(gclog.LErrorLog, "Error inserting post: ", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
)
|
||||
|
@ -37,21 +36,24 @@ func tempCleaner() {
|
|||
systemCritical := config.GetSystemCriticalConfig()
|
||||
fileSrc := path.Join(systemCritical.DocumentRoot, board.Dir, "src", post.FilenameOriginal)
|
||||
if err = os.Remove(fileSrc); err != nil {
|
||||
gclog.Printf(errStdLogs,
|
||||
"Error pruning temporary upload for %q: %s", fileSrc, err.Error())
|
||||
gcutil.LogError(err).
|
||||
Str("subject", "tempUpload").
|
||||
Str("filePath", fileSrc).Send()
|
||||
}
|
||||
|
||||
thumbSrc := gcutil.GetThumbnailPath("thread", fileSrc)
|
||||
if err = os.Remove(thumbSrc); err != nil {
|
||||
gclog.Printf(errStdLogs,
|
||||
"Error pruning temporary upload for %q: %s", thumbSrc, err.Error())
|
||||
gcutil.LogError(err).
|
||||
Str("subject", "tempUpload").
|
||||
Str("filePath", thumbSrc).Send()
|
||||
}
|
||||
|
||||
if post.ParentID == 0 {
|
||||
catalogSrc := gcutil.GetThumbnailPath("catalog", fileSrc)
|
||||
if err = os.Remove(catalogSrc); err != nil {
|
||||
gclog.Printf(errStdLogs,
|
||||
"Error pruning temporary upload for %s: %s", catalogSrc, err.Error())
|
||||
gcutil.LogError(err).
|
||||
Str("subject", "tempUpload").
|
||||
Str("filePath", catalogSrc).Send()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
)
|
||||
|
||||
|
@ -50,7 +49,8 @@ func shouldCreateThumbnail(imgPath string, imgWidth int, imgHeight int, thumbWid
|
|||
if ext == ".gif" {
|
||||
numFrames, err := numImageFrames(imgPath)
|
||||
if err != nil {
|
||||
gclog.Printf(gclog.LErrorLog, "Error processing %q: %s", imgPath, err.Error())
|
||||
gcutil.LogError(err).
|
||||
Str("imgPath", imgPath).Send()
|
||||
return true
|
||||
}
|
||||
if numFrames > 1 {
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -53,14 +53,16 @@ func CheckPostForSpam(userIP, userAgent, referrer, author, email, postContent st
|
|||
req, err := http.NewRequest("POST", "https://"+siteCfg.AkismetAPIKey+".rest.akismet.com/1.1/comment-check",
|
||||
strings.NewReader(data.Encode()))
|
||||
if err != nil {
|
||||
gclog.Print(gclog.LErrorLog, err.Error())
|
||||
gcutil.Logger().Err(err).
|
||||
Str("subject", "akismet").Send()
|
||||
return "other_failure"
|
||||
}
|
||||
req.Header.Set("User-Agent", "gochan/1.0 | Akismet/0.1")
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
gclog.Print(gclog.LErrorLog, err.Error())
|
||||
gcutil.Logger().Err(err).
|
||||
Str("subject", "akismet").Send()
|
||||
return "other_failure"
|
||||
}
|
||||
if resp.Body != nil {
|
||||
|
@ -68,19 +70,25 @@ func CheckPostForSpam(userIP, userAgent, referrer, author, email, postContent st
|
|||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
gclog.Print(gclog.LErrorLog, err.Error())
|
||||
gcutil.Logger().Err(err).
|
||||
Str("subject", "akismet").Send()
|
||||
return "other_failure"
|
||||
}
|
||||
gclog.Print(gclog.LErrorLog, "Response from Akismet: ", string(body))
|
||||
bodyStr := string(body)
|
||||
if config.GetDebugMode() {
|
||||
gcutil.Logger().Info().
|
||||
Str("subject", "akismet").
|
||||
Str("reponse", bodyStr)
|
||||
}
|
||||
|
||||
if string(body) == "true" {
|
||||
if bodyStr == "true" {
|
||||
if proTip, ok := resp.Header["X-akismet-pro-tip"]; ok && proTip[0] == "discard" {
|
||||
return "discard"
|
||||
}
|
||||
return "spam"
|
||||
} else if string(body) == "invalid" {
|
||||
} else if bodyStr == "invalid" {
|
||||
return "invalid"
|
||||
} else if string(body) == "false" {
|
||||
} else if bodyStr == "false" {
|
||||
return "ham"
|
||||
}
|
||||
}
|
||||
|
@ -89,15 +97,16 @@ func CheckPostForSpam(userIP, userAgent, referrer, author, email, postContent st
|
|||
|
||||
// ValidReferer checks to make sure that the incoming request is from the same domain (or if debug mode is enabled)
|
||||
func ValidReferer(request *http.Request) bool {
|
||||
systemCritical := config.GetSystemCriticalConfig()
|
||||
if systemCritical.DebugMode {
|
||||
if config.GetDebugMode() {
|
||||
return true
|
||||
}
|
||||
rURL, err := url.ParseRequestURI(request.Referer())
|
||||
referer := request.Referer()
|
||||
rURL, err := url.ParseRequestURI(referer)
|
||||
if err != nil {
|
||||
gclog.Println(gclog.LAccessLog|gclog.LErrorLog, "Error parsing referer URL:", err.Error())
|
||||
gcutil.Logger().Err(err).
|
||||
Str("referer", referer).
|
||||
Msg("Error parsing referer URL")
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.Index(rURL.Path, systemCritical.WebRoot) == 0
|
||||
return strings.Index(rURL.Path, config.GetSystemCriticalConfig().WebRoot) == 0
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gclog"
|
||||
"github.com/gochan-org/gochan/pkg/gctemplates"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
)
|
||||
|
@ -23,6 +22,9 @@ func ServeJSON(writer http.ResponseWriter, data map[string]interface{}) {
|
|||
func ServeError(writer http.ResponseWriter, err string, wantsJSON bool, data map[string]interface{}) {
|
||||
if wantsJSON {
|
||||
servedMap := data
|
||||
if servedMap == nil {
|
||||
servedMap = make(map[string]interface{})
|
||||
}
|
||||
if _, ok := servedMap["error"]; !ok {
|
||||
servedMap["error"] = err
|
||||
}
|
||||
|
@ -40,9 +42,8 @@ func ServeErrorPage(writer http.ResponseWriter, err string) {
|
|||
"siteConfig": config.GetSiteConfig(),
|
||||
"boardConfig": config.GetBoardConfig(""),
|
||||
"ErrorTitle": "Error :c",
|
||||
// "ErrorImage": "/error/lol 404.gif",
|
||||
"ErrorHeader": "Error",
|
||||
"ErrorText": err,
|
||||
"ErrorHeader": "Error",
|
||||
"ErrorText": err,
|
||||
}, writer, "text/html")
|
||||
}
|
||||
|
||||
|
@ -57,7 +58,10 @@ func ServeNotFound(writer http.ResponseWriter, request *http.Request) {
|
|||
} else {
|
||||
MinifyWriter(writer, errorPage, "text/html")
|
||||
}
|
||||
gclog.Printf(gclog.LAccessLog, "Error: 404 Not Found from %s @ %s", gcutil.GetRealIP(request), request.URL.Path)
|
||||
gcutil.Logger().Info().
|
||||
Str("access", request.URL.Path).
|
||||
Int("status", 404).
|
||||
Str("IP", gcutil.GetRealIP(request)).Send()
|
||||
}
|
||||
|
||||
// DeleteCookie deletes the given cookie if it exists. It returns true if it exists and false
|
||||
|
@ -72,3 +76,9 @@ func DeleteCookie(writer http.ResponseWriter, request *http.Request, cookieName
|
|||
http.SetCookie(writer, cookie)
|
||||
return true
|
||||
}
|
||||
|
||||
func IsRequestingJSON(request *http.Request) bool {
|
||||
request.ParseForm()
|
||||
field := request.Form["json"]
|
||||
return len(field) == 1 && (field[0] == "1" || field[0] == "true")
|
||||
}
|
||||
|
|
2
version
2
version
|
@ -1 +1 @@
|
|||
3.2.0
|
||||
3.3.0
|
Loading…
Add table
Add a link
Reference in a new issue