Merge branch 'main' of ssh://git.linuxhg.com:2222/Bookhoard/bookhoard

This commit is contained in:
2026-02-13 18:53:54 -05:00
38 changed files with 5464 additions and 190 deletions
+33 -34
View File
@@ -11,15 +11,12 @@
## Table of Contents
1. [Phase 1: Enhanced Authentication (Week 1)](#phase-1-enhanced-authentication)
- Database Query Addition
- Middleware Enhancement
- Router Updates
- Backend Handler Addition
- Frontend Template Updates
- Bruno API Tests
- Database Query Addition
- Middleware Enhancement
- Router Updates
- Backend Handler Addition
- Frontend Template Updates
2. [Phase 2: Kobo Integration (Week 1-2)](#phase-2-kobo-integration)
- Documentation Updates
- Test Updates
3. [Phase 3: OPDS Security (Week 2-3)](#phase-3-opds-security)
- Router Enhancement
- Bruno API Tests
@@ -281,7 +278,7 @@ koboSync.POST("/sync-from-server", cfg.DeviceAuthMiddleware.Authenticate(koboHan
**File**: `internal/router/device.go`
**Location**: After device registration routes (around line 50)
**Location**: After device registration routes (around line 25)
**Current Implementation**: Need to check what device routes exist
@@ -294,23 +291,16 @@ koboSync.POST("/sync-from-server", cfg.DeviceAuthMiddleware.Authenticate(koboHan
devices.PUT("/:id/regenerate-token", jwtMiddleware, h.RegenerateDeviceToken)
```
**Complete Context** (assuming placement after device registration routes):
**Complete Context** (token regeneration route added to existing device routes):
```go
// Device registration endpoints
devices.POST("/register", h.InitiateRegistration)
devices.POST("/approve/:registration_id", jwtMiddleware, h.ApproveDevice)
devices.POST("/reject/:registration_id", jwtMiddleware, h.RejectDevice)
devices.GET("/pending", jwtMiddleware, h.ListPendingRegistrations)
// Existing device management routes (unchanged)
devices.GET("", jwtMiddleware, cfg.DeviceHandler.ListDevices)
devices.GET("/:id", jwtMiddleware, cfg.DeviceHandler.GetDevice)
devices.PUT("/:id", jwtMiddleware, cfg.DeviceHandler.UpdateDevice)
devices.DELETE("/:id", jwtMiddleware, cfg.DeviceHandler.DeleteDevice)
// Device management endpoints
devices.GET("", jwtMiddleware, h.ListDevices)
devices.GET("/:id", jwtMiddleware, h.GetDevice)
devices.PUT("/:id", jwtMiddleware, h.UpdateDevice)
devices.DELETE("/:id", jwtMiddleware, h.DeleteDevice)
// Token regeneration endpoint (JWT authentication required)
devices.PUT("/:id/regenerate-token", jwtMiddleware, h.RegenerateDeviceToken)
// NEW: Token regeneration endpoint (JWT authentication required)
devices.PUT("/:id/regenerate-token", jwtMiddleware, cfg.DeviceHandler.RegenerateDeviceToken)
```
**Verification**: Run `go build ./internal/router`
@@ -517,13 +507,16 @@ deviceList[i] = DeviceInfo{
**File**: `templates/devices.templ`
**Location**: Lines 46-98 (device card in grid)
**Location**: Template function signature (line 5) and device card (lines 46-98)
**Current Implementation**: Device card shows device info and settings buttons
**Required Changes**:
**Required Addition**: Add buttons for copy sync URL and regenerate token
1. **Update template signature** to accept baseURL parameter (line 5):
```templ
templ Devices(user User, devices []handlers.DeviceInfo, pendingRegistrations []PendingRegistrationData, baseURL string) {
```
**REPLACE DEVICE CARD CONTENT** (lines 46-98) with:
2. **REPLACE DEVICE CARD CONTENT** (lines 46-98) with:
```templ
for _, device := range devices {
@@ -598,12 +591,12 @@ for _, device := range devices {
type="text"
id="sync-url-{ device.ID }"
readonly
value="{ fmt.Sprintf("http://YOUR_IP:8765/api/sync/kobo/%s", device.AuthToken) }"
value="{ fmt.Sprintf("%s/api/sync/kobo/%s", baseURL, device.AuthToken) }"
class="flex-1 px-3 py-2 text-xs rounded border"
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
/>
<button
onclick="copyToClipboard('{ fmt.Sprintf("http://YOUR_IP:8765/api/sync/kobo/%s", device.AuthToken) }', 'Kobo sync URL')"
onclick="copyToClipboard('{ fmt.Sprintf("%s/api/sync/kobo/%s", baseURL, device.AuthToken) }', 'Kobo sync URL', event)"
class="px-3 py-2 text-xs rounded hover:opacity-80"
style="background-color: var(--accent); color: white;"
>
@@ -628,7 +621,7 @@ for _, device := range devices {
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border); font-family: monospace;"
/>
<button
onclick="copyToClipboard('{ device.AuthToken }', 'Auth token')"
onclick="copyToClipboard('{ device.AuthToken }', 'Auth token', event)"
class="px-3 py-2 text-xs rounded hover:opacity-80"
style="background-color: var(--accent); color: white;"
>
@@ -641,7 +634,7 @@ for _, device := range devices {
<!-- Regenerate Token Button -->
<button
onclick="regenerateDeviceToken('{ device.ID }')"
onclick="regenerateDeviceToken('{ device.ID }', event)"
class="w-full px-3 py-2 text-xs rounded border hover:opacity-80"
style="border-color: var(--border); color: var(--text-secondary); background-color: var(--bg-primary);"
>
@@ -660,6 +653,13 @@ for _, device := range devices {
- Regenerate button requires confirmation
- Clear setup instructions for each device type
**Handler Update** (`internal/router/frontend.go:171`):
```go
err = templates.Devices(user, devices, pendingList, cfg.BaseURL).Render(c.Request().Context(), &buf)
```
Pass `cfg.BaseURL` to template instead of hardcoding URLs.
#### 1.6.4 Add TypeScript Device Management
**File**: `web/src/device-management.ts` (CREATE NEW FILE)
@@ -714,7 +714,7 @@ function copyToClipboard(text: string, label: string): void {
}
// Regenerate device token with confirmation
function regenerateDeviceToken(deviceId: string): void {
function regenerateDeviceToken(deviceId: string, event: Event): void {
const confirmation = '⚠️ This will revoke current token and generate a new one.\n\n' +
'The old token will immediately stop working.\n\n' +
'You will need to update your device configuration with new token.\n\n' +
@@ -1214,7 +1214,6 @@ docs {
**KEEP EXISTING REQUESTS** in `bruno/sync-kobo/api.bru` for API clients and testing (Bearer token works for non-Kobo clients)
**Verification**: Test both authentication methods work (Kobo uses URL path, API clients can use Bearer)
---
## Phase 2: Kobo Integration