add possibility to disable delete and upload of books
This commit is contained in:
@@ -151,6 +151,8 @@ npm run dev
|
||||
|
||||
### Environment Variables
|
||||
|
||||
#### Backend
|
||||
|
||||
| Variable | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `OPENAI_API_KEY` | Yes | OpenAI API key for embeddings and chat |
|
||||
@@ -159,3 +161,14 @@ npm run dev
|
||||
| `DB_USERNAME` | Yes | Database username |
|
||||
| `DB_PASSWORD` | Yes | Database password |
|
||||
| `FIGURE_STORAGE_PATH` | No | Base path for uploaded PDFs and extracted figures (default: `./uploads`) |
|
||||
| `UPLOAD_ENABLED` | No | Set to `false` to disable the book upload endpoint (default: `true`) |
|
||||
| `DELETE_ENABLED` | No | Set to `false` to disable the book delete endpoint (default: `true`) |
|
||||
|
||||
#### Frontend
|
||||
|
||||
| Variable | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `VITE_API_URL` | No | Backend API base URL (default: `/api/v1`) |
|
||||
| `VITE_APP_PASSWORD` | Yes | Shared password for HTTP Basic auth (must match `APP_PASSWORD`) |
|
||||
| `VITE_UPLOAD_ENABLED` | No | Set to `false` to hide the upload UI (default: `true`) |
|
||||
| `VITE_DELETE_ENABLED` | No | Set to `false` to hide the delete button (default: `true`) |
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.aiteacher.book;
|
||||
import com.aiteacher.document.FigureEntity;
|
||||
import com.aiteacher.document.FigureRepository;
|
||||
import com.aiteacher.document.MarkdownStorageService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -22,6 +23,12 @@ public class BookController {
|
||||
private final FigureRepository figureRepository;
|
||||
private final MarkdownStorageService markdownStorageService;
|
||||
|
||||
@Value("${app.features.upload-enabled:true}")
|
||||
private boolean uploadEnabled;
|
||||
|
||||
@Value("${app.features.delete-enabled:true}")
|
||||
private boolean deleteEnabled;
|
||||
|
||||
public BookController(BookService bookService, FigureRepository figureRepository,
|
||||
MarkdownStorageService markdownStorageService) {
|
||||
this.bookService = bookService;
|
||||
@@ -31,6 +38,7 @@ public class BookController {
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data")
|
||||
public ResponseEntity<?> upload(@RequestParam("file") MultipartFile file) throws IOException {
|
||||
if (!uploadEnabled) return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).build();
|
||||
Book book = bookService.upload(file);
|
||||
return ResponseEntity.status(HttpStatus.ACCEPTED).body(toSummaryResponse(book));
|
||||
}
|
||||
@@ -51,6 +59,7 @@ public class BookController {
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<Void> delete(@PathVariable UUID id) {
|
||||
if (!deleteEnabled) return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).build();
|
||||
bookService.delete(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@@ -52,6 +52,9 @@ logging:
|
||||
"[org.apache.pdfbox]": ERROR
|
||||
|
||||
app:
|
||||
features:
|
||||
upload-enabled: ${UPLOAD_ENABLED:true}
|
||||
delete-enabled: ${DELETE_ENABLED:true}
|
||||
auth:
|
||||
password: ${APP_PASSWORD:changeme}
|
||||
figure-storage:
|
||||
|
||||
@@ -5,3 +5,9 @@ VITE_API_URL=/api/v1
|
||||
|
||||
# Shared password for HTTP Basic auth (must match APP_PASSWORD on the backend).
|
||||
VITE_APP_PASSWORD=changeme
|
||||
|
||||
# Set to 'false' to hide the upload UI (frontend). Also set UPLOAD_ENABLED=false on the backend to block the endpoint.
|
||||
VITE_UPLOAD_ENABLED=true
|
||||
|
||||
# Set to 'false' to hide the delete button (frontend). Also set DELETE_ENABLED=false on the backend to block the endpoint.
|
||||
VITE_DELETE_ENABLED=true
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
Read
|
||||
</router-link>
|
||||
<button
|
||||
v-if="deleteEnabled"
|
||||
class="btn btn-danger"
|
||||
:disabled="book.status === 'PROCESSING' || deleting"
|
||||
@click="$emit('delete', book.id)"
|
||||
@@ -59,6 +60,7 @@ import type { Book } from '@/stores/bookStore'
|
||||
const props = defineProps<{
|
||||
book: Book
|
||||
deleting?: boolean
|
||||
deleteEnabled?: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
|
||||
Vendored
+2
@@ -3,6 +3,8 @@
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_API_URL: string
|
||||
readonly VITE_APP_PASSWORD: string
|
||||
readonly VITE_UPLOAD_ENABLED: string
|
||||
readonly VITE_DELETE_ENABLED: string
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="upload-view">
|
||||
<h1 class="page-title">Book Library</h1>
|
||||
<p class="page-subtitle">Upload medical textbooks (PDF) to build the knowledge base.</p>
|
||||
<p v-if="uploadEnabled" class="page-subtitle">Upload medical textbooks (PDF) to build the knowledge base.</p>
|
||||
|
||||
<!-- Upload Section -->
|
||||
<div class="upload-section card">
|
||||
<div v-if="uploadEnabled" class="upload-section card">
|
||||
<h2 class="section-title">Upload a Book</h2>
|
||||
|
||||
<div
|
||||
@@ -87,6 +87,7 @@
|
||||
:key="book.id"
|
||||
:book="book"
|
||||
:deleting="deletingId === book.id"
|
||||
:delete-enabled="deleteEnabled"
|
||||
@delete="handleDelete"
|
||||
/>
|
||||
</div>
|
||||
@@ -99,6 +100,9 @@ import { ref, onMounted, onUnmounted, inject } from 'vue'
|
||||
import { useBookStore } from '@/stores/bookStore'
|
||||
import BookCard from '@/components/BookCard.vue'
|
||||
|
||||
const uploadEnabled = import.meta.env.VITE_UPLOAD_ENABLED !== 'false'
|
||||
const deleteEnabled = import.meta.env.VITE_DELETE_ENABLED !== 'false'
|
||||
|
||||
const bookStore = useBookStore()
|
||||
const showToast = inject<(msg: string, type?: 'error' | 'success') => void>('showToast')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user