feat(PROJ-13,PROJ-42): REST API v1 + Gespeicherte Suchanfragen
PROJ-13: Externe REST API für CRM/ERP-Anbindung
- API-Key Middleware mit SHA-256-Hash-Lookup + Token-Bucket Rate-Limiter
- GET /api/v1/mails — Suche mit Paginierung (max 100/Seite)
- GET /api/v1/mails/{id} — Mail-Metadaten als JSON
- GET /api/v1/mails/{id}/raw — Original-EML Download
- Admin-Endpoints: POST/GET/DELETE /api/admin/apikeys
- Tenant-Isolation, Audit-Log, 405 für non-GET Methoden
PROJ-42: Gespeicherte Suchanfragen
- Tabelle saved_searches (user_id, tenant_id, name, query_json)
- GET/POST/DELETE /api/searches/saved mit Ownership-Check
- Frontend: "Suche speichern"-Button + Popover mit gespeicherten Suchen
- shadcn/ui Komponenten, Loading/Empty States
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"archivmail/internal/auth"
|
||||
)
|
||||
|
||||
// LookupAPIKey resolves an API key by its SHA-256 token hash.
|
||||
// Returns nil if not found or if the key is inactive.
|
||||
func (s *Store) LookupAPIKey(ctx context.Context, tokenHash string) (*auth.APIKeyRow, error) {
|
||||
if s.db == nil {
|
||||
return nil, fmt.Errorf("storage: no database configured")
|
||||
}
|
||||
|
||||
row := s.db.QueryRow(ctx,
|
||||
`SELECT id, tenant_id, name, role, active, rate_limit
|
||||
FROM api_keys
|
||||
WHERE token_hash = $1`,
|
||||
tokenHash,
|
||||
)
|
||||
|
||||
var k auth.APIKeyRow
|
||||
err := row.Scan(&k.ID, &k.TenantID, &k.Name, &k.Role, &k.Active, &k.RateLimit)
|
||||
if err != nil {
|
||||
// pgx returns no rows as an error; treat as "not found".
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &k, nil
|
||||
}
|
||||
|
||||
// TouchAPIKeyLastUsed updates the last_used_at timestamp for the given key ID.
|
||||
func (s *Store) TouchAPIKeyLastUsed(ctx context.Context, keyID int64) error {
|
||||
if s.db == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := s.db.Exec(ctx,
|
||||
`UPDATE api_keys SET last_used_at = NOW() WHERE id = $1`,
|
||||
keyID,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("storage: touch api key: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user