Introduces the Go photo-api service, nginx/systemd deploy wiring, and Next.js gallery/lightbox pages so event photos can be managed and browsed. Co-authored-by: Cursor <cursoragent@cursor.com>
73 lines
1.6 KiB
Go
73 lines
1.6 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// slugify follows the spirit of backend/src/lib/slugify.ts: lowercase,
|
|
// ASCII-fold common Spanish characters, dashes for everything else.
|
|
func slugify(s string) string {
|
|
replacer := strings.NewReplacer(
|
|
"á", "a", "é", "e", "í", "i", "ó", "o", "ú", "u", "ü", "u", "ñ", "n",
|
|
"Á", "a", "É", "e", "Í", "i", "Ó", "o", "Ú", "u", "Ü", "u", "Ñ", "n")
|
|
s = replacer.Replace(strings.ToLower(strings.TrimSpace(s)))
|
|
var b strings.Builder
|
|
prevDash := true // avoids a leading dash
|
|
for _, r := range s {
|
|
switch {
|
|
case unicode.IsLetter(r) && r < 128, unicode.IsDigit(r):
|
|
b.WriteRune(r)
|
|
prevDash = false
|
|
default:
|
|
if !prevDash {
|
|
b.WriteByte('-')
|
|
prevDash = true
|
|
}
|
|
}
|
|
}
|
|
out := strings.Trim(b.String(), "-")
|
|
if len(out) > 140 {
|
|
out = strings.Trim(out[:140], "-")
|
|
}
|
|
if out == "" {
|
|
out = "gallery"
|
|
}
|
|
return out
|
|
}
|
|
|
|
// uniqueSlug appends -2, -3, ... until the slug is free.
|
|
func (s *Server) uniqueSlug(ctx context.Context, title string) (string, error) {
|
|
base := slugify(title)
|
|
slug := base
|
|
for i := 2; ; i++ {
|
|
exists, err := s.db.SlugExists(ctx, slug)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if !exists {
|
|
return slug, nil
|
|
}
|
|
slug = fmt.Sprintf("%s-%d", base, i)
|
|
}
|
|
}
|
|
|
|
func newID() string {
|
|
return uuid.NewString()
|
|
}
|
|
|
|
// newShareToken returns 32 random bytes, base64url (43 chars, no padding).
|
|
func newShareToken() string {
|
|
buf := make([]byte, 32)
|
|
if _, err := rand.Read(buf); err != nil {
|
|
panic(err) // crypto/rand failure is unrecoverable
|
|
}
|
|
return base64.RawURLEncoding.EncodeToString(buf)
|
|
}
|