diff --git a/package-lock.json b/package-lock.json index ebd12ca..0ccfb03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,11 +16,13 @@ "@modelcontextprotocol/sdk": "^1.20.2", "@tiptap/starter-kit": "^3.5.1", "@types/express": "^5.0.3", + "axios": "^1.13.2", "cookie-parser": "^1.4.7", "cors": "^2.8.5", "crypto": "^1.0.1", "dotenv": "^16.5.0", "express": "^5.1.0", + "form-data": "^4.0.5", "google": "^2.1.0", "jsdom": "^27.0.0", "jsonwebtoken": "^9.0.2", @@ -48,10 +50,12 @@ "@types/jsonwebtoken": "^9.0.10", "@types/lodash.isequal": "^4.5.8", "@types/morgan": "^1.9.10", + "@types/multer": "^2.0.0", "@types/node": "^22.15.30", "@types/node-fetch": "^2.6.13", "@types/xmldom": "^0.1.34", - "esbuild": "^0.25.12" + "esbuild": "^0.25.12", + "multer": "^2.0.2" } }, "node_modules/@asamuzakjp/css-color": { @@ -1655,6 +1659,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/multer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz", + "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/node": { "version": "22.15.30", "license": "MIT", @@ -1783,6 +1797,13 @@ "node": ">= 8" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1849,6 +1870,17 @@ "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", "license": "MIT" }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "license": "MIT" @@ -1979,6 +2011,25 @@ "version": "1.0.1", "license": "BSD-3-Clause" }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dev": true, + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "license": "MIT", @@ -2137,6 +2188,47 @@ "version": "0.0.1", "license": "MIT" }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/content-disposition": { "version": "1.0.0", "license": "MIT", @@ -2912,6 +3004,26 @@ "node": ">= 0.8" } }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -2938,10 +3050,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "dev": true, + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -2958,7 +3069,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -2968,7 +3078,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -3922,6 +4031,16 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -3931,6 +4050,19 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/morgan": { "version": "1.10.0", "license": "MIT", @@ -3970,6 +4102,72 @@ "version": "2.1.3", "license": "MIT" }, + "node_modules/multer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mysql2": { "version": "3.14.3", "license": "MIT", @@ -4549,6 +4747,12 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/psl": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", @@ -5278,6 +5482,15 @@ "version": "3.9.0", "license": "MIT" }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -5559,6 +5772,13 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true, + "license": "MIT" + }, "node_modules/typescript": { "version": "5.8.3", "license": "Apache-2.0", @@ -5607,6 +5827,13 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", @@ -5888,6 +6115,16 @@ "node": ">=0.4.0" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y-prosemirror": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/y-prosemirror/-/y-prosemirror-1.3.7.tgz", diff --git a/package.json b/package.json index f03771e..3e70670 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,13 @@ "@modelcontextprotocol/sdk": "^1.20.2", "@tiptap/starter-kit": "^3.5.1", "@types/express": "^5.0.3", + "axios": "^1.13.2", "cookie-parser": "^1.4.7", "cors": "^2.8.5", "crypto": "^1.0.1", "dotenv": "^16.5.0", "express": "^5.1.0", + "form-data": "^4.0.5", "google": "^2.1.0", "jsdom": "^27.0.0", "jsonwebtoken": "^9.0.2", @@ -56,10 +58,12 @@ "@types/jsonwebtoken": "^9.0.10", "@types/lodash.isequal": "^4.5.8", "@types/morgan": "^1.9.10", + "@types/multer": "^2.0.0", "@types/node": "^22.15.30", "@types/node-fetch": "^2.6.13", "@types/xmldom": "^0.1.34", - "esbuild": "^0.25.12" + "esbuild": "^0.25.12", + "multer": "^2.0.2" }, "bugs": { "url": "https://github.com/SilverCore-Git/silvernote/issues" diff --git a/src/assets/ts/downloadFile.ts b/src/assets/ts/downloadFile.ts new file mode 100644 index 0000000..0bc80da --- /dev/null +++ b/src/assets/ts/downloadFile.ts @@ -0,0 +1,42 @@ +import axios from "axios"; +import path from "path"; +import fs from "fs"; +import { fileURLToPath } from "url"; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const uploadDir: string = path.join(__dirname, '../../temp'); + +export default async function +(uuid: string): Promise +{ + + try { + + const url = `https://db.silvernote.fr/file/see/${uuid}`; + + const response = await axios.get(url, { + responseType: 'arraybuffer', + headers: { + "Authorization": process.env.DB_API_SK_1 || "", + "X-API-Key": process.env.DB_API_SK_2 || "", + } + }); + + const disposition = response.headers['content-disposition']; + let fileName = uuid + if (disposition && disposition.includes('filename=')) { + fileName = disposition.split('filename=')[1].replace(/"/g, ''); + } + + const filePath: string = path.join(uploadDir, fileName); + + fs.writeFileSync(filePath, response.data); + + return filePath; + } catch (err: any) { + console.error('Erreur téléchargement :', err.message); + return ''; + } + +} \ No newline at end of file diff --git a/src/routes/api.db.ts b/src/routes/api.db.ts index 901529f..093454f 100644 --- a/src/routes/api.db.ts +++ b/src/routes/api.db.ts @@ -1,9 +1,18 @@ -import express, { Request, Response } from "express"; +import express, { type Request, type Response } from "express"; import note_db from "../assets/ts/notes.js"; import tag_db from "../assets/ts/tags.js"; import utils from "../assets/ts/utils.js"; import { Note, Tag } from "../assets/ts/types.js"; +import multer from 'multer'; +import path from "path"; +import fs from 'fs'; +import axios from 'axios'; +import FormData from "form-data"; +import downloadFile from "../assets/ts/downloadFile.js"; +import { fileURLToPath } from "url"; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); const router = express.Router(); @@ -110,4 +119,85 @@ router.post('/delete/tags', async (req: Request, res: Response) => { +const uploadDir = path.join(__dirname, "../temp"); +if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir); + +const storage = multer.diskStorage({ + destination: (req: Request, file: any, cb: any) => { + cb(null, uploadDir); + }, + filename: (req: Request, file: any, cb: any) => { + cb(null, file.originalname); + } +}); + +const upload = multer({ + storage, + limits: { + fileSize: 1 * 1024 * 1024 * 1024 // 16 Go + } +}); + + +router.post('/image/upload', upload.single('file'), async (req: Request, res: Response) => { + if (!req.file) { + res.json({ error: true, message: 'File not found' }); + return; + } + + const file = req.file; + const fileStream = fs.createReadStream(file.path); + + const formData = new FormData(); + formData.append("file", fileStream, { + filename: file.originalname, + contentType: file.mimetype, + }); + + try { + const _res = await axios.post("https://db.silvernote.fr/file/upload", formData, { + headers: { + ...formData.getHeaders(), + "Authorization": process.env.DB_API_SK_1 || "", + "X-API-Key": process.env.DB_API_SK_2 || "", + }, + maxBodyLength: Infinity, + }); + + res.json({ + ..._res.data, + url: 'http://localhost:3000/api/db/image/get/' + _res.data.file.UUID + }); + } catch (err: any) { + res.status(500).json({ + error: true, + message: err.message || err + }); + } +}); + + + +router.get('/image/get/:name', async (req, res) => { + + const baseName = req.params.name; + + const files = fs.readdirSync(uploadDir); + + const match = files.find(f => f.startsWith(baseName + ".")); + + if (match) + { + const filePath = path.join(uploadDir, match); + res.sendFile(filePath); + return; + } + + await downloadFile(baseName).then(filePath => { + res.sendFile(filePath); + }) + +}); + + export default router;