Replace the Node prototype (server.mjs) with the stack the merge plan calls for: Go 1.25, Gin for routing, GORM + MariaDB for persistence. Same wire contract on /api/sidecar/* so the SvelteKit client doesn't change. - Marks move from a JSON file on disk to mule_sidecar.marks (auto- migrated by GORM on first boot). The Node prototype's marks.json was dev-only; not migrated. - Folder/rename/heap-convert/duplicates handlers reproduce the prototype's behaviour, including the path-traversal defence (resolveUnderRoot + EvalSymlinks), the size-bucket prefilter for the duplicate hasher, and the background reindex fire-and-forget pattern. - Auth model unchanged: requireSession middleware proxies the caller's X-Auth-Token to PhotoPrism's /api/v1/photos?count=1 before any destructive op. - Expose pp-mariadb on 127.0.0.1:3306 in docker-compose so the host Go process can reach mule_sidecar.* without joining the container network. - Archive the Node prototype under sidecar/legacy/server.mjs for one cycle as reference. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
45 lines
1.2 KiB
Go
45 lines
1.2 KiB
Go
package main
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// requireSession is the standard auth shim every mutating handler wears.
|
|
// We don't store a shared service credential — the caller's X-Auth-Token
|
|
// is the only authority, and we probe PhotoPrism with it before doing any
|
|
// destructive work. The handler reads the validated token off the context
|
|
// via ctxToken so it can keep forwarding it to PhotoPrism for the actual
|
|
// operation.
|
|
func requireSession(pp *ppClient) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
token := c.GetHeader("X-Auth-Token")
|
|
if token == "" {
|
|
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "no token"})
|
|
return
|
|
}
|
|
if !pp.validateSession(c.Request.Context(), token) {
|
|
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid session"})
|
|
return
|
|
}
|
|
c.Set("token", token)
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// ctxToken returns the validated X-Auth-Token a previous requireSession
|
|
// middleware stored on the request. Handlers MUST run behind that
|
|
// middleware; otherwise this returns the empty string.
|
|
func ctxToken(c *gin.Context) string {
|
|
v, ok := c.Get("token")
|
|
if !ok {
|
|
return ""
|
|
}
|
|
s, ok := v.(string)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
return s
|
|
}
|