mirror of
https://github.com/docmost/docmost.git
synced 2025-11-13 08:14:06 +10:00
Setup TypeORM
This commit is contained in:
36
.gitignore
vendored
Normal file
36
.gitignore
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
.env
|
||||||
|
# compiled output
|
||||||
|
/dist
|
||||||
|
/node_modules
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
/coverage
|
||||||
|
/.nyc_output
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
/.idea
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# IDE - VSCode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
3
server/.gitignore
vendored
3
server/.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
|
.env
|
||||||
# compiled output
|
# compiled output
|
||||||
/dist
|
/dist
|
||||||
/node_modules
|
/node_modules
|
||||||
@ -32,4 +33,4 @@ lerna-debug.log*
|
|||||||
!.vscode/settings.json
|
!.vscode/settings.json
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
|||||||
@ -45,6 +45,30 @@ $ npm run start:dev
|
|||||||
$ npm run start:prod
|
$ npm run start:prod
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Migrations
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# This creates a new empty migration file named 'init'
|
||||||
|
$ npm run migration:create --name=init
|
||||||
|
|
||||||
|
# Generates 'init' migration file from existing entities to update the database schema
|
||||||
|
$ npm run migration:generate --name=init
|
||||||
|
|
||||||
|
# Runs all pending migrations to update the database schema
|
||||||
|
$ npm run migration:run
|
||||||
|
|
||||||
|
# Reverts the last executed migration
|
||||||
|
$ npm run migration:revert
|
||||||
|
|
||||||
|
# Reverts all migrations
|
||||||
|
$ npm run migration:revert
|
||||||
|
|
||||||
|
# Shows the list of executed and pending migrations
|
||||||
|
$ npm run migration:show
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Test
|
## Test
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
677
server/package-lock.json
generated
677
server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -17,16 +17,30 @@
|
|||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"test:cov": "jest --coverage",
|
"test:cov": "jest --coverage",
|
||||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
"test:e2e": "jest --config ./test/jest-e2e.json",
|
||||||
|
"typeorm": "typeorm-ts-node-commonjs -d ./src/database/typeorm.config.ts",
|
||||||
|
"migration:generate": "npm run typeorm migration:generate ./src/database/migrations/$npm_config_name",
|
||||||
|
"migration:create": "typeorm-ts-node-commonjs migration:create ./src/database/migrations/$npm_config_name",
|
||||||
|
"migration:run": "npm run typeorm migration:run",
|
||||||
|
"migration:revert": "npm run typeorm migration:revert",
|
||||||
|
"migration:revert:all": "while npm run migration:revert; do :; done",
|
||||||
|
"migration:show": "npm run typeorm migration:show"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^10.0.0",
|
"@nestjs/common": "^10.0.0",
|
||||||
|
"@nestjs/config": "^3.0.0",
|
||||||
"@nestjs/core": "^10.0.0",
|
"@nestjs/core": "^10.0.0",
|
||||||
|
"@nestjs/mapped-types": "^2.0.2",
|
||||||
"@nestjs/platform-express": "^10.0.0",
|
"@nestjs/platform-express": "^10.0.0",
|
||||||
"@nestjs/platform-fastify": "^10.1.3",
|
"@nestjs/platform-fastify": "^10.1.3",
|
||||||
|
"@nestjs/typeorm": "^10.0.0",
|
||||||
|
"class-transformer": "^0.5.1",
|
||||||
|
"class-validator": "^0.14.0",
|
||||||
"fastify": "^4.21.0",
|
"fastify": "^4.21.0",
|
||||||
|
"pg": "^8.11.2",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rxjs": "^7.8.1"
|
"rxjs": "^7.8.1",
|
||||||
|
"typeorm": "^0.3.17"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^10.0.0",
|
"@nestjs/cli": "^10.0.0",
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
|
||||||
import { AppController } from './app.controller';
|
|
||||||
import { AppService } from './app.service';
|
|
||||||
|
|
||||||
describe('AppController', () => {
|
|
||||||
let appController: AppController;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
const app: TestingModule = await Test.createTestingModule({
|
|
||||||
controllers: [AppController],
|
|
||||||
providers: [AppService],
|
|
||||||
}).compile();
|
|
||||||
|
|
||||||
appController = app.get<AppController>(AppController);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('root', () => {
|
|
||||||
it('should return "Hello World!"', () => {
|
|
||||||
expect(appController.getHello()).toBe('Hello World!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,9 +1,22 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { AppController } from './app.controller';
|
import { AppController } from './app.controller';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
|
import { CoreModule } from './core/core.module';
|
||||||
|
import { EnvironmentModule } from './environment/environment.module';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { AppDataSource } from './database/typeorm.config';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [],
|
imports: [
|
||||||
|
CoreModule,
|
||||||
|
EnvironmentModule,
|
||||||
|
TypeOrmModule.forRoot({
|
||||||
|
...AppDataSource.options,
|
||||||
|
entities: ['dist/src/**/*.entity.ts'],
|
||||||
|
migrations: ['dist/src/**/migrations/*.{ts,js}'],
|
||||||
|
autoLoadEntities: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService],
|
providers: [AppService],
|
||||||
})
|
})
|
||||||
|
|||||||
7
server/src/core/core.module.ts
Normal file
7
server/src/core/core.module.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { UserModule } from './user/user.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [UserModule],
|
||||||
|
})
|
||||||
|
export class CoreModule {}
|
||||||
1
server/src/core/user/dto/create-user.dto.ts
Normal file
1
server/src/core/user/dto/create-user.dto.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export class CreateUserDto {}
|
||||||
4
server/src/core/user/dto/update-user.dto.ts
Normal file
4
server/src/core/user/dto/update-user.dto.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { PartialType } from '@nestjs/mapped-types';
|
||||||
|
import { CreateUserDto } from './create-user.dto';
|
||||||
|
|
||||||
|
export class UpdateUserDto extends PartialType(CreateUserDto) {}
|
||||||
49
server/src/core/user/entities/user.entity.ts
Normal file
49
server/src/core/user/entities/user.entity.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import {
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
@Entity('users')
|
||||||
|
export class User {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column({ unique: true })
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
emailVerifiedAt: Date;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
password: string;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
avatar_url: string;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
locale: string;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
timezone: string;
|
||||||
|
|
||||||
|
@Column({ type: 'jsonb', nullable: true })
|
||||||
|
settings: any;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
lastLoginAt: Date;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
lastLoginIp: string;
|
||||||
|
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
4
server/src/core/user/repositories/user.repository.ts
Normal file
4
server/src/core/user/repositories/user.repository.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { User } from '../entities/user.entity';
|
||||||
|
|
||||||
|
export class UserRepository extends Repository<User> {}
|
||||||
20
server/src/core/user/user.controller.spec.ts
Normal file
20
server/src/core/user/user.controller.spec.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { UserController } from './user.controller';
|
||||||
|
import { UserService } from './user.service';
|
||||||
|
|
||||||
|
describe('UserController', () => {
|
||||||
|
let controller: UserController;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [UserController],
|
||||||
|
providers: [UserService],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
controller = module.get<UserController>(UserController);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(controller).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
37
server/src/core/user/user.controller.ts
Normal file
37
server/src/core/user/user.controller.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
Patch,
|
||||||
|
Param,
|
||||||
|
Delete,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { UserService } from './user.service';
|
||||||
|
import { CreateUserDto } from './dto/create-user.dto';
|
||||||
|
import { UpdateUserDto } from './dto/update-user.dto';
|
||||||
|
|
||||||
|
@Controller('user')
|
||||||
|
export class UserController {
|
||||||
|
constructor(private readonly userService: UserService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
create(@Body() createUserDto: CreateUserDto) {
|
||||||
|
return this.userService.create(createUserDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
findOne(@Param('id') id: string) {
|
||||||
|
return this.userService.findOne(+id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(':id')
|
||||||
|
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
|
||||||
|
return this.userService.update(+id, updateUserDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
remove(@Param('id') id: string) {
|
||||||
|
return this.userService.remove(+id);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
server/src/core/user/user.module.ts
Normal file
12
server/src/core/user/user.module.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { UserService } from './user.service';
|
||||||
|
import { UserController } from './user.controller';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { User } from './entities/user.entity';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([User])],
|
||||||
|
controllers: [UserController],
|
||||||
|
providers: [UserService],
|
||||||
|
})
|
||||||
|
export class UserModule {}
|
||||||
18
server/src/core/user/user.service.spec.ts
Normal file
18
server/src/core/user/user.service.spec.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { UserService } from './user.service';
|
||||||
|
|
||||||
|
describe('UserService', () => {
|
||||||
|
let service: UserService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [UserService],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
service = module.get<UserService>(UserService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(service).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
30
server/src/core/user/user.service.ts
Normal file
30
server/src/core/user/user.service.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { CreateUserDto } from './dto/create-user.dto';
|
||||||
|
import { UpdateUserDto } from './dto/update-user.dto';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { User } from './entities/user.entity';
|
||||||
|
import { UserRepository } from './repositories/user.repository';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserService {
|
||||||
|
constructor(@InjectRepository(User) private userRepository: UserRepository) {}
|
||||||
|
create(createUserDto: CreateUserDto) {
|
||||||
|
return 'This action adds a new user';
|
||||||
|
}
|
||||||
|
|
||||||
|
findAll() {
|
||||||
|
return `This action returns all user`;
|
||||||
|
}
|
||||||
|
|
||||||
|
findOne(id: number) {
|
||||||
|
return `This action returns a #${id} user`;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(id: number, updateUserDto: UpdateUserDto) {
|
||||||
|
return `This action updates a #${id} user`;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(id: number) {
|
||||||
|
return `This action removes a #${id} user`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
export class CreateUserTable1691158956520 implements MigrationInterface {
|
||||||
|
name = 'CreateUserTable1691158956520';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE TABLE "users" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "email" character varying NOT NULL, "emailVerifiedAt" TIMESTAMP, "password" character varying NOT NULL, "avatar_url" character varying, "locale" character varying, "timezone" character varying, "settings" jsonb, "lastLoginAt" TIMESTAMP, "lastLoginIp" character varying, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3" UNIQUE ("email"), CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY ("id"))`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`DROP TABLE "users"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
server/src/database/typeorm.config.ts
Normal file
14
server/src/database/typeorm.config.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { DataSource } from 'typeorm';
|
||||||
|
import * as dotenv from 'dotenv';
|
||||||
|
dotenv.config();
|
||||||
|
export const AppDataSource = new DataSource({
|
||||||
|
type: 'postgres',
|
||||||
|
url:
|
||||||
|
process.env.DATABASE_URL ||
|
||||||
|
'postgresql://postgres:password@localhost:5432/dc?schema=public',
|
||||||
|
entities: ['src/**/*.entity.ts'],
|
||||||
|
migrations: ['src/**/migrations/*.{ts,js}'],
|
||||||
|
subscribers: [],
|
||||||
|
synchronize: process.env.NODE_ENV === 'development',
|
||||||
|
logging: process.env.NODE_ENV === 'development',
|
||||||
|
});
|
||||||
18
server/src/environment/environment.module.ts
Normal file
18
server/src/environment/environment.module.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { Global, Module } from '@nestjs/common';
|
||||||
|
import { EnvironmentService } from './environment.service';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
import { validate } from './environment.validation';
|
||||||
|
|
||||||
|
@Global()
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
ConfigModule.forRoot({
|
||||||
|
isGlobal: true,
|
||||||
|
expandVariables: true,
|
||||||
|
validate,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
providers: [EnvironmentService],
|
||||||
|
exports: [EnvironmentService],
|
||||||
|
})
|
||||||
|
export class EnvironmentModule {}
|
||||||
18
server/src/environment/environment.service.spec.ts
Normal file
18
server/src/environment/environment.service.spec.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { EnvironmentService } from './environment.service';
|
||||||
|
|
||||||
|
describe('EnvironmentService', () => {
|
||||||
|
let service: EnvironmentService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [EnvironmentService],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
service = module.get<EnvironmentService>(EnvironmentService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(service).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
26
server/src/environment/environment.service.ts
Normal file
26
server/src/environment/environment.service.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class EnvironmentService {
|
||||||
|
constructor(private configService: ConfigService) {}
|
||||||
|
|
||||||
|
getEnv(): string {
|
||||||
|
return this.configService.get<string>('NODE_ENV');
|
||||||
|
}
|
||||||
|
|
||||||
|
getPort(): string {
|
||||||
|
return this.configService.get<string>('PORT');
|
||||||
|
}
|
||||||
|
getDatabaseURL(): string {
|
||||||
|
return this.configService.get<string>('DATABASE_URL');
|
||||||
|
}
|
||||||
|
|
||||||
|
getJwtSecret(): string {
|
||||||
|
return this.configService.get<string>('JWT_SECRET_KEY');
|
||||||
|
}
|
||||||
|
|
||||||
|
getJwtTokenExpiresIn(): string {
|
||||||
|
return this.configService.get<string>('JWT_TOKEN_EXPIRES_IN');
|
||||||
|
}
|
||||||
|
}
|
||||||
20
server/src/environment/environment.validation.ts
Normal file
20
server/src/environment/environment.validation.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { IsString, IsUrl, validateSync } from 'class-validator';
|
||||||
|
import { plainToClass } from 'class-transformer';
|
||||||
|
|
||||||
|
export class EnvironmentVariables {
|
||||||
|
@IsString()
|
||||||
|
NODE_ENV: string;
|
||||||
|
|
||||||
|
@IsUrl({ protocols: ['postgres', 'postgresql'], require_tld: false })
|
||||||
|
DATABASE_URL: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validate(config: Record<string, any>) {
|
||||||
|
const validatedConfig = plainToClass(EnvironmentVariables, config);
|
||||||
|
|
||||||
|
const errors = validateSync(validatedConfig);
|
||||||
|
if (errors.length > 0) {
|
||||||
|
throw new Error(errors.toString());
|
||||||
|
}
|
||||||
|
return validatedConfig;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user