update database
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
coverage
|
||||||
|
.git
|
||||||
|
.github
|
||||||
|
*.md
|
||||||
|
test
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
+7
-1
@@ -5,6 +5,9 @@ DB_USERNAME=
|
|||||||
DB_PASSWORD=
|
DB_PASSWORD=
|
||||||
DB_NAME=
|
DB_NAME=
|
||||||
|
|
||||||
|
# CapRover production example:
|
||||||
|
# DB_HOST=srv-captain--growdb
|
||||||
|
|
||||||
# SMS
|
# SMS
|
||||||
SMS_API_KEY=
|
SMS_API_KEY=
|
||||||
SMS_SENDER=
|
SMS_SENDER=
|
||||||
@@ -24,5 +27,8 @@ JWT_REFRESH_SECRET=
|
|||||||
JWT_REFRESH_EXPIRES_IN=7d
|
JWT_REFRESH_EXPIRES_IN=7d
|
||||||
|
|
||||||
# App
|
# App
|
||||||
PORT=3000
|
PORT=4000
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
|
|
||||||
|
# CORS (comma-separated origins, e.g. https://app.example.com)
|
||||||
|
# CORS_ORIGIN=
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 2,
|
||||||
|
"dockerfilePath": "./Dockerfile"
|
||||||
|
}
|
||||||
@@ -12,6 +12,9 @@
|
|||||||
"start:dev": "NODE_ENV=development nest start --watch",
|
"start:dev": "NODE_ENV=development nest start --watch",
|
||||||
"start:debug": "nest start --debug --watch",
|
"start:debug": "nest start --debug --watch",
|
||||||
"start:prod": "node dist/main",
|
"start:prod": "node dist/main",
|
||||||
|
"migration:run": "typeorm migration:run -d dist/database/data-source.js",
|
||||||
|
"migration:revert": "typeorm migration:revert -d dist/database/data-source.js",
|
||||||
|
"migration:generate": "typeorm migration:generate -d src/database/data-source.ts",
|
||||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
|
|||||||
+2
-11
@@ -4,6 +4,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
|||||||
import { AuthModule } from './auth/auth.module';
|
import { AuthModule } from './auth/auth.module';
|
||||||
import { CustomersModule } from './customers/customers.module';
|
import { CustomersModule } from './customers/customers.module';
|
||||||
import configuration from './config/configuration';
|
import configuration from './config/configuration';
|
||||||
|
import { getTypeOrmOptions } from './database/typeorm-options';
|
||||||
import { SalonsModule } from './salons/salons.module';
|
import { SalonsModule } from './salons/salons.module';
|
||||||
import { UsersModule } from './users/users.module';
|
import { UsersModule } from './users/users.module';
|
||||||
|
|
||||||
@@ -13,17 +14,7 @@ import { UsersModule } from './users/users.module';
|
|||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
load: [configuration],
|
load: [configuration],
|
||||||
}),
|
}),
|
||||||
TypeOrmModule.forRoot({
|
TypeOrmModule.forRoot(getTypeOrmOptions()),
|
||||||
type: 'postgres',
|
|
||||||
host: process.env.DB_HOST,
|
|
||||||
port: parseInt(process.env.DB_PORT as string),
|
|
||||||
username: process.env.DB_USERNAME,
|
|
||||||
password: process.env.DB_PASSWORD,
|
|
||||||
database: process.env.DB_NAME,
|
|
||||||
autoLoadEntities: true,
|
|
||||||
entities: [__dirname + '/**/*.entity.{js,ts}'],
|
|
||||||
synchronize: process.env.NODE_ENV !== 'production',
|
|
||||||
}),
|
|
||||||
AuthModule,
|
AuthModule,
|
||||||
UsersModule,
|
UsersModule,
|
||||||
SalonsModule,
|
SalonsModule,
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import 'dotenv/config';
|
||||||
|
import { DataSource } from 'typeorm';
|
||||||
|
|
||||||
|
export default new DataSource({
|
||||||
|
type: 'postgres',
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: parseInt(process.env.DB_PORT ?? '5432', 10),
|
||||||
|
username: process.env.DB_USERNAME,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_NAME,
|
||||||
|
entities: ['src/**/*.entity.ts'],
|
||||||
|
migrations: ['src/database/migrations/*.ts'],
|
||||||
|
});
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm';
|
||||||
|
|
||||||
|
export class InitialSchema1719600000000 implements MigrationInterface {
|
||||||
|
name = 'InitialSchema1719600000000';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE TYPE "public"."users_role_enum" AS ENUM('admin', 'salon-owner', 'customer')`,
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: 'users',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
type: 'uuid',
|
||||||
|
isPrimary: true,
|
||||||
|
generationStrategy: 'uuid',
|
||||||
|
default: 'gen_random_uuid()',
|
||||||
|
},
|
||||||
|
{ name: 'firstName', type: 'varchar', length: '100' },
|
||||||
|
{ name: 'lastName', type: 'varchar', length: '100' },
|
||||||
|
{ name: 'mobile', type: 'varchar', length: '11', isUnique: true },
|
||||||
|
{
|
||||||
|
name: 'role',
|
||||||
|
type: 'users_role_enum',
|
||||||
|
default: `'customer'`,
|
||||||
|
},
|
||||||
|
{ name: 'createdAt', type: 'timestamp', default: 'now()' },
|
||||||
|
{ name: 'updatedAt', type: 'timestamp', default: 'now()' },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: 'salons',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
type: 'uuid',
|
||||||
|
isPrimary: true,
|
||||||
|
generationStrategy: 'uuid',
|
||||||
|
default: 'gen_random_uuid()',
|
||||||
|
},
|
||||||
|
{ name: 'name', type: 'varchar', length: '200' },
|
||||||
|
{ name: 'description', type: 'text', isNullable: true },
|
||||||
|
{ name: 'address', type: 'varchar', length: '500' },
|
||||||
|
{ name: 'phone', type: 'varchar', length: '11' },
|
||||||
|
{ name: 'ownerId', type: 'uuid' },
|
||||||
|
{ name: 'createdAt', type: 'timestamp', default: 'now()' },
|
||||||
|
{ name: 'updatedAt', type: 'timestamp', default: 'now()' },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createForeignKey(
|
||||||
|
'salons',
|
||||||
|
new TableForeignKey({
|
||||||
|
columnNames: ['ownerId'],
|
||||||
|
referencedTableName: 'users',
|
||||||
|
referencedColumnNames: ['id'],
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: 'customers',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
type: 'uuid',
|
||||||
|
isPrimary: true,
|
||||||
|
generationStrategy: 'uuid',
|
||||||
|
default: 'gen_random_uuid()',
|
||||||
|
},
|
||||||
|
{ name: 'userId', type: 'uuid', isUnique: true },
|
||||||
|
{ name: 'dateOfBirth', type: 'date' },
|
||||||
|
{ name: 'address', type: 'varchar', length: '500' },
|
||||||
|
{ name: 'createdAt', type: 'timestamp', default: 'now()' },
|
||||||
|
{ name: 'updatedAt', type: 'timestamp', default: 'now()' },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createForeignKey(
|
||||||
|
'customers',
|
||||||
|
new TableForeignKey({
|
||||||
|
columnNames: ['userId'],
|
||||||
|
referencedTableName: 'users',
|
||||||
|
referencedColumnNames: ['id'],
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: 'otps',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
type: 'uuid',
|
||||||
|
isPrimary: true,
|
||||||
|
generationStrategy: 'uuid',
|
||||||
|
default: 'gen_random_uuid()',
|
||||||
|
},
|
||||||
|
{ name: 'mobile', type: 'varchar', length: '11' },
|
||||||
|
{ name: 'code', type: 'varchar' },
|
||||||
|
{ name: 'expiresAt', type: 'timestamp' },
|
||||||
|
{ name: 'attempts', type: 'int', default: 0 },
|
||||||
|
{ name: 'verified', type: 'boolean', default: false },
|
||||||
|
{ name: 'createdAt', type: 'timestamp', default: 'now()' },
|
||||||
|
],
|
||||||
|
indices: [{ columnNames: ['mobile'] }],
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: 'refresh_tokens',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
type: 'uuid',
|
||||||
|
isPrimary: true,
|
||||||
|
generationStrategy: 'uuid',
|
||||||
|
default: 'gen_random_uuid()',
|
||||||
|
},
|
||||||
|
{ name: 'userId', type: 'uuid' },
|
||||||
|
{ name: 'tokenHash', type: 'varchar' },
|
||||||
|
{ name: 'expiresAt', type: 'timestamp' },
|
||||||
|
{ name: 'revokedAt', type: 'timestamp', isNullable: true },
|
||||||
|
{ name: 'createdAt', type: 'timestamp', default: 'now()' },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createForeignKey(
|
||||||
|
'refresh_tokens',
|
||||||
|
new TableForeignKey({
|
||||||
|
columnNames: ['userId'],
|
||||||
|
referencedTableName: 'users',
|
||||||
|
referencedColumnNames: ['id'],
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: 'permissions',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
type: 'uuid',
|
||||||
|
isPrimary: true,
|
||||||
|
generationStrategy: 'uuid',
|
||||||
|
default: 'gen_random_uuid()',
|
||||||
|
},
|
||||||
|
{ name: 'code', type: 'varchar', isUnique: true },
|
||||||
|
{ name: 'description', type: 'varchar' },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: 'user_permissions',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
type: 'uuid',
|
||||||
|
isPrimary: true,
|
||||||
|
generationStrategy: 'uuid',
|
||||||
|
default: 'gen_random_uuid()',
|
||||||
|
},
|
||||||
|
{ name: 'userId', type: 'uuid' },
|
||||||
|
{ name: 'permissionId', type: 'uuid' },
|
||||||
|
],
|
||||||
|
uniques: [{ columnNames: ['userId', 'permissionId'] }],
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createForeignKey(
|
||||||
|
'user_permissions',
|
||||||
|
new TableForeignKey({
|
||||||
|
columnNames: ['userId'],
|
||||||
|
referencedTableName: 'users',
|
||||||
|
referencedColumnNames: ['id'],
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createForeignKey(
|
||||||
|
'user_permissions',
|
||||||
|
new TableForeignKey({
|
||||||
|
columnNames: ['permissionId'],
|
||||||
|
referencedTableName: 'permissions',
|
||||||
|
referencedColumnNames: ['id'],
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.dropTable('user_permissions', true);
|
||||||
|
await queryRunner.dropTable('permissions', true);
|
||||||
|
await queryRunner.dropTable('refresh_tokens', true);
|
||||||
|
await queryRunner.dropTable('otps', true);
|
||||||
|
await queryRunner.dropTable('customers', true);
|
||||||
|
await queryRunner.dropTable('salons', true);
|
||||||
|
await queryRunner.dropTable('users', true);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."users_role_enum"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
export function getTypeOrmOptions(): TypeOrmModuleOptions {
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'postgres',
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: parseInt(process.env.DB_PORT ?? '5432', 10),
|
||||||
|
username: process.env.DB_USERNAME,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_NAME,
|
||||||
|
autoLoadEntities: true,
|
||||||
|
entities: [__dirname + '/../**/*.entity.{js,ts}'],
|
||||||
|
migrations: [__dirname + '/migrations/*.{js,ts}'],
|
||||||
|
synchronize: !isProduction,
|
||||||
|
migrationsRun: isProduction,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -18,7 +18,14 @@ async function bootstrap() {
|
|||||||
app.setGlobalPrefix('api');
|
app.setGlobalPrefix('api');
|
||||||
|
|
||||||
// Cors
|
// Cors
|
||||||
|
const corsOrigin = process.env.CORS_ORIGIN;
|
||||||
|
if (corsOrigin) {
|
||||||
|
app.enableCors({
|
||||||
|
origin: corsOrigin.split(',').map((origin) => origin.trim()),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
app.enableCors();
|
app.enableCors();
|
||||||
|
}
|
||||||
|
|
||||||
await app.listen(process.env.PORT ?? 3000);
|
await app.listen(process.env.PORT ?? 3000);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user