upd
@@ -0,0 +1,3 @@
|
||||
PORT=3003
|
||||
MONGO_URI=mongodb://root:p62Z!ZatgY25@194.26.138.94:27017/
|
||||
JWT_SECRET=yDcdWJgvlj2bJAuovYfQHTvtc3U9xQPw
|
||||
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: "graff.estate-server:3003",
|
||||
exec_mode: "cluster",
|
||||
script: "yarn",
|
||||
args: "start",
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "server",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev": "nodemon src/index.ts",
|
||||
"build": "npx tsc",
|
||||
"start": "node dist/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"express-sslify": "^1.2.0",
|
||||
"mongoose": "^8.0.1",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"nodemailer": "^6.9.7",
|
||||
"sharp": "^0.32.6",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cors": "^2.8.15",
|
||||
"@types/express": "^4.17.20",
|
||||
"@types/express-sslify": "^1.2.5",
|
||||
"@types/multer": "^1.4.11",
|
||||
"@types/node": "^20.8.10",
|
||||
"@types/nodemailer": "^6.4.14",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"nodemon": "^3.0.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { connect } from "mongoose";
|
||||
|
||||
async function connectDB() {
|
||||
try {
|
||||
await connect(process.env.MONGO_URI!, { dbName: "estate" });
|
||||
console.log("MongoDB connected...");
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
export default connectDB;
|
||||
@@ -0,0 +1,25 @@
|
||||
import "dotenv/config";
|
||||
import connectDB from "./config/db";
|
||||
import express, { json } from "express";
|
||||
import cors from "cors";
|
||||
import mailRoute from "./routes/mail";
|
||||
import projectRoute from "./routes/projects";
|
||||
import uploadRoute from "./routes/upload";
|
||||
import getRegionNameRoute from "./routes/getRegionName";
|
||||
|
||||
connectDB();
|
||||
|
||||
const app = express();
|
||||
const port = process.env.PORT || 3003;
|
||||
|
||||
app.use(json());
|
||||
app.use(cors({ origin: "*" }));
|
||||
|
||||
app.use("/mail", mailRoute);
|
||||
app.use("/projects", projectRoute);
|
||||
app.use("/upload", uploadRoute);
|
||||
app.use("/getRegionName", getRegionNameRoute);
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server is listening on port ${port}`);
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Schema, model } from "mongoose";
|
||||
|
||||
const mailSchema = new Schema(
|
||||
{
|
||||
fullname: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
phone: {
|
||||
type: String,
|
||||
},
|
||||
request: {
|
||||
type: String,
|
||||
},
|
||||
referer: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
toJSON: { virtuals: true },
|
||||
toObject: { virtuals: true },
|
||||
}
|
||||
);
|
||||
|
||||
const Mail = model("Mail", mailSchema);
|
||||
|
||||
export default Mail;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Schema, model } from "mongoose";
|
||||
|
||||
const projectSchema = new Schema(
|
||||
{
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
company: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
city: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
image: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
stage: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
releaseDate: {
|
||||
type: Date,
|
||||
required: true,
|
||||
},
|
||||
devices: {
|
||||
type: Array,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
toJSON: { virtuals: true },
|
||||
toObject: { virtuals: true },
|
||||
}
|
||||
);
|
||||
|
||||
const Project = model("Project", projectSchema);
|
||||
|
||||
export default Project;
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Router } from "express";
|
||||
import fs from "fs";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", async (req, res) => {
|
||||
const ip = req.headers["x-forwarded-for"];
|
||||
|
||||
try {
|
||||
const { countryCode, region, regionName }: any = await (
|
||||
await fetch(`http://ip-api.com/json/${ip}?lang=ru`)
|
||||
).json();
|
||||
|
||||
if (countryCode === "RU") {
|
||||
fs.appendFileSync(
|
||||
"./log.txt",
|
||||
`${countryCode}-${region} (${regionName})\n`
|
||||
);
|
||||
}
|
||||
|
||||
res.json({ regionName });
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
res.json({ error: error.message });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const getRegionNameRoute = router;
|
||||
|
||||
export default getRegionNameRoute;
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Router } from "express";
|
||||
import Mail from "../models/Mail";
|
||||
import nodemailer from "nodemailer";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post("/", async (req, res) => {
|
||||
try {
|
||||
const referer = req.headers.referer;
|
||||
const { fullname, email, phone, request } = req.body;
|
||||
|
||||
await Mail.create({ fullname, email, phone, request, referer });
|
||||
|
||||
// create reusable transporter object using the default SMTP transport
|
||||
let transporter = nodemailer.createTransport({
|
||||
host: "mail.netangels.ru",
|
||||
port: 587,
|
||||
secure: false, // true for 465, false for other ports
|
||||
auth: {
|
||||
user: "test@graff.tech", // generated ethereal user
|
||||
pass: "ZmL0pKiDFWUyCDMq", // generated ethereal password
|
||||
},
|
||||
});
|
||||
|
||||
// send mail with defined transport object
|
||||
let info = await transporter.sendMail({
|
||||
from: email, // sender address
|
||||
to: "info@graff.tech", // list of receivers
|
||||
subject: "Заявка с сайта graff.estate", // Subject line
|
||||
text: `
|
||||
Имя Фамилия: ${fullname}
|
||||
Email: ${email}
|
||||
Телефон: ${phone}
|
||||
Текст запроса: ${request}
|
||||
`, // plain text body
|
||||
html: `<div>
|
||||
<p>Имя: ${fullname}</p>
|
||||
<p>Email: ${email}</p>
|
||||
<p>Телефон: ${phone}</p>
|
||||
<p>Текст запроса: ${request}</p>
|
||||
</div>`, // html body
|
||||
});
|
||||
|
||||
console.log(info);
|
||||
|
||||
console.log(req.body);
|
||||
|
||||
res.json({ ok: 1 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
res.json({ error: 1 });
|
||||
}
|
||||
});
|
||||
|
||||
const mailRoute = router;
|
||||
|
||||
export default mailRoute;
|
||||
@@ -0,0 +1,70 @@
|
||||
import { Router } from "express";
|
||||
import Project from "../models/Project";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", async (_req, res) => {
|
||||
try {
|
||||
const projects = await Project.find().sort({ releaseDate: -1 });
|
||||
|
||||
return res.json(projects);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return res.json({ error: error.message });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
router.get("/:id", async (req, res) => {
|
||||
try {
|
||||
const project = await Project.findById(req.params.id);
|
||||
|
||||
return res.json(project);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return res.json({ error: error.message });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
router.post("/", async (req, res) => {
|
||||
try {
|
||||
const project = await Project.create(req.body);
|
||||
|
||||
return res.json(project);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return res.json({ error: error.message });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
router.put("/:id", async (req, res) => {
|
||||
try {
|
||||
const project = await Project.findByIdAndUpdate(req.params.id, req.body, {
|
||||
upsert: true,
|
||||
new: true,
|
||||
});
|
||||
|
||||
return res.json(project);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return res.json({ error: error.message });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
router.delete("/:id", async (req, res) => {
|
||||
try {
|
||||
const project = await Project.findByIdAndDelete(req.params.id);
|
||||
|
||||
return res.json(project);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return res.json({ error: error.message });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const projectRoute = router;
|
||||
export default projectRoute;
|
||||
@@ -0,0 +1,78 @@
|
||||
import { Router } from "express";
|
||||
import multer, { memoryStorage } from "multer";
|
||||
import sharp from "sharp";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
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!"));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
router.get("/", (_req, res) => {
|
||||
res.json({ ok: 1 });
|
||||
});
|
||||
|
||||
router.get("/:filename", (req, res) => {
|
||||
res.sendFile(`${path.resolve("uploads")}/${req.params.filename}`);
|
||||
});
|
||||
|
||||
router.get("/:file", (req, res) => {
|
||||
const fileName = req.params.file;
|
||||
|
||||
console.log(fileName);
|
||||
|
||||
try {
|
||||
const readStream = fs.createReadStream(
|
||||
`${path.join(__dirname, "uploads")}/${fileName}`
|
||||
);
|
||||
|
||||
console.log(readStream);
|
||||
|
||||
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" });
|
||||
}
|
||||
|
||||
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}`);
|
||||
|
||||
return res.json({ file: filename });
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return res.json({ error: error.message });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const uploadRoute = router;
|
||||
|
||||
export default uploadRoute;
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "Node",
|
||||
"outDir": "./dist",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"ts-node": {
|
||||
"transpileOnly": true
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 189 KiB |