add service and first method to controller

This commit is contained in:
Николай Вигдоров
2025-03-01 23:54:08 +03:00
parent 83ad4d43cd
commit 12b3d5b23c
10 changed files with 632 additions and 17 deletions

22
src/api.responses.ts Normal file
View File

@ -0,0 +1,22 @@
import { ApiResponseOptions } from '@nestjs/swagger';
import { StorageResponse } from './schemas';
export const AUTH_SUCCESS: ApiResponseOptions = {
status: 200,
description: 'Токен пользователя',
type: String,
};
export const AUTH_ERROR: ApiResponseOptions = {
status: 406,
description:
'Ошибка, при попытке получить доступ к данным без токена или с не корректным токеном',
type: Error,
};
export const GET_STORAGES_LIST_SUCCESS: ApiResponseOptions = {
status: 200,
description: 'Список всех картинок',
type: StorageResponse,
isArray: true,
};

View File

@ -1,12 +1,68 @@
import { Controller, Get } from '@nestjs/common';
import {
Controller,
Get,
Header,
HttpCode,
Options,
Post,
Req,
} from '@nestjs/common';
import { Request } from 'express';
import { AppService } from './app.service';
import { ApiBody, ApiResponse, ApiSecurity, ApiTags } from '@nestjs/swagger';
import {
ALLOW_CREDENTIALS,
ALLOW_HEADERS,
ALLOW_METHOD,
ALLOW_ORIGIN_ALL,
CONTENT_LENGTH,
} from './consts';
import {
AUTH_ERROR,
AUTH_SUCCESS,
GET_STORAGES_LIST_SUCCESS,
} from './api.responses';
import { AuthRequest } from './schemas';
import { Storage } from './types';
@Controller()
@ApiTags('storage-app')
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
@Post('/auth')
@ApiBody({
type: AuthRequest,
description: 'Объект с логином пользователя',
})
@Header(...ALLOW_ORIGIN_ALL)
@ApiResponse(AUTH_SUCCESS)
authUser(
@Req() request: Request<null, null, { login: string }>,
): Promise<string> {
return this.appService.authUser(request.body.login);
}
@Get('/storages')
@ApiSecurity('apiKey')
@Header(...ALLOW_ORIGIN_ALL)
@ApiResponse(GET_STORAGES_LIST_SUCCESS)
@ApiResponse(AUTH_ERROR)
async getStorageList(@Req() request: Request): Promise<Storage[]> {
const { login } = await this.appService.checkRequest(
request.headers.authorization,
);
return this.appService.getStorageList(login);
}
@Options(['', '/auth'])
@Header(...ALLOW_ORIGIN_ALL)
@Header(...ALLOW_METHOD)
@Header(...ALLOW_CREDENTIALS)
@Header(...CONTENT_LENGTH)
@Header(...ALLOW_HEADERS)
@HttpCode(204)
async options(): Promise<string> {
return await Promise.resolve('');
}
}

View File

@ -1,9 +1,32 @@
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MongooseModule } from '@nestjs/mongoose';
import { DB_STORAGES, DB_USERS, MONGO_URL } from './consts';
import {
StorageDocument,
StorageScheme,
UserDocument,
UserScheme,
} from './schemas';
@Module({
imports: [],
imports: [
MongooseModule.forRoot(`${MONGO_URL}/${DB_USERS}`, {
connectionName: DB_USERS,
}),
MongooseModule.forRoot(`${MONGO_URL}/${DB_STORAGES}`, {
connectionName: DB_STORAGES,
}),
MongooseModule.forFeature(
[{ name: UserDocument.name, schema: UserScheme }],
DB_USERS,
),
MongooseModule.forFeature(
[{ name: StorageDocument.name, schema: StorageScheme }],
DB_STORAGES,
),
],
controllers: [AppController],
providers: [AppService],
})

View File

@ -1,8 +1,130 @@
import { Injectable } from '@nestjs/common';
import {
BadRequestException,
Injectable,
NotAcceptableException,
} from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { StorageDocument, UserDocument } from './schemas';
import { Model } from 'mongoose';
import { User, Storage, StorageCreate, StorageUpdate } from './types';
import { v4 } from 'uuid';
import { DB_STORAGES, DB_USERS } from './consts';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
constructor(
@InjectModel(UserDocument.name, DB_USERS)
private userModel: Model<UserDocument>,
@InjectModel(StorageDocument.name, DB_STORAGES)
private storageModel: Model<StorageDocument>,
) {}
async checkRequest(token?: string): Promise<User> {
const userList = await this.userModel.find().exec();
const searchUser = userList.find((user) => user.token === token);
if (searchUser) {
return {
login: searchUser.login,
token: searchUser.token,
};
}
throw new NotAcceptableException(`Доступ запрещен`);
}
async authUser(login: string): Promise<string> {
const userList = await this.userModel.find().exec();
const searchUser = userList.find((user) => user.login === login);
if (searchUser) {
return searchUser.token;
}
const Model = this.userModel;
const userModel = new Model({
login,
token: v4(),
});
const newUser = await userModel.save();
return newUser.token;
}
async getStorageList(login: string): Promise<Storage[]> {
const storageList = await this.storageModel.find().exec();
const preparedList = storageList.map(({ data, _id, user }) => ({
data,
id: _id as string,
user,
}));
return preparedList.filter(({ user }) => user === login);
}
async getStorageById(login: string, id: string): Promise<Storage> {
const searchStorage = await this.storageModel.findById(id);
if (searchStorage && searchStorage.user === login) {
return {
data: searchStorage.data,
id: searchStorage._id as string,
user: searchStorage.user,
};
}
throw new BadRequestException(`Storage с id - "${id}" не найдена`);
}
async addStorage(login: string, storage: StorageCreate): Promise<Storage> {
const Model = this.storageModel;
const storageModel = new Model({
data: storage.data,
user: login,
});
try {
await storageModel.validate();
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
throw new BadRequestException(e.message);
}
const newStorage = await storageModel.save();
return {
data: newStorage.data,
user: newStorage.user,
id: newStorage._id as string,
};
}
async updateStorage(
login: string,
id: string,
storage: StorageUpdate,
): Promise<Storage> {
const searchStorage = await this.storageModel.findById(id);
if (!searchStorage || searchStorage.user !== login) {
throw new BadRequestException(`Storage с id - "${id}" не найден`);
}
await searchStorage.updateOne({
data: storage.data,
});
return {
data: storage.data,
user: searchStorage.user,
id: searchStorage._id as string,
};
}
async deleteStorageById(login: string, id: string): Promise<Storage> {
const searchStorage = await this.storageModel.findById(id);
if (!searchStorage || searchStorage.user !== login) {
throw new BadRequestException(`Storage с id - "${id}" не найден`);
}
const Model = this.storageModel;
await Model.findByIdAndDelete(id);
return {
data: searchStorage.data,
user: searchStorage.user,
id: searchStorage._id as string,
};
}
}

24
src/consts.ts Normal file
View File

@ -0,0 +1,24 @@
export const MONGO_URL = 'mongodb://localhost:27017';
export const APP_CONTROLLER = 'storage-app';
export const DB_USERS = 'storage-back-users';
export const DB_STORAGES = 'storage-back-storages';
export const ALLOW_ORIGIN_ALL: [string, string] = [
'Access-Control-Allow-Origin',
'*',
];
export const ALLOW_CREDENTIALS: [string, string] = [
'Access-Control-Allow-Credentials',
'true',
];
export const CONTENT_LENGTH: [string, string] = ['Content-Length', '0'];
export const ALLOW_METHOD: [string, string] = [
'Access-Control-Allow-Methods',
'GET,HEAD,PUT,PATCH,POST,DELETE',
];
export const ALLOW_HEADERS: [string, string] = [
'Access-Control-Allow-Headers',
'Version, Authorization, Content-Type, Api-Name, x-turbo-id, x-turbo-compression, chrome-proxy, chrome-proxy-ect',
];

View File

@ -1,8 +1,28 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { APP_CONTROLLER } from './consts';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const options = new DocumentBuilder()
.addSecurity('apiKey', {
type: 'apiKey',
in: 'header',
name: 'Authorization',
})
.setTitle('Storage API')
.setDescription('API для работы с хранилищами')
.setVersion('1.0.0')
.addTag(APP_CONTROLLER)
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('api', app, document);
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
void bootstrap();

53
src/schemas.ts Normal file
View File

@ -0,0 +1,53 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { ApiProperty } from '@nestjs/swagger';
import { Document } from 'mongoose';
import { Storage } from './types';
export class AuthRequest {
@ApiProperty()
login: string;
}
@Schema()
export class UserDocument extends Document {
@Prop({
type: String,
required: true,
})
login: string;
@Prop({
type: String,
required: true,
})
token: string;
}
@Schema()
export class StorageDocument extends Document {
@Prop({
type: Object,
required: true,
})
data: object;
@Prop({
type: String,
required: true,
})
user: string;
}
export const StorageScheme = SchemaFactory.createForClass(StorageDocument);
export const UserScheme = SchemaFactory.createForClass(UserDocument);
export class StorageResponse implements Storage {
@ApiProperty()
data: object;
@ApiProperty()
user: string;
@ApiProperty()
id: string;
}

18
src/types.ts Normal file
View File

@ -0,0 +1,18 @@
export type User = {
login: string;
token: string;
};
export type Storage = {
data: object;
user: string;
id: string;
};
export type StorageCreate = {
data: object;
};
export type StorageUpdate = {
data: object;
};