Merge branch 'tariff' into 'dev'
feat: tariff See merge request pena-services/hub_admin_backend_service!3
This commit is contained in:
commit
bda96dbd14
@ -1,6 +1,7 @@
|
||||
import { setAccountRoutes } from "@/routes/account.routes";
|
||||
import { setPrivilegeRoutes } from "@/routes/privilege.routes";
|
||||
import { setRoleRoutes } from "@/routes/role.routes";
|
||||
import { setTariffRoutes } from "@/routes/tariff.routes";
|
||||
|
||||
import type { FastifyInstance } from "fastify";
|
||||
|
||||
@ -8,4 +9,5 @@ export const combineRoutes = (server: FastifyInstance): void => {
|
||||
server.register(setRoleRoutes, { prefix: "/role" });
|
||||
server.register(setAccountRoutes, { prefix: "/account" });
|
||||
server.register(setPrivilegeRoutes, { prefix: "/privilege" });
|
||||
server.register(setTariffRoutes, { prefix: "/tariff" });
|
||||
};
|
||||
|
22
src/handlers/tariff/helpers.ts
Normal file
22
src/handlers/tariff/helpers.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { validateEmptyFields } from "@/utils/validate-empty-fields";
|
||||
|
||||
import type { TariffMessage } from "@/types/messages/tariff-message.type";
|
||||
import type { ObjectWithRequiredFields } from "@/types/object-with-required-fields";
|
||||
|
||||
export const validateTariff = (tariff: TariffMessage): [ObjectWithRequiredFields<TariffMessage>, Error | null] => {
|
||||
const [validatedTariff, errorEmpty] = validateEmptyFields(
|
||||
tariff,
|
||||
["isCustom", "name", "price", "privilegies"],
|
||||
false
|
||||
);
|
||||
|
||||
if (errorEmpty) {
|
||||
return [validatedTariff, errorEmpty];
|
||||
}
|
||||
|
||||
if (isNaN(Number(validatedTariff.price))) {
|
||||
return [validatedTariff, new Error("invalid 'price' value")];
|
||||
}
|
||||
|
||||
return [validatedTariff, null];
|
||||
};
|
125
src/handlers/tariff/index.ts
Normal file
125
src/handlers/tariff/index.ts
Normal file
@ -0,0 +1,125 @@
|
||||
import { Types } from "mongoose";
|
||||
|
||||
import { TariffModel } from "@/models/tariff.model";
|
||||
import { PrivilegeModel } from "@/models/privilege.model";
|
||||
|
||||
import { validateEmptyFields } from "@/utils/validate-empty-fields";
|
||||
import { validateTariff } from "./helpers";
|
||||
|
||||
import type { FastifyReply } from "fastify";
|
||||
|
||||
import type { Privilege } from "@/types/models/privilege.type";
|
||||
import type { CreateTariffRequest, GetTariffRequest, ReplaceTariffRequest } from "./types";
|
||||
|
||||
export const getTariffs = async () => TariffModel.find({}).lean();
|
||||
|
||||
export const getTariff = async (request: GetTariffRequest, reply: FastifyReply) => {
|
||||
const [requestParams, error] = validateEmptyFields(request.params || {}, ["id"]);
|
||||
|
||||
if (error) {
|
||||
reply.status(400);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!Types.ObjectId.isValid(requestParams.id)) {
|
||||
reply.status(400);
|
||||
return new Error("invalid id");
|
||||
}
|
||||
|
||||
const tariff = await TariffModel.findById(requestParams.id).lean();
|
||||
|
||||
if (!tariff) {
|
||||
reply.status(404);
|
||||
return new Error("tariff not found");
|
||||
}
|
||||
|
||||
return tariff;
|
||||
};
|
||||
|
||||
export const createTariff = async (request: CreateTariffRequest, reply: FastifyReply) => {
|
||||
const [requestBody, error] = validateTariff(request.body || {});
|
||||
|
||||
if (error) {
|
||||
reply.status(400);
|
||||
return error;
|
||||
}
|
||||
|
||||
for (const privilege of requestBody.privilegies) {
|
||||
const findedPrivilege = await PrivilegeModel.findOne({ privilegeId: privilege.privilegeId }).lean();
|
||||
|
||||
if (!findedPrivilege) {
|
||||
reply.status(404);
|
||||
return new Error(`privilege with id ${privilege.privilegeId} not found`);
|
||||
}
|
||||
}
|
||||
|
||||
const privilegiesMap = requestBody.privilegies.reduce<Record<string, Privilege[]>>((accamulator, privilege) => {
|
||||
if (!accamulator[privilege.privilegeId]) {
|
||||
accamulator[privilege.privilegeId] = [];
|
||||
}
|
||||
|
||||
accamulator[privilege.privilegeId].push(privilege);
|
||||
|
||||
return accamulator;
|
||||
}, {});
|
||||
|
||||
const newTariff = new TariffModel({
|
||||
name: requestBody.name,
|
||||
price: requestBody.price,
|
||||
isCustom: requestBody.isCustom,
|
||||
privilegies: privilegiesMap,
|
||||
});
|
||||
|
||||
await newTariff.save();
|
||||
|
||||
return newTariff;
|
||||
};
|
||||
|
||||
export const replaceTariff = async (request: ReplaceTariffRequest, reply: FastifyReply) => {
|
||||
const [requestBody, error] = validateTariff(request.body || {});
|
||||
|
||||
if (error) {
|
||||
reply.status(400);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!Types.ObjectId.isValid(request.params?.id || "")) {
|
||||
reply.status(400);
|
||||
return new Error("invalid id");
|
||||
}
|
||||
|
||||
const tariff = await TariffModel.findById(request.params?.id);
|
||||
|
||||
if (!tariff) {
|
||||
reply.status(404);
|
||||
return new Error("tariff not found");
|
||||
}
|
||||
|
||||
for (const privilege of requestBody.privilegies) {
|
||||
const findedPrivilege = await PrivilegeModel.findOne({ privilegeId: privilege.privilegeId }).lean();
|
||||
|
||||
if (!findedPrivilege) {
|
||||
reply.status(404);
|
||||
return new Error(`privilege with id ${privilege.privilegeId} not found`);
|
||||
}
|
||||
}
|
||||
|
||||
const privilegiesMap = requestBody.privilegies.reduce<Record<string, Privilege[]>>((accamulator, privilege) => {
|
||||
if (!accamulator[privilege.privilegeId]) {
|
||||
accamulator[privilege.privilegeId] = [];
|
||||
}
|
||||
|
||||
accamulator[privilege.privilegeId].push(privilege);
|
||||
|
||||
return accamulator;
|
||||
}, {});
|
||||
|
||||
await tariff.replaceOne({
|
||||
name: requestBody.name,
|
||||
price: requestBody.price,
|
||||
isCustom: requestBody.isCustom,
|
||||
privilegies: privilegiesMap,
|
||||
});
|
||||
|
||||
return tariff;
|
||||
};
|
11
src/handlers/tariff/types.ts
Normal file
11
src/handlers/tariff/types.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import type { FastifyRequest } from "fastify";
|
||||
import type { CreateTariffRoute, ReplaceTariffRoute } from "@/types/routes/tariff-routes.type";
|
||||
|
||||
export type GetTariffRequest = FastifyRequest<{
|
||||
Params?: {
|
||||
id?: string;
|
||||
};
|
||||
}>;
|
||||
|
||||
export type CreateTariffRequest = FastifyRequest<CreateTariffRoute>;
|
||||
export type ReplaceTariffRequest = FastifyRequest<ReplaceTariffRoute>;
|
@ -1,6 +1,6 @@
|
||||
import { Schema, model, SchemaDefinition } from "mongoose";
|
||||
|
||||
import { eloquentModelSchema } from "./eloquent-model.schema";
|
||||
import { EloquentSchema } from "./eloquent-model.schema";
|
||||
|
||||
import type { Account } from "@/types/models/account.type";
|
||||
|
||||
@ -22,7 +22,7 @@ const schema: SchemaDefinition<Account> = {
|
||||
type: String,
|
||||
default: "user",
|
||||
},
|
||||
...eloquentModelSchema,
|
||||
...EloquentSchema,
|
||||
};
|
||||
|
||||
const schemaSettings = {
|
||||
|
@ -1,7 +1,8 @@
|
||||
import type { SchemaDefinition } from "mongoose";
|
||||
import { Schema, SchemaDefinition } from "mongoose";
|
||||
|
||||
import type { EloquentModel } from "@/types/models/eloquent-model.type";
|
||||
|
||||
export const eloquentModelSchema: SchemaDefinition<EloquentModel> = {
|
||||
export const schema: SchemaDefinition<EloquentModel> = {
|
||||
createdAt: {
|
||||
type: Date,
|
||||
required: true,
|
||||
@ -22,3 +23,10 @@ export const eloquentModelSchema: SchemaDefinition<EloquentModel> = {
|
||||
default: false,
|
||||
},
|
||||
};
|
||||
|
||||
const schemaSettings = {
|
||||
versionKey: false,
|
||||
collection: "privilegies",
|
||||
};
|
||||
|
||||
export const EloquentSchema = new Schema<EloquentModel>(schema, schemaSettings);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Schema, model, SchemaDefinition } from "mongoose";
|
||||
|
||||
import { eloquentModelSchema } from "./eloquent-model.schema";
|
||||
import { EloquentSchema } from "./eloquent-model.schema";
|
||||
|
||||
import type { Privilege } from "@/types/models/privilege.type";
|
||||
|
||||
@ -37,7 +37,7 @@ const schema: SchemaDefinition<Privilege> = {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
...eloquentModelSchema,
|
||||
...EloquentSchema,
|
||||
};
|
||||
|
||||
const schemaSettings = {
|
||||
@ -45,6 +45,6 @@ const schemaSettings = {
|
||||
collection: "privilegies",
|
||||
};
|
||||
|
||||
const PrivilegeSchema = new Schema<Privilege>(schema, schemaSettings);
|
||||
export const PrivilegeSchema = new Schema<Privilege>(schema, schemaSettings);
|
||||
|
||||
export const PrivilegeModel = model("Privilege", PrivilegeSchema);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Schema, model, SchemaDefinition } from "mongoose";
|
||||
|
||||
import { eloquentModelSchema } from "./eloquent-model.schema";
|
||||
import { EloquentSchema } from "./eloquent-model.schema";
|
||||
|
||||
import type { Role } from "@/types/models/role.type";
|
||||
|
||||
@ -14,7 +14,7 @@ const schema: SchemaDefinition<Role> = {
|
||||
of: Boolean,
|
||||
default: {},
|
||||
},
|
||||
...eloquentModelSchema,
|
||||
...EloquentSchema,
|
||||
};
|
||||
|
||||
const schemaSettings = {
|
||||
|
36
src/models/tariff.model.ts
Normal file
36
src/models/tariff.model.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { Schema, model, SchemaDefinition } from "mongoose";
|
||||
|
||||
import { EloquentSchema } from "./eloquent-model.schema";
|
||||
import { PrivilegeSchema } from "./privilege.model";
|
||||
|
||||
import type { Tariff } from "@/types/models/tariff.type";
|
||||
|
||||
const schema: SchemaDefinition<Tariff> = {
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
price: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
isCustom: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
privilegies: {
|
||||
type: Map,
|
||||
of: PrivilegeSchema,
|
||||
default: {},
|
||||
},
|
||||
...EloquentSchema,
|
||||
};
|
||||
|
||||
const schemaSettings = {
|
||||
versionKey: false,
|
||||
collection: "tariffs",
|
||||
};
|
||||
|
||||
const TariffSchema = new Schema<Tariff>(schema, schemaSettings);
|
||||
|
||||
export const TariffModel = model("Tariff", TariffSchema);
|
@ -1,5 +1,5 @@
|
||||
import { createAccount, getAccount } from "@/handlers/account";
|
||||
import { verifyUser } from "@/handlers/account/middleware";
|
||||
import { verifyUser } from "@/handlers/auth/middleware";
|
||||
|
||||
import type { FastifyInstance, FastifyPluginOptions } from "fastify";
|
||||
import type { GetAccountRoute, CreateAccountRoute } from "@/types/routes/account-routes.type";
|
||||
|
14
src/routes/tariff.routes.ts
Normal file
14
src/routes/tariff.routes.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { createTariff, replaceTariff, getTariff, getTariffs } from "@/handlers/tariff";
|
||||
import { verifyUser } from "@/handlers/auth/middleware";
|
||||
|
||||
import type { FastifyInstance, FastifyPluginOptions } from "fastify";
|
||||
import type { CreateTariffRoute, ReplaceTariffRoute } from "@/types/routes/tariff-routes.type";
|
||||
|
||||
export const setTariffRoutes = <T = FastifyPluginOptions>(server: FastifyInstance, opts: T, done: () => void): void => {
|
||||
server.get("/:id", getTariff);
|
||||
server.get("/", getTariffs);
|
||||
server.post<CreateTariffRoute>("/", { preHandler: [verifyUser] }, createTariff);
|
||||
server.put<ReplaceTariffRoute>("/", { preHandler: [verifyUser] }, replaceTariff);
|
||||
|
||||
done();
|
||||
};
|
154
src/swagger.json
154
src/swagger.json
@ -1,154 +0,0 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "PenaHub Server",
|
||||
"version": "1.0.11",
|
||||
"license": {
|
||||
"name": "Apache 2.0",
|
||||
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
}
|
||||
},
|
||||
"externalDocs": {
|
||||
"description": "Find out more about Swagger",
|
||||
"url": "http://swagger.io"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://84.201.155.77:8080"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"/"
|
||||
],
|
||||
"summary": "Список пользователей",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"auth"
|
||||
],
|
||||
"summary": "Страница регистрации",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/auth": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"auth"
|
||||
],
|
||||
"summary": "Регистрация нового пользователя",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/urlencoded": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"login": {
|
||||
"type": "string",
|
||||
"example": "valera24"
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "valera24@mail.com"
|
||||
},
|
||||
"phoneNumber": {
|
||||
"type": "string",
|
||||
"example": 79993337733
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"example": "secret"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/login": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"login"
|
||||
],
|
||||
"summary": "Страница авторизации",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "return",
|
||||
"in": "query",
|
||||
"description": "URL на который будет перенаправлен пользователь после авторизации",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/login": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"login"
|
||||
],
|
||||
"summary": "Авторизация пользователя",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/urlencoded": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "valera24@mail.com"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"example": "secret"
|
||||
},
|
||||
"goto": {
|
||||
"type": "string",
|
||||
"example": "http://penahub.quiz/signedup"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful (Если не передан goto)"
|
||||
},
|
||||
"304": {
|
||||
"description": "Successful (Если передан goto)"
|
||||
},
|
||||
"401": {
|
||||
"description": "password incorrect"
|
||||
},
|
||||
"404": {
|
||||
"description": "User not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
src/types/messages/tariff-message.type.ts
Normal file
10
src/types/messages/tariff-message.type.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import type { EloquentModel } from "../models/eloquent-model.type";
|
||||
import type { Privilege } from "../models/privilege.type";
|
||||
import type { Tariff } from "../models/tariff.type";
|
||||
import type { ObjectWithPossibleFields } from "../object-with-possible-fields";
|
||||
|
||||
export type TariffMessage = ObjectWithPossibleFields<
|
||||
Omit<Tariff, keyof EloquentModel | "privilegies"> & {
|
||||
privilegies: Privilege[];
|
||||
}
|
||||
>;
|
9
src/types/models/tariff.type.ts
Normal file
9
src/types/models/tariff.type.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import type { Privilege } from "./privilege.type";
|
||||
import type { EloquentModel } from "./eloquent-model.type";
|
||||
|
||||
export type Tariff = EloquentModel & {
|
||||
name: string;
|
||||
price: number;
|
||||
isCustom: boolean;
|
||||
privilegies: Record<string, Privilege>;
|
||||
};
|
3
src/types/object-with-possible-fields.ts
Normal file
3
src/types/object-with-possible-fields.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export type ObjectWithPossibleFields<KeyValue extends Record<string, unknown>> = {
|
||||
[Key in keyof KeyValue]?: KeyValue[Key];
|
||||
};
|
11
src/types/routes/tariff-routes.type.ts
Normal file
11
src/types/routes/tariff-routes.type.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import type { RequestGenericInterface } from "fastify";
|
||||
import type { TariffMessage } from "../messages/tariff-message.type";
|
||||
|
||||
export type CreateTariffRoute = RequestGenericInterface & {
|
||||
Body?: TariffMessage;
|
||||
};
|
||||
|
||||
export type ReplaceTariffRoute = RequestGenericInterface & {
|
||||
Body?: TariffMessage;
|
||||
Params?: { id?: string };
|
||||
};
|
Loading…
Reference in New Issue
Block a user