This commit is contained in:
Jan-Lukas Else 2023-12-27 11:37:58 +01:00
parent 5220c497cf
commit ad7536034a
31 changed files with 247 additions and 245 deletions

View File

@ -13,7 +13,6 @@ import (
"errors"
"fmt"
"io"
"log"
"net/http"
"strings"
"time"
@ -157,7 +156,7 @@ func (a *goBlog) apCheckMentions(p *post) {
links, err := allLinksFromHTML(pr, a.fullPostURL(p))
_ = pr.CloseWithError(err)
if err != nil {
log.Println("Failed to extract links from post: " + err.Error())
a.error("ActivityPub: Failed to extract links from post", err)
return
}
apc := a.apHttpClients[p.Blog]
@ -486,12 +485,12 @@ func (a *goBlog) apUndelete(p *post) {
func (a *goBlog) apAccept(blogName string, blog *configBlog, follow *ap.Activity) {
newFollower := follow.Actor.GetLink()
log.Println("New follow request from follower id:", newFollower.String())
a.info("AcitivyPub: New follow request from follower", "id", newFollower.String())
// Get remote actor
follower, err := a.apGetRemoteActor(newFollower, blogName)
if err != nil || follower == nil {
// Couldn't retrieve remote actor info
log.Println("Failed to retrieve remote actor info:", newFollower)
a.error("ActivityPub: Failed to retrieve remote actor info", "actor", newFollower)
return
}
// Add or update follower
@ -529,7 +528,7 @@ func (a *goBlog) apSendProfileUpdates() {
func (a *goBlog) apSendToAllFollowers(blog string, activity *ap.Activity, mentions ...string) {
inboxes, err := a.db.apGetAllInboxes(blog)
if err != nil {
log.Println("Failed to retrieve follower inboxes:", err.Error())
a.error("ActivityPub: Failed to retrieve follower inboxes", "err", err)
return
}
for _, m := range mentions {
@ -583,7 +582,7 @@ func (a *goBlog) loadActivityPubPrivateKey() error {
if keyData, err := a.db.retrievePersistentCache("activitypub_key"); err == nil && keyData != nil {
privateKeyDecoded, _ := pem.Decode(keyData)
if privateKeyDecoded == nil {
log.Println("failed to decode cached private key")
a.error("ActivityPub: failed to decode cached private key")
// continue
} else {
key, err := x509.ParsePKCS1PrivateKey(privateKeyDecoded.Bytes)

View File

@ -6,7 +6,6 @@ import (
"encoding/gob"
"fmt"
"io"
"log"
"net/http"
"time"
@ -26,7 +25,7 @@ func (a *goBlog) initAPSendQueue() {
a.listenOnQueue("ap", 30*time.Second, func(qi *queueItem, dequeue func(), reschedule func(time.Duration)) {
var r apRequest
if err := gob.NewDecoder(bytes.NewReader(qi.content)).Decode(&r); err != nil {
log.Println("activitypub queue:", err.Error())
a.error("Activitypub queue", "err", err)
dequeue()
return
}
@ -40,7 +39,7 @@ func (a *goBlog) initAPSendQueue() {
bufferpool.Put(buf)
return
}
log.Println("AP request failed for the 20th time:", r.To)
a.info("AP request failed for the 20th time", "to", r.To)
_ = a.db.apRemoveInbox(r.To)
}
dequeue()

6
app.go
View File

@ -2,6 +2,7 @@ package main
import (
"crypto/rsa"
"log/slog"
"net/http"
"sync"
@ -66,8 +67,11 @@ type goBlog struct {
inLoad sync.Once
// IndieAuth
ias *indieauth.Server
// Logs
// Logs (HTTP)
logf *rotatelogs.RotateLogs
// Logs (Program)
logger *slog.Logger
logLevel *slog.LevelVar
// Markdown
md, absoluteMd, titleMd goldmark.Markdown
// Media

View File

@ -4,7 +4,6 @@ import (
"bytes"
"context"
"io"
"log"
"net/http"
"sort"
"strings"
@ -25,7 +24,7 @@ func (a *goBlog) serveBlogroll(w http.ResponseWriter, r *http.Request) {
return a.getBlogrollOutlines(blog)
})
if err != nil {
log.Printf("Failed to get outlines: %v", err)
a.error("Blogroll: Failed to get outlines", "err", err)
a.serveError(w, r, "", http.StatusInternalServerError)
return
}
@ -48,7 +47,7 @@ func (a *goBlog) serveBlogrollExport(w http.ResponseWriter, r *http.Request) {
return a.getBlogrollOutlines(blog)
})
if err != nil {
log.Printf("Failed to get outlines: %v", err)
a.error("Blogroll: Failed to get outlines", "err", err)
a.serveError(w, r, "", http.StatusInternalServerError)
return
}

View File

@ -2,7 +2,6 @@ package main
import (
"context"
"log"
"net/http"
"net/url"
"sort"
@ -41,7 +40,7 @@ func (a *goBlog) initCache() (err error) {
ticker := time.NewTicker(15 * time.Minute)
for range ticker.C {
met := a.cache.c.Metrics
log.Println("\nCache:", met.String())
a.info("Cache metrics", "metrics", met.String())
}
}()
return

View File

@ -3,7 +3,6 @@ package main
import (
"encoding/base64"
"io"
"log"
"net/http"
"net/http/httptest"
"net/url"
@ -104,8 +103,6 @@ func Test_captchaMiddleware(t *testing.T) {
_, captchaSolved := session.Values["captcha"].(bool)
assert.False(t, captchaSolved)
log.Println("Captcha ID:", captchaId)
// Check form values
doc, err := goquery.NewDocumentFromReader(res.Body)
_ = res.Body.Close()
@ -157,8 +154,6 @@ func Test_captchaMiddleware(t *testing.T) {
_, captchaSolved = session.Values["captcha"].(bool)
assert.False(t, captchaSolved)
log.Println("Captcha ID:", captchaId)
// Check form values
doc, err = goquery.NewDocumentFromReader(res.Body)
_ = res.Body.Close()

View File

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"io"
"log"
"net/http"
"strings"
"sync/atomic"
@ -27,24 +26,24 @@ func (a *goBlog) checkAllExternalLinks() error {
if err != nil {
return err
}
return a.checkLinks(log.Writer(), posts...)
return a.checkLinks(posts...)
}
func (a *goBlog) checkLinks(w io.Writer, posts ...*post) error {
func (a *goBlog) checkLinks(posts ...*post) error {
// Get all links
allLinks, err := a.allLinksToCheck(posts...)
if err != nil {
return err
}
// Print some info
fmt.Fprintln(w, "Checking", len(allLinks), "links")
fmt.Println("Checking", len(allLinks), "links")
// Cancel context
cancelContext, cancelFunc := context.WithCancel(context.Background())
var done atomic.Bool
a.shutdown.Add(func() {
done.Store(true)
cancelFunc()
fmt.Fprintln(w, "Cancelled link check")
fmt.Println("Cancelled link check")
})
// Create HTTP cache
cache, err := ristretto.NewCache(&ristretto.Config{
@ -106,11 +105,12 @@ func (a *goBlog) checkLinks(w io.Writer, posts ...*post) error {
continue
}
if r.err != nil {
fmt.Fprintf(w, "%s in %s: %s\n", r.link, r.in, r.err.Error())
fmt.Printf("%s in %s: %s\n", r.link, r.in, r.err.Error())
} else if !successStatus(r.status) {
fmt.Fprintf(w, "%s in %s: %d (%s)\n", r.link, r.in, r.status, http.StatusText(r.status))
fmt.Printf("%s in %s: %d (%s)\n", r.link, r.in, r.status, http.StatusText(r.status))
}
}
fmt.Println("Finished link check")
return nil
}

View File

@ -2,7 +2,6 @@ package main
import (
"errors"
"log"
"net/http"
"net/url"
"strconv"
@ -386,6 +385,8 @@ func (a *goBlog) initConfig(logging bool) error {
if a.cfg.initialized {
return nil
}
// Update log level
a.updateLogLevel()
// Init database
if err := a.initDatabase(logging); err != nil {
return err
@ -561,7 +562,7 @@ func (a *goBlog) initConfig(logging bool) error {
}
// Log success
a.cfg.initialized = true
log.Println("Initialized configuration")
a.info("Initialized configuration")
return nil
}

View File

@ -2,7 +2,6 @@ package main
import (
"fmt"
"log"
"net/http"
"time"
@ -58,7 +57,7 @@ func (a *goBlog) sendContactSubmission(w http.ResponseWriter, r *http.Request) {
// Send submission
go func() {
if err := a.sendContactEmail(bc.Contact, message.String(), formEmail); err != nil {
log.Println(err.Error())
a.error("Failed to send contact email", "err", err)
}
}()
// Send notification

View File

@ -4,7 +4,6 @@ import (
"context"
"database/sql"
"errors"
"log"
"os"
"strings"
"sync"
@ -17,6 +16,7 @@ import (
)
type database struct {
a *goBlog
// Basic things
db *sql.DB // database
em sync.Mutex // command execution (insert, update, delete ...)
@ -35,7 +35,7 @@ func (a *goBlog) initDatabase(logging bool) (err error) {
return
}
if logging {
log.Println("Initialize database...")
a.info("Initialize database")
}
// Setup db
db, err := a.openDatabase(a.cfg.Db.File, logging)
@ -46,9 +46,9 @@ func (a *goBlog) initDatabase(logging bool) (err error) {
a.db = db
a.shutdown.Add(func() {
if err := db.close(); err != nil {
log.Printf("Failed to close database: %v", err)
a.error("Failed to close database", "err", err)
} else {
log.Println("Closed database")
a.info("Closed database")
}
})
if a.cfg.Db.DumpFile != "" {
@ -58,7 +58,7 @@ func (a *goBlog) initDatabase(logging bool) (err error) {
db.dump(a.cfg.Db.DumpFile)
}
if logging {
log.Println("Initialized database")
a.info("Initialized database")
}
return nil
}
@ -116,7 +116,7 @@ func (a *goBlog) openDatabase(file string, logging bool) (*database, error) {
return nil, errors.New("sqlite not compiled with FTS5")
}
// Migrate DB
err = migrateDb(db, logging)
err = a.migrateDb(db, logging)
if err != nil {
return nil, err
}
@ -144,6 +144,7 @@ func (a *goBlog) openDatabase(file string, logging bool) (*database, error) {
return nil, err
}
return &database{
a: a,
db: db,
debug: debug,
psc: psc,
@ -163,11 +164,11 @@ func (db *database) dump(file string) {
// Dump database
f, err := os.Create(file)
if err != nil {
log.Println("Error while dump db:", err.Error())
db.a.error("Error while dump db", "err", err)
return
}
if err = sqlite3dump.DumpDB(db.db, f, sqlite3dump.WithTransaction(true)); err != nil {
log.Println("Error while dump db:", err.Error())
db.a.error("Error while dump db", "err", err)
}
}
@ -202,7 +203,7 @@ func (db *database) prepare(query string, args ...any) (*sql.Stmt, []any, error)
})
if err != nil {
if db.debug {
log.Printf(`Failed to prepare query "%s": %s`, query, err.Error())
db.a.error("Failed to prepare query", "query", query, "err", err)
}
return nil, args, err
}

View File

@ -4,7 +4,6 @@ import (
"context"
"database/sql"
"fmt"
"log"
"time"
"github.com/spf13/cast"
@ -25,34 +24,27 @@ func (db *database) dbAfter(ctx context.Context, query string, args ...any) {
return
}
dur := time.Since(ctx.Value(dbHooksBegin).(time.Time))
logBuilder := builderpool.Get()
logBuilder.WriteString("\nQuery: ")
logBuilder.WriteString(`"`)
logBuilder.WriteString(query)
logBuilder.WriteString(`"`)
argsBuilder := builderpool.Get()
if len(args) > 0 {
logBuilder.WriteString("\nArgs: ")
for i, arg := range args {
if i > 0 {
logBuilder.WriteString(", ")
argsBuilder.WriteString(", ")
}
if named, ok := arg.(sql.NamedArg); ok && named.Name != "" {
logBuilder.WriteString("(")
logBuilder.WriteString(named.Name)
logBuilder.WriteString(`) "`)
logBuilder.WriteString(argToString(named.Value))
logBuilder.WriteString(`"`)
argsBuilder.WriteString("(")
argsBuilder.WriteString(named.Name)
argsBuilder.WriteString(`) '`)
argsBuilder.WriteString(argToString(named.Value))
argsBuilder.WriteString(`'`)
} else {
logBuilder.WriteString(`"`)
logBuilder.WriteString(argToString(arg))
logBuilder.WriteString(`"`)
argsBuilder.WriteString(`'`)
argsBuilder.WriteString(argToString(arg))
argsBuilder.WriteString(`'`)
}
}
}
logBuilder.WriteString("\nDuration: ")
logBuilder.WriteString(dur.String())
log.Println(logBuilder.String())
builderpool.Put(logBuilder)
db.a.debug("Database query", "query", query, "args", argsBuilder.String(), "duration", dur.String())
builderpool.Put(argsBuilder)
}
func argToString(arg any) string {

View File

@ -3,8 +3,8 @@ package main
import (
"database/sql"
"embed"
"fmt"
"io/fs"
"log"
"strings"
"github.com/lopezator/migrator"
@ -13,7 +13,7 @@ import (
//go:embed dbmigrations/*
var dbMigrations embed.FS
func migrateDb(db *sql.DB, logging bool) error {
func (a *goBlog) migrateDb(db *sql.DB, logging bool) error {
var sqlMigrations []any
err := fs.WalkDir(dbMigrations, "dbmigrations", func(path string, d fs.DirEntry, err error) error {
if err != nil || d.Type().IsDir() {
@ -41,7 +41,7 @@ func migrateDb(db *sql.DB, logging bool) error {
m, err := migrator.New(
migrator.WithLogger(migrator.LoggerFunc(func(s string, i ...any) {
if logging {
log.Printf(s, i)
a.info(fmt.Sprintf(s, i...))
}
})),
migrator.Migrations(sqlMigrations...),

View File

@ -1,9 +0,0 @@
package main
import "log"
func (a *goBlog) debug(msg ...any) {
if a.cfg.Debug {
log.Println(append([]any{"Debug:"}, msg...)...)
}
}

View File

@ -3,7 +3,6 @@ package main
import (
"encoding/json"
"errors"
"log"
"math"
"github.com/tkrajina/gpxgo/gpx"
@ -51,7 +50,7 @@ func (a *goBlog) getTrack(p *post, withMapFeatures bool) (result *trackResult, e
parseResult, err := trackParseGPX(gpxString)
if err != nil {
// Failed to parse, but just log error
log.Printf("failed to parse GPX: %v", err)
a.error("failed to parse GPX", "err", err)
return nil, nil
}

View File

@ -2,7 +2,6 @@ package main
import (
"html/template"
"log"
"os/exec"
"time"
@ -14,7 +13,7 @@ func (a *goBlog) preStartHooks() {
cfg := a.cfg.Hooks
for _, cmd := range cfg.PreStart {
func(cmd string) {
executeHookCommand("pre-start", cfg.Shell, cmd)
a.executeHookCommand("pre-start", cfg.Shell, cmd)
}(cmd)
}
}
@ -26,7 +25,7 @@ func (a *goBlog) postPostHooks(p *post) {
if hc := a.cfg.Hooks; hc != nil {
for _, cmdTmplString := range hc.PostPost {
go func(p *post, cmdTmplString string) {
a.cfg.Hooks.executeTemplateCommand("post-post", cmdTmplString, map[string]any{
a.executeHookTemplateCommand("post-post", cmdTmplString, map[string]any{
"URL": a.fullPostURL(p),
"Post": p,
})
@ -46,7 +45,7 @@ func (a *goBlog) postUpdateHooks(p *post) {
if hc := a.cfg.Hooks; hc != nil {
for _, cmdTmplString := range hc.PostUpdate {
go func(p *post, cmdTmplString string) {
a.cfg.Hooks.executeTemplateCommand("post-update", cmdTmplString, map[string]any{
a.executeHookTemplateCommand("post-update", cmdTmplString, map[string]any{
"URL": a.fullPostURL(p),
"Post": p,
})
@ -65,7 +64,7 @@ func (a *goBlog) postDeleteHooks(p *post) {
if hc := a.cfg.Hooks; hc != nil {
for _, cmdTmplString := range hc.PostDelete {
go func(p *post, cmdTmplString string) {
a.cfg.Hooks.executeTemplateCommand("post-delete", cmdTmplString, map[string]any{
a.executeHookTemplateCommand("post-delete", cmdTmplString, map[string]any{
"URL": a.fullPostURL(p),
"Post": p,
})
@ -84,7 +83,7 @@ func (a *goBlog) postUndeleteHooks(p *post) {
if hc := a.cfg.Hooks; hc != nil {
for _, cmdTmplString := range hc.PostUndelete {
go func(p *post, cmdTmplString string) {
a.cfg.Hooks.executeTemplateCommand("post-undelete", cmdTmplString, map[string]any{
a.executeHookTemplateCommand("post-undelete", cmdTmplString, map[string]any{
"URL": a.fullPostURL(p),
"Post": p,
})
@ -96,19 +95,20 @@ func (a *goBlog) postUndeleteHooks(p *post) {
}
}
func (cfg *configHooks) executeTemplateCommand(hookType string, tmpl string, data map[string]any) {
func (a *goBlog) executeHookTemplateCommand(hookType string, tmpl string, data map[string]any) {
cfg := a.cfg.Hooks
cmdTmpl, err := template.New("cmd").Parse(tmpl)
if err != nil {
log.Println("Failed to parse cmd template:", err.Error())
a.error("Failed to parse cmd template", "err", err)
return
}
cmdBuf := bufferpool.Get()
defer bufferpool.Put(cmdBuf)
if err = cmdTmpl.Execute(cmdBuf, data); err != nil {
log.Println("Failed to execute cmd template:", err.Error())
a.error("Failed to execute cmd template", "err", err)
return
}
executeHookCommand(hookType, cfg.Shell, cmdBuf.String())
a.executeHookCommand(hookType, cfg.Shell, cmdBuf.String())
}
type hourlyHookFunc func()
@ -119,7 +119,7 @@ func (a *goBlog) startHourlyHooks() {
for _, cmd := range cfg.Hourly {
c := cmd
f := func() {
executeHookCommand("hourly", cfg.Shell, c)
a.executeHookCommand("hourly", cfg.Shell, c)
}
a.hourlyHooks = append(a.hourlyHooks, f)
}
@ -135,7 +135,7 @@ func (a *goBlog) startHourlyHooks() {
ticker := time.NewTicker(1 * time.Hour)
a.shutdown.Add(func() {
ticker.Stop()
log.Println("Stopped hourly hooks")
a.info("Stopped hourly hooks")
})
for range ticker.C {
for _, f := range a.hourlyHooks {
@ -145,19 +145,19 @@ func (a *goBlog) startHourlyHooks() {
})
a.shutdown.Add(func() {
if tr.Stop() {
log.Println("Canceled hourly hooks")
a.info("Canceled hourly hooks")
}
})
}
}
func executeHookCommand(hookType, shell, cmd string) {
log.Printf("Executing %v hook: %v", hookType, cmd)
func (a *goBlog) executeHookCommand(hookType, shell, cmd string) {
a.info("Executing hook", "type", hookType, "cmd", cmd)
out, err := exec.Command(shell, "-c", cmd).CombinedOutput()
if err != nil {
log.Println("Failed to execute command:", err.Error())
a.error("Failed to execute command", "err", err, "cmd", cmd)
}
if len(out) > 0 {
log.Printf("Output:\n%v", string(out))
a.info("Hook output", "out", string(out))
}
}

17
http.go
View File

@ -4,7 +4,6 @@ import (
"database/sql"
"errors"
"fmt"
"log"
"net"
"net/http"
"sort"
@ -33,7 +32,7 @@ const (
)
func (a *goBlog) startServer() (err error) {
log.Println("Start server(s)...")
a.info("Start server(s)...")
// Load router
a.reloadRouter()
// Set basic middlewares
@ -63,7 +62,7 @@ func (a *goBlog) startServer() (err error) {
if a.cfg.Server.Tor {
go func() {
if err := a.startOnionService(finalHandler); err != nil {
log.Println("Tor failed:", err.Error())
a.error("Tor failed", "err", err)
}
}()
}
@ -82,9 +81,9 @@ func (a *goBlog) startServer() (err error) {
ReadTimeout: 5 * time.Minute,
WriteTimeout: 5 * time.Minute,
}
a.shutdown.Add(shutdownServer(httpServer, "http server"))
a.shutdown.Add(a.shutdownServer(httpServer, "http server"))
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Println("Failed to start HTTP server:", err.Error())
a.error("Failed to start HTTP server", "err", err)
}
}()
}
@ -94,7 +93,7 @@ func (a *goBlog) startServer() (err error) {
ReadTimeout: 5 * time.Minute,
WriteTimeout: 5 * time.Minute,
}
a.shutdown.Add(shutdownServer(s, "main server"))
a.shutdown.Add(a.shutdownServer(s, "main server"))
s.Addr = ":" + strconv.Itoa(a.cfg.Server.Port)
if a.cfg.Server.PublicHTTPS {
s.TLSConfig = a.getAutocertManager().TLSConfig()
@ -110,14 +109,14 @@ func (a *goBlog) startServer() (err error) {
return err
}
func shutdownServer(s *http.Server, name string) func() {
func (a *goBlog) shutdownServer(s *http.Server, name string) func() {
return func() {
toc, c := context.WithTimeout(context.Background(), 5*time.Second)
defer c()
if err := s.Shutdown(toc); err != nil {
log.Printf("Error on server shutdown (%v): %v", name, err)
a.error("Error on server shutdown (%v): %v", name, err)
}
log.Println("Stopped server:", name)
a.info("Stopped server", "name", name)
}
}

View File

@ -2,7 +2,6 @@ package main
import (
"context"
"log"
"net/http"
"github.com/carlmjohnson/requests"
@ -50,7 +49,7 @@ func (a *goBlog) indexNow(url string) {
}
key := a.indexNowKey()
if len(key) == 0 {
log.Println("Skipping IndexNow")
a.info("Skipping IndexNow")
return
}
err := requests.URL("https://api.indexnow.org/indexnow").
@ -59,10 +58,10 @@ func (a *goBlog) indexNow(url string) {
Param("key", string(key)).
Fetch(context.Background())
if err != nil {
log.Println("Sending IndexNow request failed:", err.Error())
a.error("Sending IndexNow request failed", "err", err)
return
} else {
log.Println("IndexNow request sent for", url)
a.info("IndexNow request sent", "url", url)
}
}
@ -71,7 +70,7 @@ func (a *goBlog) indexNowKey() []byte {
// Try to load key from database
keyBytes, err := a.db.retrievePersistentCache("indexnowkey")
if err != nil {
log.Println("Failed to retrieve cached IndexNow key:", err.Error())
a.error("Failed to retrieve cached IndexNow key", "err", err)
return
}
if keyBytes == nil {
@ -80,7 +79,7 @@ func (a *goBlog) indexNowKey() []byte {
// Store key in database
err = a.db.cachePersistently("indexnowkey", keyBytes)
if err != nil {
log.Println("Failed to cache IndexNow key:", err.Error())
a.error("Failed to cache IndexNow key", "err", err)
return
}
}

39
log.go Normal file
View File

@ -0,0 +1,39 @@
package main
import (
"log/slog"
"os"
)
func (a *goBlog) initLog() {
a.logLevel = new(slog.LevelVar)
a.logger = slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
Level: a.logLevel,
}))
}
func (a *goBlog) updateLogLevel() {
if a.logLevel == nil {
a.initLog()
}
if a.cfg.Debug {
a.logLevel.Set(slog.LevelDebug)
}
}
func (a *goBlog) debug(msg string, args ...any) {
a.logger.Debug(msg, args...)
}
func (a *goBlog) info(msg string, args ...any) {
a.logger.Info(msg, args...)
}
func (a *goBlog) error(msg string, args ...any) {
a.logger.Error(msg, args...)
}
func (a *goBlog) fatal(msg string, args ...any) {
a.error(msg, args...)
os.Exit(1)
}

64
main.go
View File

@ -2,7 +2,7 @@ package main
import (
"flag"
"log"
"fmt"
"net"
"net/http"
netpprof "net/http/pprof"
@ -22,17 +22,23 @@ func main() {
memprofile := flag.String("memprofile", "", "write memory profile to `file`")
configfile := flag.String("config", "", "use a specific config file")
// Init app and logger
app := &goBlog{
httpClient: newHttpClient(),
}
app.initLog()
// Init CPU and memory profiling
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatalln("could not create CPU profile: ", err)
app.fatal("could not create CPU profile", "err", err)
return
}
defer f.Close()
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatalln("could not start CPU profile: ", err)
app.fatal("could not start CPU profile", "err", err)
return
}
defer pprof.StopCPUProfile()
@ -41,29 +47,25 @@ func main() {
defer func() {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatalln("could not create memory profile: ", err.Error())
app.fatal("could not create memory profile", "err", err)
return
}
defer f.Close()
runtime.GC()
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatalln("could not write memory profile: ", err.Error())
app.fatal("could not write memory profile", "err", err)
return
}
}()
}
app := &goBlog{
httpClient: newHttpClient(),
}
// Initialize config
if err = app.loadConfigFile(*configfile); err != nil {
app.logErrAndQuit("Failed to load config file:", err.Error())
app.logErrAndQuit("Failed to load config file", "err", err)
return
}
if err = app.initConfig(false); err != nil {
app.logErrAndQuit("Failed to init config:", err.Error())
app.logErrAndQuit("Failed to init config", "err", err)
return
}
@ -83,17 +85,17 @@ func main() {
AccountName: app.cfg.User.Nick,
})
if err != nil {
app.logErrAndQuit(err.Error())
app.logErrAndQuit("Failed to generate TOTP secret", "err", err)
return
}
log.Println("TOTP-Secret:", key.Secret())
fmt.Println("TOTP-Secret:", key.Secret())
app.shutdown.ShutdownAndWait()
return
}
// Initialize plugins
if err = app.initPlugins(); err != nil {
app.logErrAndQuit("Failed to init plugins:", err.Error())
app.logErrAndQuit("Failed to init plugins", "err", err)
return
}
@ -119,13 +121,13 @@ func main() {
}
listener, err := net.Listen("tcp", pprofServer.Addr)
if err != nil {
log.Fatalln("Failed to start pprof server:", err.Error())
app.fatal("Failed to start pprof server", "err", err)
return
}
log.Println("Pprof server listening on", listener.Addr().String())
app.info("Pprof server listening", "addr", listener.Addr().String())
// Start server
if err := pprofServer.Serve(listener); err != nil {
log.Fatalln("Failed to start pprof server:", err.Error())
app.fatal("Failed to start pprof server", "err", err)
return
}
}()
@ -139,11 +141,11 @@ func main() {
app.initMarkdown()
err = app.initTemplateStrings()
if err != nil {
app.logErrAndQuit("Failed to start check:", err.Error())
app.logErrAndQuit("Failed to start check", "err", err)
}
err = app.checkAllExternalLinks()
if err != nil {
app.logErrAndQuit("Failed to start check:", err.Error())
app.logErrAndQuit("Failed to start check", "err", err)
}
app.shutdown.ShutdownAndWait()
return
@ -157,7 +159,7 @@ func main() {
}
err = app.exportMarkdownFiles(dir)
if err != nil {
app.logErrAndQuit("Failed to export markdown files:", err.Error())
app.logErrAndQuit("Failed to export markdown files", "err", err)
return
}
app.shutdown.ShutdownAndWait()
@ -173,7 +175,7 @@ func main() {
// Start the server
err = app.startServer()
if err != nil {
app.logErrAndQuit("Failed to start server(s):", err.Error())
app.logErrAndQuit("Failed to start server(s)", "err", err)
return
}
@ -184,31 +186,31 @@ func main() {
func (app *goBlog) initComponents() {
var err error
log.Println("Initialize components...")
app.info("Initialize components...")
app.initMarkdown()
if err = app.initTemplateAssets(); err != nil { // Needs minify
app.logErrAndQuit("Failed to init template assets:", err.Error())
app.logErrAndQuit("Failed to init template assets", "err", err)
return
}
if err = app.initTemplateStrings(); err != nil {
app.logErrAndQuit("Failed to init template translations:", err.Error())
app.logErrAndQuit("Failed to init template translations", "err", err)
return
}
if err = app.initCache(); err != nil {
app.logErrAndQuit("Failed to init HTTP cache:", err.Error())
app.logErrAndQuit("Failed to init HTTP cache", "err", err)
return
}
if err = app.initRegexRedirects(); err != nil {
app.logErrAndQuit("Failed to init redirects:", err.Error())
app.logErrAndQuit("Failed to init redirects", "err", err)
return
}
if err = app.initHTTPLog(); err != nil {
app.logErrAndQuit("Failed to init HTTP logging:", err.Error())
app.logErrAndQuit("Failed to init HTTP logging", "err", err)
return
}
if err = app.initActivityPub(); err != nil {
app.logErrAndQuit("Failed to init ActivityPub:", err.Error())
app.logErrAndQuit("Failed to init ActivityPub", "err", err)
return
}
app.initWebmention()
@ -221,11 +223,11 @@ func (app *goBlog) initComponents() {
app.initPostsDeleter()
app.initIndexNow()
log.Println("Initialized components")
app.info("Initialized components")
}
func (a *goBlog) logErrAndQuit(v ...any) {
log.Println(v...)
func (a *goBlog) logErrAndQuit(msg string, args ...any) {
a.error(msg, args...)
a.shutdown.ShutdownAndWait()
os.Exit(1)
}

View File

@ -7,7 +7,6 @@ import (
"fmt"
"image/png"
"io"
"log"
"net/http"
"github.com/carlmjohnson/requests"
@ -42,17 +41,18 @@ func (a *goBlog) initMediaCompressors() {
}
config := a.cfg.Micropub.MediaStorage
if key := config.TinifyKey; key != "" {
a.compressors = append(a.compressors, &tinify{key})
a.compressors = append(a.compressors, &tinify{a: a, key: key})
}
if config.CloudflareCompressionEnabled {
a.compressors = append(a.compressors, &cloudflare{})
}
if config.LocalCompressionEnabled {
a.compressors = append(a.compressors, &localMediaCompressor{})
a.compressors = append(a.compressors, &localMediaCompressor{a: a})
}
}
type tinify struct {
a *goBlog
key string
}
@ -78,12 +78,12 @@ func (tf *tinify) compress(url string, upload mediaStorageSaveFunc, hc *http.Cli
ToHeaders(headers).
Fetch(context.Background())
if err != nil {
log.Println("Tinify error:", err.Error())
tf.a.error("Tinify error", "err", err)
return "", tinifyErr
}
compressedLocation := headers.Get("Location")
if compressedLocation == "" {
log.Println("Tinify error: location header missing")
tf.a.error("Tinify error: location header missing")
return "", tinifyErr
}
// Resize and download image
@ -134,9 +134,11 @@ func (*cloudflare) compress(url string, upload mediaStorageSaveFunc, hc *http.Cl
return res, err
}
type localMediaCompressor struct{}
type localMediaCompressor struct {
a *goBlog
}
func (*localMediaCompressor) compress(url string, upload mediaStorageSaveFunc, hc *http.Client) (string, error) {
func (lc *localMediaCompressor) compress(url string, upload mediaStorageSaveFunc, hc *http.Client) (string, error) {
// Check url
fileExtension, allowed := urlHasExt(url, "jpg", "jpeg", "png")
if !allowed {
@ -150,7 +152,7 @@ func (*localMediaCompressor) compress(url string, upload mediaStorageSaveFunc, h
img, err := imaging.Decode(pr, imaging.AutoOrientation(true))
_ = pr.CloseWithError(err)
if err != nil {
log.Println("Local compressor error:", err.Error())
lc.a.error("Local compressor error", "err", err)
return "", errors.New("failed to compress image using local compressor")
}
// Resize image

View File

@ -3,7 +3,6 @@ package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"reflect"
"strconv"
@ -29,7 +28,7 @@ func (a *goBlog) sendNotification(text string) {
Text: text,
}
if err := a.db.saveNotification(n); err != nil {
log.Println("Failed to save notification:", err.Error())
a.error("Failed to save notification", "err", err)
}
if cfg := a.cfg.Notifications; cfg != nil {
p := pool.New().WithErrors()
@ -45,7 +44,7 @@ func (a *goBlog) sendNotification(text string) {
return err
})
if err := p.Wait(); err != nil {
log.Println("Failed to send notification:", err.Error())
a.error("Failed to send notification", "err", err)
}
}
}

View File

@ -1,7 +1,6 @@
package main
import (
"log"
"time"
"github.com/araddon/dateparse"
@ -13,21 +12,24 @@ func (a *goBlog) initPostsDeleter() {
})
}
const deletedPostParam = "deleted"
func (a *goBlog) checkDeletedPosts() {
// Get all posts with `deleted` parameter and a deleted status
postsToDelete, err := a.getPosts(&postsRequestConfig{
status: []postStatus{statusPublishedDeleted, statusDraftDeleted, statusScheduledDeleted},
parameter: "deleted",
parameter: deletedPostParam,
})
if err != nil {
log.Println("Error getting deleted posts:", err)
a.error("Error getting deleted posts", "err", err)
return
}
for _, post := range postsToDelete {
// Check if post is deleted for more than 7 days
if deleted, err := dateparse.ParseLocal(post.firstParameter("deleted")); err == nil && deleted.Add(time.Hour*24*7).Before(time.Now()) {
if deleted, err := dateparse.ParseLocal(post.firstParameter(deletedPostParam)); err == nil &&
deleted.Add(time.Hour*24*7).Before(time.Now()) {
if err := a.deletePost(post.Path); err != nil {
log.Println("Error deleting post:", err)
a.error("Error deleting post", "err", err)
}
}
}

View File

@ -1,7 +1,6 @@
package main
import (
"log"
"time"
)
@ -21,7 +20,7 @@ func (a *goBlog) startPostsScheduler() {
a.shutdown.Add(func() {
ticker.Stop()
done <- struct{}{}
log.Println("Posts scheduler stopped")
a.info("Posts scheduler stopped")
})
}
@ -31,16 +30,16 @@ func (a *goBlog) checkScheduledPosts() {
publishedBefore: time.Now(),
})
if err != nil {
log.Println("Error getting scheduled posts:", err)
a.error("Error getting scheduled posts", "err", err)
return
}
for _, post := range postsToPublish {
post.Status = statusPublished
err := a.replacePost(post, post.Path, statusScheduled, post.Visibility)
if err != nil {
log.Println("Error publishing scheduled post:", err)
a.error("Error publishing scheduled post", "err", err)
continue
}
log.Println("Published scheduled post:", post.Path)
a.info("Published scheduled post", "path", post.Path)
}
}

View File

@ -4,7 +4,6 @@ import (
"context"
"database/sql"
"errors"
"log"
"sync"
"time"
@ -101,7 +100,7 @@ func (a *goBlog) listenOnQueue(queueName string, wait time.Duration, process que
}
qi, err := a.peekQueue(queueContext, queueName)
if err != nil {
log.Println("queue peek error:", err.Error())
a.error("queue peek error", "err", err)
continue queueLoop
}
if qi == nil {
@ -117,17 +116,17 @@ func (a *goBlog) listenOnQueue(queueName string, wait time.Duration, process que
qi,
func() {
if err := a.dequeue(qi); err != nil {
log.Println("queue dequeue error:", err.Error())
a.error("queue dequeue error", "err", err)
}
},
func(dur time.Duration) {
if err := a.reschedule(qi, dur); err != nil {
log.Println("queue reschedule error:", err.Error())
a.error("queue reschedule error", "err", err)
}
},
)
}
log.Println("stopped queue:", queueName)
a.info("stopped queue", "name", queueName)
wg.Done()
}()
}

View File

@ -4,7 +4,6 @@ import (
"bytes"
"database/sql"
"encoding/gob"
"log"
"net/http"
"strings"
"time"
@ -27,7 +26,7 @@ func (a *goBlog) initSessions() {
"delete from sessions where expires < @now",
sql.Named("now", utcNowString()),
); err != nil {
log.Println("Failed to delete expired sessions:", err.Error())
a.error("Failed to delete expired sessions", "err", err)
}
}
deleteExpiredSessions()

View File

@ -2,7 +2,6 @@ package main
import (
"errors"
"log"
"net/url"
"strconv"
@ -11,10 +10,10 @@ import (
)
func (a *goBlog) initTelegram() {
a.pPostHooks = append(a.pPostHooks, a.tgPost(false))
a.pPostHooks = append(a.pPostHooks, func(p *post) { a.tgPost(p, false) })
a.pUpdateHooks = append(a.pUpdateHooks, a.tgUpdate)
a.pDeleteHooks = append(a.pDeleteHooks, a.tgDelete)
a.pUndeleteHooks = append(a.pUndeleteHooks, a.tgPost(true))
a.pUndeleteHooks = append(a.pUndeleteHooks, func(p *post) { a.tgPost(p, true) })
}
func (tg *configTelegram) enabled() bool {
@ -24,39 +23,37 @@ func (tg *configTelegram) enabled() bool {
return true