Rename project from TopDeck to Canon
- Module github.com/cdnutter/canon, binary/command cmd/canon, Docker image and compose project "canon" - Environment variables now use the CANON_ prefix - Default site title is "<Your name>'s Canon" (or "Canon" with no name); a Site title set in Settings still overrides it - Existing topdeck.db (and WAL files) is renamed to canon.db on start - README: upgrade notes, including moving the old compose volume Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
58a29b7339
commit
3923444b3b
26 files changed
+215
-120
No files matched your search
@@ -5,7 +5,7 @@ import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/cdnutter/topdeck/internal/store"
|
||||
"github.com/cdnutter/canon/internal/store"
|
||||
)
|
||||
|
||||
// adminPage carries a JSON blob the page script boots from.
|
||||
|
||||
+18
-17
@@ -10,10 +10,10 @@ import (
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/cdnutter/topdeck/internal/auth"
|
||||
"github.com/cdnutter/topdeck/internal/imgcache"
|
||||
"github.com/cdnutter/topdeck/internal/providers"
|
||||
"github.com/cdnutter/topdeck/internal/store"
|
||||
"github.com/cdnutter/canon/internal/auth"
|
||||
"github.com/cdnutter/canon/internal/imgcache"
|
||||
"github.com/cdnutter/canon/internal/providers"
|
||||
"github.com/cdnutter/canon/internal/store"
|
||||
)
|
||||
|
||||
// ---- lists ----
|
||||
@@ -466,19 +466,20 @@ func (s *Server) apiSettings(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
u, _ := s.currentUser(r)
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"siteTitle": set[store.SettingSiteTitle],
|
||||
"ownerName": site.OwnerName,
|
||||
"tagline": site.Tagline,
|
||||
"avatar": site.AvatarURL,
|
||||
"theme": site.Theme,
|
||||
"customCss": set[store.SettingCustomCSS],
|
||||
"previewCount": site.PreviewCount,
|
||||
"showNotes": site.ShowNotes,
|
||||
"musicProvider": orDefault(set[store.SettingMusicProvider], "deezer"),
|
||||
"tvProvider": orDefault(set[store.SettingTVProvider], "tvdb"),
|
||||
"secrets": secrets,
|
||||
"themes": Themes,
|
||||
"username": u.Username,
|
||||
"siteTitle": set[store.SettingSiteTitle],
|
||||
"siteTitleShown": site.Title,
|
||||
"ownerName": site.OwnerName,
|
||||
"tagline": site.Tagline,
|
||||
"avatar": site.AvatarURL,
|
||||
"theme": site.Theme,
|
||||
"customCss": set[store.SettingCustomCSS],
|
||||
"previewCount": site.PreviewCount,
|
||||
"showNotes": site.ShowNotes,
|
||||
"musicProvider": orDefault(set[store.SettingMusicProvider], "deezer"),
|
||||
"tvProvider": orDefault(set[store.SettingTVProvider], "tvdb"),
|
||||
"secrets": secrets,
|
||||
"themes": Themes,
|
||||
"username": u.Username,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cdnutter/topdeck/internal/auth"
|
||||
"github.com/cdnutter/canon/internal/auth"
|
||||
)
|
||||
|
||||
type loginPage struct {
|
||||
@@ -143,7 +143,7 @@ func (s *Server) handleSetup(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
if s.setupCode == "" || subtle.ConstantTimeCompare([]byte(code), []byte(normalizeCode(s.setupCode))) != 1 {
|
||||
s.loginLimit.Fail(ip)
|
||||
fail(http.StatusForbidden, "That setup code isn't right. It's printed in the server log (docker compose logs topdeck).")
|
||||
fail(http.StatusForbidden, "That setup code isn't right. It's printed in the server log (docker compose logs canon).")
|
||||
return
|
||||
}
|
||||
if page.Username == "" || len(page.Username) > 64 {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/cdnutter/topdeck/internal/store"
|
||||
"github.com/cdnutter/canon/internal/store"
|
||||
)
|
||||
|
||||
// ItemView is an item shaped for public display.
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cdnutter/topdeck/internal/store"
|
||||
"github.com/cdnutter/topdeck/web"
|
||||
"github.com/cdnutter/canon/internal/store"
|
||||
"github.com/cdnutter/canon/web"
|
||||
)
|
||||
|
||||
// Site is the owner-configurable identity shown on every page.
|
||||
@@ -53,7 +53,7 @@ func (s *Server) site(ctx context.Context) Site {
|
||||
ShowNotes: set[store.SettingShowNotes] != "false",
|
||||
}
|
||||
if site.Title == "" {
|
||||
site.Title = "TopDeck"
|
||||
site.Title = DefaultTitle(site.OwnerName)
|
||||
}
|
||||
if !validTheme(site.Theme) {
|
||||
site.Theme = DefaultTheme
|
||||
@@ -71,6 +71,15 @@ func (s *Server) site(ctx context.Context) Site {
|
||||
return site
|
||||
}
|
||||
|
||||
// DefaultTitle is the site title when the owner hasn't set one:
|
||||
// "Chris's Canon", or just "Canon" before they've entered a name.
|
||||
func DefaultTitle(ownerName string) string {
|
||||
if ownerName = strings.TrimSpace(ownerName); ownerName == "" {
|
||||
return "Canon"
|
||||
}
|
||||
return ownerName + "'s Canon"
|
||||
}
|
||||
|
||||
func (s *Server) handleCustomCSS(w http.ResponseWriter, r *http.Request) {
|
||||
set, err := s.store.Settings(r.Context())
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package server
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestDefaultTitle(t *testing.T) {
|
||||
for in, want := range map[string]string{"": "Canon", " ": "Canon", "Chris": "Chris's Canon", " Ana ": "Ana's Canon"} {
|
||||
if got := DefaultTitle(in); got != want {
|
||||
t.Errorf("DefaultTitle(%q) = %q, want %q", in, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,16 +18,16 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cdnutter/topdeck/internal/auth"
|
||||
"github.com/cdnutter/topdeck/internal/config"
|
||||
"github.com/cdnutter/topdeck/internal/imgcache"
|
||||
"github.com/cdnutter/topdeck/internal/providers"
|
||||
"github.com/cdnutter/topdeck/internal/store"
|
||||
"github.com/cdnutter/topdeck/web"
|
||||
"github.com/cdnutter/canon/internal/auth"
|
||||
"github.com/cdnutter/canon/internal/config"
|
||||
"github.com/cdnutter/canon/internal/imgcache"
|
||||
"github.com/cdnutter/canon/internal/providers"
|
||||
"github.com/cdnutter/canon/internal/store"
|
||||
"github.com/cdnutter/canon/web"
|
||||
)
|
||||
|
||||
const (
|
||||
sessionCookie = "topdeck_session"
|
||||
sessionCookie = "canon_session"
|
||||
sessionTTL = 30 * 24 * time.Hour
|
||||
)
|
||||
|
||||
@@ -240,7 +240,7 @@ func (s *Server) requireUser(h http.HandlerFunc) http.HandlerFunc {
|
||||
// preflight, which blocks CSRF.
|
||||
func (s *Server) requireAPI(h http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("X-TopDeck") != "1" || !s.sameOrigin(r) {
|
||||
if r.Header.Get("X-Canon") != "1" || !s.sameOrigin(r) {
|
||||
writeError(w, http.StatusForbidden, "Missing request header")
|
||||
return
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user