Compare commits

..

11 Commits

Author SHA1 Message Date
Primakov Alexandr Alexandrovich
72a2667549 1.2.1 2025-02-08 10:39:49 +03:00
Primakov Alexandr Alexandrovich
39db7b4d26 fix 2025-02-08 10:39:45 +03:00
aa
ff25c0ecb9 Merge pull request 'fix path' (#93) from gamehub into master
Reviewed-on: #93
2025-02-08 10:34:05 +03:00
aaeii
f1a93bffb5 fix path 2025-02-08 10:33:24 +03:00
aa
aa231d4f43 Merge pull request 'upd json' (#92) from gamehub into master
Reviewed-on: #92
2025-02-08 09:59:04 +03:00
aaeii
f254d57db4 upd json 2025-02-08 09:57:34 +03:00
106f835934 Merge pull request 'dogsitters-finder' (#91) from dogsitters-finder into master
Reviewed-on: #91
2025-02-08 04:50:31 +03:00
f9b30a4cfd Merge branch 'master' into dogsitters-finder
Some checks failed
platform/multy-stub/pipeline/head There was a failure building this commit
2025-02-08 04:45:56 +03:00
5e4a99529d Add backend and db settings 2025-02-08 04:44:17 +03:00
4d585002d7 Add backend and DB settings 2025-02-08 04:38:22 +03:00
b073fe3fdf Merge pull request 'Изменены запросы и добавлены новые' (#89) from dogsitters-finder-2 into master
Reviewed-on: #89
2025-02-08 02:39:15 +03:00
13 changed files with 550 additions and 22 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "multi-stub",
"version": "1.2.0",
"version": "1.2.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "multi-stub",
"version": "1.2.0",
"version": "1.2.1",
"license": "MIT",
"dependencies": {
"ai": "^4.1.13",

View File

@@ -1,6 +1,6 @@
{
"name": "multi-stub",
"version": "1.2.0",
"version": "1.2.1",
"description": "",
"main": "index.js",
"scripts": {

View File

@@ -81,7 +81,7 @@ app.use(require("./root"))
app.use("/kfu-m-24-1", require("./routers/kfu-m-24-1"))
app.use("/epja-2024-1", require("./routers/epja-2024-1"))
app.use("/v1/todo", require("./routers/todo"))
app.use("/dogsitters-finder", require("./routers/dogsitters-finder"))
// app.use("/dogsitters-finder", require("./routers/dogsitters-finder"))
app.use("/kazan-explore", require("./routers/kazan-explore"))
app.use("/edateam", require("./routers/edateam-legacy"))
app.use("/dry-wash", require("./routers/dry-wash"))

View File

@@ -0,0 +1,74 @@
const { Router } = require("express");
const hash = require("pbkdf2-password")();
const { promisify } = require("node:util");
const jwt = require('jsonwebtoken')
const { getAnswer } = require("../../utils/common");
const { AuthModel } = require("./model/todo/auth");
const { TOKEN_KEY } = require('./const')
const { UserModel } = require("./model/todo/user");
const { requiredValidate } = require('./utils')
const router = Router();
router.post(
"/signup",
requiredValidate("login", "password", "email"),
async (req, res, next) => {
const { login, password, email } = req.body
const user = await AuthModel.findOne({ login });
if (user) {
throw new Error("Пользователь с таким логином уже существует");
}
hash({ password }, async function (err, pass, salt, hash) {
if (err) return next(err);
const user = await UserModel.create({ login, email });
await AuthModel.create({ login, hash, salt, userId: user.id });
res.json(getAnswer(null, { ok: true }))
})
}
)
function authenticate(login, pass, cb) {
AuthModel.findOne({ login }).populate('userId').exec().then((user) => {
if (!user) return cb(null, null)
hash({ password: pass, salt: user.salt }, function (err, pass, salt, hash) {
if (err) return cb(err)
if (hash === user.hash) return cb(null, user)
cb(null, null)
})
})
}
const auth = promisify(authenticate)
router.post('/signin', requiredValidate('login', 'password'), async (req, res) => {
const { login, password } = req.body
const user = await auth(login, password)
if (!user) {
throw new Error("Неверный логин или пароль")
}
const accessToken = jwt.sign({
...JSON.parse(JSON.stringify(user.userId)),
}, TOKEN_KEY, {
expiresIn: '12h'
})
res.json(getAnswer(null, {
user: user.userId,
token: accessToken,
}))
})
module.exports = router

View File

@@ -0,0 +1,3 @@
exports.DSF_AUTH_PASSWD_MODEL_NAME = 'DSF_AUTH_PASSWD'
exports.DSF_AUTH_USER_MODEL_NAME = 'DSF_AUTH_USER'
exports.DSF_INTERACTION_MODEL_NAME = 'DSF_INTERACTION'

View File

@@ -66,6 +66,4 @@ router.get("/auth/session", (request, response) => {
console.log("token e:", e);
return response.status(403).json({ error: "Invalid token" });
}
});
module.exports = router;
});

View File

@@ -0,0 +1,44 @@
const { Schema, model } = require("mongoose");
const {
DSF_AUTH_PASSWD_MODEL_NAME,
DSF_AUTH_USER_MODEL_NAME,
} = require("../../const");
const schema = new Schema({
login: {
type: String,
required: true,
unique: true
},
hash: {
type: String,
required: true
},
salt: {
type: String,
required: true
},
userId: {
type: Schema.Types.ObjectId,
ref: DSF_AUTH_USER_MODEL_NAME
},
created: {
type: Date,
default: () => new Date().toISOString(),
},
});
schema.set("toJSON", {
virtuals: true,
versionKey: false,
transform: function (doc, ret) {
delete ret._id;
},
});
schema.virtual("id").get(function () {
return this._id.toHexString();
});
exports.AuthModel = model(DSF_AUTH_PASSWD_MODEL_NAME, schema);

View File

@@ -0,0 +1,24 @@
const { Schema, model } = require("mongoose");
const { DSF_AUTH_USER_MODEL_NAME, DSF_INTERACTION_MODEL_NAME } = require("../../const");
const interactionSchema = new Schema({
owner_id: {
type: Schema.Types.ObjectId,
ref: DSF_AUTH_USER_MODEL_NAME,
required: true
},
dogsitter_id: {
type: Schema.Types.ObjectId,
ref: DSF_AUTH_USER_MODEL_NAME,
required: true
},
timestamp: {
type: Date,
default: Date.now
}
});
interactionSchema.index({ owner_id: 1, dogsitter_id: 1 });
module.exports.Interaction = model(DSF_INTERACTION_MODEL_NAME, interactionSchema);

View File

@@ -0,0 +1,83 @@
const { Schema, model } = require("mongoose");
const { DSF_AUTH_USER_MODEL_NAME } = require("../../const");
const userSchema = new Schema({
phone_number: {
type: String,
required: true,
unique: true,
match: /^\+?\d{10,15}$/
},
first_name: {
type: String,
required: true,
trim: true
},
second_name: {
type: String,
required: true,
trim: true
},
role: {
type: String,
enum: ["dogsitter", "owner"],
required: true
},
location: {
type: String,
required: function() {
return this.role === "dogsitter";
}
},
price: {
type: Number,
min: 0,
required: function() {
return this.role === "dogsitter";
}
},
about_me: {
type: String,
maxlength: 500
},
rating: {
type: Number,
min: 0,
max: 5,
default: 0
},
ratings: {
type: [Number],
default: [],
validate: {
validator: function(arr) {
return arr.every(v => v >= 0 && v <= 5);
},
message: "Рейтинг должен быть в диапазоне от 0 до 5!"
}
},
tg: {
type: String,
match: /^[a-zA-Z0-9_]{5,32}$/
},
created: {
type: Date,
default: Date.now
}
});
userSchema.virtual("id").get(function() {
return this._id.toHexString();
});
userSchema.set("toJSON", {
virtuals: true,
versionKey: false,
transform: function(doc, ret) {
delete ret._id;
delete ret.__v;
}
});
module.exports.User = model(DSF_AUTH_USER_MODEL_NAME, userSchema);

View File

@@ -0,0 +1,149 @@
const { Router } = require('express')
const { expressjwt } = require('express-jwt')
const { getAnswer } = require('../../utils/common')
const { User, Interaction } = require('./model')
const { TOKEN_KEY } = require('./const')
const { requiredValidate } = require('./utils')
const router = Router()
// Получение списка пользователей
router.get('/users', async (req, res) => {
const users = await User.find()
.select('-__v -ratings -phone_number')
.lean()
console.log('get users successfull')
res.send(getAnswer(null, users))
})
// Получение конкретного пользователя
router.get('/dogsitter-viewing', async (req, res) => {
const { userId } = req.params
const user = await User.findById(userId)
.select('-__v -ratings')
.lean()
if (!user) {
return res.status(404).send(getAnswer(new Error('Пользователь не найден')))
}
res.send(getAnswer(null, user))
})
router.use(expressjwt({ secret: TOKEN_KEY, algorithms: ['HS256'] }))
// Добавление оценки пользователю
router.post('/dogsitter-viewing/rating', requiredValidate('value'), async (req, res) => {
const { userId } = req.params
const { value } = req.body
const authUserId = req.auth.id
try {
const user = await User.findById(userId)
if (!user) throw new Error('Пользователь не найден')
if (user.role !== 'dogsitter') throw new Error('Нельзя оценивать этого пользователя')
if (user.id === authUserId) throw new Error('Нельзя оценивать самого себя')
user.ratings.push(Number(value))
user.rating = user.ratings.reduce((a, b) => a + b, 0) / user.ratings.length
const updatedUser = await user.save()
res.send(getAnswer(null, {
id: updatedUser.id,
rating: updatedUser.rating.toFixed(1),
totalRatings: updatedUser.ratings.length
}))
} catch (error) {
res.status(400).send(getAnswer(error))
}
})
// Обновление информации пользователя
router.patch('/users', async (req, res) => {
const { userId } = req.params
const updates = req.body
try {
const user = await User.findByIdAndUpdate(userId, updates, { new: true })
.select('-__v -ratings')
if (!user) throw new Error('Пользователь не найден')
res.send(getAnswer(null, user))
} catch (error) {
res.status(400).send(getAnswer(error))
}
})
// Создание объекта взаимодействия
router.post('/interactions',
expressjwt({ secret: TOKEN_KEY, algorithms: ['HS256'] }),
requiredValidate('dogsitter_id'),
async (req, res) => {
try {
const { dogsitter_id } = req.body
const owner_id = req.auth.id // ID из JWT токена
// Проверка существования пользователей
const [owner, dogsitter] = await Promise.all([
User.findById(owner_id),
User.findById(dogsitter_id)
])
if (!owner || owner.role !== 'owner') {
throw new Error('Владелец не найден или имеет неверную роль')
}
if (!dogsitter || dogsitter.role !== 'dogsitter') {
throw new Error('Догситтер не найден или имеет неверную роль')
}
// Создание взаимодействия
const interaction = await Interaction.create({
owner_id,
dogsitter_id
})
res.send(getAnswer(null, {
id: interaction.id,
timestamp: interaction.timestamp
}))
} catch (error) {
res.status(400).send(getAnswer(error))
}
}
)
router.get('/interactions/check', async (req, res) => {
const { owner_id, dogsitter_id } = req.query;
if (!owner_id || !dogsitter_id) {
return res.status(400).send(getAnswer('Missing owner_id or dogsitter_id'));
}
try {
// Поиск взаимодействий по owner_id и dogsitter_id
const interactions = await Interaction.find({ owner_id, dogsitter_id })
.select('-__v') // Выбираем только нужные поля
.lean();
if (interactions.length === 0) {
return res.status(404).send(getAnswer('No interactions found'));
}
res.send(getAnswer(null, interactions));
} catch (error) {
console.error('Error checking interactions:', error);
res.status(500).send(getAnswer('Internal Server Error'));
}
});
module.exports = router

View File

@@ -8,6 +8,10 @@ router.get("/update-like", (request, response) => {
response.send(require("./json/gamepage/success.json"));
});
router.get("/add-to-cart", (request, response) => {
response.send(require("./json/home-page-data/games-in-cart.json"));
});
router.get("/categories", (request, response) => {
response.send(require("./json/home-page-data/all-games.json"));
});
@@ -16,18 +20,32 @@ router.get("/favourites", (request, response) => {
response.send(require("./json/home-page-data/all-games.json"));
});
// router.get("/shopping-cart", (request, response) => {
// response.send(require("../json/shopping-cart/success.json"));
// });
router.get("/shopping-cart", (request, response) => {
response.send(require("./json/shopping-cart/success.json"));
response.send(require("./json/home-page-data/games-in-cart.json"));
});
router.get("/home", (request, response) => {
response.send(require("./json/home-page-data/success.json"));
// Добавляем поддержку разных ответов для /home
router.get("/home", (req, res) => {
if (stubs.home === "success") {
res.send(require("./json/home-page-data/success.json"));
} else if (stubs.home === "empty") {
res.send({ data: [] }); // Отправляем пустой массив
} else {
res.status(500).json({ success: false, message: "Server error" });
}
});
router.get("/all-games", (request, response) => {
response.send(require("./json/home-page-data/all-games.json"));
});
const stubs = {
home: "success",
};
// // Маршрут для обновления лайков
// router.post("/update-like", (request, response) => {
@@ -42,7 +60,6 @@ router.get("/all-games", (request, response) => {
// });
// });
const fs = require("fs").promises;
const path = require("path");
@@ -53,7 +70,7 @@ const commentsFilePath = path.join(__dirname, "./json/gamepage/success.json");
async function readComments() {
const data = await fs.readFile(commentsFilePath, "utf-8");
const parsedData = JSON.parse(data);
console.log("Прочитанные данные:", parsedData); // Логируем полученные данные
console.log("Прочитанные данные:", parsedData); // Логируем полученные данные
return parsedData;
}
// Write to JSON file
@@ -92,5 +109,137 @@ router.post("/update-like", async (req, res) => {
}
});
// Путь к JSON-файлу с корзиной
const cartFilePath = path.join(
__dirname,
"./json/home-page-data/games-in-cart.json"
);
// Функция для чтения JSON-файла
async function readCart() {
const data = await fs.readFile(cartFilePath, "utf-8");
return JSON.parse(data);
}
// Функция для записи в JSON-файл
async function writeCart(data) {
await fs.writeFile(cartFilePath, JSON.stringify(data, null, 2), "utf-8");
}
// Маршрут для добавления/удаления товара в корзине
router.post("/add-to-cart", async (req, res) => {
const { id, action } = req.body;
// Проверка наличия id и action
if (id === undefined || action === undefined) {
return res
.status(400)
.json({ success: false, message: "Invalid id or action" });
}
try {
const cartData = await readCart();
let ids = cartData.data.ids;
if (action === "add") {
// Если action "add", добавляем товар, если его нет в корзине
if (!ids?.includes(id)) {
ids.push(id);
}
} else if (action === "remove") {
// Если action "remove", удаляем товар, если он есть в корзине
if (ids?.includes(id)) {
ids = ids.filter((item) => item !== id);
}
} else {
// Если action невалиден
return res
.status(400)
.json({ success: false, message: "Invalid action" });
}
// Записываем обновленные данные обратно в файл
cartData.data.ids = ids;
await writeCart(cartData);
res.status(200).json({
success: true,
message: "Cart updated successfully",
data: cartData.data, // Возвращаем обновленные данные
});
} catch (error) {
console.error("Error updating cart:", error);
res.status(500).json({ success: false, message: "Server error" });
}
});
module.exports = router;
const createElement = (key, value, buttonTitle) => `
<label>
<input name="${key}" type="radio" ${
stubs[key] === value ? "checked" : ""
} onclick="fetch('/api/admin/set/${key}/${value}')"/>
${buttonTitle || value}
</label>
`;
router.get("/admin/home", (request, response) => {
response.send(`
<div>
<fieldset>
<legend>Настройка данных для /home</legend>
${createElement("home", "success", "Отдать успешный ответ")}
${createElement("home", "empty", "Отдать пустой массив")}
${createElement("home", "error", "Отдать ошибку")}
</fieldset>
</div>
`);
});
router.get("/admin/game-page", (request, response) => {
response.send(`
<div>
<fieldset>
<legend>Настройка данных для /game-page</legend>
${createElement("game-page", "success", "Отдать успешный ответ")}
${createElement("game-page", "empty", "Отдать пустой массив")}
${createElement("game-page", "error", "Отдать ошибку")}
</fieldset>
</div>
`);
});
router.get("/admin/categories", (request, response) => {
response.send(`
<div>
<fieldset>
<legend>Настройка данных для /categories</legend>
${createElement("categories", "success", "Отдать успешный ответ")}
${createElement("categories", "empty", "Отдать пустой массив")}
${createElement("categories", "error", "Отдать ошибку")}
</fieldset>
</div>
`);
});
router.get("/admin/favourites", (request, response) => {
response.send(`
<div>
<fieldset>
<legend>Настройка данных для /favourites</legend>
${createElement("favourites", "success", "Отдать успешный ответ")}
${createElement("favourites", "empty", "Отдать пустой массив")}
${createElement("favourites", "error", "Отдать ошибку")}
</fieldset>
</div>
`);
});
router.get("/admin/set/:key/:value", (request, response) => {
const { key, value } = request.params;
stubs[key] = value;
response.send("Настройки обновлены!");
});

View File

@@ -5,28 +5,28 @@
{
"username": ользователь1",
"text": "Текст комментария 1",
"likes": 11,
"likes": 13,
"rating": 8,
"date": "2025-03-01T10:00:00Z"
},
{
"username": ользователь2",
"text": "Текст комментария 2",
"likes": 7,
"likes": 10,
"rating": 7,
"date": "2025-01-01T10:00:00Z"
},
{
"username": ользователь3",
"text": "Текст комментария 3",
"likes": 2,
"likes": 4,
"rating": 3,
"date": "2025-02-01T10:00:00Z"
},
{
"username": ользователь4",
"text": "Текст комментария 4",
"likes": 15,
"likes": 18,
"rating": 2,
"date": "2025-12-01T10:00:00Z"
}

View File

@@ -105,23 +105,27 @@
{
"image": "news1",
"text": "Разработчики Delta Force: Hawk Ops представили крупномасштабный режим Havoc Warfare",
"imgPath": "img_news_1"
"imgPath": "img_news_1",
"link": "https://gamemag.ru/news/185583/delta-force-hawk-ops-gameplay-showcase-havoc-warfare"
},
{
"image": "news2",
"text": "Первый трейлер Assassins Creed Shadows — с темнокожим самураем в феодальной Японии",
"imgPath": "img_news_2"
"imgPath": "img_news_2",
"link": "https://stopgame.ru/newsdata/62686/pervyy_trailer_assassin_s_creed_shadows_s_temnokozhim_samuraem_v_feodalnoy_yaponii"
},
{
"image": "news3",
"text": "Призрак Цусимы» вышел на ПК — и уже ставит рекорды для Sony",
"imgPath": "img_news_3"
"imgPath": "img_news_3",
"link": "https://stopgame.ru/newsdata/62706/prizrak_cusimy_vyshel_na_pk_i_uzhe_stavit_rekordy_dlya_sony"
},
{
"image": "news4",
"text": "Авторы Skull and Bones расширяют планы на второй сезо",
"imgPath": "img_news_4"
"text": "Авторы Skull and Bones расширяют планы на второй сезон",
"imgPath": "img_news_4",
"link": "https://stopgame.ru/newsdata/62711/avtory_skull_and_bones_rasshiryayut_plany_na_vtoroy_sezon"
}
]
}
}
}