добавил eslint и prettier, реализовал функционал для сущность Пользователь

This commit is contained in:
dyakonovr 2024-03-25 21:20:08 +04:00
parent 14e1f464b1
commit 83010d4a0a
19 changed files with 3065 additions and 101 deletions

View File

@ -3,6 +3,9 @@ import dataProvider from "./dataProvider";
import RolesList from "./components/Role/List";
import RoleCreate from "./components/Role/Create";
import RoleEdit from "./components/Role/Edit";
import UsersList from "./components/User/List";
import UserCreate from "./components/User/Create";
import UserEdit from "./components/User/Edit";
function App() {
return (
@ -13,6 +16,12 @@ function App() {
create={<RoleCreate />}
edit={<RoleEdit />}
/>
<Resource
name="users"
list={<UsersList />}
create={<UserCreate />}
edit={<UserEdit />}
/>
</Admin>
);
}

View File

@ -0,0 +1,27 @@
import {
Create,
PasswordInput,
ReferenceInput,
SelectInput,
SimpleForm,
TextInput,
required,
} from "react-admin";
function UserCreate() {
return (
<Create>
<SimpleForm>
<TextInput source="nickname" fullWidth />
<TextInput source="email" fullWidth />
<TextInput source="avatar" fullWidth />
<ReferenceInput source="role_id" reference="roles">
<SelectInput optionText="name" fullWidth validate={required()} />
</ReferenceInput>
<PasswordInput source="password" fullWidth />
</SimpleForm>
</Create>
);
}
export default UserCreate;

View File

@ -0,0 +1,28 @@
import {
Edit,
PasswordInput,
ReferenceInput,
SelectInput,
SimpleForm,
TextInput,
required,
} from "react-admin";
import Title from "../Title";
function UserEdit() {
return (
<Edit title={<Title prefixText="Пользователь" />} actions={false}>
<SimpleForm>
<TextInput source="nickname" fullWidth />
<TextInput source="email" fullWidth />
<TextInput source="avatar" fullWidth />
<ReferenceInput source="role_id" reference="roles">
<SelectInput optionText="name" fullWidth validate={required()} />
</ReferenceInput>
<PasswordInput source="password" fullWidth />
</SimpleForm>
</Edit>
);
}
export default UserEdit;

View File

@ -0,0 +1,17 @@
import { List, Datagrid, TextField, EditButton } from "react-admin";
function UsersList() {
return (
<List>
<Datagrid>
<TextField source="id" />
<TextField source="nickname" />
<TextField source="email" />
<TextField source="avatar" />
<EditButton />
</Datagrid>
</List>
);
};
export default UsersList;

34
server/.eslintrc Normal file
View File

@ -0,0 +1,34 @@
{
"env": { "browser": true, "es2020": true },
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": { "ecmaVersion": "latest", "sourceType": "module" },
"plugins": ["import"],
"rules": {
"@typescript-eslint/consistent-type-imports": "error",
"prettier/prettier": ["warn", { "endOfLine": "auto" }],
"no-console": "warn",
"prefer-const": "error",
"comma-dangle": ["warn", "never"],
"semi": ["warn", "always"],
"import/order": [
"warn",
{
"groups": [
"builtin",
"external",
"internal",
"parent",
"sibling",
"index",
"object",
"type"
]
}
]
}
}

9
server/.prettierrc Normal file
View File

@ -0,0 +1,9 @@
{
"semi": true,
"jsxSingleQuote": false,
"bracketSpacing": true,
"trailingComma": "none",
"printWidth": 90,
"tabWidth": 2,
"arrowParens": "always"
}

View File

@ -0,0 +1,5 @@
export type RangeQueryParamType = [number, number];
export type SortQueryParamType = [string, string] | Array<[string, string]>;
export type FilterQueryParamType = {
[key: string]: unknown
}

View File

@ -1,20 +1,23 @@
import { NextFunction, Request, Response } from "express";
import Role from "../../models/role.model";
import { ApiErrorHandler } from "../../error/api-error.handler";
import { IRoleDto } from "./role.dto";
import { Order } from "sequelize";
type RangeQueryParamType = [number, number];
type SortQueryParamType = [string, string] | Array<[string, string]>;
type FilterQueryParamType = {
[key: string]: unknown
}
import type { Order } from "sequelize";
import type { IRoleDto } from "./role.dto";
import type { NextFunction, Request, Response } from "express";
import type {
FilterQueryParamType,
RangeQueryParamType,
SortQueryParamType
} from "../query-param.types";
class RoleController {
getAll = async (req: Request, res: Response, next: NextFunction) => {
try {
const range: RangeQueryParamType = req.query.range ? JSON.parse(req.query.range.toString()) : [0, 10];
const sort: SortQueryParamType = req.query.sort ? JSON.parse(req.query.sort.toString()) : ["id", "ASC"];
const range: RangeQueryParamType = req.query.range
? JSON.parse(req.query.range.toString())
: [0, 10];
const sort: SortQueryParamType = req.query.sort
? JSON.parse(req.query.sort.toString())
: ["id", "ASC"];
const roles = await Role.findAndCountAll({
offset: range[0],
@ -26,19 +29,28 @@ class RoleController {
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
}
};
// getMany = async (req, res, next) => {
// try {
// const filter = JSON.parse(req.query.filter);
// const users = await User.findAll({ attributes: { exclude: ["slug"] }, where: { id: filter.ids } });
// return res.json({ data: users });
// } catch (error) {
// next(ApiErrorHandler.internal(error.message));
// }
// }
getMany = async (req: Request, res: Response<{ data: Role[] }>, next: NextFunction) => {
try {
const filter: FilterQueryParamType = req.query.filter
? JSON.parse(req.query.filter.toString())
: { ids: [] };
create = async (req: Request<{}, {}, IRoleDto>, res: Response<Role>, next: NextFunction) => {
const roles = await Role.findAll({
where: { id: filter.ids as number[] }
});
return res.json({ data: roles });
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
};
create = async (
req: Request<object, object, IRoleDto>,
res: Response<Role>,
next: NextFunction
) => {
try {
const { name } = req.body;
const role = await Role.create({ name: name.toUpperCase() });
@ -46,9 +58,13 @@ class RoleController {
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
}
};
getById = async (req: Request<{ id: number }>, res: Response<Role>, next: NextFunction) => {
getById = async (
req: Request<{ id: number }>,
res: Response<Role>,
next: NextFunction
) => {
try {
const { id } = req.params;
const role = await Role.findByPk(id);
@ -58,9 +74,13 @@ class RoleController {
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
}
};
update = async (req: Request<{ id: number }, {}, IRoleDto>, res: Response<Role>, next: NextFunction) => {
update = async (
req: Request<{ id: number }, object, IRoleDto>,
res: Response<Role>,
next: NextFunction
) => {
try {
const { id } = req.params;
const { name } = req.body;
@ -75,9 +95,13 @@ class RoleController {
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
}
};
delete = async (req: Request<{ id: number }>, res: Response<Role>, next: NextFunction) => {
delete = async (
req: Request<{ id: number }>,
res: Response<Role>,
next: NextFunction
) => {
try {
const { id } = req.params;
const role = await Role.findByPk(id);
@ -89,11 +113,17 @@ class RoleController {
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
}
};
deleteMany = async (req: Request, res: Response<{data: Role[]}>, next: NextFunction) => {
deleteMany = async (
req: Request,
res: Response<{ data: Role[] }>,
next: NextFunction
) => {
try {
const filter: FilterQueryParamType = req.query.filter ? JSON.parse(req.query.filter.toString()) : {};
const filter: FilterQueryParamType = req.query.filter
? JSON.parse(req.query.filter.toString())
: {};
const roles = await Role.findAll({ where: filter });
Role.destroy({ where: filter });
@ -102,7 +132,7 @@ class RoleController {
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
}
};
}
export default new RoleController();
export default new RoleController();

View File

@ -0,0 +1,144 @@
import Role from "../../models/role.model";
import { ApiErrorHandler } from "../../error/api-error.handler";
import User from "../../models/user.model";
import type { IUserDto } from "./user.dto";
import type { Order } from "sequelize";
import type {
FilterQueryParamType,
RangeQueryParamType,
SortQueryParamType
} from "../query-param.types";
import type { NextFunction, Request, Response } from "express";
class UserController {
getAll = async (
req: Request,
res: Response<{ data: User[]; total: number }>,
next: NextFunction
) => {
try {
const range: RangeQueryParamType = req.query.range
? JSON.parse(req.query.range.toString())
: [0, 10];
const sort: SortQueryParamType = req.query.sort
? JSON.parse(req.query.sort.toString())
: ["id", "ASC"];
const users = await User.findAndCountAll({
attributes: { exclude: ["password"] },
include: Role,
offset: range[0],
limit: range[1] - range[0] + 1,
order: [sort] as Order
});
return res.json({ data: users.rows, total: users.count });
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
};
// getMany = async (req, res, next) => {
// try {
// const filter = JSON.parse(req.query.filter);
// const users = await User.findAll({ attributes: { exclude: ["slug"] }, where: { id: filter.ids } });
// return res.json({ data: users });
// } catch (error) {
// next(ApiErrorHandler.internal(error.message));
// }
// }
create = async (
req: Request<object, object, IUserDto>,
res: Response<User>,
next: NextFunction
) => {
try {
const { nickname, avatar, email, password, role_id } = req.body;
const user = await User.create({ nickname, avatar, email, password, role_id });
return res.json(user);
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
};
getById = async (
req: Request<{ id: number }>,
res: Response<User>,
next: NextFunction
) => {
try {
const { id } = req.params;
const user = await User.findByPk(id);
if (!user) return next(ApiErrorHandler.notFound("Такого пользователя не найдено"));
return res.json(user);
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
};
update = async (
req: Request<{ id: number }, object, IUserDto>,
res: Response<User>,
next: NextFunction
) => {
try {
const { id } = req.params;
const { nickname, avatar, email, password, role_id } = req.body;
const user = await User.findByPk(id);
if (!user) return next(ApiErrorHandler.internal("Такого пользователя не найдено"));
user.nickname = nickname;
user.avatar = avatar;
user.email = email;
user.password = password;
user.role_id = role_id;
await user.save();
return res.json(user);
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
};
delete = async (
req: Request<{ id: number }>,
res: Response<User>,
next: NextFunction
) => {
try {
const { id } = req.params;
const user = await User.findByPk(id);
if (!user) return next(ApiErrorHandler.internal("Такого пользователя не найдено"));
await user.destroy();
return res.json(user);
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
};
deleteMany = async (
req: Request,
res: Response<{ data: User[] }>,
next: NextFunction
) => {
try {
const filter: FilterQueryParamType = req.query.filter
? JSON.parse(req.query.filter.toString())
: {};
const users = await User.findAll({ where: filter });
User.destroy({ where: filter });
return res.json({ data: users });
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
};
}
export default new UserController();

View File

@ -0,0 +1,7 @@
export interface IUserDto {
nickname: string;
avatar: string;
email: string;
password: string;
role_id: number;
}

View File

@ -1,6 +1,7 @@
import dotenv from "dotenv";
import { Sequelize } from "sequelize-typescript";
import Role from "./models/role.model";
import User from "./models/user.model";
dotenv.config();
@ -11,5 +12,5 @@ export const sequelize = new Sequelize({
password: process.env.DB_PASSWORD,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
models: [Role]
models: [Role, User]
});

View File

@ -1,9 +1,10 @@
import { Table, Column, Model, DataType, Length } from 'sequelize-typescript';
import { Table, Column, Model, DataType, Length, HasMany } from "sequelize-typescript";
import User from "./user.model";
@Table({
timestamps: false,
tableName: "roles",
modelName: "Role",
modelName: "Role"
})
export default class Role extends Model {
@Column({
@ -16,7 +17,11 @@ export default class Role extends Model {
@Length({ min: 1, max: 20 })
@Column({
allowNull: false,
defaultValue: "Unknown",
type: DataType.STRING
})
declare name: string;
}
@HasMany(() => User, "role_id")
declare users: User[];
}

View File

@ -0,0 +1,72 @@
import {
Table,
Column,
Model,
DataType,
IsEmail,
CreatedAt,
UpdatedAt,
ForeignKey,
BelongsTo
} from "sequelize-typescript";
import Role from "./role.model";
@Table({
timestamps: true,
tableName: "users",
modelName: "user"
})
export default class User extends Model {
@Column({
primaryKey: true,
autoIncrement: true,
type: DataType.INTEGER
})
declare id: number;
@Column({
allowNull: false,
unique: true,
defaultValue: "Unknown",
type: DataType.STRING
})
declare nickname: string;
@Column({
defaultValue: "Unknown",
type: DataType.STRING
})
declare avatar: string;
@IsEmail
@Column({
allowNull: false,
unique: true,
defaultValue: "Unknown",
type: DataType.STRING
})
declare email: string;
@Column({
allowNull: false,
defaultValue: "Unknown",
type: DataType.STRING
})
declare password: string;
@CreatedAt
declare created_at: Date;
@UpdatedAt
declare updated_at: Date;
@ForeignKey(() => Role)
@Column({
allowNull: false,
type: DataType.INTEGER
})
declare role_id: number;
@BelongsTo(() => Role, "role_id")
declare role: Role;
}

2676
server/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,14 +2,22 @@
"dependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.3.1",
"argon2": "^0.31.2",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"express": "^4.18.2",
"pg": "^8.11.3",
"pg-hstore": "^2.3.4",
"prettier": "^3.2.5",
"sequelize": "^6.33.0",
"sequelize-typescript": "^2.1.6"
"sequelize-typescript": "^2.1.6",
"tsx": "^4.7.1"
},
"devDependencies": {
"@faker-js/faker": "^8.3.1",

View File

@ -1,6 +1,8 @@
import { Router } from "express";
import roleRouter from "./routes/role.router";
import userRouter from "./routes/user.router";
export const router = Router();
router.use("/roles", roleRouter);
router.use("/roles", roleRouter);
router.use("/users", userRouter);

View File

@ -5,6 +5,7 @@ const roleRouter = Router();
roleRouter.post("/", RoleController.create);
roleRouter.get("/", RoleController.getAll);
roleRouter.get("/get-many", RoleController.getMany);
roleRouter.get("/:id", RoleController.getById);
roleRouter.put("/:id", RoleController.update);
roleRouter.delete("/delete-many", RoleController.deleteMany);

View File

@ -0,0 +1,13 @@
import { Router } from "express";
import UserController from "../../controllers/user/user.controller";
const userRouter = Router();
userRouter.post("/", UserController.create);
userRouter.get("/", UserController.getAll);
userRouter.get("/:id", UserController.getById);
userRouter.put("/:id", UserController.update);
userRouter.delete("/delete-many", UserController.deleteMany);
userRouter.delete("/:id", UserController.delete);
export default userRouter;