Compare commits
9 Commits
6546225e4f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d1d625dd4 | |||
| 3f67c79cca | |||
| c489417874 | |||
| cee79e205c | |||
| 43005b7d15 | |||
| f839d215f3 | |||
| 88fcc33e87 | |||
| e0befd6375 | |||
| 1526e52d60 |
@ -1,6 +0,0 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
dist
|
||||
.env
|
||||
.git
|
||||
.gitignore
|
||||
41
.drone.yml
Normal file
41
.drone.yml
Normal file
@ -0,0 +1,41 @@
|
||||
kind: pipeline
|
||||
type: kubernetes
|
||||
name: ci
|
||||
|
||||
steps:
|
||||
- name: prepare
|
||||
image: alpine:3.19
|
||||
environment:
|
||||
GITEA_TOKEN:
|
||||
from_secret: GITEA_TOKEN
|
||||
commands:
|
||||
- apk add --no-cache git bash yq
|
||||
- git clone --depth 1 https://token:$GITEA_TOKEN@git.vigdorov.ru/vigdorov/ci-templates.git .ci
|
||||
- chmod +x .ci/scripts/*.sh
|
||||
- bash .ci/scripts/prepare.sh
|
||||
|
||||
- name: build
|
||||
image: gcr.io/kaniko-project/executor:v1.23.2-debug
|
||||
depends_on: [prepare]
|
||||
environment:
|
||||
HARBOR_USER:
|
||||
from_secret: HARBOR_USER
|
||||
HARBOR_PASSWORD:
|
||||
from_secret: HARBOR_PASSWORD
|
||||
commands:
|
||||
- /busybox/sh .ci/scripts/build.sh
|
||||
|
||||
- name: deploy
|
||||
image: alpine:3.19
|
||||
depends_on: [build]
|
||||
environment:
|
||||
KUBE_CONFIG:
|
||||
from_secret: KUBE_CONFIG
|
||||
commands:
|
||||
- apk add --no-cache bash yq kubectl helm
|
||||
- bash .ci/scripts/deploy.sh
|
||||
|
||||
|
||||
trigger:
|
||||
branch: [main, master, develop]
|
||||
event: [push, custom]
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -54,3 +54,5 @@ pids
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
.serena
|
||||
.claude
|
||||
|
||||
30
.http
30
.http
@ -1,8 +1,8 @@
|
||||
### use REST Client plugin for VSCode https://marketplace.visualstudio.com/items?itemName=humao.rest-client
|
||||
@host = http://localhost:4005
|
||||
@host = https://simple-storage.vigdorov.ru
|
||||
@user = test_user
|
||||
@auth = 7b5da8a1-b64c-43ea-90f3-cdd3da507504
|
||||
@storage_id = 67c420ab38fafe445411e76a
|
||||
@auth = 6d4c2f3e-e9ae-4a57-8a10-91656e4902eb
|
||||
@storage_id = 6817ac44687546864fb1f5ac
|
||||
|
||||
### Auth
|
||||
POST {{host}}/auth HTTP/1.1
|
||||
@ -28,10 +28,9 @@ Authorization: {{auth}}
|
||||
|
||||
{
|
||||
"data": {
|
||||
"users": ["ivan", "maria"],
|
||||
"count": 2
|
||||
"tasks": []
|
||||
},
|
||||
"storageName": "users"
|
||||
"storageName": "tasks"
|
||||
}
|
||||
|
||||
### Get storage
|
||||
@ -46,8 +45,23 @@ Authorization: {{auth}}
|
||||
|
||||
{
|
||||
"data": {
|
||||
"users": ["ivan", "maria", "fedor"],
|
||||
"count": 3
|
||||
"tasks": [
|
||||
{
|
||||
"title": "task #1"
|
||||
},
|
||||
{
|
||||
"title": "task #1"
|
||||
},
|
||||
{
|
||||
"title": "task #1"
|
||||
},
|
||||
{
|
||||
"title": "task #1"
|
||||
},
|
||||
{
|
||||
"title": "task #1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1
.npmrc
Normal file
1
.npmrc
Normal file
@ -0,0 +1 @@
|
||||
@vigdorov:registry=https://git.vigdorov.ru/api/packages/vigdorov/npm/
|
||||
@ -1,4 +1 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
"@vigdorov/prettier-config"
|
||||
|
||||
58
CLAUDE.md
Normal file
58
CLAUDE.md
Normal file
@ -0,0 +1,58 @@
|
||||
# Simple Storage
|
||||
|
||||
Учебный backend-проект для обучения студентов основам NestJS.
|
||||
|
||||
## Назначение
|
||||
|
||||
Простой REST API сервис для хранения данных. Используется как демонстрационный проект в рамках обучения backend-разработке.
|
||||
|
||||
## Стек
|
||||
|
||||
- **Framework:** NestJS 11
|
||||
- **Language:** TypeScript
|
||||
- **Runtime:** Node.js
|
||||
- **Database:** PostgreSQL (shared-db) + TypeORM
|
||||
- **CI/CD:** Drone CI + ci-templates
|
||||
|
||||
|
||||
## Структура
|
||||
|
||||
```
|
||||
src/
|
||||
├── main.ts # Точка входа
|
||||
├── app.module.ts # Корневой модуль (ConfigModule + TypeORM)
|
||||
├── app.controller.ts # REST контроллер
|
||||
├── app.service.ts # Бизнес-логика (TypeORM Repository)
|
||||
├── entities/
|
||||
│ ├── user.entity.ts # Entity: users
|
||||
│ └── storage.entity.ts # Entity: storages
|
||||
├── health/
|
||||
│ ├── health.module.ts # Health check модуль
|
||||
│ └── health.controller.ts # GET /health
|
||||
├── schemas.ts # Swagger DTO
|
||||
├── types.ts # TypeScript типы
|
||||
├── consts.ts # Константы
|
||||
└── api.responses.ts # Форматы ответов API
|
||||
```
|
||||
|
||||
## Команды
|
||||
|
||||
```bash
|
||||
npm install # Установка зависимостей
|
||||
npm run start:dev # Запуск в dev режиме
|
||||
npm run build # Сборка
|
||||
npm run start:prod # Production запуск
|
||||
```
|
||||
|
||||
## Деплой
|
||||
|
||||
- **CI/CD:** Drone CI через ci-templates (service.yaml + .drone.yml)
|
||||
- **Namespace:** backend-for-learning
|
||||
- **URL:** https://simple-storage.vigdorov.ru
|
||||
- **Health:** GET /health
|
||||
|
||||
## Локальная разработка
|
||||
|
||||
Переменные окружения в `.env.local`:
|
||||
- `DATABASE_HOST` — хост PostgreSQL (localhost для dev через NodePort :30432)
|
||||
- `DATABASE_PORT`, `DATABASE_USER`, `DATABASE_PASSWORD`, `DATABASE_NAME`
|
||||
23
Dockerfile
23
Dockerfile
@ -1,23 +0,0 @@
|
||||
# Этап сборки
|
||||
FROM node:18-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# Этап продакшна
|
||||
FROM node:18-alpine
|
||||
WORKDIR /app
|
||||
COPY --from=build /app/package*.json ./
|
||||
COPY --from=build /app/dist ./dist
|
||||
RUN npm ci --only=production
|
||||
|
||||
# Переменные окружения
|
||||
ENV NODE_ENV production
|
||||
|
||||
# Пользователь без привилегий для безопасности
|
||||
USER node
|
||||
|
||||
EXPOSE 3000
|
||||
CMD ["node", "dist/main"]
|
||||
@ -1,35 +1,2 @@
|
||||
// @ts-check
|
||||
import eslint from '@eslint/js';
|
||||
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
||||
import globals from 'globals';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default tseslint.config(
|
||||
{
|
||||
ignores: ['eslint.config.mjs'],
|
||||
},
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.recommendedTypeChecked,
|
||||
eslintPluginPrettierRecommended,
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node,
|
||||
...globals.jest,
|
||||
},
|
||||
ecmaVersion: 5,
|
||||
sourceType: 'module',
|
||||
parserOptions: {
|
||||
projectService: true,
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-floating-promises': 'warn',
|
||||
'@typescript-eslint/no-unsafe-argument': 'warn'
|
||||
},
|
||||
},
|
||||
);
|
||||
import {node} from '@vigdorov/eslint-config';
|
||||
export default node();
|
||||
|
||||
3051
package-lock.json
generated
3051
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "simple-storage",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
@ -24,11 +24,14 @@
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^11.0.1",
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"@nestjs/mongoose": "^11.0.1",
|
||||
"@nestjs/config": "^4.0.0",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/swagger": "^11.0.6",
|
||||
"mongoose": "^8.11.0",
|
||||
"@nestjs/terminus": "^11.0.0",
|
||||
"@nestjs/typeorm": "^11.0.0",
|
||||
"pg": "^8.13.1",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"typeorm": "^0.3.20",
|
||||
"rxjs": "^7.8.1",
|
||||
"uuid": "^11.1.0"
|
||||
},
|
||||
@ -36,6 +39,9 @@
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "^9.18.0",
|
||||
"@nestjs/cli": "^11.0.0",
|
||||
"@vigdorov/eslint-config": "^1.0.1",
|
||||
"@vigdorov/prettier-config": "^1.0.0",
|
||||
"@vigdorov/typescript-config": "^1.1.0",
|
||||
"@nestjs/schematics": "^11.0.0",
|
||||
"@nestjs/testing": "^11.0.1",
|
||||
"@swc/cli": "^0.6.0",
|
||||
|
||||
24
service.yaml
Normal file
24
service.yaml
Normal file
@ -0,0 +1,24 @@
|
||||
service:
|
||||
name: simple-storage
|
||||
type: api
|
||||
|
||||
deploy:
|
||||
namespace: backend-for-learning
|
||||
domain: simple-storage.vigdorov.ru
|
||||
|
||||
infrastructure:
|
||||
postgres: true
|
||||
|
||||
env:
|
||||
- name: DATABASE_HOST
|
||||
value: "simple-storage-postgres"
|
||||
- name: DATABASE_PORT
|
||||
value: "5432"
|
||||
- name: DATABASE_USER
|
||||
value: "simple_storage_user"
|
||||
- name: DATABASE_PASSWORD
|
||||
value: "SimpleStorage_DB_2025"
|
||||
- name: DATABASE_NAME
|
||||
value: "simple_storage_db"
|
||||
- name: CORS_ORIGIN
|
||||
value: "*"
|
||||
@ -32,7 +32,11 @@ import {
|
||||
MANIPULATE_STORAGE_SUCCESS,
|
||||
GET_STORAGES_LIST_SUCCESS,
|
||||
} from './api.responses';
|
||||
import { AuthRequest, StorageCreateRequest } from './schemas';
|
||||
import {
|
||||
AuthRequest,
|
||||
StorageCreateRequest,
|
||||
StorageUpdateRequest,
|
||||
} from './schemas';
|
||||
import { Storage, StorageCreate, StorageList, StorageUpdate } from './types';
|
||||
|
||||
@Controller()
|
||||
@ -107,6 +111,10 @@ export class AppController {
|
||||
name: 'id',
|
||||
description: 'id storage',
|
||||
})
|
||||
@ApiBody({
|
||||
type: StorageUpdateRequest,
|
||||
description: 'Объект обновления storage',
|
||||
})
|
||||
@Header(...ALLOW_ORIGIN_ALL)
|
||||
@ApiResponse(MANIPULATE_STORAGE_SUCCESS)
|
||||
@ApiResponse(AUTH_ERROR)
|
||||
|
||||
@ -1,31 +1,34 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
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';
|
||||
import { UserEntity } from './entities/user.entity';
|
||||
import { StorageEntity } from './entities/storage.entity';
|
||||
import { HealthModule } from './health/health.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
MongooseModule.forRoot(`${MONGO_URL}/${DB_USERS}`, {
|
||||
connectionName: DB_USERS,
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
envFilePath: ['.env.local', '.env'],
|
||||
}),
|
||||
MongooseModule.forRoot(`${MONGO_URL}/${DB_STORAGES}`, {
|
||||
connectionName: DB_STORAGES,
|
||||
TypeOrmModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
useFactory: (configService: ConfigService) => ({
|
||||
type: 'postgres',
|
||||
host: configService.get('DATABASE_HOST', 'localhost'),
|
||||
port: configService.get('DATABASE_PORT', 5432),
|
||||
username: configService.get('DATABASE_USER', 'postgres'),
|
||||
password: configService.get('DATABASE_PASSWORD', 'postgres'),
|
||||
database: configService.get('DATABASE_NAME', 'simple_storage_db'),
|
||||
entities: [UserEntity, StorageEntity],
|
||||
synchronize: true,
|
||||
}),
|
||||
MongooseModule.forFeature(
|
||||
[{ name: UserDocument.name, schema: UserScheme }],
|
||||
DB_USERS,
|
||||
),
|
||||
MongooseModule.forFeature(
|
||||
[{ name: StorageDocument.name, schema: StorageScheme }],
|
||||
DB_STORAGES,
|
||||
),
|
||||
}),
|
||||
TypeOrmModule.forFeature([UserEntity, StorageEntity]),
|
||||
HealthModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
|
||||
@ -3,9 +3,11 @@ import {
|
||||
Injectable,
|
||||
NotAcceptableException,
|
||||
} from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { StorageDocument, UserDocument } from './schemas';
|
||||
import { Model } from 'mongoose';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
import { UserEntity } from './entities/user.entity';
|
||||
import { StorageEntity } from './entities/storage.entity';
|
||||
import {
|
||||
User,
|
||||
Storage,
|
||||
@ -13,21 +15,18 @@ import {
|
||||
StorageUpdate,
|
||||
StorageList,
|
||||
} from './types';
|
||||
import { v4 } from 'uuid';
|
||||
import { DB_STORAGES, DB_USERS } from './consts';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
constructor(
|
||||
@InjectModel(UserDocument.name, DB_USERS)
|
||||
private userModel: Model<UserDocument>,
|
||||
@InjectModel(StorageDocument.name, DB_STORAGES)
|
||||
private storageModel: Model<StorageDocument>,
|
||||
@InjectRepository(UserEntity)
|
||||
private userRepository: Repository<UserEntity>,
|
||||
@InjectRepository(StorageEntity)
|
||||
private storageRepository: Repository<StorageEntity>,
|
||||
) {}
|
||||
|
||||
async checkRequest(token?: string): Promise<User> {
|
||||
const userList = await this.userModel.find().exec();
|
||||
const searchUser = userList.find((user) => user.token === token);
|
||||
const searchUser = await this.userRepository.findOne({ where: { token } });
|
||||
if (searchUser) {
|
||||
return {
|
||||
login: searchUser.login,
|
||||
@ -38,37 +37,36 @@ export class AppService {
|
||||
}
|
||||
|
||||
async authUser(login: string): Promise<string> {
|
||||
const userList = await this.userModel.find().exec();
|
||||
const searchUser = userList.find((user) => user.login === login);
|
||||
const searchUser = await this.userRepository.findOne({ where: { login } });
|
||||
if (searchUser) {
|
||||
return searchUser.token;
|
||||
}
|
||||
const Model = this.userModel;
|
||||
const userModel = new Model({
|
||||
const newUser = await this.userRepository.save({
|
||||
login,
|
||||
token: v4(),
|
||||
});
|
||||
const newUser = await userModel.save();
|
||||
return newUser.token;
|
||||
}
|
||||
|
||||
async getStorageList(login: string): Promise<StorageList> {
|
||||
const storageList = await this.storageModel.find().exec();
|
||||
const preparedList = storageList.map(({ _id, user, storageName }) => ({
|
||||
id: _id as string,
|
||||
const storageList = await this.storageRepository.find({
|
||||
where: { user: login },
|
||||
});
|
||||
return storageList.map(({ id, user, storageName }) => ({
|
||||
id,
|
||||
user,
|
||||
storageName,
|
||||
}));
|
||||
|
||||
return preparedList.filter(({ user }) => user === login);
|
||||
}
|
||||
|
||||
async getStorageById(login: string, id: string): Promise<Storage> {
|
||||
const searchStorage = await this.storageModel.findById(id);
|
||||
const searchStorage = await this.storageRepository.findOne({
|
||||
where: { id },
|
||||
});
|
||||
if (searchStorage && searchStorage.user === login) {
|
||||
return {
|
||||
data: searchStorage.data,
|
||||
id: searchStorage._id as string,
|
||||
id: searchStorage.id,
|
||||
storageName: searchStorage.storageName,
|
||||
user: searchStorage.user,
|
||||
};
|
||||
@ -77,25 +75,19 @@ export class AppService {
|
||||
}
|
||||
|
||||
async addStorage(login: string, storage: StorageCreate): Promise<Storage> {
|
||||
const Model = this.storageModel;
|
||||
const storageModel = new Model({
|
||||
if (!storage.data || !storage.storageName) {
|
||||
throw new BadRequestException('data и storageName обязательны');
|
||||
}
|
||||
const newStorage = await this.storageRepository.save({
|
||||
data: storage.data,
|
||||
storageName: storage.storageName,
|
||||
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,
|
||||
storageName: newStorage.storageName,
|
||||
id: newStorage._id as string,
|
||||
id: newStorage.id,
|
||||
};
|
||||
}
|
||||
|
||||
@ -104,7 +96,9 @@ export class AppService {
|
||||
id: string,
|
||||
storage: StorageUpdate,
|
||||
): Promise<Storage> {
|
||||
const searchStorage = await this.storageModel.findById(id);
|
||||
const searchStorage = await this.storageRepository.findOne({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!searchStorage || searchStorage.user !== login) {
|
||||
throw new BadRequestException(`Storage с id - "${id}" не найден`);
|
||||
@ -112,7 +106,7 @@ export class AppService {
|
||||
|
||||
const updatedStorageName = storage.storageName ?? searchStorage.storageName;
|
||||
|
||||
await searchStorage.updateOne({
|
||||
await this.storageRepository.update(id, {
|
||||
data: storage.data,
|
||||
storageName: updatedStorageName,
|
||||
});
|
||||
@ -121,24 +115,25 @@ export class AppService {
|
||||
data: storage.data,
|
||||
user: searchStorage.user,
|
||||
storageName: updatedStorageName,
|
||||
id: searchStorage._id as string,
|
||||
id: searchStorage.id,
|
||||
};
|
||||
}
|
||||
|
||||
async deleteStorageById(login: string, id: string): Promise<Storage> {
|
||||
const searchStorage = await this.storageModel.findById(id);
|
||||
const searchStorage = await this.storageRepository.findOne({
|
||||
where: { id },
|
||||
});
|
||||
if (!searchStorage || searchStorage.user !== login) {
|
||||
throw new BadRequestException(`Storage с id - "${id}" не найден`);
|
||||
}
|
||||
const Model = this.storageModel;
|
||||
|
||||
await Model.findByIdAndDelete(id);
|
||||
await this.storageRepository.delete(id);
|
||||
|
||||
return {
|
||||
data: searchStorage.data,
|
||||
user: searchStorage.user,
|
||||
storageName: searchStorage.storageName,
|
||||
id: searchStorage._id as string,
|
||||
id: searchStorage.id,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,3 @@
|
||||
// Подключение к MongoDB
|
||||
export const MONGO_URL = process.env.MONGODB_URI || 'mongodb://localhost:27017';
|
||||
|
||||
// Имя базы данных для пользователей и хранилищ
|
||||
export const DB_USERS = process.env.DB_USERS || 'storage-back-users';
|
||||
export const DB_STORAGES = process.env.DB_STORAGES || 'storage-back-storages';
|
||||
|
||||
// Порт приложения
|
||||
export const APP_PORT = parseInt(process.env.APP_PORT || '3000', 10);
|
||||
|
||||
|
||||
16
src/entities/storage.entity.ts
Normal file
16
src/entities/storage.entity.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
||||
|
||||
@Entity('storages')
|
||||
export class StorageEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'jsonb' })
|
||||
data: object;
|
||||
|
||||
@Column({ type: 'varchar' })
|
||||
storageName: string;
|
||||
|
||||
@Column({ type: 'varchar' })
|
||||
user: string;
|
||||
}
|
||||
13
src/entities/user.entity.ts
Normal file
13
src/entities/user.entity.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
||||
|
||||
@Entity('users')
|
||||
export class UserEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'varchar', unique: true })
|
||||
login: string;
|
||||
|
||||
@Column({ type: 'varchar' })
|
||||
token: string;
|
||||
}
|
||||
20
src/health/health.controller.ts
Normal file
20
src/health/health.controller.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import {
|
||||
HealthCheckService,
|
||||
HealthCheck,
|
||||
TypeOrmHealthIndicator,
|
||||
} from '@nestjs/terminus';
|
||||
|
||||
@Controller('health')
|
||||
export class HealthController {
|
||||
constructor(
|
||||
private health: HealthCheckService,
|
||||
private db: TypeOrmHealthIndicator,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@HealthCheck()
|
||||
check() {
|
||||
return this.health.check([() => this.db.pingCheck('database')]);
|
||||
}
|
||||
}
|
||||
9
src/health/health.module.ts
Normal file
9
src/health/health.module.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TerminusModule } from '@nestjs/terminus';
|
||||
import { HealthController } from './health.controller';
|
||||
|
||||
@Module({
|
||||
imports: [TerminusModule],
|
||||
controllers: [HealthController],
|
||||
})
|
||||
export class HealthModule {}
|
||||
@ -1,6 +1,4 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Document } from 'mongoose';
|
||||
import { Storage, StorageCreate } from './types';
|
||||
|
||||
export class AuthRequest {
|
||||
@ -8,45 +6,6 @@ export class AuthRequest {
|
||||
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,
|
||||
})
|
||||
storageName: string;
|
||||
|
||||
@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;
|
||||
@ -68,3 +27,8 @@ export class StorageCreateRequest implements StorageCreate {
|
||||
@ApiProperty()
|
||||
storageName: string;
|
||||
}
|
||||
|
||||
export class StorageUpdateRequest {
|
||||
@ApiProperty()
|
||||
data: object;
|
||||
}
|
||||
|
||||
@ -23,5 +23,5 @@ export type StorageCreate = {
|
||||
|
||||
export type StorageUpdate = {
|
||||
data: object;
|
||||
storageName?: string;
|
||||
storageName: string;
|
||||
};
|
||||
|
||||
@ -1,19 +1,10 @@
|
||||
{
|
||||
"extends": "@vigdorov/typescript-config/node",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "ES2023",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"strictNullChecks": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitAny": false,
|
||||
"strictBindCallApply": false,
|
||||
"noFallthroughCasesInSwitch": false
|
||||
|
||||
Reference in New Issue
Block a user