wip 16
This commit is contained in:
@@ -69,7 +69,6 @@ func (s *Server) GetActivity(ctx context.Context, request GetActivityRequestObje
|
||||
|
||||
response := ActivityResponse{
|
||||
Activities: apiActivities,
|
||||
User: UserData{Username: auth.UserName, IsAdmin: auth.IsAdmin},
|
||||
}
|
||||
return GetActivity200JSONResponse(response), nil
|
||||
}
|
||||
|
||||
@@ -159,7 +159,6 @@ type Activity struct {
|
||||
// ActivityResponse defines model for ActivityResponse.
|
||||
type ActivityResponse struct {
|
||||
Activities []Activity `json:"activities"`
|
||||
User UserData `json:"user"`
|
||||
}
|
||||
|
||||
// BackupType defines model for BackupType.
|
||||
@@ -217,7 +216,6 @@ type Document struct {
|
||||
type DocumentResponse struct {
|
||||
Document Document `json:"document"`
|
||||
Progress *Progress `json:"progress,omitempty"`
|
||||
User UserData `json:"user"`
|
||||
}
|
||||
|
||||
// DocumentsResponse defines model for DocumentsResponse.
|
||||
@@ -248,7 +246,6 @@ type GraphDataPoint struct {
|
||||
// GraphDataResponse defines model for GraphDataResponse.
|
||||
type GraphDataResponse struct {
|
||||
GraphData []GraphDataPoint `json:"graph_data"`
|
||||
User UserData `json:"user"`
|
||||
}
|
||||
|
||||
// HomeResponse defines model for HomeResponse.
|
||||
@@ -256,7 +253,6 @@ type HomeResponse struct {
|
||||
DatabaseInfo DatabaseInfo `json:"database_info"`
|
||||
GraphData GraphDataResponse `json:"graph_data"`
|
||||
Streaks StreaksResponse `json:"streaks"`
|
||||
User UserData `json:"user"`
|
||||
UserStatistics UserStatisticsResponse `json:"user_statistics"`
|
||||
}
|
||||
|
||||
@@ -349,13 +345,11 @@ type ProgressListResponse struct {
|
||||
PreviousPage *int64 `json:"previous_page,omitempty"`
|
||||
Progress *[]Progress `json:"progress,omitempty"`
|
||||
Total *int64 `json:"total,omitempty"`
|
||||
User *UserData `json:"user,omitempty"`
|
||||
}
|
||||
|
||||
// ProgressResponse defines model for ProgressResponse.
|
||||
type ProgressResponse struct {
|
||||
Progress *Progress `json:"progress,omitempty"`
|
||||
User *UserData `json:"user,omitempty"`
|
||||
}
|
||||
|
||||
// SearchItem defines model for SearchItem.
|
||||
@@ -387,7 +381,6 @@ type SettingsResponse struct {
|
||||
// StreaksResponse defines model for StreaksResponse.
|
||||
type StreaksResponse struct {
|
||||
Streaks []UserStreak `json:"streaks"`
|
||||
User UserData `json:"user"`
|
||||
}
|
||||
|
||||
// UpdateSettingsRequest defines model for UpdateSettingsRequest.
|
||||
@@ -413,7 +406,6 @@ type UserData struct {
|
||||
// UserStatisticsResponse defines model for UserStatisticsResponse.
|
||||
type UserStatisticsResponse struct {
|
||||
Duration LeaderboardData `json:"duration"`
|
||||
User UserData `json:"user"`
|
||||
Words LeaderboardData `json:"words"`
|
||||
Wpm LeaderboardData `json:"wpm"`
|
||||
}
|
||||
@@ -495,6 +487,21 @@ type CreateDocumentMultipartBody struct {
|
||||
DocumentFile openapi_types.File `json:"document_file"`
|
||||
}
|
||||
|
||||
// EditDocumentJSONBody defines parameters for EditDocument.
|
||||
type EditDocumentJSONBody struct {
|
||||
Author *string `json:"author,omitempty"`
|
||||
CoverGbid *string `json:"cover_gbid,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Isbn10 *string `json:"isbn10,omitempty"`
|
||||
Isbn13 *string `json:"isbn13,omitempty"`
|
||||
Title *string `json:"title,omitempty"`
|
||||
}
|
||||
|
||||
// UploadDocumentCoverMultipartBody defines parameters for UploadDocumentCover.
|
||||
type UploadDocumentCoverMultipartBody struct {
|
||||
CoverFile openapi_types.File `json:"cover_file"`
|
||||
}
|
||||
|
||||
// GetProgressListParams defines parameters for GetProgressList.
|
||||
type GetProgressListParams struct {
|
||||
Page *int64 `form:"page,omitempty" json:"page,omitempty"`
|
||||
@@ -534,6 +541,12 @@ type LoginJSONRequestBody = LoginRequest
|
||||
// CreateDocumentMultipartRequestBody defines body for CreateDocument for multipart/form-data ContentType.
|
||||
type CreateDocumentMultipartRequestBody CreateDocumentMultipartBody
|
||||
|
||||
// EditDocumentJSONRequestBody defines body for EditDocument for application/json ContentType.
|
||||
type EditDocumentJSONRequestBody EditDocumentJSONBody
|
||||
|
||||
// UploadDocumentCoverMultipartRequestBody defines body for UploadDocumentCover for multipart/form-data ContentType.
|
||||
type UploadDocumentCoverMultipartRequestBody UploadDocumentCoverMultipartBody
|
||||
|
||||
// PostSearchFormdataRequestBody defines body for PostSearch for application/x-www-form-urlencoded ContentType.
|
||||
type PostSearchFormdataRequestBody PostSearchFormdataBody
|
||||
|
||||
@@ -587,9 +600,15 @@ type ServerInterface interface {
|
||||
// Get a single document
|
||||
// (GET /documents/{id})
|
||||
GetDocument(w http.ResponseWriter, r *http.Request, id string)
|
||||
// Update document editable fields
|
||||
// (POST /documents/{id})
|
||||
EditDocument(w http.ResponseWriter, r *http.Request, id string)
|
||||
// Get document cover image
|
||||
// (GET /documents/{id}/cover)
|
||||
GetDocumentCover(w http.ResponseWriter, r *http.Request, id string)
|
||||
// Upload document cover image
|
||||
// (POST /documents/{id}/cover)
|
||||
UploadDocumentCover(w http.ResponseWriter, r *http.Request, id string)
|
||||
// Download document file
|
||||
// (GET /documents/{id}/file)
|
||||
GetDocumentFile(w http.ResponseWriter, r *http.Request, id string)
|
||||
@@ -1042,6 +1061,37 @@ func (siw *ServerInterfaceWrapper) GetDocument(w http.ResponseWriter, r *http.Re
|
||||
handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// EditDocument operation middleware
|
||||
func (siw *ServerInterfaceWrapper) EditDocument(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var err error
|
||||
|
||||
// ------------- Path parameter "id" -------------
|
||||
var id string
|
||||
|
||||
err = runtime.BindStyledParameterWithOptions("simple", "id", r.PathValue("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: ""})
|
||||
if err != nil {
|
||||
siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
|
||||
return
|
||||
}
|
||||
|
||||
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.EditDocument(w, r, id)
|
||||
}))
|
||||
|
||||
for _, middleware := range siw.HandlerMiddlewares {
|
||||
handler = middleware(handler)
|
||||
}
|
||||
|
||||
handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// GetDocumentCover operation middleware
|
||||
func (siw *ServerInterfaceWrapper) GetDocumentCover(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -1073,6 +1123,37 @@ func (siw *ServerInterfaceWrapper) GetDocumentCover(w http.ResponseWriter, r *ht
|
||||
handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// UploadDocumentCover operation middleware
|
||||
func (siw *ServerInterfaceWrapper) UploadDocumentCover(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var err error
|
||||
|
||||
// ------------- Path parameter "id" -------------
|
||||
var id string
|
||||
|
||||
err = runtime.BindStyledParameterWithOptions("simple", "id", r.PathValue("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: ""})
|
||||
if err != nil {
|
||||
siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
|
||||
return
|
||||
}
|
||||
|
||||
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.UploadDocumentCover(w, r, id)
|
||||
}))
|
||||
|
||||
for _, middleware := range siw.HandlerMiddlewares {
|
||||
handler = middleware(handler)
|
||||
}
|
||||
|
||||
handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// GetDocumentFile operation middleware
|
||||
func (siw *ServerInterfaceWrapper) GetDocumentFile(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -1528,7 +1609,9 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
|
||||
m.HandleFunc("GET "+options.BaseURL+"/documents", wrapper.GetDocuments)
|
||||
m.HandleFunc("POST "+options.BaseURL+"/documents", wrapper.CreateDocument)
|
||||
m.HandleFunc("GET "+options.BaseURL+"/documents/{id}", wrapper.GetDocument)
|
||||
m.HandleFunc("POST "+options.BaseURL+"/documents/{id}", wrapper.EditDocument)
|
||||
m.HandleFunc("GET "+options.BaseURL+"/documents/{id}/cover", wrapper.GetDocumentCover)
|
||||
m.HandleFunc("POST "+options.BaseURL+"/documents/{id}/cover", wrapper.UploadDocumentCover)
|
||||
m.HandleFunc("GET "+options.BaseURL+"/documents/{id}/file", wrapper.GetDocumentFile)
|
||||
m.HandleFunc("GET "+options.BaseURL+"/home", wrapper.GetHome)
|
||||
m.HandleFunc("GET "+options.BaseURL+"/home/graph", wrapper.GetGraphData)
|
||||
@@ -2112,6 +2195,60 @@ func (response GetDocument500JSONResponse) VisitGetDocumentResponse(w http.Respo
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type EditDocumentRequestObject struct {
|
||||
Id string `json:"id"`
|
||||
Body *EditDocumentJSONRequestBody
|
||||
}
|
||||
|
||||
type EditDocumentResponseObject interface {
|
||||
VisitEditDocumentResponse(w http.ResponseWriter) error
|
||||
}
|
||||
|
||||
type EditDocument200JSONResponse DocumentResponse
|
||||
|
||||
func (response EditDocument200JSONResponse) VisitEditDocumentResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(200)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type EditDocument400JSONResponse ErrorResponse
|
||||
|
||||
func (response EditDocument400JSONResponse) VisitEditDocumentResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(400)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type EditDocument401JSONResponse ErrorResponse
|
||||
|
||||
func (response EditDocument401JSONResponse) VisitEditDocumentResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(401)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type EditDocument404JSONResponse ErrorResponse
|
||||
|
||||
func (response EditDocument404JSONResponse) VisitEditDocumentResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(404)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type EditDocument500JSONResponse ErrorResponse
|
||||
|
||||
func (response EditDocument500JSONResponse) VisitEditDocumentResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(500)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type GetDocumentCoverRequestObject struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
@@ -2185,6 +2322,60 @@ func (response GetDocumentCover500JSONResponse) VisitGetDocumentCoverResponse(w
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type UploadDocumentCoverRequestObject struct {
|
||||
Id string `json:"id"`
|
||||
Body *multipart.Reader
|
||||
}
|
||||
|
||||
type UploadDocumentCoverResponseObject interface {
|
||||
VisitUploadDocumentCoverResponse(w http.ResponseWriter) error
|
||||
}
|
||||
|
||||
type UploadDocumentCover200JSONResponse DocumentResponse
|
||||
|
||||
func (response UploadDocumentCover200JSONResponse) VisitUploadDocumentCoverResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(200)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type UploadDocumentCover400JSONResponse ErrorResponse
|
||||
|
||||
func (response UploadDocumentCover400JSONResponse) VisitUploadDocumentCoverResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(400)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type UploadDocumentCover401JSONResponse ErrorResponse
|
||||
|
||||
func (response UploadDocumentCover401JSONResponse) VisitUploadDocumentCoverResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(401)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type UploadDocumentCover404JSONResponse ErrorResponse
|
||||
|
||||
func (response UploadDocumentCover404JSONResponse) VisitUploadDocumentCoverResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(404)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type UploadDocumentCover500JSONResponse ErrorResponse
|
||||
|
||||
func (response UploadDocumentCover500JSONResponse) VisitUploadDocumentCoverResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(500)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type GetDocumentFileRequestObject struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
@@ -2682,9 +2873,15 @@ type StrictServerInterface interface {
|
||||
// Get a single document
|
||||
// (GET /documents/{id})
|
||||
GetDocument(ctx context.Context, request GetDocumentRequestObject) (GetDocumentResponseObject, error)
|
||||
// Update document editable fields
|
||||
// (POST /documents/{id})
|
||||
EditDocument(ctx context.Context, request EditDocumentRequestObject) (EditDocumentResponseObject, error)
|
||||
// Get document cover image
|
||||
// (GET /documents/{id}/cover)
|
||||
GetDocumentCover(ctx context.Context, request GetDocumentCoverRequestObject) (GetDocumentCoverResponseObject, error)
|
||||
// Upload document cover image
|
||||
// (POST /documents/{id}/cover)
|
||||
UploadDocumentCover(ctx context.Context, request UploadDocumentCoverRequestObject) (UploadDocumentCoverResponseObject, error)
|
||||
// Download document file
|
||||
// (GET /documents/{id}/file)
|
||||
GetDocumentFile(ctx context.Context, request GetDocumentFileRequestObject) (GetDocumentFileResponseObject, error)
|
||||
@@ -3165,6 +3362,39 @@ func (sh *strictHandler) GetDocument(w http.ResponseWriter, r *http.Request, id
|
||||
}
|
||||
}
|
||||
|
||||
// EditDocument operation middleware
|
||||
func (sh *strictHandler) EditDocument(w http.ResponseWriter, r *http.Request, id string) {
|
||||
var request EditDocumentRequestObject
|
||||
|
||||
request.Id = id
|
||||
|
||||
var body EditDocumentJSONRequestBody
|
||||
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.EditDocument(ctx, request.(EditDocumentRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "EditDocument")
|
||||
}
|
||||
|
||||
response, err := handler(r.Context(), w, r, request)
|
||||
|
||||
if err != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, err)
|
||||
} else if validResponse, ok := response.(EditDocumentResponseObject); ok {
|
||||
if err := validResponse.VisitEditDocumentResponse(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))
|
||||
}
|
||||
}
|
||||
|
||||
// GetDocumentCover operation middleware
|
||||
func (sh *strictHandler) GetDocumentCover(w http.ResponseWriter, r *http.Request, id string) {
|
||||
var request GetDocumentCoverRequestObject
|
||||
@@ -3191,6 +3421,39 @@ func (sh *strictHandler) GetDocumentCover(w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
}
|
||||
|
||||
// UploadDocumentCover operation middleware
|
||||
func (sh *strictHandler) UploadDocumentCover(w http.ResponseWriter, r *http.Request, id string) {
|
||||
var request UploadDocumentCoverRequestObject
|
||||
|
||||
request.Id = id
|
||||
|
||||
if reader, err := r.MultipartReader(); err != nil {
|
||||
sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode multipart body: %w", err))
|
||||
return
|
||||
} else {
|
||||
request.Body = reader
|
||||
}
|
||||
|
||||
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
|
||||
return sh.ssi.UploadDocumentCover(ctx, request.(UploadDocumentCoverRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "UploadDocumentCover")
|
||||
}
|
||||
|
||||
response, err := handler(r.Context(), w, r, request)
|
||||
|
||||
if err != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, err)
|
||||
} else if validResponse, ok := response.(UploadDocumentCoverResponseObject); ok {
|
||||
if err := validResponse.VisitUploadDocumentCoverResponse(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))
|
||||
}
|
||||
}
|
||||
|
||||
// GetDocumentFile operation middleware
|
||||
func (sh *strictHandler) GetDocumentFile(w http.ResponseWriter, r *http.Request, id string) {
|
||||
var request GetDocumentFileRequestObject
|
||||
|
||||
@@ -52,7 +52,10 @@ func (s *Server) Login(ctx context.Context, request LoginRequestObject) (LoginRe
|
||||
}
|
||||
}
|
||||
|
||||
session, _ := store.Get(r, "token")
|
||||
session, err := store.Get(r, "token")
|
||||
if err != nil {
|
||||
return Login401JSONResponse{Code: 401, Message: "Unauthorized"}, nil
|
||||
}
|
||||
|
||||
// Configure cookie options to work with Vite proxy
|
||||
// For localhost development, we need SameSite to allow cookies across ports
|
||||
@@ -101,7 +104,10 @@ func (s *Server) Logout(ctx context.Context, request LogoutRequestObject) (Logou
|
||||
}
|
||||
}
|
||||
|
||||
session, _ := store.Get(r, "token")
|
||||
session, err := store.Get(r, "token")
|
||||
if err != nil {
|
||||
return Logout401JSONResponse{Code: 401, Message: "Unauthorized"}, nil
|
||||
}
|
||||
|
||||
// Configure cookie options (same as login)
|
||||
session.Options.SameSite = http.SameSiteLaxMode
|
||||
@@ -143,6 +149,15 @@ func (s *Server) getSessionFromContext(ctx context.Context) (authData, bool) {
|
||||
return auth, true
|
||||
}
|
||||
|
||||
// isAdmin checks if a user has admin privileges
|
||||
func (s *Server) isAdmin(ctx context.Context) bool {
|
||||
auth, ok := s.getSessionFromContext(ctx)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return auth.IsAdmin
|
||||
}
|
||||
|
||||
// getRequestFromContext extracts the HTTP request from context
|
||||
func (s *Server) getRequestFromContext(ctx context.Context) *http.Request {
|
||||
r, ok := ctx.Value("request").(*http.Request)
|
||||
|
||||
@@ -154,12 +154,111 @@ func (s *Server) GetDocument(ctx context.Context, request GetDocumentRequestObje
|
||||
|
||||
response := DocumentResponse{
|
||||
Document: apiDoc,
|
||||
User: UserData{Username: auth.UserName, IsAdmin: auth.IsAdmin},
|
||||
Progress: progress,
|
||||
}
|
||||
return GetDocument200JSONResponse(response), nil
|
||||
}
|
||||
|
||||
// POST /documents/{id}
|
||||
func (s *Server) EditDocument(ctx context.Context, request EditDocumentRequestObject) (EditDocumentResponseObject, error) {
|
||||
auth, ok := s.getSessionFromContext(ctx)
|
||||
if !ok {
|
||||
return EditDocument401JSONResponse{Code: 401, Message: "Unauthorized"}, nil
|
||||
}
|
||||
|
||||
if request.Body == nil {
|
||||
return EditDocument400JSONResponse{Code: 400, Message: "Missing request body"}, nil
|
||||
}
|
||||
|
||||
// Validate document exists and get current state
|
||||
currentDoc, err := s.db.Queries.GetDocument(ctx, request.Id)
|
||||
if err != nil {
|
||||
return EditDocument404JSONResponse{Code: 404, Message: "Document not found"}, nil
|
||||
}
|
||||
|
||||
// Validate at least one editable field is provided
|
||||
if request.Body.Title == nil &&
|
||||
request.Body.Author == nil &&
|
||||
request.Body.Description == nil &&
|
||||
request.Body.Isbn10 == nil &&
|
||||
request.Body.Isbn13 == nil &&
|
||||
request.Body.CoverGbid == nil {
|
||||
return EditDocument400JSONResponse{Code: 400, Message: "No editable fields provided"}, nil
|
||||
}
|
||||
|
||||
// Handle cover via Google Books ID
|
||||
var coverFileName *string
|
||||
if request.Body.CoverGbid != nil {
|
||||
coverDir := filepath.Join(s.cfg.DataPath, "covers")
|
||||
fileName, err := metadata.CacheCoverWithContext(ctx, *request.Body.CoverGbid, coverDir, request.Id, true)
|
||||
if err == nil {
|
||||
coverFileName = fileName
|
||||
}
|
||||
}
|
||||
|
||||
// Update document with provided editable fields only
|
||||
updatedDoc, err := s.db.Queries.UpsertDocument(ctx, database.UpsertDocumentParams{
|
||||
ID: request.Id,
|
||||
Title: request.Body.Title,
|
||||
Author: request.Body.Author,
|
||||
Description: request.Body.Description,
|
||||
Isbn10: request.Body.Isbn10,
|
||||
Isbn13: request.Body.Isbn13,
|
||||
Coverfile: coverFileName,
|
||||
// Preserve existing values for non-editable fields
|
||||
Md5: currentDoc.Md5,
|
||||
Basepath: currentDoc.Basepath,
|
||||
Filepath: currentDoc.Filepath,
|
||||
Words: currentDoc.Words,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("UpsertDocument DB Error:", err)
|
||||
return EditDocument500JSONResponse{Code: 500, Message: "Failed to update document"}, nil
|
||||
}
|
||||
|
||||
// Get progress for the document
|
||||
progressRow, err := s.db.Queries.GetDocumentProgress(ctx, database.GetDocumentProgressParams{
|
||||
UserID: auth.UserName,
|
||||
DocumentID: request.Id,
|
||||
})
|
||||
var progress *Progress
|
||||
if err == nil {
|
||||
progress = &Progress{
|
||||
UserId: &progressRow.UserID,
|
||||
DocumentId: &progressRow.DocumentID,
|
||||
DeviceName: &progressRow.DeviceName,
|
||||
Percentage: &progressRow.Percentage,
|
||||
CreatedAt: ptrOf(parseTime(progressRow.CreatedAt)),
|
||||
}
|
||||
}
|
||||
|
||||
var percentage *float32
|
||||
if progress != nil && progress.Percentage != nil {
|
||||
percentage = ptrOf(float32(*progress.Percentage))
|
||||
}
|
||||
|
||||
apiDoc := Document{
|
||||
Id: updatedDoc.ID,
|
||||
Title: *updatedDoc.Title,
|
||||
Author: *updatedDoc.Author,
|
||||
Description: updatedDoc.Description,
|
||||
Isbn10: updatedDoc.Isbn10,
|
||||
Isbn13: updatedDoc.Isbn13,
|
||||
Words: updatedDoc.Words,
|
||||
Filepath: updatedDoc.Filepath,
|
||||
CreatedAt: parseTime(updatedDoc.CreatedAt),
|
||||
UpdatedAt: parseTime(updatedDoc.UpdatedAt),
|
||||
Deleted: updatedDoc.Deleted,
|
||||
Percentage: percentage,
|
||||
}
|
||||
|
||||
response := DocumentResponse{
|
||||
Document: apiDoc,
|
||||
Progress: progress,
|
||||
}
|
||||
return EditDocument200JSONResponse(response), nil
|
||||
}
|
||||
|
||||
// deriveBaseFileName builds the base filename for a given MetadataInfo object.
|
||||
func deriveBaseFileName(metadataInfo *metadata.MetadataInfo) string {
|
||||
// Derive New FileName
|
||||
@@ -296,8 +395,12 @@ func (s *Server) GetDocumentCover(ctx context.Context, request GetDocumentCoverR
|
||||
var coverDir string = filepath.Join(s.cfg.DataPath, "covers")
|
||||
|
||||
if needMetadataFetch {
|
||||
// Create context with timeout for metadata service calls
|
||||
metadataCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Identify Documents & Save Covers
|
||||
metadataResults, err := metadata.SearchMetadata(metadata.SOURCE_GBOOK, metadata.MetadataInfo{
|
||||
metadataResults, err := metadata.SearchMetadataWithContext(metadataCtx, metadata.SOURCE_GBOOK, metadata.MetadataInfo{
|
||||
Title: document.Title,
|
||||
Author: document.Author,
|
||||
})
|
||||
@@ -306,7 +409,7 @@ func (s *Server) GetDocumentCover(ctx context.Context, request GetDocumentCoverR
|
||||
firstResult := metadataResults[0]
|
||||
|
||||
// Save Cover
|
||||
fileName, err := metadata.CacheCover(*firstResult.ID, coverDir, document.ID, false)
|
||||
fileName, err := metadata.CacheCoverWithContext(metadataCtx, *firstResult.ID, coverDir, document.ID, false)
|
||||
if err == nil {
|
||||
cachedCoverFile = *fileName
|
||||
}
|
||||
@@ -368,6 +471,136 @@ func (s *Server) GetDocumentCover(ctx context.Context, request GetDocumentCoverR
|
||||
}, nil
|
||||
}
|
||||
|
||||
// POST /documents/{id}/cover
|
||||
func (s *Server) UploadDocumentCover(ctx context.Context, request UploadDocumentCoverRequestObject) (UploadDocumentCoverResponseObject, error) {
|
||||
auth, ok := s.getSessionFromContext(ctx)
|
||||
if !ok {
|
||||
return UploadDocumentCover401JSONResponse{Code: 401, Message: "Unauthorized"}, nil
|
||||
}
|
||||
|
||||
if request.Body == nil {
|
||||
return UploadDocumentCover400JSONResponse{Code: 400, Message: "Missing request body"}, nil
|
||||
}
|
||||
|
||||
// Validate document exists
|
||||
_, err := s.db.Queries.GetDocument(ctx, request.Id)
|
||||
if err != nil {
|
||||
return UploadDocumentCover404JSONResponse{Code: 404, Message: "Document not found"}, nil
|
||||
}
|
||||
|
||||
// Read multipart form
|
||||
form, err := request.Body.ReadForm(32 << 20) // 32MB max
|
||||
if err != nil {
|
||||
log.Error("ReadForm error:", err)
|
||||
return UploadDocumentCover500JSONResponse{Code: 500, Message: "Failed to read form"}, nil
|
||||
}
|
||||
|
||||
// Get file from form
|
||||
fileField := form.File["cover_file"]
|
||||
if len(fileField) == 0 {
|
||||
return UploadDocumentCover400JSONResponse{Code: 400, Message: "No file provided"}, nil
|
||||
}
|
||||
|
||||
file := fileField[0]
|
||||
|
||||
// Validate file extension
|
||||
if !strings.HasSuffix(strings.ToLower(file.Filename), ".jpg") && !strings.HasSuffix(strings.ToLower(file.Filename), ".png") {
|
||||
return UploadDocumentCover400JSONResponse{Code: 400, Message: "Only JPG and PNG files are allowed"}, nil
|
||||
}
|
||||
|
||||
// Open file
|
||||
f, err := file.Open()
|
||||
if err != nil {
|
||||
log.Error("Open file error:", err)
|
||||
return UploadDocumentCover500JSONResponse{Code: 500, Message: "Failed to open file"}, nil
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Read file content
|
||||
data, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
log.Error("Read file error:", err)
|
||||
return UploadDocumentCover500JSONResponse{Code: 500, Message: "Failed to read file"}, nil
|
||||
}
|
||||
|
||||
// Validate actual content type
|
||||
contentType := http.DetectContentType(data)
|
||||
allowedTypes := map[string]bool{
|
||||
"image/jpeg": true,
|
||||
"image/png": true,
|
||||
}
|
||||
if !allowedTypes[contentType] {
|
||||
return UploadDocumentCover400JSONResponse{
|
||||
Code: 400,
|
||||
Message: fmt.Sprintf("Invalid file type: %s. Only JPG and PNG files are allowed.", contentType),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Derive storage path
|
||||
coverDir := filepath.Join(s.cfg.DataPath, "covers")
|
||||
fileName := fmt.Sprintf("%s%s", request.Id, strings.ToLower(filepath.Ext(file.Filename)))
|
||||
safePath := filepath.Join(coverDir, fileName)
|
||||
|
||||
// Save file
|
||||
err = os.WriteFile(safePath, data, 0644)
|
||||
if err != nil {
|
||||
log.Error("Save file error:", err)
|
||||
return UploadDocumentCover500JSONResponse{Code: 500, Message: "Unable to save cover"}, nil
|
||||
}
|
||||
|
||||
// Upsert document with new cover
|
||||
updatedDoc, err := s.db.Queries.UpsertDocument(ctx, database.UpsertDocumentParams{
|
||||
ID: request.Id,
|
||||
Coverfile: &fileName,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("UpsertDocument DB error:", err)
|
||||
return UploadDocumentCover500JSONResponse{Code: 500, Message: "Failed to save cover"}, nil
|
||||
}
|
||||
|
||||
// Get progress for the document
|
||||
progressRow, err := s.db.Queries.GetDocumentProgress(ctx, database.GetDocumentProgressParams{
|
||||
UserID: auth.UserName,
|
||||
DocumentID: request.Id,
|
||||
})
|
||||
var progress *Progress
|
||||
if err == nil {
|
||||
progress = &Progress{
|
||||
UserId: &progressRow.UserID,
|
||||
DocumentId: &progressRow.DocumentID,
|
||||
DeviceName: &progressRow.DeviceName,
|
||||
Percentage: &progressRow.Percentage,
|
||||
CreatedAt: ptrOf(parseTime(progressRow.CreatedAt)),
|
||||
}
|
||||
}
|
||||
|
||||
var percentage *float32
|
||||
if progress != nil && progress.Percentage != nil {
|
||||
percentage = ptrOf(float32(*progress.Percentage))
|
||||
}
|
||||
|
||||
apiDoc := Document{
|
||||
Id: updatedDoc.ID,
|
||||
Title: *updatedDoc.Title,
|
||||
Author: *updatedDoc.Author,
|
||||
Description: updatedDoc.Description,
|
||||
Isbn10: updatedDoc.Isbn10,
|
||||
Isbn13: updatedDoc.Isbn13,
|
||||
Words: updatedDoc.Words,
|
||||
Filepath: updatedDoc.Filepath,
|
||||
CreatedAt: parseTime(updatedDoc.CreatedAt),
|
||||
UpdatedAt: parseTime(updatedDoc.UpdatedAt),
|
||||
Deleted: updatedDoc.Deleted,
|
||||
Percentage: percentage,
|
||||
}
|
||||
|
||||
response := DocumentResponse{
|
||||
Document: apiDoc,
|
||||
Progress: progress,
|
||||
}
|
||||
return UploadDocumentCover200JSONResponse(response), nil
|
||||
}
|
||||
|
||||
// GET /documents/{id}/file
|
||||
func (s *Server) GetDocumentFile(ctx context.Context, request GetDocumentFileRequestObject) (GetDocumentFileResponseObject, error) {
|
||||
// Authentication is handled by middleware, which also adds auth data to context
|
||||
@@ -416,7 +649,7 @@ func (s *Server) GetDocumentFile(ctx context.Context, request GetDocumentFileReq
|
||||
|
||||
// POST /documents
|
||||
func (s *Server) CreateDocument(ctx context.Context, request CreateDocumentRequestObject) (CreateDocumentResponseObject, error) {
|
||||
auth, ok := s.getSessionFromContext(ctx)
|
||||
_, ok := s.getSessionFromContext(ctx)
|
||||
if !ok {
|
||||
return CreateDocument401JSONResponse{Code: 401, Message: "Unauthorized"}, nil
|
||||
}
|
||||
@@ -460,6 +693,15 @@ func (s *Server) CreateDocument(ctx context.Context, request CreateDocumentReque
|
||||
return CreateDocument500JSONResponse{Code: 500, Message: "Failed to read file"}, nil
|
||||
}
|
||||
|
||||
// Validate actual content type
|
||||
contentType := http.DetectContentType(data)
|
||||
if contentType != "application/epub+zip" && contentType != "application/zip" {
|
||||
return CreateDocument400JSONResponse{
|
||||
Code: 400,
|
||||
Message: fmt.Sprintf("Invalid file type: %s. Only EPUB files are allowed.", contentType),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create temp file to get metadata
|
||||
tempFile, err := os.CreateTemp("", "book")
|
||||
if err != nil {
|
||||
@@ -502,7 +744,6 @@ func (s *Server) CreateDocument(ctx context.Context, request CreateDocumentReque
|
||||
}
|
||||
response := DocumentResponse{
|
||||
Document: apiDoc,
|
||||
User: UserData{Username: auth.UserName, IsAdmin: auth.IsAdmin},
|
||||
}
|
||||
return CreateDocument200JSONResponse(response), nil
|
||||
}
|
||||
@@ -551,7 +792,6 @@ func (s *Server) CreateDocument(ctx context.Context, request CreateDocumentReque
|
||||
|
||||
response := DocumentResponse{
|
||||
Document: apiDoc,
|
||||
User: UserData{Username: auth.UserName, IsAdmin: auth.IsAdmin},
|
||||
}
|
||||
|
||||
return CreateDocument200JSONResponse(response), nil
|
||||
|
||||
@@ -54,23 +54,11 @@ func (s *Server) GetHome(ctx context.Context, request GetHomeRequestObject) (Get
|
||||
},
|
||||
Streaks: StreaksResponse{
|
||||
Streaks: convertStreaks(streaks),
|
||||
User: UserData{
|
||||
Username: auth.UserName,
|
||||
IsAdmin: auth.IsAdmin,
|
||||
},
|
||||
},
|
||||
GraphData: GraphDataResponse{
|
||||
GraphData: convertGraphData(graphData),
|
||||
User: UserData{
|
||||
Username: auth.UserName,
|
||||
IsAdmin: auth.IsAdmin,
|
||||
},
|
||||
},
|
||||
UserStatistics: arrangeUserStatistics(userStats),
|
||||
User: UserData{
|
||||
Username: auth.UserName,
|
||||
IsAdmin: auth.IsAdmin,
|
||||
},
|
||||
}
|
||||
|
||||
return GetHome200JSONResponse(response), nil
|
||||
@@ -91,10 +79,6 @@ func (s *Server) GetStreaks(ctx context.Context, request GetStreaksRequestObject
|
||||
|
||||
response := StreaksResponse{
|
||||
Streaks: convertStreaks(streaks),
|
||||
User: UserData{
|
||||
Username: auth.UserName,
|
||||
IsAdmin: auth.IsAdmin,
|
||||
},
|
||||
}
|
||||
|
||||
return GetStreaks200JSONResponse(response), nil
|
||||
@@ -115,10 +99,6 @@ func (s *Server) GetGraphData(ctx context.Context, request GetGraphDataRequestOb
|
||||
|
||||
response := GraphDataResponse{
|
||||
GraphData: convertGraphData(graphData),
|
||||
User: UserData{
|
||||
Username: auth.UserName,
|
||||
IsAdmin: auth.IsAdmin,
|
||||
},
|
||||
}
|
||||
|
||||
return GetGraphData200JSONResponse(response), nil
|
||||
@@ -126,7 +106,7 @@ func (s *Server) GetGraphData(ctx context.Context, request GetGraphDataRequestOb
|
||||
|
||||
// GET /home/statistics
|
||||
func (s *Server) GetUserStatistics(ctx context.Context, request GetUserStatisticsRequestObject) (GetUserStatisticsResponseObject, error) {
|
||||
auth, ok := s.getSessionFromContext(ctx)
|
||||
_, ok := s.getSessionFromContext(ctx)
|
||||
if !ok {
|
||||
return GetUserStatistics401JSONResponse{Code: 401, Message: "Unauthorized"}, nil
|
||||
}
|
||||
@@ -138,11 +118,6 @@ func (s *Server) GetUserStatistics(ctx context.Context, request GetUserStatistic
|
||||
}
|
||||
|
||||
response := arrangeUserStatistics(userStats)
|
||||
response.User = UserData{
|
||||
Username: auth.UserName,
|
||||
IsAdmin: auth.IsAdmin,
|
||||
}
|
||||
|
||||
return GetUserStatistics200JSONResponse(response), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -233,13 +233,10 @@ components:
|
||||
properties:
|
||||
document:
|
||||
$ref: '#/components/schemas/Document'
|
||||
user:
|
||||
$ref: '#/components/schemas/UserData'
|
||||
progress:
|
||||
$ref: '#/components/schemas/Progress'
|
||||
required:
|
||||
- document
|
||||
- user
|
||||
|
||||
ProgressListResponse:
|
||||
type: object
|
||||
@@ -248,8 +245,6 @@ components:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Progress'
|
||||
user:
|
||||
$ref: '#/components/schemas/UserData'
|
||||
page:
|
||||
type: integer
|
||||
format: int64
|
||||
@@ -271,8 +266,6 @@ components:
|
||||
properties:
|
||||
progress:
|
||||
$ref: '#/components/schemas/Progress'
|
||||
user:
|
||||
$ref: '#/components/schemas/UserData'
|
||||
|
||||
ActivityResponse:
|
||||
type: object
|
||||
@@ -281,11 +274,8 @@ components:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Activity'
|
||||
user:
|
||||
$ref: '#/components/schemas/UserData'
|
||||
required:
|
||||
- activities
|
||||
- user
|
||||
|
||||
Device:
|
||||
type: object
|
||||
@@ -423,11 +413,8 @@ components:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UserStreak'
|
||||
user:
|
||||
$ref: '#/components/schemas/UserData'
|
||||
required:
|
||||
- streaks
|
||||
- user
|
||||
|
||||
GraphDataPoint:
|
||||
type: object
|
||||
@@ -448,11 +435,8 @@ components:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/GraphDataPoint'
|
||||
user:
|
||||
$ref: '#/components/schemas/UserData'
|
||||
required:
|
||||
- graph_data
|
||||
- user
|
||||
|
||||
LeaderboardEntry:
|
||||
type: object
|
||||
@@ -500,13 +484,10 @@ components:
|
||||
$ref: '#/components/schemas/LeaderboardData'
|
||||
words:
|
||||
$ref: '#/components/schemas/LeaderboardData'
|
||||
user:
|
||||
$ref: '#/components/schemas/UserData'
|
||||
required:
|
||||
- wpm
|
||||
- duration
|
||||
- words
|
||||
- user
|
||||
|
||||
HomeResponse:
|
||||
type: object
|
||||
@@ -519,14 +500,11 @@ components:
|
||||
$ref: '#/components/schemas/GraphDataResponse'
|
||||
user_statistics:
|
||||
$ref: '#/components/schemas/UserStatisticsResponse'
|
||||
user:
|
||||
$ref: '#/components/schemas/UserData'
|
||||
required:
|
||||
- database_info
|
||||
- streaks
|
||||
- graph_data
|
||||
- user_statistics
|
||||
- user
|
||||
|
||||
BackupType:
|
||||
type: string
|
||||
@@ -765,6 +743,69 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
post:
|
||||
summary: Update document editable fields
|
||||
operationId: editDocument
|
||||
tags:
|
||||
- Documents
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
author:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
isbn10:
|
||||
type: string
|
||||
isbn13:
|
||||
type: string
|
||||
cover_gbid:
|
||||
type: string
|
||||
security:
|
||||
- BearerAuth: []
|
||||
responses:
|
||||
200:
|
||||
description: Successful response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DocumentResponse'
|
||||
400:
|
||||
description: Bad request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
401:
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
404:
|
||||
description: Document not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
500:
|
||||
description: Internal server error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/documents/{id}/cover:
|
||||
get:
|
||||
@@ -804,6 +845,62 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
post:
|
||||
summary: Upload document cover image
|
||||
operationId: uploadDocumentCover
|
||||
tags:
|
||||
- Documents
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
cover_file:
|
||||
type: string
|
||||
format: binary
|
||||
required:
|
||||
- cover_file
|
||||
security:
|
||||
- BearerAuth: []
|
||||
responses:
|
||||
200:
|
||||
description: Cover uploaded
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DocumentResponse'
|
||||
400:
|
||||
description: Bad request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
401:
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
404:
|
||||
description: Document not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
500:
|
||||
description: Internal server error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/documents/{id}/file:
|
||||
get:
|
||||
|
||||
@@ -70,7 +70,6 @@ func (s *Server) GetProgressList(ctx context.Context, request GetProgressListReq
|
||||
|
||||
response := ProgressListResponse{
|
||||
Progress: &apiProgress,
|
||||
User: &UserData{Username: auth.UserName, IsAdmin: auth.IsAdmin},
|
||||
Page: &page,
|
||||
Limit: &limit,
|
||||
NextPage: nextPage,
|
||||
@@ -119,7 +118,6 @@ func (s *Server) GetProgress(ctx context.Context, request GetProgressRequestObje
|
||||
|
||||
response := ProgressResponse{
|
||||
Progress: &apiProgress,
|
||||
User: &UserData{Username: auth.UserName, IsAdmin: auth.IsAdmin},
|
||||
}
|
||||
|
||||
return GetProgress200JSONResponse(response), nil
|
||||
|
||||
@@ -60,6 +60,28 @@ func (s *Server) authMiddleware(handler StrictHandlerFunc, operationID string) S
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Check admin status for admin-only endpoints
|
||||
adminEndpoints := []string{
|
||||
"GetAdmin",
|
||||
"PostAdminAction",
|
||||
"GetUsers",
|
||||
"UpdateUser",
|
||||
"GetImportDirectory",
|
||||
"PostImport",
|
||||
"GetImportResults",
|
||||
"GetLogs",
|
||||
}
|
||||
|
||||
for _, adminEndpoint := range adminEndpoints {
|
||||
if operationID == adminEndpoint && !auth.IsAdmin {
|
||||
// Write 403 response directly
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(403)
|
||||
json.NewEncoder(w).Encode(ErrorResponse{Code: 403, Message: "Admin privileges required"})
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Store auth in context for handlers to access
|
||||
ctx = context.WithValue(ctx, "auth", auth)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user