feat: Enhance API startup script and add file hash management for photos
This commit improves the `run_api_with_worker.sh` script by ensuring the virtual environment is created if it doesn't exist and dependencies are installed. It also adds a check to ensure the database schema is up to date. Additionally, new functionality has been introduced to calculate and store file hashes for uploaded photos, preventing duplicates. The database schema has been updated to include a `file_hash` column in the `photos` table, along with an index for efficient querying. The frontend has been updated to handle warnings for duplicate photos during the review process. Documentation has been updated to reflect these changes.
This commit is contained in:
@@ -36,6 +36,7 @@ export interface ReviewResponse {
|
||||
approved: number
|
||||
rejected: number
|
||||
errors: string[]
|
||||
warnings?: string[] // Informational messages (e.g., duplicates)
|
||||
}
|
||||
|
||||
export const pendingPhotosApi = {
|
||||
|
||||
@@ -16,6 +16,12 @@ export default function PendingPhotos() {
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const [statusFilter, setStatusFilter] = useState<string>('pending')
|
||||
const [imageUrls, setImageUrls] = useState<Record<number, string>>({})
|
||||
const [notification, setNotification] = useState<{
|
||||
approved: number
|
||||
rejected: number
|
||||
warnings: string[]
|
||||
errors: string[]
|
||||
} | null>(null)
|
||||
const imageUrlsRef = useRef<Record<number, string>>({})
|
||||
|
||||
const loadPendingPhotos = useCallback(async () => {
|
||||
@@ -232,19 +238,20 @@ export default function PendingPhotos() {
|
||||
decisions: decisionsList,
|
||||
})
|
||||
|
||||
const message = [
|
||||
`✅ Approved: ${response.approved}`,
|
||||
`❌ Rejected: ${response.rejected}`,
|
||||
response.errors.length > 0 ? `⚠️ Errors: ${response.errors.length}` : '',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('\n')
|
||||
|
||||
alert(message)
|
||||
// Show custom notification instead of alert
|
||||
setNotification({
|
||||
approved: response.approved,
|
||||
rejected: response.rejected,
|
||||
warnings: response.warnings || [],
|
||||
errors: response.errors,
|
||||
})
|
||||
|
||||
if (response.errors.length > 0) {
|
||||
console.error('Errors:', response.errors)
|
||||
}
|
||||
if (response.warnings && response.warnings.length > 0) {
|
||||
console.info('Warnings:', response.warnings)
|
||||
}
|
||||
|
||||
// Reload the list to show updated status
|
||||
await loadPendingPhotos()
|
||||
@@ -263,6 +270,39 @@ export default function PendingPhotos() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Notification */}
|
||||
{notification && (
|
||||
<div className="mb-4 bg-white border border-gray-200 rounded-lg shadow-lg p-4">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-green-600 text-lg">✅</span>
|
||||
<span className="font-medium">Approved: {notification.approved}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-red-600 text-lg">✓</span>
|
||||
<span className="font-medium">Rejected: {notification.rejected}</span>
|
||||
</div>
|
||||
{notification.warnings.length > 0 && (
|
||||
<div className="text-xs text-gray-600 ml-7">
|
||||
{notification.warnings.join(', ')}
|
||||
</div>
|
||||
)}
|
||||
{notification.errors.length > 0 && (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-yellow-600 text-lg">⚠️</span>
|
||||
<span className="font-medium">Errors: {notification.errors.length}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setNotification(null)}
|
||||
className="mt-3 px-3 py-1.5 text-sm text-gray-600 bg-gray-50 rounded hover:bg-gray-100 hover:text-gray-700 transition-colors"
|
||||
>
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-6">Manage User Uploaded Photos</h1>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user