refactor: remove redundant comments and improve code readability

Remove trivial comments that duplicate what the code already expresses clearly.
Keep only essential documentation for public exports as per Go conventions.
This improves code maintainability and follows the principle that code should
be self-documenting.
This commit is contained in:
Benjamin
2025-09-15 22:59:50 +02:00
parent aebef77434
commit 1a41d15df9
15 changed files with 0 additions and 117 deletions

View File

@@ -16,13 +16,11 @@ import (
func main() {
ctx := context.Background()
// Create server instance (Community Edition)
server, err := web.NewServer(ctx)
if err != nil {
log.Fatalf("Failed to create server: %v", err)
}
// Start server in a goroutine
go func() {
log.Printf("Community Edition server starting on %s", server.GetAddr())
if err := server.Start(); err != nil && !errors.Is(err, http.ErrServerClosed) {
@@ -30,14 +28,12 @@ func main() {
}
}()
// Wait for interrupt signal for graceful shutdown
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down Community Edition server...")
// Graceful shutdown with timeout
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

View File

@@ -32,7 +32,6 @@ func main() {
command := args[0]
// Open database connection
db, err := sql.Open("postgres", *dbDSN)
if err != nil {
log.Fatal("Cannot connect to database:", err)
@@ -41,13 +40,11 @@ func main() {
_ = db.Close()
}(db)
// Create postgres driver
driver, err := postgres.WithInstance(db, &postgres.Config{})
if err != nil {
log.Fatal("Cannot create database driver:", err)
}
// Create migrator
m, err := migrate.NewWithDatabaseInstance(*migrationsPath, "postgres", driver)
if err != nil {
log.Fatal("Cannot create migrator:", err)

View File

@@ -258,28 +258,22 @@ func (s *SignatureService) RebuildChain(ctx context.Context) error {
logger.Logger.Info("Starting chain rebuild", "totalSignatures", len(signatures))
// First signature (genesis) should have null prev_hash
if signatures[0].PrevHash != nil {
// Reset genesis signature
signatures[0].PrevHash = nil
if err := s.repo.Create(ctx, signatures[0]); err != nil {
logger.Logger.Warn("Failed to update genesis signature", "id", signatures[0].ID, "error", err)
}
}
// Process subsequent signatures
for i := 1; i < len(signatures); i++ {
current := signatures[i]
previous := signatures[i-1]
expectedHash := previous.ComputeRecordHash()
// Update if hash is missing or incorrect
if current.PrevHash == nil || *current.PrevHash != expectedHash {
current.PrevHash = &expectedHash
// Note: This would require an UPDATE method in the repository
// For now, we'll log what needs to be updated
logger.Logger.Info("Chain rebuild needed for signature",
"id", current.ID,
"expectedHash", expectedHash[:16]+"...",

View File

@@ -121,7 +121,6 @@ func (s *OauthService) GetAuthURL(nextURL string) string {
}
func (s *OauthService) HandleCallback(ctx context.Context, code, state string) (*models.User, string, error) {
// Parse state to get next URL
parts := strings.SplitN(state, ":", 2)
nextURL := "/"
if len(parts) == 2 {
@@ -130,13 +129,11 @@ func (s *OauthService) HandleCallback(ctx context.Context, code, state string) (
}
}
// Exchange code for token
token, err := s.oauthConfig.Exchange(ctx, code)
if err != nil {
return nil, nextURL, fmt.Errorf("oauth exchange failed: %w", err)
}
// Get user info
client := s.oauthConfig.Client(ctx, token)
resp, err := client.Get(s.userInfoURL)
if err != nil || resp.StatusCode != 200 {
@@ -151,7 +148,6 @@ func (s *OauthService) HandleCallback(ctx context.Context, code, state string) (
return nil, nextURL, fmt.Errorf("failed to parse user info: %w", err)
}
// Check domain restriction
if !s.IsAllowedDomain(user.Email) {
return nil, nextURL, models.ErrDomainNotAllowed
}
@@ -170,7 +166,6 @@ func (s *OauthService) IsAllowedDomain(email string) bool {
)
}
// parseUserInfo extracts user information from different OAuth2 providers
func (s *OauthService) parseUserInfo(resp *http.Response) (*models.User, error) {
var rawUser map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&rawUser); err != nil {
@@ -181,7 +176,6 @@ func (s *OauthService) parseUserInfo(resp *http.Response) (*models.User, error)
user := &models.User{}
// Extract user ID (sub field or id field depending on provider)
if sub, ok := rawUser["sub"].(string); ok {
user.Sub = sub
} else if id, ok := rawUser["id"]; ok {
@@ -190,15 +184,12 @@ func (s *OauthService) parseUserInfo(resp *http.Response) (*models.User, error)
return nil, fmt.Errorf("missing user ID in response")
}
// Extract email
if email, ok := rawUser["email"].(string); ok {
user.Email = email
} else {
// Some providers might have email in a different field or structure
return nil, fmt.Errorf("missing email in user info response")
}
// Extract user name with fallback strategy
var name string
if preferredName, ok := rawUser["preferred_username"].(string); ok && preferredName != "" {
name = preferredName
@@ -223,7 +214,6 @@ func (s *OauthService) parseUserInfo(resp *http.Response) (*models.User, error)
"email", user.Email,
"name", user.Name)
// Validate extracted data
if !user.IsValid() {
return nil, fmt.Errorf("invalid user data extracted: sub=%s, email=%s", user.Sub, user.Email)
}

View File

@@ -50,21 +50,17 @@ type ServerConfig struct {
func Load() (*Config, error) {
config := &Config{}
// App config
baseURL := mustGetEnv("ACKIFY_BASE_URL")
config.App.BaseURL = baseURL
config.App.Organisation = mustGetEnv("ACKIFY_ORGANISATION")
config.App.SecureCookies = strings.HasPrefix(strings.ToLower(baseURL), "https://")
// Database config
config.Database.DSN = mustGetEnv("ACKIFY_DB_DSN")
// OAuth config
config.OAuth.ClientID = mustGetEnv("ACKIFY_OAUTH_CLIENT_ID")
config.OAuth.ClientSecret = mustGetEnv("ACKIFY_OAUTH_CLIENT_SECRET")
config.OAuth.AllowedDomain = os.Getenv("ACKIFY_OAUTH_ALLOWED_DOMAIN")
// Configure OAuth endpoints based on provider or use custom URLs
provider := strings.ToLower(getEnv("ACKIFY_OAUTH_PROVIDER", ""))
switch provider {
case "google":
@@ -84,7 +80,6 @@ func Load() (*Config, error) {
config.OAuth.UserInfoURL = fmt.Sprintf("%s/api/v4/user", gitlabURL)
config.OAuth.Scopes = []string{"read_user", "profile"}
default:
// Custom OAuth provider - all URLs must be explicitly set
config.OAuth.AuthURL = mustGetEnv("ACKIFY_OAUTH_AUTH_URL")
config.OAuth.TokenURL = mustGetEnv("ACKIFY_OAUTH_TOKEN_URL")
config.OAuth.UserInfoURL = mustGetEnv("ACKIFY_OAUTH_USERINFO_URL")
@@ -98,7 +93,6 @@ func Load() (*Config, error) {
}
config.OAuth.CookieSecret = cookieSecret
// Server config
config.Server.ListenAddr = getEnv("ACKIFY_LISTEN_ADDR", ":8080")
return config, nil
@@ -126,17 +120,14 @@ func getEnv(key, defaultValue string) string {
func parseCookieSecret() ([]byte, error) {
raw := os.Getenv("ACKIFY_OAUTH_COOKIE_SECRET")
if raw == "" {
// Generate random 32 bytes for development
secret := securecookie.GenerateRandomKey(32)
fmt.Println("[WARN] ACKIFY_OAUTH_COOKIE_SECRET not set, generated volatile secret (sessions reset on restart)")
return secret, nil
}
// Try base64 decoding first
if decoded, err := base64.StdEncoding.DecodeString(raw); err == nil && (len(decoded) == 32 || len(decoded) == 64) {
return decoded, nil
}
// Fallback to raw bytes
return []byte(raw), nil
}

View File

@@ -21,7 +21,6 @@ func InitDB(ctx context.Context, config Config) (*sql.DB, error) {
return nil, fmt.Errorf("failed to open database: %w", err)
}
// Test connection with timeout
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()

View File

@@ -25,7 +25,6 @@ type TestDB struct {
func SetupTestDB(t *testing.T) *TestDB {
t.Helper()
// Skip if not in integrations test mode
if os.Getenv("INTEGRATION_TESTS") == "" {
t.Skip("Skipping integrations test (INTEGRATION_TESTS not set)")
}
@@ -40,7 +39,6 @@ func SetupTestDB(t *testing.T) *TestDB {
t.Fatalf("Failed to connect to test database: %v", err)
}
// Verify connection
if err := db.Ping(); err != nil {
t.Fatalf("Failed to ping test database: %v", err)
}
@@ -51,12 +49,10 @@ func SetupTestDB(t *testing.T) *TestDB {
dbName: fmt.Sprintf("test_%d_%d", time.Now().UnixNano(), os.Getpid()),
}
// Create test schema
if err := testDB.createSchema(); err != nil {
t.Fatalf("Failed to create test schema: %v", err)
}
// Clean up on test completion
t.Cleanup(func() {
testDB.Cleanup()
})

View File

@@ -58,12 +58,10 @@ func (h *AuthHandlers) HandleOAuthCallback(w http.ResponseWriter, r *http.Reques
return
}
// Parse and validate next URL
if nextURL == "" {
nextURL = "/"
}
// Basic URL validation to prevent open redirects
if parsedURL, err := url.Parse(nextURL); err != nil ||
(parsedURL.Host != "" && parsedURL.Host != r.Host) {
nextURL = "/"

View File

@@ -82,7 +82,6 @@ var BadgeThemes = struct {
},
}
// generateBadge creates a PNG badge
func (h *BadgeHandler) generateBadge(isSigned bool) []byte {
img := image.NewRGBA(image.Rect(0, 0, badgeSize, badgeSize))
@@ -94,7 +93,6 @@ func (h *BadgeHandler) generateBadge(isSigned bool) []byte {
return h.encodeToPNG(img)
}
// getBadgeColors returns appropriate colors based on signing status
func (h *BadgeHandler) getBadgeColors(isSigned bool) BadgeColors {
if isSigned {
return BadgeThemes.Success
@@ -102,12 +100,10 @@ func (h *BadgeHandler) getBadgeColors(isSigned bool) BadgeColors {
return BadgeThemes.Error
}
// drawBackground fills the image with background color
func (h *BadgeHandler) drawBackground(img *image.RGBA, bgColor color.RGBA) {
draw.Draw(img, img.Bounds(), &image.Uniform{C: bgColor}, image.Point{}, draw.Src)
}
// drawBorder draws a circular border around the badge
func (h *BadgeHandler) drawBorder(img *image.RGBA, borderColor color.RGBA) {
cx, cy, r := badgeSize/2, badgeSize/2, badgeSize/2-3
for y := 0; y < badgeSize; y++ {
@@ -121,7 +117,6 @@ func (h *BadgeHandler) drawBorder(img *image.RGBA, borderColor color.RGBA) {
}
}
// drawIcon draws the appropriate icon based on signing status
func (h *BadgeHandler) drawIcon(img *image.RGBA, isSigned bool, iconColor color.RGBA) {
if isSigned {
h.drawCheckmark(img, badgeSize, iconColor)
@@ -130,7 +125,6 @@ func (h *BadgeHandler) drawIcon(img *image.RGBA, isSigned bool, iconColor color.
}
}
// encodeToPNG encodes the image to PNG format
func (h *BadgeHandler) encodeToPNG(img *image.RGBA) []byte {
buf := bytes.NewBuffer(nil)
_ = png.Encode(buf, img)
@@ -142,7 +136,6 @@ func (h *BadgeHandler) drawCheckmark(img *image.RGBA, size int, col color.RGBA)
cx, cy := size/2, size/2
scale := float64(size) / 64.0
// Checkmark path points (scaled)
points := [][2]int{
{int(18 * scale), int(32 * scale)},
{int(28 * scale), int(42 * scale)},
@@ -154,11 +147,9 @@ func (h *BadgeHandler) drawCheckmark(img *image.RGBA, size int, col color.RGBA)
thickness = 2
}
// Draw first stroke (left part of check)
h.drawThickLine(img, cx+points[0][0]-cx, cy+points[0][1]-cy,
cx+points[1][0]-cx, cy+points[1][1]-cy, thickness, col)
// Draw second stroke (right part of check)
h.drawThickLine(img, cx+points[1][0]-cx, cy+points[1][1]-cy,
cx+points[2][0]-cx, cy+points[2][1]-cy, thickness, col)
}

View File

@@ -64,7 +64,6 @@ type SignatoryInfo struct {
// HandleOEmbed handles oEmbed requests for signature lists
func (h *OEmbedHandler) HandleOEmbed(w http.ResponseWriter, r *http.Request) {
// Parse query parameters
targetURL := r.URL.Query().Get("url")
format := r.URL.Query().Get("format")
maxWidth := r.URL.Query().Get("maxwidth")
@@ -75,25 +74,21 @@ func (h *OEmbedHandler) HandleOEmbed(w http.ResponseWriter, r *http.Request) {
return
}
// Default format is JSON
if format == "" {
format = "json"
}
// Only support JSON format for now
if format != "json" {
http.Error(w, "Only JSON format is supported", http.StatusNotImplemented)
return
}
// Extract document ID from URL
docID, err := h.extractDocIDFromURL(targetURL)
if err != nil {
http.Error(w, "Invalid URL format", http.StatusBadRequest)
return
}
// Get signatures for the document
ctx := r.Context()
signatures, err := h.signatureService.GetDocumentSignatures(ctx, docID)
if err != nil {
@@ -119,7 +114,6 @@ func (h *OEmbedHandler) HandleOEmbed(w http.ResponseWriter, r *http.Request) {
}
}
// Render embedded HTML
embedHTML, err := h.renderEmbeddedHTML(SignatoryData{
DocID: docID,
Signatures: signatories,
@@ -133,7 +127,6 @@ func (h *OEmbedHandler) HandleOEmbed(w http.ResponseWriter, r *http.Request) {
return
}
// Parse dimensions
width := 480 // Default width
height := 320 // Default height
@@ -176,7 +169,6 @@ func (h *OEmbedHandler) HandleEmbedView(w http.ResponseWriter, r *http.Request)
return
}
// Get signatures for the document
ctx := r.Context()
signatures, err := h.signatureService.GetDocumentSignatures(ctx, docID)
if err != nil {
@@ -226,12 +218,10 @@ func (h *OEmbedHandler) extractDocIDFromURL(targetURL string) (string, error) {
return "", err
}
// Try to extract from query parameter
if docID := parsedURL.Query().Get("doc"); docID != "" {
return docID, nil
}
// Try to extract from path (e.g., /embed/doc_123 or /status/doc_123)
pathParts := strings.Split(strings.Trim(parsedURL.Path, "/"), "/")
if len(pathParts) >= 2 && (pathParts[0] == "embed" || pathParts[0] == "status" || pathParts[0] == "sign") {
return pathParts[1], nil

View File

@@ -118,7 +118,6 @@ func (h *SignatureHandlers) HandleSignGET(w http.ResponseWriter, r *http.Request
signedAt = signature.SignedAtUTC.Format("02/01/2006 à 15:04:05")
}
// If no service info from URL, try to get it from stored signature
if serviceInfo == nil && signature.Referer != nil {
if sigServiceInfo := signature.GetServiceInfo(); sigServiceInfo != nil {
serviceInfo = &struct {
@@ -190,7 +189,6 @@ func (h *SignatureHandlers) HandleSignPOST(w http.ResponseWriter, r *http.Reques
err = h.signatureService.CreateSignature(ctx, request)
if err != nil {
if errors.Is(err, models.ErrSignatureAlreadyExists) {
// Redirect to view existing signature
http.Redirect(w, r, buildSignURL(h.baseURL, docID), http.StatusFound)
return
}
@@ -198,7 +196,6 @@ func (h *SignatureHandlers) HandleSignPOST(w http.ResponseWriter, r *http.Reques
return
}
// Redirect to view the created signature
http.Redirect(w, r, buildSignURL(h.baseURL, docID), http.StatusFound)
}
@@ -228,12 +225,10 @@ func (h *SignatureHandlers) HandleStatusJSON(w http.ResponseWriter, r *http.Requ
"signed_at": sig.SignedAtUTC,
}
// Add username if available
if sig.UserName != nil && *sig.UserName != "" {
sigData["user_name"] = *sig.UserName
}
// Add service information if available
if serviceInfo := sig.GetServiceInfo(); serviceInfo != nil {
sigData["service"] = map[string]interface{}{
"name": serviceInfo.Name,

View File

@@ -7,11 +7,9 @@ import (
"strings"
)
// validateDocID extracts and validates document ID from request
func validateDocID(r *http.Request) (string, error) {
var docID string
// Try query parameter first, then form value
docID = strings.TrimSpace(r.URL.Query().Get("doc"))
if docID == "" {
docID = strings.TrimSpace(r.FormValue("doc"))
@@ -24,17 +22,14 @@ func validateDocID(r *http.Request) (string, error) {
return docID, nil
}
// buildSignURL constructs a sign URL with proper escaping
func buildSignURL(baseURL, docID string) string {
return fmt.Sprintf("%s/sign?doc=%s", baseURL, url.QueryEscape(docID))
}
// buildLoginURL constructs a login URL with next parameter
func buildLoginURL(nextURL string) string {
return "/login?next=" + url.QueryEscape(nextURL)
}
// validateUserIdentifier extracts and validates user identifier from request
func validateUserIdentifier(r *http.Request) (string, error) {
userIdentifier := strings.TrimSpace(r.URL.Query().Get("user"))
if userIdentifier == "" {

View File

@@ -46,7 +46,6 @@ func (s *Ed25519Signer) GetPublicKey() string {
return base64.StdEncoding.EncodeToString(s.publicKey)
}
// canonicalPayload creates a canonical payload for signing
func canonicalPayload(docID string, user *models.User, timestamp time.Time, nonce string) []byte {
return []byte(fmt.Sprintf(
"doc_id=%s\nuser_sub=%s\nuser_email=%s\nsigned_at=%s\nnonce=%s\n",
@@ -58,7 +57,6 @@ func canonicalPayload(docID string, user *models.User, timestamp time.Time, nonc
))
}
// loadOrGenerateKeys loads existing keys or generates new ones
func loadOrGenerateKeys() (ed25519.PrivateKey, ed25519.PublicKey, error) {
b64Key := strings.TrimSpace(os.Getenv("ACKIFY_ED25519_PRIVATE_KEY"))
@@ -74,7 +72,6 @@ func loadOrGenerateKeys() (ed25519.PrivateKey, ed25519.PublicKey, error) {
return privateKey, publicKey, nil
}
// Generate new keys
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, nil, fmt.Errorf("failed to generate keys: %w", err)

View File

@@ -14,9 +14,7 @@ func DetectServiceFromReferrer(referrerParam string) *ServiceInfo {
return nil
}
// Mapping des paramètres referrer vers les services
switch referrerParam {
// Google services
case "google-docs":
return &ServiceInfo{Name: "Google Docs", Icon: "https://cdn.simpleicons.org/googledocs", Type: "docs", Referrer: referrerParam}
case "google-sheets":
@@ -27,57 +25,36 @@ func DetectServiceFromReferrer(referrerParam string) *ServiceInfo {
return &ServiceInfo{Name: "Google Drive", Icon: "https://cdn.simpleicons.org/googledrive", Type: "storage", Referrer: referrerParam}
case "google":
return &ServiceInfo{Name: "Google", Icon: "https://cdn.simpleicons.org/google", Type: "google", Referrer: referrerParam}
// Notion
case "notion":
return &ServiceInfo{Name: "Notion", Icon: "https://cdn.simpleicons.org/notion", Type: "notes", Referrer: referrerParam}
// Confluence
case "confluence":
return &ServiceInfo{Name: "Confluence", Icon: "https://cdn.simpleicons.org/confluence", Type: "wiki", Referrer: referrerParam}
// Microsoft
case "microsoft":
return &ServiceInfo{Name: "Microsoft Office", Icon: "https://cdn.simpleicons.org/microsoft", Type: "office", Referrer: referrerParam}
// GitHub
case "github":
return &ServiceInfo{Name: "GitHub", Icon: "https://cdn.simpleicons.org/github", Type: "code", Referrer: referrerParam}
// GitLab
case "gitlab":
return &ServiceInfo{Name: "GitLab", Icon: "https://cdn.simpleicons.org/gitlab", Type: "code", Referrer: referrerParam}
// Outline
case "outline":
return &ServiceInfo{Name: "Outline", Icon: "https://cdn.simpleicons.org/outline", Type: "wiki", Referrer: referrerParam}
// Communication
case "slack":
return &ServiceInfo{Name: "Slack", Icon: "https://cdn.simpleicons.org/slack", Type: "chat", Referrer: referrerParam}
case "discord":
return &ServiceInfo{Name: "Discord", Icon: "https://cdn.simpleicons.org/discord", Type: "chat", Referrer: referrerParam}
// Project management
case "trello":
return &ServiceInfo{Name: "Trello", Icon: "https://cdn.simpleicons.org/trello", Type: "boards", Referrer: referrerParam}
case "asana":
return &ServiceInfo{Name: "Asana", Icon: "https://cdn.simpleicons.org/asana", Type: "tasks", Referrer: referrerParam}
case "monday":
return &ServiceInfo{Name: "Monday.com", Icon: "https://cdn.simpleicons.org/monday", Type: "project", Referrer: referrerParam}
// Design
case "figma":
return &ServiceInfo{Name: "Figma", Icon: "https://cdn.simpleicons.org/figma", Type: "design", Referrer: referrerParam}
case "miro":
return &ServiceInfo{Name: "Miro", Icon: "https://cdn.simpleicons.org/miro", Type: "whiteboard", Referrer: referrerParam}
// Storage
case "dropbox":
return &ServiceInfo{Name: "Dropbox", Icon: "https://cdn.simpleicons.org/dropbox", Type: "storage", Referrer: referrerParam}
default:
// Paramètre referrer personnalisé - utiliser tel quel
return &ServiceInfo{Name: referrerParam, Icon: "https://cdn.simpleicons.org/link", Type: "custom", Referrer: referrerParam}
}
}

View File

@@ -28,13 +28,11 @@ type Server struct {
// NewServer creates a new Ackify server instance
func NewServer(ctx context.Context) (*Server, error) {
// Initialize infrastructure
cfg, db, tmpl, signer, err := initInfrastructure(ctx)
if err != nil {
return nil, fmt.Errorf("failed to initialize infrastructure: %w", err)
}
// Initialize services
authService := auth.NewOAuthService(auth.Config{
BaseURL: cfg.App.BaseURL,
ClientID: cfg.OAuth.ClientID,
@@ -48,11 +46,9 @@ func NewServer(ctx context.Context) (*Server, error) {
SecureCookies: cfg.App.SecureCookies,
})
// Initialize signatures
signatureRepo := database.NewSignatureRepository(db)
signatureService := services.NewSignatureService(signatureRepo, signer)
// Initialize handlers
authHandlers := handlers.NewAuthHandlers(authService, cfg.App.BaseURL)
authMiddleware := handlers.NewAuthMiddleware(authService, cfg.App.BaseURL)
signatureHandlers := handlers.NewSignatureHandlers(signatureService, authService, tmpl, cfg.App.BaseURL)
@@ -60,10 +56,8 @@ func NewServer(ctx context.Context) (*Server, error) {
oembedHandler := handlers.NewOEmbedHandler(signatureService, tmpl, cfg.App.BaseURL, cfg.App.Organisation)
healthHandler := handlers.NewHealthHandler()
// Setup HTTP router
router := setupRouter(authHandlers, authMiddleware, signatureHandlers, badgeHandler, oembedHandler, healthHandler)
// Create HTTP server
httpServer := &http.Server{
Addr: cfg.Server.ListenAddr,
Handler: handlers.SecureHeaders(router),
@@ -107,15 +101,12 @@ func (s *Server) RegisterRoutes(fn func(r *chi.Mux)) {
fn(s.router)
}
// initInfrastructure initializes the basic infrastructure components
func initInfrastructure(ctx context.Context) (*config.Config, *sql.DB, *template.Template, *crypto.Ed25519Signer, error) {
// Load configuration
cfg, err := config.Load()
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("failed to load config: %w", err)
}
// Initialize database
db, err := database.InitDB(ctx, database.Config{
DSN: cfg.Database.DSN,
})
@@ -123,13 +114,11 @@ func initInfrastructure(ctx context.Context) (*config.Config, *sql.DB, *template
return nil, nil, nil, nil, fmt.Errorf("failed to initialize database: %w", err)
}
// Initialize templates
tmpl, err := initTemplates()
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("failed to initialize templates: %w", err)
}
// Initialize cryptographic signer
signer, err := crypto.NewEd25519Signer()
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("failed to initialize signer: %w", err)
@@ -138,7 +127,6 @@ func initInfrastructure(ctx context.Context) (*config.Config, *sql.DB, *template
return cfg, db, tmpl, signer, nil
}
// setupRouter configures all HTTP routes
func setupRouter(
authHandlers *handlers.AuthHandlers,
authMiddleware *handlers.AuthMiddleware,
@@ -149,7 +137,6 @@ func setupRouter(
) *chi.Mux {
router := chi.NewRouter()
// Public routes
router.Get("/", signatureHandlers.HandleIndex)
router.Get("/login", authHandlers.HandleLogin)
router.Get("/logout", authHandlers.HandleLogout)
@@ -160,28 +147,23 @@ func setupRouter(
router.Get("/embed", oembedHandler.HandleEmbedView)
router.Get("/health", healthHandler.HandleHealth)
// Protected routes (require authentication)
router.Get("/sign", authMiddleware.RequireAuth(signatureHandlers.HandleSignGET))
router.Post("/sign", authMiddleware.RequireAuth(signatureHandlers.HandleSignPOST))
router.Get("/signatures", authMiddleware.RequireAuth(signatureHandlers.HandleUserSignatures))
// Note: Enterprise routes can be added via RegisterRoutes method
return router
}
// initTemplates initializes HTML templates from filesystem
func initTemplates() (*template.Template, error) {
templatesDir := getTemplatesDir()
// Parse the base template first
baseTemplatePath := filepath.Join(templatesDir, "base.html.tpl")
tmpl, err := template.New("base").ParseFiles(baseTemplatePath)
if err != nil {
return nil, fmt.Errorf("failed to parse base template: %w", err)
}
// Parse the additional templates
additionalTemplates := []string{"index.html.tpl", "sign.html.tpl", "signatures.html.tpl", "embed.html.tpl"}
for _, templateFile := range additionalTemplates {
templatePath := filepath.Join(templatesDir, templateFile)
@@ -194,14 +176,11 @@ func initTemplates() (*template.Template, error) {
return tmpl, nil
}
// getTemplatesDir resolves the templates directory path
func getTemplatesDir() string {
// Check environment variable
if envDir := os.Getenv("ACKIFY_TEMPLATES_DIR"); envDir != "" {
return envDir
}
// Default behavior: try to resolve from executable location
if execPath, err := os.Executable(); err == nil {
execDir := filepath.Dir(execPath)
defaultDir := filepath.Join(execDir, "templates")
@@ -210,7 +189,6 @@ func getTemplatesDir() string {
}
}
// Fallback for development: check multiple possible paths
possiblePaths := []string{
"templates", // When running from project root
"./templates", // Alternative relative path
@@ -222,6 +200,5 @@ func getTemplatesDir() string {
}
}
// Final fallback - let the error happen in template loading
return "templates"
}