Skip to content

Commit ada26e7

Browse files
authored
Merge pull request #9 from Dialogue-Bot/DIAL-11-Add-DB-channeltype-channel
DIAL-11-Add-DB-channeltype-channel
2 parents 73a92f3 + 9acfa91 commit ada26e7

File tree

23 files changed

+504
-49
lines changed

23 files changed

+504
-49
lines changed

server/.vscode/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,8 @@
55
},
66
"editor.formatOnSave": true,
77
"editor.defaultFormatter": "esbenp.prettier-vscode",
8-
"editor.formatOnSaveMode": "file"
8+
"editor.formatOnSaveMode": "file",
9+
"[typescript]": {
10+
"editor.defaultFormatter": "vscode.typescript-language-features"
11+
}
912
}

server/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"lodash": "^4.17.21",
5151
"mime": "^4.0.1",
5252
"mime-types": "^2.1.35",
53+
"moment": "^2.30.1",
5354
"morgan": "^1.10.0",
5455
"multer": "1.4.5-lts.1",
5556
"nodemailer": "^6.9.8",

server/src/constants/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ export const ENDPOINTS = {
2525
SINGLE: '/upload/single',
2626
MULTIPLE: '/upload/multiple',
2727
},
28+
CHANNEL: {
29+
INDEX: '/channel',
30+
DELETE: '/channel/delete',
31+
DELETES: '/channel/deletes',
32+
}
2833
};
2934

3035
export const LOCALE_KEY = 'lang';
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { LOCALE_KEY } from "@/constants";
2+
import { PagingDTO } from "@/dtos/paging.dto";
3+
import { LocaleService } from "@/i18n/ctx";
4+
import { ChannelService } from "@/services/channels.service";
5+
import { catchAsync } from "@/utils/catch-async";
6+
import { plainToClass } from "class-transformer";
7+
import { validateOrReject } from "class-validator";
8+
import { StatusCodes } from "http-status-codes";
9+
import Container from "typedi";
10+
11+
export class ChannelController {
12+
public channelService = Container.get(ChannelService);
13+
public localeService = Container.get<LocaleService>(LOCALE_KEY);
14+
15+
public createChannel = catchAsync(async (req, res) => {
16+
await this.channelService.create(req.body);
17+
res.status(StatusCodes.OK).json({
18+
message: this.localeService.i18n().CHANNEL.CREATE_SUCCESS(),
19+
});
20+
});
21+
22+
public updateChannel = catchAsync(async (req, res) => {
23+
await this.channelService.updateById(req.params.id, req.body);
24+
res.status(StatusCodes.OK).json({
25+
message: this.localeService.i18n().CHANNEL.UPDATE_SUCCESS(),
26+
});
27+
})
28+
29+
public deleteChannel = catchAsync(async (req, res) => {
30+
await this.channelService.deleteById(req.params.id);
31+
res.status(StatusCodes.OK).json({
32+
message: this.localeService.i18n().CHANNEL.DELETE_CHANNEL_SUCCESS(),
33+
});
34+
})
35+
36+
public deleteMultipleChannel = catchAsync(async (req, res) => {
37+
await this.channelService.deleteByIds(req.body.id);
38+
res.status(StatusCodes.OK).json({
39+
message: this.localeService.i18n().CHANNEL.DELETE_MULTIPLE_CHANNELS_SUCCESS(),
40+
});
41+
})
42+
43+
public getChannelsPaging = catchAsync(async (req, res) => {
44+
const paging = plainToClass(PagingDTO, req.query);
45+
await validateOrReject(paging);
46+
47+
const result = await this.channelService.getChannelsPaging(paging);
48+
res.status(StatusCodes.OK).json({ result });
49+
})
50+
}

server/src/database/migrations/meta/_journal.json

Whitespace-only changes.

server/src/database/schema.ts

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
1+
import { createId } from '@paralleldrive/cuid2';
2+
import { relations } from 'drizzle-orm';
13
import {
2-
integer,
4+
boolean,
35
pgEnum,
46
pgTable,
57
text,
68
timestamp,
7-
varchar,
8-
doublePrecision,
9-
index,
109
uniqueIndex,
11-
primaryKey,
12-
json,
13-
boolean,
14-
serial,
10+
varchar
1511
} from 'drizzle-orm/pg-core';
16-
import { createId } from '@paralleldrive/cuid2';
1712
import { MAX_ID_LENGTH } from '../constants';
18-
import { relations } from 'drizzle-orm';
1913

2014
export const roles = pgEnum('roles', ['ADMIN', 'USER']);
2115

@@ -43,27 +37,35 @@ export const users = pgTable(
4337
);
4438

4539
export const channelTypes = pgTable(
46-
'channel_types', // cho nay minh theo conversation channel_types nha
40+
'channel_types',
4741
{
48-
id: serial('id')
49-
.primaryKey(),
42+
id: varchar('id', {
43+
length: MAX_ID_LENGTH,
44+
})
45+
.primaryKey()
46+
.$defaultFn(() => createId()),
5047
name: text('name').unique().notNull(),
5148
description: text('description').unique().notNull(),
49+
deleted: boolean('deleted').default(false),
5250
}
5351
)
5452

5553
export const channels = pgTable(
5654
'channels',
5755
{
58-
id: serial('id')
59-
.primaryKey(),
56+
id: varchar('id', {
57+
length: MAX_ID_LENGTH,
58+
})
59+
.primaryKey()
60+
.$defaultFn(() => createId()),
6061
contactId: text('contact_id').unique().notNull(),
6162
contactName: text('contact_name').notNull(),
62-
credentials: json('credentials'),
63-
active: boolean('active').default(true),
64-
channelTypeId: integer('channel_type_id').notNull(),
63+
credentials: text('credentials'),
64+
active: boolean('active'),
65+
deleted: boolean('deleted').default(false),
66+
channelTypeId: text('channel_type_id').notNull(),
6567
createdAt: timestamp('created_at').defaultNow(),
66-
updatedAt: timestamp('update_at').defaultNow(),
68+
updatedAt: timestamp('updated_at'),
6769
}
6870
)
6971

server/src/database/seed.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import * as bcrypt from 'bcrypt';
12
import { db } from "./db";
2-
import { channelTypes } from "./schema";
3+
import { channelTypes, users } from "./schema";
34

45
async function seedChannelTypes() {
56
try {
@@ -15,4 +16,14 @@ async function seedChannelTypes() {
1516
}
1617
}
1718

18-
seedChannelTypes();
19+
async function seedDefaultAccount() {
20+
try {
21+
const hashedPassword = await bcrypt.hash("Hello@123", 10);
22+
await db.insert(users).values({email: "[email protected]", password: hashedPassword, name: "admin",});
23+
} catch (error) {
24+
console.error(`Can't create default account`);
25+
}
26+
}
27+
28+
seedChannelTypes();
29+
seedDefaultAccount();

server/src/database/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,7 @@ export const ROLES = z.enum(schema.roles.enumValues);
4040
export type TNewUser = typeof schema.users.$inferInsert;
4141

4242
export type TUpdateUser = Partial<TNewUser>;
43+
44+
export type TNewChannel = typeof schema.channels.$inferInsert;
45+
46+
export type TUpdateChannel = Partial<TNewChannel>;

server/src/dtos/channels.dto.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { getCurrentLocale } from '@/i18n/get-current';
2+
import { Transform } from "class-transformer";
3+
import { IsArray, IsBoolean, IsNotEmpty, IsString } from "class-validator";
4+
5+
export class ChannelDTO {
6+
@IsString()
7+
@IsNotEmpty({
8+
message: () => getCurrentLocale().VALIDATE.REQUIRED({ field: 'ContactId' }),
9+
})
10+
@Transform(({ value }) => value.trim())
11+
contactId: string;
12+
13+
@IsString()
14+
@IsNotEmpty({
15+
message: () => getCurrentLocale().VALIDATE.REQUIRED({ field: 'Contact name' }),
16+
})
17+
@Transform(({ value }) => value.trim())
18+
contactName: string;
19+
20+
@IsString()
21+
@IsNotEmpty({
22+
message: () => getCurrentLocale().VALIDATE.REQUIRED({ field: 'Channel type' }),
23+
})
24+
channelTypeId: string;
25+
26+
@IsString()
27+
credentials: string;
28+
29+
@IsBoolean()
30+
active: boolean | false;
31+
}
32+
33+
export class DeleteChannelDTO {
34+
@IsArray()
35+
@Transform(({ value }) => value ?? [])
36+
id: string[];
37+
}

server/src/dtos/paging.dto.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Transform, Type } from "class-transformer";
2+
import { IsNumber, IsOptional, IsString } from "class-validator";
3+
4+
export class PagingDTO {
5+
@IsNumber()
6+
@Type(() => Number)
7+
@Transform(({ value }) => parseInt(value) ?? 1)
8+
@IsOptional()
9+
page: number | 0;
10+
11+
@IsNumber()
12+
@Type(() => Number)
13+
@Transform(({ value }) => parseInt(value) ?? 20)
14+
@IsOptional()
15+
limit: number | 20;
16+
17+
@IsString()
18+
@IsOptional()
19+
orderBy: string;
20+
21+
@IsString()
22+
@Transform(({ value }) => value ?? 'asc')
23+
@IsOptional()
24+
sortType: 'asc' | 'desc';
25+
26+
@IsString()
27+
@IsOptional()
28+
q: string;
29+
}

0 commit comments

Comments
 (0)