diff --git a/backend/package-lock.json b/backend/package-lock.json
index 3967c09..560722a 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -9,6 +9,7 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
+ "@bytescale/sdk": "^3.53.0",
"@editorjs/editorjs": "^2.31.4",
"@editorjs/header": "^2.8.8",
"@editorjs/list": "^2.0.9",
@@ -21,6 +22,7 @@
"express-session": "^1.19.0",
"jsonwebtoken": "^9.0.3",
"multer": "^2.1.1",
+ "node-fetch": "^3.3.2",
"postmark": "^4.0.7",
"prisma": "^6.19.2",
"slugify": "^1.6.9"
@@ -29,6 +31,12 @@
"nodemon": "^3.1.11"
}
},
+ "node_modules/@bytescale/sdk": {
+ "version": "3.53.0",
+ "resolved": "https://registry.npmjs.org/@bytescale/sdk/-/sdk-3.53.0.tgz",
+ "integrity": "sha512-qCeNup3pSjaklXuBrO9JeKbozZEs/PjQEvuqCiOAWLBRl6lDjd0V9gRVYqyttPimXYFoV+J/7dmPWtK6RfOABQ==",
+ "license": "MIT"
+ },
"node_modules/@codexteam/icons": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.0.5.tgz",
@@ -600,6 +608,15 @@
"url": "https://opencollective.com/express"
}
},
+ "node_modules/data-uri-to-buffer": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -900,6 +917,29 @@
"node": ">=8.0.0"
}
},
+ "node_modules/fetch-blob": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "node-domexception": "^1.0.0",
+ "web-streams-polyfill": "^3.0.3"
+ },
+ "engines": {
+ "node": "^12.20 || >= 14.13"
+ }
+ },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -991,6 +1031,18 @@
"node": ">= 0.6"
}
},
+ "node_modules/formdata-polyfill": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+ "license": "MIT",
+ "dependencies": {
+ "fetch-blob": "^3.1.2"
+ },
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -1522,6 +1574,44 @@
"node": "^18 || ^20 || >= 21"
}
},
+ "node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+ "deprecated": "Use your platform's native DOMException instead",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "github",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.5.0"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+ "license": "MIT",
+ "dependencies": {
+ "data-uri-to-buffer": "^4.0.0",
+ "fetch-blob": "^3.1.4",
+ "formdata-polyfill": "^4.0.10"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/node-fetch"
+ }
+ },
"node_modules/node-fetch-native": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz",
@@ -2216,6 +2306,15 @@
"node": ">= 0.8"
}
},
+ "node_modules/web-streams-polyfill": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
diff --git a/backend/package.json b/backend/package.json
index b18dc4c..d208b7c 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -15,6 +15,7 @@
"license": "ISC",
"type": "module",
"dependencies": {
+ "@bytescale/sdk": "^3.53.0",
"@editorjs/editorjs": "^2.31.4",
"@editorjs/header": "^2.8.8",
"@editorjs/list": "^2.0.9",
@@ -27,6 +28,7 @@
"express-session": "^1.19.0",
"jsonwebtoken": "^9.0.3",
"multer": "^2.1.1",
+ "node-fetch": "^3.3.2",
"postmark": "^4.0.7",
"prisma": "^6.19.2",
"slugify": "^1.6.9"
diff --git a/backend/src/routes/upload.routes.js b/backend/src/routes/upload.routes.js
index c6a4556..9197686 100644
--- a/backend/src/routes/upload.routes.js
+++ b/backend/src/routes/upload.routes.js
@@ -1,15 +1,35 @@
import express from "express";
-import {upload} from "../controllers/upload.controller.js";
+import * as Bytescale from "@bytescale/sdk";
+import multer from "multer";
const router = express.Router();
-router.post("/image", upload.single("image"), (req, res) => {
- res.json({
- success: 1,
- file: {
- url: `http://localhost:3000/uploads/blog/${req.file.filename}`,
- },
- });
+const uploadManager = new Bytescale.UploadManager({
+ apiKey: process.env.BYTESCALE_SECRET_API_KEY,
+});
+
+const storage = multer.memoryStorage();
+const upload = multer({storage});
+
+router.post("/", upload.single("file"), async (req, res) => {
+ try {
+ const file = req.file;
+ const {folderPath} = req.body;
+
+ const result = await uploadManager.upload({
+ data: file.buffer,
+ name: file.originalname,
+ mime: file.mimetype,
+ path: {
+ folderPath: folderPath || "/general",
+ },
+ });
+
+ res.json({fileUrl: result.fileUrl});
+ } catch (error) {
+ console.error(error);
+ res.status(500).json({error: "Upload failed"});
+ }
});
export default router;
diff --git a/frontend/src/components/BytescaleUploader/BytescaleUploader.tsx b/frontend/src/components/BytescaleUploader/BytescaleUploader.tsx
index 7636aa7..792612f 100644
--- a/frontend/src/components/BytescaleUploader/BytescaleUploader.tsx
+++ b/frontend/src/components/BytescaleUploader/BytescaleUploader.tsx
@@ -1,7 +1,7 @@
-import { useState, useRef } from "react";
-import * as Bytescale from "@bytescale/sdk";
-import { Button } from "@/components/ui/button";
-import { User, X, Loader2 } from "lucide-react";
+import {useState, useRef} from "react";
+import {Button} from "@/components/ui/button";
+import {User, X, Loader2} from "lucide-react";
+import axios from "axios";
interface BytescaleUploaderProps {
value: string;
@@ -9,11 +9,11 @@ interface BytescaleUploaderProps {
folderPath: "/doctors" | "/departments" | "/news";
}
-const uploadManager = new Bytescale.UploadManager({
- apiKey: "public_223k2cv3MyaAxBeyALCfJa8EMLek",
-});
-
-export function BytescaleUploader({ value, onChange }: BytescaleUploaderProps) {
+export function BytescaleUploader({
+ value,
+ onChange,
+ folderPath,
+}: BytescaleUploaderProps) {
const [isUploading, setIsUploading] = useState(false);
const fileInputRef = useRef