From 7e4c5e243a7664a15dd3b31cc7c556cbb393d3c9 Mon Sep 17 00:00:00 2001 From: tranngocbach Date: Sun, 19 May 2024 23:05:31 +0700 Subject: [PATCH 1/4] fea: upload books --- client/src/pages/admin/HomeAdmin.jsx | 23 + client/src/pages/admin/UploadDialogAdmin.jsx | 212 ++++++ client/src/routes.jsx | 8 + server/app.js | 2 + server/package-lock.json | 720 ++++++++++++++++++- server/package.json | 1 + server/routes/uploadBook.js | 33 + server/services/firebase.js | 33 + 8 files changed, 1029 insertions(+), 3 deletions(-) create mode 100644 client/src/pages/admin/HomeAdmin.jsx create mode 100644 client/src/pages/admin/UploadDialogAdmin.jsx create mode 100644 server/routes/uploadBook.js create mode 100644 server/services/firebase.js diff --git a/client/src/pages/admin/HomeAdmin.jsx b/client/src/pages/admin/HomeAdmin.jsx new file mode 100644 index 0000000..ed283a0 --- /dev/null +++ b/client/src/pages/admin/HomeAdmin.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import Box from '@mui/material/Box'; +import UploadDialogAdmin from './UploadDialogAdmin'; + +function Home() { + return ( + + + + + + + + + ); +} + +export default Home; diff --git a/client/src/pages/admin/UploadDialogAdmin.jsx b/client/src/pages/admin/UploadDialogAdmin.jsx new file mode 100644 index 0000000..0e28a6e --- /dev/null +++ b/client/src/pages/admin/UploadDialogAdmin.jsx @@ -0,0 +1,212 @@ +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import { + TextField, Typography, Box, Card, +} from '@mui/material'; +import axios from 'axios'; +import Button from '@mui/material/Button'; +import Snackbar from '@mui/material/Snackbar'; +import CloudUploadIcon from '@mui/icons-material/CloudUpload'; +import PictureAsPdfOutlinedIcon from '@mui/icons-material/PictureAsPdfOutlined'; +import { v4 as uuidv4 } from 'uuid'; +import convertSize from '../../utils/unitConverter'; +import { Languages } from '../../constants'; +/** + * Renders a non-modal dialog to upload new document. + */ +export default function UploadDialogAdmin({ sx }) { + const [isbn, setISBN] = useState(''); + const [description, setDescription] = useState(''); + const [title, setTitle] = useState(''); + const [author, setAuthor] = useState(''); + const [showToast, setShowToast] = useState(false); + const [snackbarMessage, setSnackbarMessage] = useState(''); + const [bookFile, setBookFile] = useState(null); + + const validateForm = () => { + const newErrors = {}; + if (!isbn) newErrors.field1 = 'ISBN is required'; + if (!title) newErrors.field2 = 'Title is required'; + if (!author) newErrors.field3 = 'Author is required'; + if (!description) newErrors.field3 = 'Description is required'; + if (!bookFile) newErrors.field4 = 'Book file is required'; + return newErrors; + }; + + const handleUpload = async () => { + const newErrors = validateForm(); + if (Object.keys(newErrors).length === 0) { + setSnackbarMessage('Book uploaded successfully!'); + setShowToast(true); + // Perform book uploading logic here + const formData = new FormData(); + formData.append('file', bookFile); + formData.append('file-book', JSON.stringify({ + id: uuidv4(), + title, + content: null, + language: Languages.English, + isbn, + author, + description, + })); + const uploadApi = `${process.env.REACT_APP_SERVER_URL}/api/admin/uploadBook`; + const response = await axios.post(uploadApi, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + console.log(response); + } else { + setSnackbarMessage('Please fill in all required fields.'); + setShowToast(true); + } + }; + + const handleCloseToast = () => { + setShowToast(false); + }; + + const handleISBN = async () => { + try { + const response = await axios.get(`https://openlibrary.org/api/books?bibkeys=ISBN:${isbn}&format=json&jscmd=data`); + const { data } = response; + const bookKey = `ISBN:${isbn}`; + if (data[bookKey]) { + const book = data[bookKey]; + setTitle(book.title); + setAuthor(book.authors ? book.authors.map((authorr) => authorr.name).join(', ') : 'N/A'); + setDescription(book.notes || ''); + } + } catch (error) { + console.log(error); + } + }; + + useEffect( + () => { + if (isbn !== '') handleISBN(); + else { + setTitle(''); + setAuthor(''); + setDescription(''); + } + }, + [isbn], + ); + + return ( + + + Add a book + + + setISBN(e.target.value)} + /> + setTitle(e.target.value)} + /> + setAuthor(e.target.value)} + /> + setDescription(e.target.value)} + /> + {/* Display uploaded file */} + {bookFile && ( + + + + {bookFile.name} + + + {convertSize(bookFile.size)} + + + + )} + {/* Upload button */} +
+ + +
+ + +
+
+ ); +} + +UploadDialogAdmin.propTypes = { + sx: PropTypes.instanceOf(Object), +}; + +UploadDialogAdmin.defaultProps = { + sx: {}, +}; diff --git a/client/src/routes.jsx b/client/src/routes.jsx index d2180a2..9b1fc1f 100644 --- a/client/src/routes.jsx +++ b/client/src/routes.jsx @@ -3,6 +3,7 @@ import Home from './pages/Home'; import Reader from './pages/Reader'; import Library from './pages/Library'; import NotFound from './pages/NotFound'; +import HomeAdmin from './pages/admin/HomeAdmin'; const routes = [ { @@ -33,6 +34,13 @@ const routes = [ wrapInMainLayout: true, showInNavbar: false, }, + { + name: 'Home Admin', + path: '/admin', + element: , + wrapInMainLayout: true, + showInNavbar: false, + }, ]; export default routes; diff --git a/server/app.js b/server/app.js index 37d9509..9d5d04e 100644 --- a/server/app.js +++ b/server/app.js @@ -10,6 +10,7 @@ const indexRouter = require('./routes/index'); const uploadRouter = require('./routes/upload'); const downloadRouter = require('./routes/download'); const summarizeRouter = require('./routes/summarize'); +const uploadBook = require('./routes/uploadBook'); const app = express(); @@ -31,6 +32,7 @@ app.use('/', indexRouter); app.use('/api/upload', uploadRouter); app.use('/api/download', downloadRouter); app.use('/api/summarize', summarizeRouter); +app.use('/api/admin', uploadBook); // catch 404 and forward to error handler app.use((req, res, next) => { diff --git a/server/package-lock.json b/server/package-lock.json index fde5c95..71232d7 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -14,6 +14,7 @@ "cors": "^2.8.5", "debug": "~2.6.9", "express": "^4.19.2", + "firebase": "^10.12.0", "http-errors": "~1.6.3", "jade": "^1.9.2", "morgan": "~1.9.1", @@ -219,6 +220,547 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@firebase/analytics": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.3.tgz", + "integrity": "sha512-pMADbAgmfM3vDSeINCw0qSTBA9nn6so8min2KaBfu5eda5kfemb/DeawNUKOIxrP3yV4teJgCKA3JFomfnozEg==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/installations": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.9.tgz", + "integrity": "sha512-ZKXaUixA+drbf3meX1bhPCG90UWrpw1KDrCydhe2Uf0VFZmZyVVr0bAcVpqLm29W4td7qp2RpFjVwercZ5mxTg==", + "dependencies": { + "@firebase/analytics": "0.10.3", + "@firebase/analytics-types": "0.8.2", + "@firebase/component": "0.6.7", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.2.tgz", + "integrity": "sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw==" + }, + "node_modules/@firebase/app": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.3.tgz", + "integrity": "sha512-+pW2wNjijh88aFRjNWhDNlVJI5vB7q1IKYEE79a7ErxwNS/Bo+oh16aAAPvunhT06EF5I8y9gAlNuHNN8u4z8g==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "idb": "7.1.1", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.4.tgz", + "integrity": "sha512-2tjRDaxcM5G7BEpytiDcIl+NovV99q8yEqRMKDbn4J4i/XjjuThuB4S+4PkmTnZiCbdLXQiBhkVxNlUDcfog5Q==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.11.tgz", + "integrity": "sha512-t01zaH3RJpKEey0nGduz3Is+uSz7Sj4U5nwOV6lWb+86s5xtxpIvBJzu/lKxJfYyfZ29eJwpdjEgT1/lm4iQyA==", + "dependencies": { + "@firebase/app-check": "0.8.4", + "@firebase/app-check-types": "0.5.2", + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz", + "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.2.tgz", + "integrity": "sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.2.33", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.33.tgz", + "integrity": "sha512-CLXhYJBtLuHXCUvs894gpXEXhZ7Nhytn2icLLIesH+hPLnyBeBf2CSve6Wjig+TOxTdwOQUzdtYpdjmeeYDfpw==", + "dependencies": { + "@firebase/app": "0.10.3", + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz", + "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==" + }, + "node_modules/@firebase/auth": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.7.3.tgz", + "integrity": "sha512-RiU1PjziOxLuyswtYtLK2qSjHIQJQGCk1h986SUFRbMQfzLXbQg8ZgXwxac1UAfDOzgzqPNCXhBuIlSK2UomoQ==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0", + "undici": "5.28.4" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.8.tgz", + "integrity": "sha512-qUgmv/mcth9wHPTOCKgAOeHe5c+BIOJVcbX2RfcjlXO3xnd8nRafwEkZKBNJUjy4oihYhqFMEMnTHLhwLJwLig==", + "dependencies": { + "@firebase/auth": "1.7.3", + "@firebase/auth-types": "0.12.2", + "@firebase/component": "0.6.7", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0", + "undici": "5.28.4" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz", + "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==" + }, + "node_modules/@firebase/auth-types": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.2.tgz", + "integrity": "sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.7.tgz", + "integrity": "sha512-baH1AA5zxfaz4O8w0vDwETByrKTQqB5CDjRls79Sa4eAGAoERw4Tnung7XbMl3jbJ4B/dmmtsMrdki0KikwDYA==", + "dependencies": { + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.5.tgz", + "integrity": "sha512-cAfwBqMQuW6HbhwI3Cb/gDqZg7aR0OmaJ85WUxlnoYW2Tm4eR0hFl5FEijI3/gYPUiUcUPQvTkGV222VkT7KPw==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.5.tgz", + "integrity": "sha512-NDSMaDjQ+TZEMDMmzJwlTL05kh1+0Y84C+kVMaOmNOzRGRM7VHi29I6YUhCetXH+/b1Wh4ZZRyp1CuWkd8s6hg==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/database": "1.0.5", + "@firebase/database-types": "1.0.3", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.3.tgz", + "integrity": "sha512-39V/Riv2R3O/aUjYKh0xypj7NTNXNAK1bcgY5Kx+hdQPRS/aPTS8/5c0CGFYKgVuFbYlnlnhrCTYsh2uNhGwzA==", + "dependencies": { + "@firebase/app-types": "0.9.2", + "@firebase/util": "1.9.6" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.6.2.tgz", + "integrity": "sha512-sxHtvmfH/1689aPQRxOXBWDumaPqg5AjQVkfwpt+Z3rnaa0aLJlrt2PZs9Xh04qbmWiwtkDgztFmoR/aQdMQJQ==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "@firebase/webchannel-wrapper": "1.0.0", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0", + "undici": "5.28.4" + }, + "engines": { + "node": ">=10.10.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.31.tgz", + "integrity": "sha512-YbR9GGLfYY9A5Qh2SyyNz7EsNeC5SRjzgRxtMtqz2s2es+p+5sDfFUUNKqpgVaIcnoPGOtvCLhNNWG/TBmlQjw==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/firestore": "4.6.2", + "@firebase/firestore-types": "3.0.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.2.tgz", + "integrity": "sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.11.5", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.5.tgz", + "integrity": "sha512-qrHJ+l62mZiU5UZiVi84t/iLXZlhRuSvBQsa2qvNLgPsEWR7wdpWhRmVdB7AU8ndkSHJjGlMICqrVnz47sgU7Q==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.7", + "@firebase/messaging-interop-types": "0.2.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0", + "undici": "5.28.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.11.tgz", + "integrity": "sha512-Qn+ts/M6Lj2/6i1cp5V5TRR+Hi9kyXyHbo+w9GguINJ87zxrCe6ulx3TI5AGQkoQa8YFHUhT3DMGmLFiJjWTSQ==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/functions": "0.11.5", + "@firebase/functions-types": "0.6.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.2.tgz", + "integrity": "sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w==" + }, + "node_modules/@firebase/installations": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.7.tgz", + "integrity": "sha512-i6iGoXRu5mX4rTsiMSSKrgh9pSEzD4hwBEzRh5kEhOTr8xN/wvQcCPZDSMVYKwM2XyCPBLVq0JzjyerwL0Rihg==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/util": "1.9.6", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.7.tgz", + "integrity": "sha512-RPcbD+3nqHbnhVjIOpWK2H5qzZ8pAAAScceiWph0VNTqpKyPQ5tDcp4V5fS0ELpfgsHYvroMLDKfeHxpfvm8cw==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/installations": "0.6.7", + "@firebase/installations-types": "0.5.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.2.tgz", + "integrity": "sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.2.tgz", + "integrity": "sha512-Q1VuA5M1Gjqrwom6I6NUU4lQXdo9IAQieXlujeHZWvRt1b7qQ0KwBaNAjgxG27jgF9/mUwsNmO8ptBCGVYhB0A==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.9", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.9.tgz", + "integrity": "sha512-IH+JJmzbFGZXV3+TDyKdqqKPVfKRqBBg2BfYYOy7cm7J+SwV+uJMe8EnDKYeQLEQhtpwciPfJ3qQXJs2lbxDTw==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/installations": "0.6.7", + "@firebase/messaging-interop-types": "0.2.2", + "@firebase/util": "1.9.6", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.9.tgz", + "integrity": "sha512-5jN6wyhwPgBH02zOtmmoOeyfsmoD7ty48D1m0vVPsFg55RqN2Z3Q9gkZ5GmPklFPjTPLcxB1ObcHOZvThTkm7g==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/messaging": "0.12.9", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.2.tgz", + "integrity": "sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA==" + }, + "node_modules/@firebase/performance": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.7.tgz", + "integrity": "sha512-d+Q4ltjdJZqjzcdms5i0UC9KLYX7vKGcygZ+7zHA/Xk+bAbMD2CPU0nWTnlNFWifZWIcXZ/2mAMvaGMW3lypUA==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/installations": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.7.tgz", + "integrity": "sha512-cb8ge/5iTstxfIGW+iiY+7l3FtN8gobNh9JSQNZgLC9xmcfBYWEs8IeEWMI6S8T+At0oHc3lv+b2kpRMUWr8zQ==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/performance": "0.6.7", + "@firebase/performance-types": "0.2.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.2.tgz", + "integrity": "sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA==" + }, + "node_modules/@firebase/remote-config": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.7.tgz", + "integrity": "sha512-5oPNrPFLsbsjpq0lUEIXoDF2eJK7vAbyXe/DEuZQxnwJlfR7aQbtUlEkRgQWcicXpyDmAmDLo7q7lDbCYa6CpA==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/installations": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.7.tgz", + "integrity": "sha512-Fq0oneQ4SluLnfr5/HfzRS1TZf1ANj1rWbCCW3+oC98An3nE+sCdp+FSuHsEVNwgMg4Tkwx9Oom2lkKeU+Vn+w==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/remote-config": "0.4.7", + "@firebase/remote-config-types": "0.3.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.2.tgz", + "integrity": "sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA==" + }, + "node_modules/@firebase/storage": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.12.5.tgz", + "integrity": "sha512-nGWBOGFNr10j0LA4NJ3/Yh3us/lb0Q1xSIKZ38N6FcS+vY54nqJ7k3zE3PENregHC8+8txRow++A568G3v8hOA==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0", + "undici": "5.28.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.8.tgz", + "integrity": "sha512-qDfY9kMb6Ch2hZb40sBjDQ8YPxbjGOxuT+gU1Z0iIVSSpSX0f4YpGJCypUXiA0T11n6InCXB+T/Dknh2yxVTkg==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/storage": "0.12.5", + "@firebase/storage-types": "0.8.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.2.tgz", + "integrity": "sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.6.tgz", + "integrity": "sha512-IBr1MZbp4d5MjBCXL3TW1dK/PDXX4yOGbiwRNh1oAbE/+ci5Uuvy9KIrsFYY80as1I0iOaD5oOMA9Q8j4TJWcw==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/vertexai-preview": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@firebase/vertexai-preview/-/vertexai-preview-0.0.1.tgz", + "integrity": "sha512-N8m9Xr0YZKy0t9SpQDuHrL2ppEAT/iqf88Y/O00QNA/Td/BMCL8sJ0c+Savh1TVrqh0rNp9n6HkZ39e/O5mwhA==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.0.tgz", + "integrity": "sha512-zuWxyfXNbsKbm96HhXzainONPFqRcoZblQ++e9cAIGUuHfl2cFSBzW01jtesqWG/lqaUyX3H8O1y9oWboGNQBA==" + }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.14", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.14.tgz", + "integrity": "sha512-nOpuzZ2G3IuMFN+UPPpKrC6NsLmWsTqSsm66IRfnBt1D4pwTqE27lmbpcPM+l2Ua4gE7PfjRHI6uedAy7hoXUw==", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@huggingface/inference": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/@huggingface/inference/-/inference-2.6.7.tgz", @@ -318,6 +860,60 @@ "node": ">= 8" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, "node_modules/@puppeteer/browsers": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.3.tgz", @@ -375,7 +971,6 @@ "version": "20.12.8", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz", "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==", - "optional": true, "dependencies": { "undici-types": "~5.26.4" } @@ -2309,6 +2904,17 @@ "reusify": "^1.0.4" } }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -2414,6 +3020,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/firebase": { + "version": "10.12.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.12.0.tgz", + "integrity": "sha512-31UZyAK0+VZmF9jR/+6g31uyqWjBKsG+TV3ndRJEkw6+Skctb5ZX0+Ezq/pbC68iIRJ5TujOjyl632vTOqyS1w==", + "dependencies": { + "@firebase/analytics": "0.10.3", + "@firebase/analytics-compat": "0.2.9", + "@firebase/app": "0.10.3", + "@firebase/app-check": "0.8.4", + "@firebase/app-check-compat": "0.3.11", + "@firebase/app-compat": "0.2.33", + "@firebase/app-types": "0.9.2", + "@firebase/auth": "1.7.3", + "@firebase/auth-compat": "0.5.8", + "@firebase/database": "1.0.5", + "@firebase/database-compat": "1.0.5", + "@firebase/firestore": "4.6.2", + "@firebase/firestore-compat": "0.3.31", + "@firebase/functions": "0.11.5", + "@firebase/functions-compat": "0.3.11", + "@firebase/installations": "0.6.7", + "@firebase/installations-compat": "0.2.7", + "@firebase/messaging": "0.12.9", + "@firebase/messaging-compat": "0.2.9", + "@firebase/performance": "0.6.7", + "@firebase/performance-compat": "0.2.7", + "@firebase/remote-config": "0.4.7", + "@firebase/remote-config-compat": "0.2.7", + "@firebase/storage": "0.12.5", + "@firebase/storage-compat": "0.3.8", + "@firebase/util": "1.9.6", + "@firebase/vertexai-preview": "0.0.1" + } + }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -2796,6 +3436,11 @@ "node": ">= 0.6" } }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -2873,6 +3518,11 @@ "node": ">=0.10.0" } }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -3392,6 +4042,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -3419,6 +4074,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4177,6 +4837,29 @@ "node": ">=0.4.0" } }, + "node_modules/protobufjs": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.3.0.tgz", + "integrity": "sha512-YWD03n3shzV9ImZRX3ccbjqLxj7NokGN0V/ESiBV5xWqrommYHYiihuIyavq03pWSGqlyvYUFmfoMKd+1rPA/g==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -5326,11 +6009,21 @@ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" }, + "node_modules/undici": { + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "optional": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/universalify": { "version": "2.0.1", @@ -5412,6 +6105,27 @@ "node": ">=0.10.0" } }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/server/package.json b/server/package.json index 21290b2..4eaf763 100644 --- a/server/package.json +++ b/server/package.json @@ -13,6 +13,7 @@ "cors": "^2.8.5", "debug": "~2.6.9", "express": "^4.19.2", + "firebase": "^10.12.0", "http-errors": "~1.6.3", "jade": "^1.9.2", "morgan": "~1.9.1", diff --git a/server/routes/uploadBook.js b/server/routes/uploadBook.js new file mode 100644 index 0000000..54790af --- /dev/null +++ b/server/routes/uploadBook.js @@ -0,0 +1,33 @@ +const express = require('express'); +const fs = require('fs'); +const { updateBook } = require('../services/firebase'); + +const upload = require('../middlewares/multer'); +const { processFiles } = require('../controllers/uploadController'); + +const router = express.Router(); + +router.post('/uploadBook', upload.single('file'), async (req, res, next) => { + try { + const { file } = req; + const files = [file]; + const fileBook = JSON.parse(req.body['file-book']); + const fileBooks = [fileBook]; + const result = await processFiles(files, fileBooks); + const bookFileName = result[0].resultFile; + const pdfFilePath = `${bookFileName}.json`; + const jsonContent = fs.readFileSync(`./results/${pdfFilePath}`, 'utf8'); + const { content } = JSON.parse(jsonContent); + + const { + isbn, title, author, language, description, + } = fileBook; + + const book = await updateBook(author, description, language, title, content, isbn); + res.send(`Book uploaded successfully!${this.toString(book)}`); + } catch (error) { + next(error); + } +}); + +module.exports = router; diff --git a/server/services/firebase.js b/server/services/firebase.js new file mode 100644 index 0000000..569469f --- /dev/null +++ b/server/services/firebase.js @@ -0,0 +1,33 @@ +// Import the functions you need from the SDKs you need +const { initializeApp } = require('firebase/app'); +const { getFirestore, doc, setDoc } = require('firebase/firestore'); +// TODO: Add SDKs for Firebase products that you want to use +// https://firebase.google.com/docs/web/setup#available-libraries + +// Your web app's Firebase configuration +const firebaseConfig = { + apiKey: process.env.REACT_APP_FIREBASE_API_KEY, + authDomain: 'lexis-18c6c.firebaseapp.com', + projectId: 'lexis-18c6c', + storageBucket: 'lexis-18c6c.appspot.com', + messagingSenderId: '425004594144', + appId: '1:425004594144:web:11b5f10635d87d476bb185', +}; + +// Initialize Firebase +const firebaseapp = initializeApp(firebaseConfig); +const db = getFirestore(firebaseapp); + +const updateBook = async (author, description, lang, title, content, isbn) => { + const result = await setDoc(doc(db, 'books', isbn), { + author, + description, + lang, + name: title, + content, + isbn, + }); + return result; +}; + +module.exports = { updateBook }; From 6d6a905dc362179d9221cd1ae39fd7eacf29d356 Mon Sep 17 00:00:00 2001 From: tranngocbach Date: Mon, 20 May 2024 00:48:54 +0700 Subject: [PATCH 2/4] fix: axios error by adding try catch --- client/src/pages/admin/UploadDialogAdmin.jsx | 56 +++++++++++--------- server/routes/uploadBook.js | 1 + server/services/firebase.js | 4 +- 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/client/src/pages/admin/UploadDialogAdmin.jsx b/client/src/pages/admin/UploadDialogAdmin.jsx index 0e28a6e..c9b4256 100644 --- a/client/src/pages/admin/UploadDialogAdmin.jsx +++ b/client/src/pages/admin/UploadDialogAdmin.jsx @@ -34,31 +34,37 @@ export default function UploadDialogAdmin({ sx }) { }; const handleUpload = async () => { - const newErrors = validateForm(); - if (Object.keys(newErrors).length === 0) { - setSnackbarMessage('Book uploaded successfully!'); - setShowToast(true); - // Perform book uploading logic here - const formData = new FormData(); - formData.append('file', bookFile); - formData.append('file-book', JSON.stringify({ - id: uuidv4(), - title, - content: null, - language: Languages.English, - isbn, - author, - description, - })); - const uploadApi = `${process.env.REACT_APP_SERVER_URL}/api/admin/uploadBook`; - const response = await axios.post(uploadApi, formData, { - headers: { - 'Content-Type': 'multipart/form-data', - }, - }); - console.log(response); - } else { - setSnackbarMessage('Please fill in all required fields.'); + try { + const newErrors = validateForm(); + if (Object.keys(newErrors).length === 0) { + setSnackbarMessage('Book uploaded successfully!'); + setShowToast(true); + // Perform book uploading logic here + const formData = new FormData(); + formData.append('file', bookFile); + formData.append('file-book', JSON.stringify({ + id: uuidv4(), + title, + content: null, + language: Languages.English, + isbn, + author, + description, + })); + const uploadApi = `${process.env.REACT_APP_SERVER_URL}/api/admin/uploadBook`; + await axios.post(uploadApi, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + // console.log(response); + } else { + setSnackbarMessage('Please fill in all required fields.'); + setShowToast(true); + } + } catch (error) { + console.log(error); + setSnackbarMessage('Failed to upload book!'); setShowToast(true); } }; diff --git a/server/routes/uploadBook.js b/server/routes/uploadBook.js index 54790af..750f36b 100644 --- a/server/routes/uploadBook.js +++ b/server/routes/uploadBook.js @@ -22,6 +22,7 @@ router.post('/uploadBook', upload.single('file'), async (req, res, next) => { const { isbn, title, author, language, description, } = fileBook; + console.log(author, description, language, title, content, isbn); const book = await updateBook(author, description, language, title, content, isbn); res.send(`Book uploaded successfully!${this.toString(book)}`); diff --git a/server/services/firebase.js b/server/services/firebase.js index 569469f..3a4bc6d 100644 --- a/server/services/firebase.js +++ b/server/services/firebase.js @@ -18,12 +18,12 @@ const firebaseConfig = { const firebaseapp = initializeApp(firebaseConfig); const db = getFirestore(firebaseapp); -const updateBook = async (author, description, lang, title, content, isbn) => { +const updateBook = async (author, description, lang, name, content, isbn) => { const result = await setDoc(doc(db, 'books', isbn), { author, description, lang, - name: title, + name, content, isbn, }); From 65365157ddf430047c0837d047279ed4f8fc4400 Mon Sep 17 00:00:00 2001 From: Hien Doan Date: Mon, 20 May 2024 11:21:07 +0700 Subject: [PATCH 3/4] fix: ignore results and uploads file in nodemon --- server/package.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/package.json b/server/package.json index 4eaf763..9cc99a7 100644 --- a/server/package.json +++ b/server/package.json @@ -32,5 +32,11 @@ "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.29.1" + }, + "nodemonConfig": { + "ignore": [ + "results", + "uploads" + ] } } From c1e2a2e52ad3ad3e3722c0d27d1ca97a84dce2bb Mon Sep 17 00:00:00 2001 From: tranngocbach Date: Mon, 20 May 2024 11:30:15 +0700 Subject: [PATCH 4/4] fea: add book cover url --- client/src/pages/admin/UploadDialogAdmin.jsx | 3 +++ server/routes/uploadBook.js | 6 +++--- server/services/firebase.js | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/client/src/pages/admin/UploadDialogAdmin.jsx b/client/src/pages/admin/UploadDialogAdmin.jsx index c9b4256..350c54c 100644 --- a/client/src/pages/admin/UploadDialogAdmin.jsx +++ b/client/src/pages/admin/UploadDialogAdmin.jsx @@ -22,6 +22,7 @@ export default function UploadDialogAdmin({ sx }) { const [showToast, setShowToast] = useState(false); const [snackbarMessage, setSnackbarMessage] = useState(''); const [bookFile, setBookFile] = useState(null); + const [bookCoverUrl, setBookCoverUrl] = useState(''); const validateForm = () => { const newErrors = {}; @@ -50,6 +51,7 @@ export default function UploadDialogAdmin({ sx }) { isbn, author, description, + cover: bookCoverUrl, })); const uploadApi = `${process.env.REACT_APP_SERVER_URL}/api/admin/uploadBook`; await axios.post(uploadApi, formData, { @@ -83,6 +85,7 @@ export default function UploadDialogAdmin({ sx }) { setTitle(book.title); setAuthor(book.authors ? book.authors.map((authorr) => authorr.name).join(', ') : 'N/A'); setDescription(book.notes || ''); + setBookCoverUrl(book.cover ? book.cover.medium : ''); } } catch (error) { console.log(error); diff --git a/server/routes/uploadBook.js b/server/routes/uploadBook.js index 750f36b..8f0e397 100644 --- a/server/routes/uploadBook.js +++ b/server/routes/uploadBook.js @@ -20,11 +20,11 @@ router.post('/uploadBook', upload.single('file'), async (req, res, next) => { const { content } = JSON.parse(jsonContent); const { - isbn, title, author, language, description, + isbn, title, author, language, description, cover, } = fileBook; - console.log(author, description, language, title, content, isbn); + console.log(author, description, language, title, content, isbn, cover); - const book = await updateBook(author, description, language, title, content, isbn); + const book = await updateBook(author, description, language, title, content, isbn, cover); res.send(`Book uploaded successfully!${this.toString(book)}`); } catch (error) { next(error); diff --git a/server/services/firebase.js b/server/services/firebase.js index 3a4bc6d..a0602f7 100644 --- a/server/services/firebase.js +++ b/server/services/firebase.js @@ -18,7 +18,7 @@ const firebaseConfig = { const firebaseapp = initializeApp(firebaseConfig); const db = getFirestore(firebaseapp); -const updateBook = async (author, description, lang, name, content, isbn) => { +const updateBook = async (author, description, lang, name, content, isbn, cover) => { const result = await setDoc(doc(db, 'books', isbn), { author, description, @@ -26,6 +26,7 @@ const updateBook = async (author, description, lang, name, content, isbn) => { name, content, isbn, + cover, }); return result; };