implement platform config
This commit is contained in:
46
src/AppConfig/appconfig.constant.ts
Normal file
46
src/AppConfig/appconfig.constant.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
export const ConfigConstants: Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
name: string
|
||||||
|
key: string
|
||||||
|
value: string
|
||||||
|
visible: boolean
|
||||||
|
}
|
||||||
|
> = {
|
||||||
|
SLOT_DURATION: {
|
||||||
|
name: 'Slot Duration',
|
||||||
|
key: 'SLOT_DURATION',
|
||||||
|
value: '60',
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
SLOT_BREAK_DURATION: {
|
||||||
|
name: 'Slot Break Duration',
|
||||||
|
key: 'SLOT_BREAK_DURATION',
|
||||||
|
value: '15',
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
MID_DAY_BREAK_TIME_START: {
|
||||||
|
name: 'Mid Day Break Time Start',
|
||||||
|
key: 'MID_DAY_BREAK_TIME_START',
|
||||||
|
value: new Date(new Date().setHours(12, 0, 0, 0)).toISOString(),
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
MID_DAY_BREAK_TIME_END: {
|
||||||
|
name: 'Mid Day Break Time End',
|
||||||
|
key: 'MID_DAY_BREAK_TIME_END',
|
||||||
|
value: new Date(new Date().setHours(13, 0, 0, 0)).toISOString(),
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
SLOT_START_TIME: {
|
||||||
|
name: 'Slot Start Time',
|
||||||
|
key: 'SLOT_START_TIME',
|
||||||
|
value: new Date(new Date().setHours(8, 0, 0, 0)).toISOString(),
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
SLOT_END_TIME: {
|
||||||
|
name: 'Slot End Time',
|
||||||
|
key: 'SLOT_END_TIME',
|
||||||
|
value: new Date(new Date().setHours(22, 0, 0, 0)).toISOString(),
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -49,7 +49,7 @@ export class AppConfigSchema extends PothosSchema {
|
|||||||
type: [this.appConfig()],
|
type: [this.appConfig()],
|
||||||
description: 'Get all app configs',
|
description: 'Get all app configs',
|
||||||
args: this.builder.generator.findManyArgs('Config'),
|
args: this.builder.generator.findManyArgs('Config'),
|
||||||
resolve: async (query, root, args) => {
|
resolve: async (query, _root, args) => {
|
||||||
return await this.prisma.config.findMany({
|
return await this.prisma.config.findMany({
|
||||||
...query,
|
...query,
|
||||||
where: args.filter ?? undefined,
|
where: args.filter ?? undefined,
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Injectable, OnModuleInit } from '@nestjs/common'
|
import { Injectable, OnModuleInit } from '@nestjs/common'
|
||||||
|
|
||||||
// import { ConfigConstant } from 'src/common/constant/config.constant';
|
|
||||||
import { PrismaService } from 'src/Prisma/prisma.service'
|
import { PrismaService } from 'src/Prisma/prisma.service'
|
||||||
|
import { ConfigConstants } from './appconfig.constant'
|
||||||
|
import { Config } from '@prisma/client'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppConfigService implements OnModuleInit {
|
export class AppConfigService implements OnModuleInit {
|
||||||
@@ -9,21 +10,49 @@ export class AppConfigService implements OnModuleInit {
|
|||||||
|
|
||||||
async onModuleInit() {
|
async onModuleInit() {
|
||||||
// get each config from database, if not exist, create default config
|
// get each config from database, if not exist, create default config
|
||||||
// const configs = await this.prisma.config.findMany();
|
const configs = await this.prisma.config.findMany()
|
||||||
// if (configs.length === 0) {
|
if (configs.length === 0) {
|
||||||
// await this.prisma.config.createMany({
|
Object.entries(ConfigConstants).forEach(async ([_key, value]) => {
|
||||||
// data: Object.values(ConfigConstant).map((config) => ({
|
await this.prisma.config.create({
|
||||||
// ...config,
|
data: {
|
||||||
// })),
|
name: value.name,
|
||||||
// });
|
key: value.key,
|
||||||
// }
|
value: value.value,
|
||||||
// // regenerate missing config
|
visible: value.visible,
|
||||||
// for (const config of Object.values(ConfigConstant)) {
|
},
|
||||||
// if (!configs.find((c) => c.key === config.key)) {
|
})
|
||||||
// await this.prisma.config.create({
|
})
|
||||||
// data: config,
|
}
|
||||||
// });
|
}
|
||||||
// }
|
|
||||||
// }
|
async getConfig(key: string) {
|
||||||
|
return await this.prisma.config.findUnique({
|
||||||
|
where: { key },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async getVisibleConfigs() {
|
||||||
|
return await this.prisma.config.findMany({
|
||||||
|
where: { visible: true },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateConfig(key: string, value: string) {
|
||||||
|
return await this.prisma.config.update({
|
||||||
|
where: { key },
|
||||||
|
data: { value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateVisibleConfigs(configs: Config[]) {
|
||||||
|
return await this.prisma.config.updateMany({
|
||||||
|
data: configs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteConfig(key: string) {
|
||||||
|
return await this.prisma.config.delete({
|
||||||
|
where: { key },
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export class CategorySchema extends PothosSchema {
|
|||||||
'Retrieve a list of categories with optional filtering, ordering, and pagination.',
|
'Retrieve a list of categories with optional filtering, ordering, and pagination.',
|
||||||
type: [this.category()],
|
type: [this.category()],
|
||||||
args: this.builder.generator.findManyArgs('Category'),
|
args: this.builder.generator.findManyArgs('Category'),
|
||||||
resolve: async (query, root, args) => {
|
resolve: async (query, _root, args) => {
|
||||||
return await this.prisma.category.findMany({
|
return await this.prisma.category.findMany({
|
||||||
...query,
|
...query,
|
||||||
skip: args.skip ?? undefined,
|
skip: args.skip ?? undefined,
|
||||||
@@ -83,7 +83,7 @@ export class CategorySchema extends PothosSchema {
|
|||||||
description: 'Retrieve a single category by its unique identifier.',
|
description: 'Retrieve a single category by its unique identifier.',
|
||||||
type: this.category(),
|
type: this.category(),
|
||||||
args: this.builder.generator.findUniqueArgs('Category'),
|
args: this.builder.generator.findUniqueArgs('Category'),
|
||||||
resolve: async (query, root, args) => {
|
resolve: async (query, _root, args) => {
|
||||||
return await this.prisma.category.findUnique({
|
return await this.prisma.category.findUnique({
|
||||||
...query,
|
...query,
|
||||||
where: args.where ?? undefined,
|
where: args.where ?? undefined,
|
||||||
@@ -95,7 +95,7 @@ export class CategorySchema extends PothosSchema {
|
|||||||
'Retrieve a list of subcategories with optional filtering, ordering, and pagination.',
|
'Retrieve a list of subcategories with optional filtering, ordering, and pagination.',
|
||||||
type: [this.subCategory()],
|
type: [this.subCategory()],
|
||||||
args: this.builder.generator.findManyArgs('SubCategory'),
|
args: this.builder.generator.findManyArgs('SubCategory'),
|
||||||
resolve: async (query, root, args) => {
|
resolve: async (query, _root, args) => {
|
||||||
return await this.prisma.subCategory.findMany({
|
return await this.prisma.subCategory.findMany({
|
||||||
...query,
|
...query,
|
||||||
where: args.filter ?? undefined,
|
where: args.filter ?? undefined,
|
||||||
@@ -118,7 +118,7 @@ export class CategorySchema extends PothosSchema {
|
|||||||
required: true,
|
required: true,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
resolve: async (query, root, args) => {
|
resolve: async (_query, _root, args) => {
|
||||||
return await this.prisma.category.create({
|
return await this.prisma.category.create({
|
||||||
data: args.input,
|
data: args.input,
|
||||||
})
|
})
|
||||||
@@ -133,7 +133,7 @@ export class CategorySchema extends PothosSchema {
|
|||||||
required: true,
|
required: true,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
resolve: async (query, root, args) => {
|
resolve: async (_query, _root, args) => {
|
||||||
return await this.prisma.category.createManyAndReturn({
|
return await this.prisma.category.createManyAndReturn({
|
||||||
data: args.data,
|
data: args.data,
|
||||||
skipDuplicates: true,
|
skipDuplicates: true,
|
||||||
@@ -150,7 +150,7 @@ export class CategorySchema extends PothosSchema {
|
|||||||
required: true,
|
required: true,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
resolve: async (query, root, args) => {
|
resolve: async (_query, _root, args) => {
|
||||||
return await this.prisma.subCategory.create({
|
return await this.prisma.subCategory.create({
|
||||||
data: args.input,
|
data: args.input,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export class MailService {
|
|||||||
to: string[],
|
to: string[],
|
||||||
subject: string,
|
subject: string,
|
||||||
template: string,
|
template: string,
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||||
context: any,
|
context: any,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
72
src/Mail/templates/ModeratorInvitation.pug
Normal file
72
src/Mail/templates/ModeratorInvitation.pug
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
doctype html
|
||||||
|
html
|
||||||
|
head
|
||||||
|
meta(charset="UTF-8")
|
||||||
|
title Thông báo chọn lựa quản trị viên cho người điều hành
|
||||||
|
style.
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #f0f8ff;
|
||||||
|
color: #333;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 20px auto;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
text-align: center;
|
||||||
|
background-color: #457D84; /* Medium teal */
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
}
|
||||||
|
.header h1 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 20px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.content p {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 12px 20px;
|
||||||
|
background-color: #2BD4E2; /* Bright aqua */
|
||||||
|
color: #ffffff;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 16px;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #555;
|
||||||
|
padding: 10px;
|
||||||
|
border-top: 1px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
body
|
||||||
|
.container
|
||||||
|
.header
|
||||||
|
h1 Thông báo chọn lựa quản trị viên
|
||||||
|
.content
|
||||||
|
p Chào #{USER_NAME},
|
||||||
|
p Chúng tôi vui mừng thông báo rằng bạn đã được chọn làm người điều hành cho nền tảng của chúng tôi.
|
||||||
|
p Để bắt đầu, vui lòng nhấn vào nút dưới đây để truy cập vào trang quản lý của bạn.
|
||||||
|
a.button(href="https://admin.epess.org") Truy cập Trang Quản Lý
|
||||||
|
p Nếu bạn có bất kỳ câu hỏi nào, đừng ngần ngại liên hệ với chúng tôi.
|
||||||
|
.footer
|
||||||
|
p Trân trọng,
|
||||||
|
p EPESS
|
||||||
|
p Nền tảng hỗ trợ viết luận
|
||||||
@@ -50,7 +50,7 @@ export class ManagedServiceSchema extends PothosSchema {
|
|||||||
managedService: t.field({
|
managedService: t.field({
|
||||||
type: this.managedService(),
|
type: this.managedService(),
|
||||||
args: this.builder.generator.findUniqueArgs('ManagedService'),
|
args: this.builder.generator.findUniqueArgs('ManagedService'),
|
||||||
resolve: async (parent, args, ctx) => {
|
resolve: async (_parent, args, _ctx) => {
|
||||||
return this.prisma.managedService.findUnique({
|
return this.prisma.managedService.findUnique({
|
||||||
where: args.where,
|
where: args.where,
|
||||||
})
|
})
|
||||||
@@ -60,7 +60,7 @@ export class ManagedServiceSchema extends PothosSchema {
|
|||||||
managedServices: t.field({
|
managedServices: t.field({
|
||||||
type: [this.managedService()],
|
type: [this.managedService()],
|
||||||
args: this.builder.generator.findManyArgs('ManagedService'),
|
args: this.builder.generator.findManyArgs('ManagedService'),
|
||||||
resolve: async (parent, args, ctx) => {
|
resolve: async (_parent, args, _ctx) => {
|
||||||
return this.prisma.managedService.findMany({
|
return this.prisma.managedService.findMany({
|
||||||
where: args.filter ?? undefined,
|
where: args.filter ?? undefined,
|
||||||
orderBy: args.orderBy ?? undefined,
|
orderBy: args.orderBy ?? undefined,
|
||||||
@@ -83,7 +83,7 @@ export class ManagedServiceSchema extends PothosSchema {
|
|||||||
description: 'The data for the managed service.',
|
description: 'The data for the managed service.',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
resolve: async (query, root, args, ctx, info) => {
|
resolve: async (query, _root, args, _ctx, _info) => {
|
||||||
return await this.prisma.managedService.create({
|
return await this.prisma.managedService.create({
|
||||||
...query,
|
...query,
|
||||||
data: args.input,
|
data: args.input,
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export class MilestoneSchema extends PothosSchema {
|
|||||||
args: this.builder.generator.findManyArgs('Milestone'),
|
args: this.builder.generator.findManyArgs('Milestone'),
|
||||||
description:
|
description:
|
||||||
'Retrieve a list of milestones with optional filtering, ordering, and pagination.',
|
'Retrieve a list of milestones with optional filtering, ordering, and pagination.',
|
||||||
resolve: async (query, root, args, ctx, info) => {
|
resolve: async (query, _root, args, _ctx, _info) => {
|
||||||
return await this.prisma.milestone.findMany({
|
return await this.prisma.milestone.findMany({
|
||||||
...query,
|
...query,
|
||||||
skip: args.skip ?? undefined,
|
skip: args.skip ?? undefined,
|
||||||
@@ -73,7 +73,7 @@ export class MilestoneSchema extends PothosSchema {
|
|||||||
type: this.milestone(),
|
type: this.milestone(),
|
||||||
args: this.builder.generator.findUniqueArgs('Milestone'),
|
args: this.builder.generator.findUniqueArgs('Milestone'),
|
||||||
description: 'Retrieve a single milestone by its unique identifier.',
|
description: 'Retrieve a single milestone by its unique identifier.',
|
||||||
resolve: async (query, root, args, ctx, info) => {
|
resolve: async (query, _root, args, _ctx, _info) => {
|
||||||
return await this.prisma.milestone.findUnique({
|
return await this.prisma.milestone.findUnique({
|
||||||
...query,
|
...query,
|
||||||
where: args.where,
|
where: args.where,
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export class ScheduleSchema extends PothosSchema {
|
|||||||
type: this.schedule(),
|
type: this.schedule(),
|
||||||
description: 'Retrieve a single schedule by its unique identifier.',
|
description: 'Retrieve a single schedule by its unique identifier.',
|
||||||
args: this.builder.generator.findUniqueArgs('Schedule'),
|
args: this.builder.generator.findUniqueArgs('Schedule'),
|
||||||
resolve: async (query, root, args, ctx, info) => {
|
resolve: async (query, _root, args, _ctx, _info) => {
|
||||||
return await this.prisma.schedule.findUnique({
|
return await this.prisma.schedule.findUnique({
|
||||||
...query,
|
...query,
|
||||||
where: args.where,
|
where: args.where,
|
||||||
@@ -99,7 +99,7 @@ export class ScheduleSchema extends PothosSchema {
|
|||||||
args: this.builder.generator.findManyArgs('Schedule'),
|
args: this.builder.generator.findManyArgs('Schedule'),
|
||||||
description:
|
description:
|
||||||
'Retrieve a list of schedules with optional filtering, ordering, and pagination.',
|
'Retrieve a list of schedules with optional filtering, ordering, and pagination.',
|
||||||
resolve: async (query, root, args, ctx, info) => {
|
resolve: async (query, _root, args, _ctx, _info) => {
|
||||||
return await this.prisma.schedule.findMany({
|
return await this.prisma.schedule.findMany({
|
||||||
...query,
|
...query,
|
||||||
skip: args.skip ?? undefined,
|
skip: args.skip ?? undefined,
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ export class UserSchema extends PothosSchema {
|
|||||||
required: true,
|
required: true,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
resolve: async (query, root, args) => {
|
resolve: async (query, _root, args) => {
|
||||||
return await this.prisma.user.update({
|
return await this.prisma.user.update({
|
||||||
...query,
|
...query,
|
||||||
where: args.where,
|
where: args.where,
|
||||||
@@ -214,22 +214,42 @@ export class UserSchema extends PothosSchema {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
sendEmailTest: t.field({
|
inviteModerator: t.field({
|
||||||
type: 'String',
|
type: 'String',
|
||||||
args: {
|
args: {
|
||||||
to: t.arg({ type: 'String', required: true }),
|
email: t.arg({ type: 'String', required: true }),
|
||||||
},
|
},
|
||||||
resolve: async (_parent, args) => {
|
resolve: async (_parent, args, ctx) => {
|
||||||
|
return this.prisma.$transaction(async (tx) => {
|
||||||
|
// check context
|
||||||
|
if (ctx.isSubscription) {
|
||||||
|
throw new Error('Not allowed')
|
||||||
|
}
|
||||||
|
// check context is admin
|
||||||
|
if (ctx.http.me.role !== 'ADMIN') {
|
||||||
|
throw new UnauthorizedException(`Only admin can invite moderator`)
|
||||||
|
}
|
||||||
|
let user
|
||||||
|
// perform update role
|
||||||
|
try {
|
||||||
|
user = await tx.user.update({
|
||||||
|
where: { email: args.email },
|
||||||
|
data: { role: 'MODERATOR' },
|
||||||
|
})
|
||||||
|
} catch (_error) {
|
||||||
|
throw new Error(`User ${args.email} not found`)
|
||||||
|
}
|
||||||
|
// send email
|
||||||
await this.mailService.sendTemplateEmail(
|
await this.mailService.sendTemplateEmail(
|
||||||
[args.to],
|
[args.email],
|
||||||
'Bạn đã được mời làm việc tại Trung tâm băng đĩa lậu hải ngoại',
|
'Thông báo chọn lựa quản trị viên cho người điều hành',
|
||||||
'MentorInvitation',
|
'ModeratorInvitation',
|
||||||
{
|
{
|
||||||
center_name: 'băng đĩa lậu hải ngoại',
|
USER_NAME: user.name,
|
||||||
invite_url: 'https://epess.org',
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return 'Email sent'
|
return 'Invited'
|
||||||
|
})
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
// export class ConfigConstant {
|
|
||||||
// static SLOT_DURATION: number = 60;
|
|
||||||
// static SLOT_BREAK_DURATION: number = 15;
|
|
||||||
// static MID_DAY_BREAK_TIME_START: Date = new Date().setHours(12, 0, 0, 0);
|
|
||||||
// static MID_DAY_BREAK_TIME_END: Date = new Date().setHours(13, 0, 0, 0);
|
|
||||||
// static SLOT_START_TIME: Date = new Date().setHours(8, 0, 0, 0);
|
|
||||||
// static SLOT_END_TIME: Date = new Date().setHours(22, 0, 0, 0);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export const ConfigConstant: Record<string, any> = {
|
|
||||||
// SLOT_DURATION: 60,
|
|
||||||
// SLOT_BREAK_DURATION: 15,
|
|
||||||
// MID_DAY_BREAK_TIME_START: {
|
|
||||||
// hour: 12,
|
|
||||||
// minute: 0,
|
|
||||||
// second: 0,
|
|
||||||
// millisecond: 0,
|
|
||||||
// },
|
|
||||||
// MID_DAY_BREAK_TIME_END: {
|
|
||||||
// hour: 13,
|
|
||||||
// minute: 0,
|
|
||||||
// second: 0,
|
|
||||||
// millisecond: 0,
|
|
||||||
// },
|
|
||||||
// SLOT_START_TIME: {
|
|
||||||
// hour: 8,
|
|
||||||
// minute: 0,
|
|
||||||
// second: 0,
|
|
||||||
// millisecond: 0,
|
|
||||||
// },
|
|
||||||
// SLOT_END_TIME: {
|
|
||||||
// hour: 22,
|
|
||||||
// minute: 0,
|
|
||||||
// second: 0,
|
|
||||||
// millisecond: 0,
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
Reference in New Issue
Block a user