feat: deepseek is insanly good

This commit is contained in:
didier committed 2026-06-16 13:02:13 -04:00
1 parent be9d6b610f
commit 56da3bae61
5 files changed
+112 -51

No files matched your search

+1
View File
@@ -12,3 +12,4 @@ ADMIN_PASSWORD=change-me
# MCP_TOKEN=legacy-token # MCP_TOKEN=legacy-token
PORT=8080 PORT=8080
SQLITE_PATH=flashcard.db SQLITE_PATH=flashcard.db
HMAC_KEY_FILE=.secret
+1
View File
@@ -9,6 +9,7 @@ result
# Environment # Environment
.env .env
.secret
# IDE # IDE
.idea/ .idea/
+94 -48
View File
@@ -2,14 +2,17 @@ package main
import ( import (
"context" "context"
"crypto/hmac"
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha256"
"database/sql" "database/sql"
"encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"os" "os"
"strconv"
"strings" "strings"
"time" "time"
@@ -74,6 +77,29 @@ func hashToken(token string) string {
return hex.EncodeToString(h[:]) return hex.EncodeToString(h[:])
} }
func loadHMACKey(path string, logger *zap.Logger) ([]byte, error) {
if data, err := os.ReadFile(path); err == nil {
key, err := base64.StdEncoding.DecodeString(strings.TrimSpace(string(data)))
if err == nil && len(key) >= 32 {
return key, nil
}
logger.Warn("invalid HMAC key file, regenerating", zap.Error(err))
}
key := make([]byte, 32)
if _, err := rand.Read(key); err != nil {
return nil, fmt.Errorf("generate HMAC key: %w", err)
}
encoded := base64.StdEncoding.EncodeToString(key)
if err := os.WriteFile(path, []byte(encoded+"\n"), 0600); err != nil {
return nil, fmt.Errorf("write HMAC key: %w", err)
}
logger.Info("generated new HMAC key", zap.String("path", path))
return key, nil
}
func (s *sqliteStore) countUsers(ctx context.Context) (int, error) { func (s *sqliteStore) countUsers(ctx context.Context) (int, error) {
var n int var n int
err := s.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM users`).Scan(&n) err := s.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM users`).Scan(&n)
@@ -276,44 +302,86 @@ func getUserFromContext(r *http.Request) *userRow {
return nil return nil
} }
func authMiddleware(store *sqliteStore, logger *zap.Logger, next http.Handler) http.HandlerFunc { func signCookie(key []byte, userID int64, username string) string {
expiry := time.Now().Add(30 * 24 * time.Hour).Unix()
payload := fmt.Sprintf("%d|%s|%d", userID, username, expiry)
mac := hmac.New(sha256.New, key)
mac.Write([]byte(payload))
sig := hex.EncodeToString(mac.Sum(nil))
return base64.RawURLEncoding.EncodeToString([]byte(payload + "|" + sig))
}
func verifyCookie(key []byte, encoded string) (int64, string, bool) {
raw, err := base64.RawURLEncoding.DecodeString(encoded)
if err != nil {
return 0, "", false
}
parts := strings.SplitN(string(raw), "|", 4)
if len(parts) != 4 {
return 0, "", false
}
userID, username, expiryStr, sig := parts[0], parts[1], parts[2], parts[3]
payload := userID + "|" + username + "|" + expiryStr
mac := hmac.New(sha256.New, key)
mac.Write([]byte(payload))
expected := hex.EncodeToString(mac.Sum(nil))
if !hmac.Equal([]byte(sig), []byte(expected)) {
return 0, "", false
}
expiryUnix, err := strconv.ParseInt(expiryStr, 10, 64)
if err != nil || time.Now().Unix() > expiryUnix {
return 0, "", false
}
id, err := strconv.ParseInt(userID, 10, 64)
if err != nil {
return 0, "", false
}
return id, username, true
}
func authMiddleware(store *sqliteStore, hmacKey []byte, logger *zap.Logger, next http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodOptions { if r.Method == http.MethodOptions {
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
return return
} }
token := "" var user *userRow
cookie, err := r.Cookie("st_pierre_session") cookie, err := r.Cookie("st_pierre_session")
if err == nil && cookie.Value != "" { if err == nil && cookie.Value != "" {
token = cookie.Value if id, username, ok := verifyCookie(hmacKey, cookie.Value); ok {
u, err := store.getUserByID(r.Context(), id)
if err == nil && u != nil && u.Active && u.Username == username {
user = u
}
}
} }
if token == "" { if user == nil {
authHeader := r.Header.Get("Authorization") authHeader := r.Header.Get("Authorization")
if strings.HasPrefix(authHeader, "Bearer ") { if strings.HasPrefix(authHeader, "Bearer ") {
token = strings.TrimPrefix(authHeader, "Bearer ") token := strings.TrimPrefix(authHeader, "Bearer ")
} session, u, err := store.getSessionByToken(r.Context(), token)
}
if token == "" {
writeUnauthorized(w)
return
}
session, user, err := store.getSessionByToken(r.Context(), token)
if err != nil { if err != nil {
logger.Error("auth lookup failed", zap.Error(err)) logger.Error("token lookup failed", zap.Error(err))
writeUnauthorized(w) }
return if session != nil && session.Active && u != nil && u.Active {
store.updateSessionLastUsed(r.Context(), session.ID)
user = u
}
} }
if session == nil || !session.Active || !user.Active {
writeUnauthorized(w)
return
} }
store.updateSessionLastUsed(r.Context(), session.ID) if user == nil {
writeUnauthorized(w)
return
}
ctx := context.WithValue(r.Context(), userContextKey, user) ctx := context.WithValue(r.Context(), userContextKey, user)
next.ServeHTTP(w, r.WithContext(ctx)) next.ServeHTTP(w, r.WithContext(ctx))
@@ -376,7 +444,7 @@ func getenvDefault(key, fallback string) string {
return fallback return fallback
} }
func loginHandler(store *sqliteStore, logger *zap.Logger) http.HandlerFunc { func loginHandler(store *sqliteStore, hmacKey []byte, logger *zap.Logger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost { if r.Method != http.MethodPost {
writeJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "method not allowed"}) writeJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "method not allowed"})
@@ -405,31 +473,18 @@ func loginHandler(store *sqliteStore, logger *zap.Logger) http.HandlerFunc {
return return
} }
token, err := generateToken() signed := signCookie(hmacKey, user.ID, user.Username)
if err != nil {
logger.Error("generate token failed", zap.Error(err))
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "internal error"})
return
}
session, err := store.createSession(r.Context(), user.ID, token, "UI login")
if err != nil {
logger.Error("create session failed", zap.Error(err))
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "internal error"})
return
}
http.SetCookie(w, &http.Cookie{ http.SetCookie(w, &http.Cookie{
Name: "st_pierre_session", Name: "st_pierre_session",
Value: token, Value: signed,
Path: "/", Path: "/",
HttpOnly: true, HttpOnly: true,
SameSite: http.SameSiteLaxMode, SameSite: http.SameSiteLaxMode,
Expires: time.Now().Add(365 * 24 * time.Hour), Expires: time.Now().Add(30 * 24 * time.Hour),
}) })
writeJSON(w, http.StatusOK, map[string]any{ writeJSON(w, http.StatusOK, map[string]any{
"session": session,
"user": map[string]any{ "user": map[string]any{
"id": user.ID, "id": user.ID,
"username": user.Username, "username": user.Username,
@@ -438,21 +493,13 @@ func loginHandler(store *sqliteStore, logger *zap.Logger) http.HandlerFunc {
} }
} }
func logoutHandler(store *sqliteStore) http.HandlerFunc { func logoutHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost { if r.Method != http.MethodPost {
writeJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "method not allowed"}) writeJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "method not allowed"})
return return
} }
cookie, err := r.Cookie("st_pierre_session")
if err == nil && cookie.Value != "" {
session, _, _ := store.getSessionByToken(r.Context(), cookie.Value)
if session != nil {
store.deactivateSession(r.Context(), session.ID, session.UserID)
}
}
http.SetCookie(w, &http.Cookie{ http.SetCookie(w, &http.Cookie{
Name: "st_pierre_session", Name: "st_pierre_session",
Value: "", Value: "",
@@ -731,4 +778,3 @@ func getUserHandler(store *sqliteStore, logger *zap.Logger) http.HandlerFunc {
writeJSON(w, http.StatusOK, user) writeJSON(w, http.StatusOK, user)
} }
} }
+7
View File
@@ -124,6 +124,12 @@
default = "/var/lib/st-pierre/st-pierre.sqlite"; default = "/var/lib/st-pierre/st-pierre.sqlite";
description = "Path to the SQLite database file."; description = "Path to the SQLite database file.";
}; };
hmacKeyFile = lib.mkOption {
type = lib.types.path;
default = "/var/lib/st-pierre/secret";
description = "Path to the HMAC key file for cookie signing. Will be auto-generated if missing.";
};
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
@@ -170,6 +176,7 @@
EMBEDDING_BASE_URL = cfg.embeddingBaseUrl; EMBEDDING_BASE_URL = cfg.embeddingBaseUrl;
EMBEDDING_MODEL = cfg.embeddingModel; EMBEDDING_MODEL = cfg.embeddingModel;
SQLITE_PATH = builtins.toString cfg.sqlitePath; SQLITE_PATH = builtins.toString cfg.sqlitePath;
HMAC_KEY_FILE = builtins.toString cfg.hmacKeyFile;
GIT_COMMIT = gitHash; GIT_COMMIT = gitHash;
BUILD_DATE = lastUpdated; BUILD_DATE = lastUpdated;
} }
+9 -3
View File
@@ -41,6 +41,12 @@ func main() {
store.bootstrapAdmin(context.Background(), logger) store.bootstrapAdmin(context.Background(), logger)
hmacKeyFile := envOrDefault("HMAC_KEY_FILE", ".secret")
hmacKey, err := loadHMACKey(hmacKeyFile, logger)
if err != nil {
logger.Fatal("failed to load HMAC key", zap.Error(err))
}
searchEngine := createSearchEngine(logger, store, openAIKey) searchEngine := createSearchEngine(logger, store, openAIKey)
mcpServer := server.NewMCPServer( mcpServer := server.NewMCPServer(
@@ -59,7 +65,7 @@ func main() {
streamableServer := server.NewStreamableHTTPServer(mcpServer) streamableServer := server.NewStreamableHTTPServer(mcpServer)
withAuth := func(next http.Handler) http.Handler { withAuth := func(next http.Handler) http.Handler {
return authMiddleware(store, logger, next) return authMiddleware(store, hmacKey, logger, next)
} }
mux := http.NewServeMux() mux := http.NewServeMux()
@@ -78,8 +84,8 @@ func main() {
mux.Handle("/mcp", withAuth(streamableServer)) mux.Handle("/mcp", withAuth(streamableServer))
mux.HandleFunc("/api/v1/auth/login", loginHandler(store, logger)) mux.HandleFunc("/api/v1/auth/login", loginHandler(store, hmacKey, logger))
mux.HandleFunc("/api/v1/auth/logout", logoutHandler(store)) mux.HandleFunc("/api/v1/auth/logout", logoutHandler())
mux.HandleFunc("/api/v1/auth/me", withAuth(getUserHandler(store, logger)).ServeHTTP) mux.HandleFunc("/api/v1/auth/me", withAuth(getUserHandler(store, logger)).ServeHTTP)
mux.HandleFunc("/api/v1/auth/password", withAuth(changePasswordHandler(store, logger)).ServeHTTP) mux.HandleFunc("/api/v1/auth/password", withAuth(changePasswordHandler(store, logger)).ServeHTTP)