diff --git a/config/setup.go b/config/setup.go index 37d83cf..f1478a9 100644 --- a/config/setup.go +++ b/config/setup.go @@ -1,219 +1,219 @@ package config import ( "fmt" "github.com/fatih/color" "github.com/manifoldco/promptui" "github.com/mitchellh/go-wordwrap" "strconv" ) func Configure() error { c, err := Load() var action string if err != nil { fmt.Println("No configuration yet. Creating new.") c = New() action = "generate" } else { fmt.Println("Configuration loaded.") action = "update" } title := color.New(color.Bold, color.BgGreen).PrintlnFunc() intro := color.New(color.Bold, color.FgWhite).PrintlnFunc() fmt.Println() intro(" ✍ Write Freely Configuration ✍") fmt.Println() fmt.Println(wordwrap.WrapString(" This quick configuration process will "+action+" the application's config file, "+FileName+".\n\n It validates your input along the way, so you can be sure any future errors aren't caused by a bad configuration. If you'd rather configure your server manually, instead run: writefreely --create-config and edit that file.", 75)) fmt.Println() title(" Server setup ") fmt.Println() tmpls := &promptui.PromptTemplates{ Success: "{{ . | bold | faint }}: ", } selTmpls := &promptui.SelectTemplates{ - Selected: fmt.Sprintf(`{{.Label}} {{ . | faint }}`, promptui.IconGood), + Selected: fmt.Sprintf(`{{.Label}} {{ . | faint }}`), } prompt := promptui.Prompt{ Templates: tmpls, Label: "Local port", Validate: validatePort, Default: fmt.Sprintf("%d", c.Server.Port), } port, err := prompt.Run() if err != nil { return err } c.Server.Port, _ = strconv.Atoi(port) // Ignore error, as we've already validated number fmt.Println() title(" Database setup ") fmt.Println() prompt = promptui.Prompt{ Templates: tmpls, Label: "Username", Validate: validateNonEmpty, Default: c.Database.User, } c.Database.User, err = prompt.Run() if err != nil { return err } prompt = promptui.Prompt{ Templates: tmpls, Label: "Password", Validate: validateNonEmpty, Default: c.Database.Password, Mask: '*', } c.Database.Password, err = prompt.Run() if err != nil { return err } prompt = promptui.Prompt{ Templates: tmpls, Label: "Database name", Validate: validateNonEmpty, Default: c.Database.Database, } c.Database.Database, err = prompt.Run() if err != nil { return err } prompt = promptui.Prompt{ Templates: tmpls, Label: "Host", Validate: validateNonEmpty, Default: c.Database.Host, } c.Database.Host, err = prompt.Run() if err != nil { return err } prompt = promptui.Prompt{ Templates: tmpls, Label: "Port", Validate: validatePort, Default: fmt.Sprintf("%d", c.Database.Port), } dbPort, err := prompt.Run() if err != nil { return err } c.Database.Port, _ = strconv.Atoi(dbPort) // Ignore error, as we've already validated number fmt.Println() title(" App setup ") fmt.Println() selPrompt := promptui.Select{ Templates: selTmpls, Label: "Site type", Items: []string{"Single user blog", "Multi-user instance"}, } _, usersType, err := selPrompt.Run() if err != nil { return err } c.App.SingleUser = usersType == "Single user" // TODO: if c.App.SingleUser { // prompt for username // prompt for password // create blog siteNameLabel := "Instance name" if c.App.SingleUser { siteNameLabel = "Blog name" } prompt = promptui.Prompt{ Templates: tmpls, Label: siteNameLabel, Validate: validateNonEmpty, Default: c.App.SiteName, } c.App.SiteName, err = prompt.Run() if err != nil { return err } prompt = promptui.Prompt{ Templates: tmpls, Label: "Public URL", Validate: validateDomain, Default: c.App.Host, } c.App.Host, err = prompt.Run() if err != nil { return err } if !c.App.SingleUser { selPrompt = promptui.Select{ Templates: selTmpls, Label: "Registration", Items: []string{"Open", "Closed"}, } _, regType, err := selPrompt.Run() if err != nil { return err } c.App.OpenRegistration = regType == "Open" prompt = promptui.Prompt{ Templates: tmpls, Label: "Max blogs per user", Default: fmt.Sprintf("%d", c.App.MaxBlogs), } maxBlogs, err := prompt.Run() if err != nil { return err } c.App.MaxBlogs, _ = strconv.Atoi(maxBlogs) // Ignore error, as we've already validated number } selPrompt = promptui.Select{ Templates: selTmpls, Label: "Federation", Items: []string{"Enabled", "Disabled"}, } _, fedType, err := selPrompt.Run() if err != nil { return err } c.App.Federation = fedType == "Enabled" if c.App.Federation { selPrompt = promptui.Select{ Templates: selTmpls, Label: "Federation usage stats", Items: []string{"Public", "Private"}, } _, fedStatsType, err := selPrompt.Run() if err != nil { return err } c.App.PublicStats = fedStatsType == "Public" selPrompt = promptui.Select{ Templates: selTmpls, Label: "Instance metadata privacy", Items: []string{"Public", "Private"}, } _, fedStatsType, err = selPrompt.Run() if err != nil { return err } c.App.Private = fedStatsType == "Private" } return Save(c) } diff --git a/nodeinfo.go b/nodeinfo.go index fc36c34..a0e7a63 100644 --- a/nodeinfo.go +++ b/nodeinfo.go @@ -1,87 +1,86 @@ package writefreely import ( - "fmt" "github.com/writeas/go-nodeinfo" "github.com/writeas/web-core/log" "github.com/writeas/writefreely/config" ) type nodeInfoResolver struct { cfg *config.Config db *datastore } func nodeInfoConfig(cfg *config.Config) *nodeinfo.Config { name := cfg.App.SiteName return &nodeinfo.Config{ BaseURL: cfg.App.Host, InfoURL: "/api/nodeinfo", Metadata: nodeinfo.Metadata{ NodeName: name, NodeDescription: "Minimal, federated blogging platform.", Private: cfg.App.Private, Software: nodeinfo.SoftwareMeta{ HomePage: softwareURL, GitHub: "https://github.com/writeas/writefreely", Follow: "https://writing.exchange/@write_as", }, }, Protocols: []nodeinfo.NodeProtocol{ nodeinfo.ProtocolActivityPub, }, Services: nodeinfo.Services{ Inbound: []nodeinfo.NodeService{}, Outbound: []nodeinfo.NodeService{}, }, Software: nodeinfo.SoftwareInfo{ Name: serverSoftware, Version: softwareVer, }, } } func (r nodeInfoResolver) IsOpenRegistration() (bool, error) { return r.cfg.App.OpenRegistration, nil } func (r nodeInfoResolver) Usage() (nodeinfo.Usage, error) { var collCount, postCount, activeHalfYear, activeMonth int err := r.db.QueryRow(`SELECT COUNT(*) FROM collections`).Scan(&collCount) if err != nil { collCount = 0 } err = r.db.QueryRow(`SELECT COUNT(*) FROM posts`).Scan(&postCount) if err != nil { log.Error("Unable to fetch post counts: %v", err) } if r.cfg.App.PublicStats { // Display bi-yearly / monthly stats - err = r.db.QueryRow(fmt.Sprintf(`SELECT COUNT(*) FROM ( + err = r.db.QueryRow(`SELECT COUNT(*) FROM ( SELECT DISTINCT collection_id FROM posts INNER JOIN collections c ON collection_id = c.id WHERE collection_id IS NOT NULL - AND updated > DATE_SUB(NOW(), INTERVAL 6 MONTH)) co`, CollPublic)).Scan(&activeHalfYear) + AND updated > DATE_SUB(NOW(), INTERVAL 6 MONTH)) co`).Scan(&activeHalfYear) - err = r.db.QueryRow(fmt.Sprintf(`SELECT COUNT(*) FROM ( + err = r.db.QueryRow(`SELECT COUNT(*) FROM ( SELECT DISTINCT collection_id FROM posts INNER JOIN FROM collections c ON collection_id = c.id WHERE collection_id IS NOT NULL - AND updated > DATE_SUB(NOW(), INTERVAL 1 MONTH)) co`, CollPublic)).Scan(&activeMonth) + AND updated > DATE_SUB(NOW(), INTERVAL 1 MONTH)) co`).Scan(&activeMonth) } return nodeinfo.Usage{ Users: nodeinfo.UsageUsers{ Total: collCount, ActiveHalfYear: activeHalfYear, ActiveMonth: activeMonth, }, LocalPosts: postCount, }, nil }