cerca

lean forum software (pmc local branch)
Log | Files | Refs | README | LICENSE

commit 7913fe3eaf88810973278501d1578dc197c3ecbe
parent 31cbdc01e6051987f3ad5133b96590a7ec11ff55
Author: cblgh <cblgh@cblgh.org>
Date:   Thu, 20 Oct 2022 10:57:07 +0200

cleanup & decide on initial structure for content files

Diffstat:
Mdefaults/sample-config.toml | 12++++++------
Mdefaults/sample-logo.html | 1-
Mrun.go | 2+-
Mserver/server.go | 62++++++++++++++++++++++++++------------------------------------
Mtypes/types.go | 12+-----------
Mutil/util.go | 30++++++++++++++++++++++++------
6 files changed, 58 insertions(+), 61 deletions(-)

diff --git a/defaults/sample-config.toml b/defaults/sample-config.toml @@ -1,10 +1,10 @@ [general] name = "" -conduct_url = "" # optional + recommended: if omitted, the checkboxes in registration relating to CoC will be hidden -language = "" # Swedish, English +conduct_url = "" # optional + recommended: if omitted, the CoC checkboxes in /register will be hidden +language = "" # Swedish, English. contributions for more translations welcome! [documents] -logo = "logo.html" -about = "about.md" -rules = "rules.md" -verification_explanation = "verification-instructions.md" +logo = "content/logo.html" # can contain emoji, <img>, <svg> etc. see defaults/sample-logo.html in repo for instructions +about = "content/about.md" +rules = "content/rules.md" +verification_explanation = "content/verification-instructions.md" diff --git a/defaults/sample-logo.html b/defaults/sample-logo.html @@ -5,7 +5,6 @@ <path d="M125,65 A60,60 0 0,1 185,125 L125,125 Z M185,245 A60,60 0 0,1 125,185 L185,185 Z M65,65 A60,60 0 0,1 125,125 L65,125 Z M65,245 A60,60 0 0,0 125,185 L65,185 Z M245,65 A60,60 0 0,0 185,125 L245,125 Z M245,245 A60,60 0 0,1 185,185 L245,185 Z"/> </g> </svg> ---> <!-- here's how to have a png logo it's important with the path starting with /assets/, and that the loaded asset is inside dir <cercaroot>/html/assets diff --git a/run.go b/run.go @@ -43,7 +43,7 @@ func main() { flag.StringVar(&allowlistLocation, "allowlist", "", "domains which can be used to read verification codes from during registration") flag.StringVar(&sessionKey, "authkey", "", "session cookies authentication key") flag.StringVar(&configPath, "config", "cerca.toml", "config and settings file containing cerca's customizations") - flag.StringVar(&dataDir, "data", "./data", "directory where cerca will dump its files (database and customizable documents)") + flag.StringVar(&dataDir, "data", "./data", "directory where cerca will dump its database") flag.Parse() if len(sessionKey) == 0 { complain("please pass a random session auth key with --authkey") diff --git a/server/server.go b/server/server.go @@ -27,21 +27,6 @@ import ( "github.com/carlmjohnson/requests" ) -/* 2022-09-20: customizable stuff -* CommunityName -* CommunityLogo -* CommunityLink -* ForumName - */ - -// TODO (2022-09-20): make verification instructions another md file to load, pass path from config -/* -* pass in: -* registration rules -* verification instructions -* code of conduct link - */ - /* TODO (2022-01-03): include csrf token via gorilla, or w/e, when rendering */ type TemplateData struct { @@ -104,7 +89,6 @@ type RequestHandler struct { } var developing bool -var config types.Config func dump(err error) { if developing { @@ -218,6 +202,9 @@ func (h RequestHandler) renderView(res http.ResponseWriter, viewName string, dat data.Title = strings.ReplaceAll(viewName, "-", " ") } + if h.config.Community.Name != "" { + data.ForumName = h.config.Community.Name + } if data.ForumName == "" { data.ForumName = "Forum" } @@ -734,7 +721,6 @@ func (h RequestHandler) DeletePostRoute(res http.ResponseWriter, req *http.Reque func Serve(allowlist []string, sessionKey string, isdev bool, dir string, conf types.Config) { port := ":8272" - config = conf if isdev { developing = true @@ -763,7 +749,6 @@ func Serve(allowlist []string, sessionKey string, isdev bool, dir string, conf t type CercaForum struct { http.ServeMux Directory string - Files map[string][]byte } func (u *CercaForum) directory() string { @@ -778,41 +763,46 @@ func (u *CercaForum) directory() string { return u.Directory } -func (c *CercaForum) loadFile(key, filepath, defaultContent string) { - _, err := util.CreateIfNotExist(filepath, defaultContent) - util.Check(err, "create if not exist (%s) %s", key, filepath) - c.Files[key], err = os.ReadFile(filepath) - util.Check(err, "read %s", filepath) -} - // NewServer sets up a new CercaForum object. Always use this to initialize // new CercaForum objects. Pass the result to http.Serve() with your choice // of net.Listener. -func NewServer(allowlist []string, sessionKey, dir string, conf types.Config) (*CercaForum, error) { +func NewServer(allowlist []string, sessionKey, dir string, config types.Config) (*CercaForum, error) { s := &CercaForum{ ServeMux: http.ServeMux{}, Directory: dir, - Files: make(map[string][]byte), } dbpath := filepath.Join(s.directory(), "forum.db") db := database.InitDB(dbpath) - // TODO (2022-10-18): introduce step where if config document path is empty => config.Documents.<path> = - // filepath.Join(s.directory(), <name>) + // TODO? (2022-10-18): introduce step where if config document path is empty => + // cconfig.Documents.<path> = filepath.Join(s.directory(), <name>) // load the documents specified in the config // iff document doesn't exist, dump a default document where it should be and read that - s.loadFile("about", config.Documents.AboutPath, defaults.DEFAULT_ABOUT) - s.loadFile("rules", config.Documents.RegisterRulesPath, defaults.DEFAULT_RULES) - s.loadFile("verification-instructions", config.Documents.VerificationExplanationPath, defaults.DEFAULT_VERIFICATION) - s.loadFile("logo", config.Documents.LogoPath, defaults.DEFAULT_LOGO) - - /* note: be careful with trailing slashes; go's default handler is a bit sensitive */ + type triple struct { key, docpath, content string } + triples := []triple{ + {"about", config.Documents.AboutPath, defaults.DEFAULT_ABOUT}, + {"rules", config.Documents.RegisterRulesPath, defaults.DEFAULT_RULES}, + {"verification-instructions", config.Documents.VerificationExplanationPath, defaults.DEFAULT_VERIFICATION}, + {"logo", config.Documents.LogoPath, defaults.DEFAULT_LOGO}, + } + + files := make(map[string][]byte) + for _, t := range triples { + data, err := util.LoadFile(t.key, t.docpath, t.content) + if err != nil { + return s, err + } + files[t.key] = data + } + + // TODO (2022-10-20): when receiving user request, inspect user-agent language and change language from server default translator := i18n.Init(config.Community.Language) templates := template.Must(generateTemplates(config, translator)) - handler := RequestHandler{&db, session.New(sessionKey, developing), allowlist, s.Files, config, translator, templates} + handler := RequestHandler{&db, session.New(sessionKey, developing), allowlist, files, config, translator, templates} + /* note: be careful with trailing slashes; go's default handler is a bit sensitive */ // TODO (2022-01-10): introduce middleware to make sure there is never an issue with trailing slashes s.ServeMux.HandleFunc("/reset/", handler.ResetPasswordRoute) s.ServeMux.HandleFunc("/about", handler.AboutRoute) diff --git a/types/types.go b/types/types.go @@ -1,11 +1,6 @@ package types type Config struct { - // use as: - // config.Files["about"] -> about markdown - // config.Files["rules"] -> rules explanation markdown - // config.Files["verification"] -> verification explanation - Community struct { Name string `json:"name"` ConductLink string `json:"conduct_url"` @@ -16,19 +11,14 @@ type Config struct { LogoPath string `json:"logo"` AboutPath string `json:"about"` RegisterRulesPath string `json:"rules"` - VerificationExplanationPath string `json:"verification_explanation"` + VerificationExplanationPath string `json:"verification_instructions"` } `json:"documents"` } /* -config.Community.Name -config.Community.Link -config.Community.ConductLink - config structure ["General"] Name = "Merveilles" -Link = "https://wiki.xxiivv.com/site/merveilles.html" ConductLink = "https://github.com/merveilles/Resources/blob/master/CONDUCT.md" diff --git a/util/util.go b/util/util.go @@ -8,6 +8,7 @@ import ( "encoding/json" "errors" "io/fs" + "path/filepath" "fmt" "html/template" "log" @@ -154,12 +155,16 @@ func Capitalize(s string) string { return strings.ToUpper(string(s[0])) + s[1:] } -func CreateIfNotExist(filepath, content string) (bool, error) { - _, err := os.Stat(filepath) +func CreateIfNotExist(docpath, content string) (bool, error) { + err := os.MkdirAll(filepath.Dir(docpath), 0750) + if err != nil { + return false, err + } + _, err = os.Stat(docpath) if err != nil { // if the file doesn't exist, create it if errors.Is(err, fs.ErrNotExist) { - err = os.WriteFile(filepath, []byte(content), 0777) + err = os.WriteFile(docpath, []byte(content), 0777) if err != nil { return false, err } @@ -172,9 +177,6 @@ func CreateIfNotExist(filepath, content string) (bool, error) { return false, nil } -// TODO (2022-09-21): -// * DONE go:embed sample-config.toml ---> defaults.DEFAULT_<x> -// * util.checkFileExists(path, mockContents) func ReadConfig(confpath string) types.Config { ed := Describe("config") _, err := CreateIfNotExist(confpath, defaults.DEFAULT_CONFIG) @@ -191,3 +193,19 @@ func ReadConfig(confpath string) types.Config { return conf } + +func LoadFile(key, docpath, defaultContent string) ([]byte, error) { + ed := Describe("load file") + _, err := CreateIfNotExist(docpath, defaultContent) + err = ed.Eout(err, "create if not exist (%s) %s", key, docpath) + if err != nil { + return nil, err + } + data, err := os.ReadFile(docpath) + err = ed.Eout(err, "read %s", docpath) + if err != nil { + return nil, err + } + return data, nil +} +