feat: console help, examples and named loggers (SummerCMS v0.4.0)

Every command gets long help and examples, descriptions in imperative
sentence case, and a warn-only TestCommandsDocumented over bonfire.CheckDocs.
The plugin logs through a diary component logger instead of the process
default logger. Requires SummerCMS v0.4.0; command names and flags unchanged.

user:token:create and user:token:list gain --json, user:backfill-handles a
progress bar; planning IDs leave the descriptions.
This commit is contained in:
Jakub Zych
2026-10-21 18:03:50 +02:00
parent 35cf16689d
commit 9986342c23
17 changed files with 342 additions and 39 deletions

View File

@@ -10,7 +10,6 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"net"
"net/http"
"net/url"
@@ -110,7 +109,7 @@ func Login(app *backpack.App) http.HandlerFunc {
}
// A failed last_seen write is logged and never fails the login.
if err := classes.TouchLastSeen(ctx, gdb, user.ID); err != nil {
slog.Default().Warn("golem15.user: stamp last_seen on login", "user_id", user.ID, "err", err)
pluginLog.Warn("golem15.user: stamp last_seen on login", "user_id", user.ID, "err", err)
}
token, err := mintFor(app, secret, user, requestURL(r))
if err != nil {
@@ -204,7 +203,7 @@ func Refresh(app *backpack.App) http.HandlerFunc {
if sub, _, _, _, err := bouncer.VerifyClaims(next, secret); err == nil {
if id, err := strconv.ParseUint(sub, 10, 64); err == nil && id != 0 {
if err := classes.TouchLastSeen(r.Context(), gdb, uint(id)); err != nil {
slog.Default().Warn("golem15.user: stamp last_seen on refresh", "user_id", id, "err", err)
pluginLog.Warn("golem15.user: stamp last_seen on refresh", "user_id", id, "err", err)
}
}
}
@@ -269,14 +268,14 @@ func ForgotPassword(app *backpack.App) http.HandlerFunc {
ctx := codeContext(r.Context(), app)
user, err := classes.LookupByEmail(ctx, gdb, strField(fields, "email"))
if err != nil {
slog.Default().Error("golem15.user: forgot-password lookup", "err", err)
pluginLog.Error("golem15.user: forgot-password lookup", "err", err)
writeJSON(w, http.StatusOK, map[string]any{"message": forgotPasswordBody})
return
}
if user != nil {
code, err := classes.IssueResetCode(ctx, gdb, user)
if err != nil {
slog.Default().Error("golem15.user: issue reset code", "err", err)
pluginLog.Error("golem15.user: issue reset code", "err", err)
} else {
packed := strconv.FormatUint(uint64(user.ID), 10) + "!" + code
sendTemplateVars(ctx, app, mailName(ctx, user, "golem15.user::mail.restore"), user.Email, mailVars(displayName(user), resetLink(app, user.ID, code), packed))
@@ -536,7 +535,7 @@ func ChangePassword(app *backpack.App) http.HandlerFunc {
return
}
if !user.HasSelfSetPassword {
slog.Default().Error("golem15.user: change-password requires a self-set password", "user_id", user.ID)
pluginLog.Error("golem15.user: change-password requires a self-set password", "user_id", user.ID)
writeOpaque500(w)
return
}
@@ -690,7 +689,7 @@ func Register(app *backpack.App) http.HandlerFunc {
vars := mailVars(displayName(user), "", "")
code, err := classes.IssueActivationCode(ctx, gdb, user)
if err != nil {
slog.Default().Error("golem15.user: issue activation code", "err", err)
pluginLog.Error("golem15.user: issue activation code", "err", err)
} else {
packed := strconv.FormatUint(uint64(user.ID), 10) + "!" + code
vars = mailVars(displayName(user), activationLink(app, user.ID, code), packed)
@@ -935,11 +934,11 @@ func sendTemplateVars(ctx context.Context, app *backpack.App, template, to strin
}
mailer, ok := app.Lookup[postcard.Mailer]()
if !ok || mailer == nil {
slog.Default().Error("golem15.user: mailer is not configured", "template", template)
pluginLog.Error("golem15.user: mailer is not configured", "template", template)
return
}
if err := mailer.Send(ctx, postcard.Message{Template: template, To: []string{to}, Vars: vars}); err != nil {
slog.Default().Error("golem15.user: send mail", "template", template, "err", err)
pluginLog.Error("golem15.user: send mail", "template", template, "err", err)
}
}
@@ -1198,7 +1197,7 @@ func UploadAvatar(app *backpack.App) http.HandlerFunc {
}
bucket, ok := app.Lookup[*blob.Bucket]()
if !ok || bucket == nil {
slog.Default().Error("golem15.user: avatar bucket is not configured")
pluginLog.Error("golem15.user: avatar bucket is not configured")
writeOpaque500(w)
return
}