chore(api): update to allow CRUD progress and activity in v1

This commit is contained in:
2026-04-03 10:37:50 -04:00
parent decc3f0195
commit 8ec3349b7c
12 changed files with 851 additions and 35 deletions

View File

@@ -2,7 +2,9 @@ package v1
import (
"context"
"time"
log "github.com/sirupsen/logrus"
"reichard.io/antholume/database"
)
@@ -72,3 +74,78 @@ func (s *Server) GetActivity(ctx context.Context, request GetActivityRequestObje
}
return GetActivity200JSONResponse(response), nil
}
// POST /activity
func (s *Server) CreateActivity(ctx context.Context, request CreateActivityRequestObject) (CreateActivityResponseObject, error) {
auth, ok := s.getSessionFromContext(ctx)
if !ok {
return CreateActivity401JSONResponse{Code: 401, Message: "Unauthorized"}, nil
}
if request.Body == nil {
return CreateActivity400JSONResponse{Code: 400, Message: "Request body is required"}, nil
}
tx, err := s.db.DB.Begin()
if err != nil {
log.Error("Transaction Begin DB Error:", err)
return CreateActivity500JSONResponse{Code: 500, Message: "Database error"}, nil
}
committed := false
defer func() {
if committed {
return
}
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Debug("Transaction Rollback DB Error:", rollbackErr)
}
}()
qtx := s.db.Queries.WithTx(tx)
allDocumentsMap := make(map[string]struct{})
for _, item := range request.Body.Activity {
allDocumentsMap[item.DocumentId] = struct{}{}
}
for documentID := range allDocumentsMap {
if _, err := qtx.UpsertDocument(ctx, database.UpsertDocumentParams{ID: documentID}); err != nil {
log.Error("UpsertDocument DB Error:", err)
return CreateActivity400JSONResponse{Code: 400, Message: "Invalid document"}, nil
}
}
if _, err := qtx.UpsertDevice(ctx, database.UpsertDeviceParams{
ID: request.Body.DeviceId,
UserID: auth.UserName,
DeviceName: request.Body.DeviceName,
LastSynced: time.Now().UTC().Format(time.RFC3339),
}); err != nil {
log.Error("UpsertDevice DB Error:", err)
return CreateActivity400JSONResponse{Code: 400, Message: "Invalid device"}, nil
}
for _, item := range request.Body.Activity {
if _, err := qtx.AddActivity(ctx, database.AddActivityParams{
UserID: auth.UserName,
DocumentID: item.DocumentId,
DeviceID: request.Body.DeviceId,
StartTime: time.Unix(item.StartTime, 0).UTC().Format(time.RFC3339),
Duration: item.Duration,
StartPercentage: float64(item.Page) / float64(item.Pages),
EndPercentage: float64(item.Page+1) / float64(item.Pages),
}); err != nil {
log.Error("AddActivity DB Error:", err)
return CreateActivity400JSONResponse{Code: 400, Message: "Invalid activity"}, nil
}
}
if err := tx.Commit(); err != nil {
log.Error("Transaction Commit DB Error:", err)
return CreateActivity500JSONResponse{Code: 500, Message: "Database error"}, nil
}
committed = true
response := CreateActivityResponse{Added: int64(len(request.Body.Activity))}
return CreateActivity200JSONResponse(response), nil
}

View File

@@ -164,6 +164,27 @@ type ActivityResponse struct {
// BackupType defines model for BackupType.
type BackupType string
// CreateActivityItem defines model for CreateActivityItem.
type CreateActivityItem struct {
DocumentId string `json:"document_id"`
Duration int64 `json:"duration"`
Page int64 `json:"page"`
Pages int64 `json:"pages"`
StartTime int64 `json:"start_time"`
}
// CreateActivityRequest defines model for CreateActivityRequest.
type CreateActivityRequest struct {
Activity []CreateActivityItem `json:"activity"`
DeviceId string `json:"device_id"`
DeviceName string `json:"device_name"`
}
// CreateActivityResponse defines model for CreateActivityResponse.
type CreateActivityResponse struct {
Added int64 `json:"added"`
}
// DatabaseInfo defines model for DatabaseInfo.
type DatabaseInfo struct {
ActivitySize int64 `json:"activity_size"`
@@ -335,9 +356,11 @@ type OperationType string
type Progress struct {
Author *string `json:"author,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
DeviceId *string `json:"device_id,omitempty"`
DeviceName *string `json:"device_name,omitempty"`
DocumentId *string `json:"document_id,omitempty"`
Percentage *float64 `json:"percentage,omitempty"`
Progress *string `json:"progress,omitempty"`
Title *string `json:"title,omitempty"`
UserId *string `json:"user_id,omitempty"`
}
@@ -388,6 +411,21 @@ type StreaksResponse struct {
Streaks []UserStreak `json:"streaks"`
}
// UpdateProgressRequest defines model for UpdateProgressRequest.
type UpdateProgressRequest struct {
DeviceId string `json:"device_id"`
DeviceName string `json:"device_name"`
DocumentId string `json:"document_id"`
Percentage float64 `json:"percentage"`
Progress string `json:"progress"`
}
// UpdateProgressResponse defines model for UpdateProgressResponse.
type UpdateProgressResponse struct {
DocumentId string `json:"document_id"`
Timestamp time.Time `json:"timestamp"`
}
// UpdateSettingsRequest defines model for UpdateSettingsRequest.
type UpdateSettingsRequest struct {
NewPassword *string `json:"new_password,omitempty"`
@@ -533,6 +571,9 @@ type PostSearchFormdataBody struct {
Title string `form:"title" json:"title"`
}
// CreateActivityJSONRequestBody defines body for CreateActivity for application/json ContentType.
type CreateActivityJSONRequestBody = CreateActivityRequest
// PostAdminActionMultipartRequestBody defines body for PostAdminAction for multipart/form-data ContentType.
type PostAdminActionMultipartRequestBody PostAdminActionMultipartBody
@@ -557,6 +598,9 @@ type EditDocumentJSONRequestBody EditDocumentJSONBody
// UploadDocumentCoverMultipartRequestBody defines body for UploadDocumentCover for multipart/form-data ContentType.
type UploadDocumentCoverMultipartRequestBody UploadDocumentCoverMultipartBody
// UpdateProgressJSONRequestBody defines body for UpdateProgress for application/json ContentType.
type UpdateProgressJSONRequestBody = UpdateProgressRequest
// PostSearchFormdataRequestBody defines body for PostSearch for application/x-www-form-urlencoded ContentType.
type PostSearchFormdataRequestBody PostSearchFormdataBody
@@ -568,6 +612,9 @@ type ServerInterface interface {
// Get activity data
// (GET /activity)
GetActivity(w http.ResponseWriter, r *http.Request, params GetActivityParams)
// Create activity records
// (POST /activity)
CreateActivity(w http.ResponseWriter, r *http.Request)
// Get admin page data
// (GET /admin)
GetAdmin(w http.ResponseWriter, r *http.Request)
@@ -643,6 +690,9 @@ type ServerInterface interface {
// List progress records
// (GET /progress)
GetProgressList(w http.ResponseWriter, r *http.Request, params GetProgressListParams)
// Update document progress
// (PUT /progress)
UpdateProgress(w http.ResponseWriter, r *http.Request)
// Get document progress
// (GET /progress/{id})
GetProgress(w http.ResponseWriter, r *http.Request, id string)
@@ -726,6 +776,26 @@ func (siw *ServerInterfaceWrapper) GetActivity(w http.ResponseWriter, r *http.Re
handler.ServeHTTP(w, r)
}
// CreateActivity operation middleware
func (siw *ServerInterfaceWrapper) CreateActivity(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, BearerAuthScopes, []string{})
r = r.WithContext(ctx)
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
siw.Handler.CreateActivity(w, r)
}))
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler.ServeHTTP(w, r)
}
// GetAdmin operation middleware
func (siw *ServerInterfaceWrapper) GetAdmin(w http.ResponseWriter, r *http.Request) {
@@ -1371,6 +1441,26 @@ func (siw *ServerInterfaceWrapper) GetProgressList(w http.ResponseWriter, r *htt
handler.ServeHTTP(w, r)
}
// UpdateProgress operation middleware
func (siw *ServerInterfaceWrapper) UpdateProgress(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, BearerAuthScopes, []string{})
r = r.WithContext(ctx)
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
siw.Handler.UpdateProgress(w, r)
}))
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler.ServeHTTP(w, r)
}
// GetProgress operation middleware
func (siw *ServerInterfaceWrapper) GetProgress(w http.ResponseWriter, r *http.Request) {
@@ -1638,6 +1728,7 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
}
m.HandleFunc("GET "+options.BaseURL+"/activity", wrapper.GetActivity)
m.HandleFunc("POST "+options.BaseURL+"/activity", wrapper.CreateActivity)
m.HandleFunc("GET "+options.BaseURL+"/admin", wrapper.GetAdmin)
m.HandleFunc("POST "+options.BaseURL+"/admin", wrapper.PostAdminAction)
m.HandleFunc("GET "+options.BaseURL+"/admin/import", wrapper.GetImportDirectory)
@@ -1663,6 +1754,7 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
m.HandleFunc("GET "+options.BaseURL+"/home/streaks", wrapper.GetStreaks)
m.HandleFunc("GET "+options.BaseURL+"/info", wrapper.GetInfo)
m.HandleFunc("GET "+options.BaseURL+"/progress", wrapper.GetProgressList)
m.HandleFunc("PUT "+options.BaseURL+"/progress", wrapper.UpdateProgress)
m.HandleFunc("GET "+options.BaseURL+"/progress/{id}", wrapper.GetProgress)
m.HandleFunc("GET "+options.BaseURL+"/search", wrapper.GetSearch)
m.HandleFunc("POST "+options.BaseURL+"/search", wrapper.PostSearch)
@@ -1707,6 +1799,50 @@ func (response GetActivity500JSONResponse) VisitGetActivityResponse(w http.Respo
return json.NewEncoder(w).Encode(response)
}
type CreateActivityRequestObject struct {
Body *CreateActivityJSONRequestBody
}
type CreateActivityResponseObject interface {
VisitCreateActivityResponse(w http.ResponseWriter) error
}
type CreateActivity200JSONResponse CreateActivityResponse
func (response CreateActivity200JSONResponse) VisitCreateActivityResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
return json.NewEncoder(w).Encode(response)
}
type CreateActivity400JSONResponse ErrorResponse
func (response CreateActivity400JSONResponse) VisitCreateActivityResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(400)
return json.NewEncoder(w).Encode(response)
}
type CreateActivity401JSONResponse ErrorResponse
func (response CreateActivity401JSONResponse) VisitCreateActivityResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(401)
return json.NewEncoder(w).Encode(response)
}
type CreateActivity500JSONResponse ErrorResponse
func (response CreateActivity500JSONResponse) VisitCreateActivityResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(500)
return json.NewEncoder(w).Encode(response)
}
type GetAdminRequestObject struct {
}
@@ -2730,6 +2866,50 @@ func (response GetProgressList500JSONResponse) VisitGetProgressListResponse(w ht
return json.NewEncoder(w).Encode(response)
}
type UpdateProgressRequestObject struct {
Body *UpdateProgressJSONRequestBody
}
type UpdateProgressResponseObject interface {
VisitUpdateProgressResponse(w http.ResponseWriter) error
}
type UpdateProgress200JSONResponse UpdateProgressResponse
func (response UpdateProgress200JSONResponse) VisitUpdateProgressResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
return json.NewEncoder(w).Encode(response)
}
type UpdateProgress400JSONResponse ErrorResponse
func (response UpdateProgress400JSONResponse) VisitUpdateProgressResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(400)
return json.NewEncoder(w).Encode(response)
}
type UpdateProgress401JSONResponse ErrorResponse
func (response UpdateProgress401JSONResponse) VisitUpdateProgressResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(401)
return json.NewEncoder(w).Encode(response)
}
type UpdateProgress500JSONResponse ErrorResponse
func (response UpdateProgress500JSONResponse) VisitUpdateProgressResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(500)
return json.NewEncoder(w).Encode(response)
}
type GetProgressRequestObject struct {
Id string `json:"id"`
}
@@ -2935,6 +3115,9 @@ type StrictServerInterface interface {
// Get activity data
// (GET /activity)
GetActivity(ctx context.Context, request GetActivityRequestObject) (GetActivityResponseObject, error)
// Create activity records
// (POST /activity)
CreateActivity(ctx context.Context, request CreateActivityRequestObject) (CreateActivityResponseObject, error)
// Get admin page data
// (GET /admin)
GetAdmin(ctx context.Context, request GetAdminRequestObject) (GetAdminResponseObject, error)
@@ -3010,6 +3193,9 @@ type StrictServerInterface interface {
// List progress records
// (GET /progress)
GetProgressList(ctx context.Context, request GetProgressListRequestObject) (GetProgressListResponseObject, error)
// Update document progress
// (PUT /progress)
UpdateProgress(ctx context.Context, request UpdateProgressRequestObject) (UpdateProgressResponseObject, error)
// Get document progress
// (GET /progress/{id})
GetProgress(ctx context.Context, request GetProgressRequestObject) (GetProgressResponseObject, error)
@@ -3082,6 +3268,37 @@ func (sh *strictHandler) GetActivity(w http.ResponseWriter, r *http.Request, par
}
}
// CreateActivity operation middleware
func (sh *strictHandler) CreateActivity(w http.ResponseWriter, r *http.Request) {
var request CreateActivityRequestObject
var body CreateActivityJSONRequestBody
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err))
return
}
request.Body = &body
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
return sh.ssi.CreateActivity(ctx, request.(CreateActivityRequestObject))
}
for _, middleware := range sh.middlewares {
handler = middleware(handler, "CreateActivity")
}
response, err := handler(r.Context(), w, r, request)
if err != nil {
sh.options.ResponseErrorHandlerFunc(w, r, err)
} else if validResponse, ok := response.(CreateActivityResponseObject); ok {
if err := validResponse.VisitCreateActivityResponse(w); err != nil {
sh.options.ResponseErrorHandlerFunc(w, r, err)
}
} else if response != nil {
sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
}
}
// GetAdmin operation middleware
func (sh *strictHandler) GetAdmin(w http.ResponseWriter, r *http.Request) {
var request GetAdminRequestObject
@@ -3764,6 +3981,37 @@ func (sh *strictHandler) GetProgressList(w http.ResponseWriter, r *http.Request,
}
}
// UpdateProgress operation middleware
func (sh *strictHandler) UpdateProgress(w http.ResponseWriter, r *http.Request) {
var request UpdateProgressRequestObject
var body UpdateProgressJSONRequestBody
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err))
return
}
request.Body = &body
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
return sh.ssi.UpdateProgress(ctx, request.(UpdateProgressRequestObject))
}
for _, middleware := range sh.middlewares {
handler = middleware(handler, "UpdateProgress")
}
response, err := handler(r.Context(), w, r, request)
if err != nil {
sh.options.ResponseErrorHandlerFunc(w, r, err)
} else if validResponse, ok := response.(UpdateProgressResponseObject); ok {
if err := validResponse.VisitUpdateProgressResponse(w); err != nil {
sh.options.ResponseErrorHandlerFunc(w, r, err)
}
} else if response != nil {
sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
}
}
// GetProgress operation middleware
func (sh *strictHandler) GetProgress(w http.ResponseWriter, r *http.Request, id string) {
var request GetProgressRequestObject

View File

@@ -92,9 +92,13 @@ components:
type: string
device_name:
type: string
device_id:
type: string
percentage:
type: number
format: double
progress:
type: string
document_id:
type: string
user_id:
@@ -103,6 +107,88 @@ components:
type: string
format: date-time
UpdateProgressRequest:
type: object
properties:
document_id:
type: string
percentage:
type: number
format: double
progress:
type: string
device_id:
type: string
device_name:
type: string
required:
- document_id
- percentage
- progress
- device_id
- device_name
UpdateProgressResponse:
type: object
properties:
document_id:
type: string
timestamp:
type: string
format: date-time
required:
- document_id
- timestamp
CreateActivityItem:
type: object
properties:
document_id:
type: string
start_time:
type: integer
format: int64
duration:
type: integer
format: int64
page:
type: integer
format: int64
pages:
type: integer
format: int64
required:
- document_id
- start_time
- duration
- page
- pages
CreateActivityRequest:
type: object
properties:
device_id:
type: string
device_name:
type: string
activity:
type: array
items:
$ref: '#/components/schemas/CreateActivityItem'
required:
- device_id
- device_name
- activity
CreateActivityResponse:
type: object
properties:
added:
type: integer
format: int64
required:
- added
Activity:
type: object
properties:
@@ -1003,6 +1089,44 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
put:
summary: Update document progress
operationId: updateProgress
tags:
- Progress
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateProgressRequest'
responses:
200:
description: Progress updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateProgressResponse'
400:
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
401:
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
500:
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/progress/{id}:
get:
@@ -1093,6 +1217,44 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
post:
summary: Create activity records
operationId: createActivity
tags:
- Activity
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateActivityRequest'
responses:
200:
description: Activity created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/CreateActivityResponse'
400:
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
401:
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
500:
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/settings:
get:

View File

@@ -3,9 +3,10 @@ package v1
import (
"context"
"math"
"time"
"reichard.io/antholume/database"
log "github.com/sirupsen/logrus"
"reichard.io/antholume/database"
)
// GET /progress
@@ -26,9 +27,9 @@ func (s *Server) GetProgressList(ctx context.Context, request GetProgressListReq
}
filter := database.GetProgressParams{
UserID: auth.UserName,
Offset: (page - 1) * limit,
Limit: limit,
UserID: auth.UserName,
Offset: (page - 1) * limit,
Limit: limit,
}
if request.Params.Document != nil && *request.Params.Document != "" {
@@ -45,7 +46,7 @@ func (s *Server) GetProgressList(ctx context.Context, request GetProgressListReq
total := int64(len(progress))
var nextPage *int64
var previousPage *int64
// Calculate total pages
totalPages := int64(math.Ceil(float64(total) / float64(limit)))
if page < totalPages {
@@ -58,13 +59,13 @@ func (s *Server) GetProgressList(ctx context.Context, request GetProgressListReq
apiProgress := make([]Progress, len(progress))
for i, row := range progress {
apiProgress[i] = Progress{
Title: row.Title,
Author: row.Author,
DeviceName: &row.DeviceName,
Percentage: &row.Percentage,
DocumentId: &row.DocumentID,
UserId: &row.UserID,
CreatedAt: parseTimePtr(row.CreatedAt),
Title: row.Title,
Author: row.Author,
DeviceName: &row.DeviceName,
Percentage: &row.Percentage,
DocumentId: &row.DocumentID,
UserId: &row.UserID,
CreatedAt: parseTimePtr(row.CreatedAt),
}
}
@@ -87,33 +88,23 @@ func (s *Server) GetProgress(ctx context.Context, request GetProgressRequestObje
return GetProgress401JSONResponse{Code: 401, Message: "Unauthorized"}, nil
}
filter := database.GetProgressParams{
UserID: auth.UserName,
DocFilter: true,
DocumentID: request.Id,
Offset: 0,
Limit: 1,
}
progress, err := s.db.Queries.GetProgress(ctx, filter)
row, err := s.db.Queries.GetDocumentProgress(ctx, database.GetDocumentProgressParams{
UserID: auth.UserName,
DocumentID: request.Id,
})
if err != nil {
log.Error("GetProgress DB Error:", err)
log.Error("GetDocumentProgress DB Error:", err)
return GetProgress404JSONResponse{Code: 404, Message: "Progress not found"}, nil
}
if len(progress) == 0 {
return GetProgress404JSONResponse{Code: 404, Message: "Progress not found"}, nil
}
row := progress[0]
apiProgress := Progress{
Title: row.Title,
Author: row.Author,
DeviceName: &row.DeviceName,
Percentage: &row.Percentage,
DocumentId: &row.DocumentID,
UserId: &row.UserID,
CreatedAt: parseTimePtr(row.CreatedAt),
DeviceName: &row.DeviceName,
DeviceId: &row.DeviceID,
Percentage: &row.Percentage,
Progress: &row.Progress,
DocumentId: &row.DocumentID,
UserId: &row.UserID,
CreatedAt: parseTimePtr(row.CreatedAt),
}
response := ProgressResponse{
@@ -121,4 +112,52 @@ func (s *Server) GetProgress(ctx context.Context, request GetProgressRequestObje
}
return GetProgress200JSONResponse(response), nil
}
}
// PUT /progress
func (s *Server) UpdateProgress(ctx context.Context, request UpdateProgressRequestObject) (UpdateProgressResponseObject, error) {
auth, ok := s.getSessionFromContext(ctx)
if !ok {
return UpdateProgress401JSONResponse{Code: 401, Message: "Unauthorized"}, nil
}
if request.Body == nil {
return UpdateProgress400JSONResponse{Code: 400, Message: "Request body is required"}, nil
}
if _, err := s.db.Queries.UpsertDevice(ctx, database.UpsertDeviceParams{
ID: request.Body.DeviceId,
UserID: auth.UserName,
DeviceName: request.Body.DeviceName,
LastSynced: time.Now().UTC().Format(time.RFC3339),
}); err != nil {
log.Error("UpsertDevice DB Error:", err)
return UpdateProgress500JSONResponse{Code: 500, Message: "Database error"}, nil
}
if _, err := s.db.Queries.UpsertDocument(ctx, database.UpsertDocumentParams{
ID: request.Body.DocumentId,
}); err != nil {
log.Error("UpsertDocument DB Error:", err)
return UpdateProgress500JSONResponse{Code: 500, Message: "Database error"}, nil
}
progress, err := s.db.Queries.UpdateProgress(ctx, database.UpdateProgressParams{
Percentage: request.Body.Percentage,
DocumentID: request.Body.DocumentId,
DeviceID: request.Body.DeviceId,
UserID: auth.UserName,
Progress: request.Body.Progress,
})
if err != nil {
log.Error("UpdateProgress DB Error:", err)
return UpdateProgress400JSONResponse{Code: 400, Message: "Invalid request"}, nil
}
response := UpdateProgressResponse{
DocumentId: progress.DocumentID,
Timestamp: parseTime(progress.CreatedAt),
}
return UpdateProgress200JSONResponse(response), nil
}