upd
This commit is contained in:
+2
-2
@@ -1,2 +1,2 @@
|
||||
VITE_API_URL=https://graff.estate
|
||||
# VITE_API_URL=http://192.168.1.171:3003
|
||||
VITE_API_URL=https://graff.estate/api
|
||||
# VITE_API_URL=http://localhost:3003
|
||||
@@ -7,7 +7,7 @@ function ModalContainer() {
|
||||
return (
|
||||
<div
|
||||
// onClick={() => setModal(null)}
|
||||
className={`fixed p-8 top-0 left-0 w-full h-full flex justify-center items-center bg-black bg-opacity-80 overflow-auto transition-opacity`}
|
||||
className={`fixed p-8 top-0 left-0 z-10 w-full h-full flex justify-center items-center bg-black bg-opacity-80 overflow-auto transition-opacity`}
|
||||
>
|
||||
<div onClick={(e) => e.stopPropagation()} className="cursor-default">
|
||||
{modal}
|
||||
|
||||
@@ -28,7 +28,7 @@ function ProjectCard({
|
||||
<div
|
||||
className="group-hover:scale-110 transition-transform duration-500 absolute top-0 left-0 w-full h-full bg-cover bg-center bg-no-repeat"
|
||||
style={{
|
||||
backgroundImage: `url(${import.meta.env.VITE_API_URL}/api/upload/${image})`,
|
||||
backgroundImage: `url(${import.meta.env.VITE_API_URL}/upload/${image})`,
|
||||
}}
|
||||
></div>
|
||||
<div className="absolute top-0 left-0 w-full h-full bg-gradient-card"></div>
|
||||
|
||||
@@ -44,7 +44,7 @@ function CreateProjectModal() {
|
||||
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
image: `${import.meta.env.VITE_API_URL}/api/${file}`,
|
||||
image: file,
|
||||
}));
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
@@ -143,7 +143,9 @@ function CreateProjectModal() {
|
||||
className="absolute opacity-0"
|
||||
onChange={handleChangeFile}
|
||||
/>
|
||||
<p>{file ? file.name : "Выберите изображение"}</p>
|
||||
<p className="truncate">
|
||||
{file ? file.name : "Выберите изображение"}
|
||||
</p>
|
||||
|
||||
{previewFile && <img src={previewFile} alt="" />}
|
||||
</label>
|
||||
|
||||
@@ -42,13 +42,13 @@ function EditProjectModal({ projectId }: EditProjectModalProps) {
|
||||
formData.append("file", file);
|
||||
|
||||
try {
|
||||
const { file: url }: { file: string } = await api
|
||||
const { file }: { file: string } = await api
|
||||
.post("upload", { body: formData })
|
||||
.json();
|
||||
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
image: `${import.meta.env.VITE_API_URL}/api/${url}`,
|
||||
image: file,
|
||||
}));
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
@@ -161,12 +161,17 @@ function EditProjectModal({ projectId }: EditProjectModalProps) {
|
||||
className="absolute opacity-0"
|
||||
onChange={handleChangeFile}
|
||||
/>
|
||||
<p>{file ? file.name : "Выберите изображение"}</p>
|
||||
<p className="truncate">{file ? file.name : "Выберите изображение"}</p>
|
||||
|
||||
{previewFile ? (
|
||||
<img src={previewFile} alt="" />
|
||||
) : (
|
||||
project.image && <img src={project.image} alt="" />
|
||||
project.image && (
|
||||
<img
|
||||
src={`${import.meta.env.VITE_API_URL}/upload/${project.image}`}
|
||||
alt=""
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</label>
|
||||
|
||||
|
||||
+6
-6
@@ -1,18 +1,18 @@
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||
import App from "./App.tsx";
|
||||
import "./index.css";
|
||||
// import ProjectsPage from "./pages/ProjectsPage.tsx";
|
||||
import App from "./App.tsx";
|
||||
import ProjectsPage from "./pages/ProjectsPage.tsx";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: "/",
|
||||
element: <App />,
|
||||
},
|
||||
// {
|
||||
// path: "/projects",
|
||||
// element: <ProjectsPage />,
|
||||
// },
|
||||
{
|
||||
path: "/projects",
|
||||
element: <ProjectsPage />,
|
||||
},
|
||||
]);
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
|
||||
@@ -34,12 +34,12 @@ function ProjectsPage() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen 2xl:px-10 xl:px-8 sm:px-6 px-4 overflow-x-clip">
|
||||
<div className="relative conatiner mx-auto 2xl:max-w-screen-2xl">
|
||||
<div className="py-8 flex flex-col gap-8">
|
||||
<Button onClick={handleClickCreateProject}>
|
||||
Добавить проект
|
||||
</Button>
|
||||
<div className="min-h-screen">
|
||||
<div className="fixed top-0 left-0 z-10 p-4 bg-[#14161F] border-b border-white border-opacity-10 w-full shadow-2xl">
|
||||
<Button onClick={handleClickCreateProject}>Добавить проект</Button>
|
||||
</div>
|
||||
<div className="conatiner mx-auto 2xl:px-10 xl:px-8 sm:px-6 px-4 2xl:max-w-screen-2xl mt-20">
|
||||
<div className="relative py-8 flex flex-col gap-8">
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
{projects.map((project, index) => (
|
||||
<div key={index} className="relative">
|
||||
@@ -49,17 +49,49 @@ function ProjectsPage() {
|
||||
onClick={() =>
|
||||
setModal(<EditProjectModal projectId={project.id!} />)
|
||||
}
|
||||
className="px-3 py-2 border bg-black bg-opacity-60 hover:bg-opacity-70 transition-opacity"
|
||||
className="group relative p-2 bg-black bg-opacity-60 hover:bg-opacity-70 transition-opacity rounded-full"
|
||||
>
|
||||
Редактировать
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className="w-6 h-6"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"
|
||||
/>
|
||||
</svg>
|
||||
<span className="pointer-events-none group-hover:opacity-100 opacity-0 transition-opacity absolute -bottom-[90%] left-[50%] -translate-x-[50%] bg-neutral-900 px-2 py-1 text-sm rounded-lg">
|
||||
Редактировать
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() =>
|
||||
setModal(<DeleteProjectModal projectId={project.id!} />)
|
||||
}
|
||||
className="px-3 py-2 border bg-black bg-opacity-60 hover:bg-opacity-70 transition-opacity"
|
||||
className="group relative p-2 bg-black bg-opacity-60 hover:bg-opacity-70 transition-opacity rounded-full"
|
||||
>
|
||||
Удалить
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className="w-6 h-6"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
|
||||
/>
|
||||
</svg>
|
||||
<span className="pointer-events-none group-hover:opacity-100 opacity-0 transition-opacity absolute -bottom-[90%] left-[50%] -translate-x-[50%] bg-neutral-900 px-2 py-1 text-sm rounded-lg">
|
||||
Удалить
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
function TestPage() {
|
||||
return (
|
||||
<video
|
||||
src="https://graff.estate/videos/features/nks_infra.mp4"
|
||||
preload="metadata"
|
||||
className="video-loader"
|
||||
></video>
|
||||
);
|
||||
}
|
||||
|
||||
export default TestPage;
|
||||
@@ -1,7 +1,7 @@
|
||||
import ky from "ky";
|
||||
|
||||
const api = ky.extend({
|
||||
prefixUrl: `${import.meta.env.VITE_API_URL}/api`,
|
||||
prefixUrl: import.meta.env.VITE_API_URL,
|
||||
});
|
||||
|
||||
export default api;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export default function randomNumber(min: number, max: number) {
|
||||
return Math.floor(Math.random() * (max - min) + min);
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import react from "@vitejs/plugin-react-swc";
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(({ mode }) => {
|
||||
process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
|
||||
// const env = loadEnv(mode, process.cwd());
|
||||
|
||||
return {
|
||||
plugins: [react()],
|
||||
|
||||
@@ -4,6 +4,7 @@ import express, { json } from "express";
|
||||
import cors from "cors";
|
||||
import mailRoute from "./routes/mail";
|
||||
import projectRoute from "./routes/projects";
|
||||
import uploadRoute from "./routes/upload";
|
||||
|
||||
connectDB();
|
||||
|
||||
@@ -15,6 +16,7 @@ app.use(cors({ origin: "*" }));
|
||||
|
||||
app.use("/mail", mailRoute);
|
||||
app.use("/projects", projectRoute);
|
||||
app.use("/upload", uploadRoute);
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server is listening on port ${port}`);
|
||||
|
||||
+56
-55
@@ -1,77 +1,78 @@
|
||||
import { Router } from "express";
|
||||
// import multer, { memoryStorage } from "multer";
|
||||
// import sharp from "sharp";
|
||||
// import fs from "fs";
|
||||
import multer, { memoryStorage } from "multer";
|
||||
import sharp from "sharp";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
// import { v4 as uuidv4 } from "uuid";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// const upload = multer({
|
||||
// dest: "uploads/",
|
||||
// storage: memoryStorage(),
|
||||
// fileFilter: (_req, file, cb) => {
|
||||
// if (file.mimetype.split("/")[0] === "image") {
|
||||
// cb(null, true);
|
||||
// } else {
|
||||
// cb(new Error("Only images are allowed!"));
|
||||
// }
|
||||
// },
|
||||
// });
|
||||
const upload = multer({
|
||||
dest: "uploads/",
|
||||
storage: memoryStorage(),
|
||||
fileFilter: (_req, file, cb) => {
|
||||
if (file.mimetype.split("/")[0] === "image") {
|
||||
cb(null, true);
|
||||
} else {
|
||||
cb(new Error("Only images are allowed!"));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
router.get("/", (_req, res) => {
|
||||
res.json({ ok: 1 });
|
||||
});
|
||||
|
||||
// router.get("/:filename", (req, res) => {
|
||||
// res.sendFile(`${path.resolve("uploads")}/${req.params.filename}`);
|
||||
// });
|
||||
router.get("/:filename", (req, res) => {
|
||||
res.sendFile(`${path.resolve("uploads")}/${req.params.filename}`);
|
||||
});
|
||||
|
||||
// router.get("/:file", (req, res) => {
|
||||
// const fileName = req.params.file;
|
||||
router.get("/:file", (req, res) => {
|
||||
const fileName = req.params.file;
|
||||
|
||||
// console.log(fileName);
|
||||
console.log(fileName);
|
||||
|
||||
// try {
|
||||
// const readStream = fs.createReadStream(
|
||||
// `${path.join(__dirname, "uploads")}/${fileName}`
|
||||
// );
|
||||
try {
|
||||
const readStream = fs.createReadStream(
|
||||
`${path.join(__dirname, "uploads")}/${fileName}`
|
||||
);
|
||||
|
||||
// console.log(readStream);
|
||||
console.log(readStream);
|
||||
|
||||
// readStream.pipe(res);
|
||||
// } catch (error) {
|
||||
// if (error instanceof Error) {
|
||||
// return res.json({ error: error.message });
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
readStream.pipe(res);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return res.json({ error: error.message });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// router.post("/", upload.single("file"), async (req, res) => {
|
||||
// if (!req.file) {
|
||||
// return res.json({ error: "req.file" });
|
||||
// }
|
||||
router.post("/", upload.single("file"), async (req, res) => {
|
||||
if (!req.file) {
|
||||
return res.json({ error: "req.file" });
|
||||
}
|
||||
|
||||
// try {
|
||||
// const filename = `${uuidv4()}.jpg`;
|
||||
try {
|
||||
const filename = `${uuidv4()}.jpg`;
|
||||
|
||||
// await sharp(req.file.buffer)
|
||||
// .resize({
|
||||
// width: 728,
|
||||
// height: 728,
|
||||
// fit: "inside",
|
||||
// withoutEnlargement: true,
|
||||
// })
|
||||
// .jpeg({ quality: 90 })
|
||||
// .toFile(`uploads/${filename}`);
|
||||
await sharp(req.file.buffer)
|
||||
.resize({
|
||||
width: 728,
|
||||
height: 728,
|
||||
fit: "inside",
|
||||
withoutEnlargement: true,
|
||||
})
|
||||
.jpeg({ quality: 90 })
|
||||
.toFile(`uploads/${filename}`);
|
||||
|
||||
// return res.json({ file: `/upload/${filename}` });
|
||||
// } catch (error) {
|
||||
// if (error instanceof Error) {
|
||||
// return res.json({ error: error.message });
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
return res.json({ file: filename });
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return res.json({ error: error.message });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const uploadRoute = router;
|
||||
|
||||
export default uploadRoute;
|
||||
|
||||
Reference in New Issue
Block a user