Files
comitium/core/core.go
T

174 lines
5.4 KiB
Go

// Copyright (C) 2021 nytpu
// SPDX-License-Identifier: AGPL-3.0-only
// For more license details, see LICENSE or <https://www.gnu.org/licenses/agpl-3.0.html>.
// This file incorporates code from makeworld's amfora:
// https://github.com/makeworld-the-better-one/amfora/blob/master/subscriptions/structs.go
// Original source is Copyright 2020 makeworld and is licensed under the
// terms of the GNU General Public License Version 3.0.
// https://www.gnu.org/licenses/gpl-3.0-standalone.html
// package core implements the structs and other necessary resources for storing
// and managing feeds and pages
package core
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sync"
"time"
"golang.nytpu.com/comitium/localizations"
)
// Global instance of FullData
var Data = FullData{
FeedsMu: &sync.RWMutex{},
PagesMu: &sync.RWMutex{},
// Maps are created in init()
}
// the Header and Footer of feeds.gmi.
// set in Init, either to the contents of header.gmi or to the default string
var (
Header string
Footer string
)
// Timeout for requests
var Timeout int
// Number of days of history to keep
var HistoryDays time.Time
var Localizer *localizations.Localizer
// have to do this instead of the automatically called init() because
// initialization stuff requires parsing of command line args first
func Init(dataPath string, localizer *localizations.Localizer, days int) error {
Localizer = localizer
// initialize comitium.json & data maps
f, err := os.Open(filepath.Join(dataPath, "comitium.json"))
if err == nil {
// File exists and could be opened
fi, err := f.Stat()
if err == nil && fi.Size() > 0 {
// File is not empty
jsonBytes, err := ioutil.ReadAll(f)
f.Close()
if err != nil {
return fmt.Errorf(localizer.Get("errors.read_file_error", &localizations.Replacements{"file": "comitium.json", "error": err}))
}
err = json.Unmarshal(jsonBytes, &Data)
if err != nil {
return fmt.Errorf(localizer.Get("errors.corrupt_json_error", &localizations.Replacements{"error": err}))
}
}
f.Close()
} else if !os.IsNotExist(err) {
// There's an error opening the file, but it's not bc is doesn't exist
return fmt.Errorf(localizer.Get("errors.read_file_error", &localizations.Replacements{"file": "comitium.json", "error": err}))
}
if Data.Feeds == nil {
Data.Feeds = make(map[string]*Feed)
}
if Data.Pages == nil {
Data.Pages = make(map[string]*Page)
}
// initialize header.gmi and footer.gmi
Header, err = getHeaderFooter(
dataPath, "header.gmi",
TranslateString("# %s\n\n", "files.feed_default_header", nil), "",
)
if err != nil {
return err
}
footer, err := getHeaderFooter(
dataPath, "footer.gmi", "---\n\n",
TranslateString(
"=> https://git.nytpu.com/comitium/about/ %s\n",
"files.comitium_footer",
&localizations.Replacements{"version": Version, "commit": Commit},
),
)
if err != nil {
return err
}
Footer = fmt.Sprintf("\n\n%s", footer)
HistoryDays = time.Now().AddDate(0, 0, -days)
Timeout = 10
return nil
}
// gets a header or footer from a datapath, or uses a default. extraString is a
// string to append no matter what
func getHeaderFooter(dataPath, filename, defaultString, extraString string) (main string, err error) {
f, err := os.Open(filepath.Join(dataPath, filename))
if err == nil {
fi, err := f.Stat()
if err == nil && fi.Size() > 0 {
headerBytes, err := ioutil.ReadAll(f)
f.Close()
if err != nil {
return "", fmt.Errorf(Localizer.Get("errors.read_file_error", &localizations.Replacements{"file": "header.gmi", "error": err}))
}
main = string(headerBytes)
}
f.Close()
} else if !os.IsNotExist(err) {
return "", fmt.Errorf(Localizer.Get("errors.read_file_error", &localizations.Replacements{"file": "header.gmi", "error": err}))
}
if main == "" {
main = defaultString
}
return main + extraString, nil
}
// WriteJSON writes a FullData to disk
func (d *FullData) WriteJSON(dataPath string) error {
// use .new so if there's a corruption when writing the old file is intact
path := filepath.Join(dataPath, "comitium.json.new")
d.RLock()
jsonBytes, err := json.MarshalIndent(&d, "", "\t")
d.RUnlock()
if err != nil {
return err
}
err = ioutil.WriteFile(path, jsonBytes, 0666)
if err != nil {
return err
}
// if full write was successful then overwrite original
err = os.Rename(path, filepath.Join(dataPath, "comitium.json"))
return err
}
// TranslateString takes a template string, a localizer tag, and a localizer
// replacement set and will get the localized string from the localizer tag,
// replace everything using the localizer replacements, and then substitute that
// string into the template string.
func TranslateString(template, localizerTag string, replacements *localizations.Replacements) string {
return TranslateStringLocalizer(Localizer, template, localizerTag, replacements)
}
// TranslateStringLocalizer takes a localizations.Localizer, template string, a
// localizer tag, and a localizer replacement set and will get the localized
// string from the localizer tag, replace everything using the localizer
// replacements, and then substitute that string into the template string.
// Should not be used once core.Init() is called, instead use TranslateString().
func TranslateStringLocalizer(localizer *localizations.Localizer, template, localizerTag string, replacements *localizations.Replacements) string {
return fmt.Sprintf(template, localizer.Get(localizerTag, replacements))
}