Enhance NestJS application with SWC builder configuration, add @nestjs/devtools-integration for development support, and refactor various components for improved readability. Update package dependencies and streamline import statements across multiple files.
This commit is contained in:
@@ -6,6 +6,14 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"watchAssets": true,
|
"watchAssets": true,
|
||||||
"deleteOutDir": true,
|
"deleteOutDir": true,
|
||||||
|
"builder": {
|
||||||
|
"type": "swc",
|
||||||
|
"options": {
|
||||||
|
"copyFiles": true,
|
||||||
|
"includeDotfiles": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"typeCheck": true,
|
||||||
"assets": [
|
"assets": [
|
||||||
{
|
{
|
||||||
"include": "**/*.pug",
|
"include": "**/*.pug",
|
||||||
|
|||||||
1561
package-lock.json
generated
1561
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@@ -44,6 +44,7 @@
|
|||||||
"@nestjs/common": "^10.0.0",
|
"@nestjs/common": "^10.0.0",
|
||||||
"@nestjs/config": "^3.2.3",
|
"@nestjs/config": "^3.2.3",
|
||||||
"@nestjs/core": "^10.0.0",
|
"@nestjs/core": "^10.0.0",
|
||||||
|
"@nestjs/devtools-integration": "^0.1.6",
|
||||||
"@nestjs/event-emitter": "^2.1.1",
|
"@nestjs/event-emitter": "^2.1.1",
|
||||||
"@nestjs/graphql": "^12.2.0",
|
"@nestjs/graphql": "^12.2.0",
|
||||||
"@nestjs/jwt": "^10.2.0",
|
"@nestjs/jwt": "^10.2.0",
|
||||||
@@ -141,19 +142,13 @@
|
|||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"moduleFileExtensions": [
|
"moduleFileExtensions": ["js", "json", "ts"],
|
||||||
"js",
|
|
||||||
"json",
|
|
||||||
"ts"
|
|
||||||
],
|
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"testRegex": ".*\\.spec\\.ts$",
|
"testRegex": ".*\\.spec\\.ts$",
|
||||||
"transform": {
|
"transform": {
|
||||||
"^.+\\.(t|j)s$": "ts-jest"
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
},
|
},
|
||||||
"collectCoverageFrom": [
|
"collectCoverageFrom": ["**/*.(t|j)s"],
|
||||||
"**/*.(t|j)s"
|
|
||||||
],
|
|
||||||
"coverageDirectory": "../coverage",
|
"coverageDirectory": "../coverage",
|
||||||
"testEnvironment": "node"
|
"testEnvironment": "node"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,13 +1,4 @@
|
|||||||
import {
|
import { Controller, Get, Post, Put, Delete, Param, Body, Headers } from '@nestjs/common'
|
||||||
Controller,
|
|
||||||
Get,
|
|
||||||
Post,
|
|
||||||
Put,
|
|
||||||
Delete,
|
|
||||||
Param,
|
|
||||||
Body,
|
|
||||||
Headers,
|
|
||||||
} from '@nestjs/common'
|
|
||||||
import { ClerkService } from './clerk.service'
|
import { ClerkService } from './clerk.service'
|
||||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'
|
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'
|
||||||
@ApiTags('Clerk')
|
@ApiTags('Clerk')
|
||||||
@@ -18,7 +9,7 @@ export class ClerkController {
|
|||||||
@Post('webhook')
|
@Post('webhook')
|
||||||
@ApiOperation({ summary: 'Clerk Webhook' })
|
@ApiOperation({ summary: 'Clerk Webhook' })
|
||||||
@ApiResponse({ status: 200, description: 'Webhook created successfully' })
|
@ApiResponse({ status: 200, description: 'Webhook created successfully' })
|
||||||
webhook(@Headers() headers: Headers, @Body() body: any) {
|
webhook(@Body() body: any) {
|
||||||
return this.clerkService.webhook(body)
|
return this.clerkService.webhook(body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
export enum DocumentEvent {
|
export enum DocumentEvent {
|
||||||
CREATED = 'document_created',
|
|
||||||
CHANGED = 'document_changed',
|
CHANGED = 'document_changed',
|
||||||
DELETED = 'document_deleted',
|
DELETED = 'document_deleted',
|
||||||
SAVED = 'document_saved',
|
SAVED = 'document_saved',
|
||||||
PAGE_CREATED = 'document_page_created',
|
PAGE_CREATED = 'document_page_created',
|
||||||
PAGE_DELETED = 'document_page_deleted',
|
PAGE_DELETED = 'document_page_deleted',
|
||||||
DOCUMENT_CREATED = 'document_created',
|
|
||||||
ACTIVE_DOCUMENT_ID_CHANGED = 'document_active_document_id_changed',
|
ACTIVE_DOCUMENT_ID_CHANGED = 'document_active_document_id_changed',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import { Inject, Injectable, Logger } from '@nestjs/common'
|
import { Inject, Injectable, Logger } from '@nestjs/common'
|
||||||
import {
|
import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos'
|
||||||
Pothos,
|
|
||||||
PothosRef,
|
|
||||||
PothosSchema,
|
|
||||||
SchemaBuilderToken,
|
|
||||||
} from '@smatch-corp/nestjs-pothos'
|
|
||||||
import { Builder, SchemaContext } from '../Graphql/graphql.builder'
|
import { Builder, SchemaContext } from '../Graphql/graphql.builder'
|
||||||
import { PrismaService } from '../Prisma/prisma.service'
|
import { PrismaService } from '../Prisma/prisma.service'
|
||||||
import { DocumentEvent } from './document.event'
|
import { DocumentEvent } from './document.event'
|
||||||
@@ -99,10 +94,7 @@ export class DocumentSchema extends PothosSchema {
|
|||||||
...query,
|
...query,
|
||||||
orderBy: args.orderBy ?? undefined,
|
orderBy: args.orderBy ?? undefined,
|
||||||
where: {
|
where: {
|
||||||
OR: [
|
OR: [{ ownerId: ctx.http.me.id }, { collaborators: { some: { userId: ctx.http.me.id } } }],
|
||||||
{ ownerId: ctx.http.me.id },
|
|
||||||
{ collaborators: { some: { userId: ctx.http.me.id } } },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -138,11 +130,7 @@ export class DocumentSchema extends PothosSchema {
|
|||||||
if (ctx.isSubscription) throw new Error('Not allowed')
|
if (ctx.isSubscription) throw new Error('Not allowed')
|
||||||
const userId = ctx.http?.me?.id
|
const userId = ctx.http?.me?.id
|
||||||
if (!userId) throw new Error('User not found')
|
if (!userId) throw new Error('User not found')
|
||||||
const fileUrl = await this.minio.getFileUrl(
|
const fileUrl = await this.minio.getFileUrl('document', 'document', 'document')
|
||||||
'document',
|
|
||||||
'document',
|
|
||||||
'document',
|
|
||||||
)
|
|
||||||
if (!fileUrl) throw new Error('File not found')
|
if (!fileUrl) throw new Error('File not found')
|
||||||
const document = await this.prisma.document.create({
|
const document = await this.prisma.document.create({
|
||||||
...query,
|
...query,
|
||||||
@@ -211,10 +199,7 @@ export class DocumentSchema extends PothosSchema {
|
|||||||
delta,
|
delta,
|
||||||
senderId: ctx.http?.me?.id,
|
senderId: ctx.http?.me?.id,
|
||||||
}
|
}
|
||||||
ctx.http.pubSub.publish(
|
ctx.http.pubSub.publish(`${DocumentEvent.CHANGED}.${args.documentId}`, documentDelta)
|
||||||
`${DocumentEvent.CHANGED}.${args.documentId}`,
|
|
||||||
documentDelta,
|
|
||||||
)
|
|
||||||
return documentDelta
|
return documentDelta
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -275,9 +260,7 @@ export class DocumentSchema extends PothosSchema {
|
|||||||
if (
|
if (
|
||||||
document.ownerId !== ctx.http?.me?.id &&
|
document.ownerId !== ctx.http?.me?.id &&
|
||||||
!document.isPublic &&
|
!document.isPublic &&
|
||||||
!document.collaborators.some(
|
!document.collaborators.some((c) => c.userId === ctx.http?.me?.id && c.writable)
|
||||||
(c) => c.userId === ctx.http?.me?.id && c.writable,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
throw new Error('User is not owner or collaborator of document')
|
throw new Error('User is not owner or collaborator of document')
|
||||||
return await this.prisma.document.update({
|
return await this.prisma.document.update({
|
||||||
@@ -302,8 +285,7 @@ export class DocumentSchema extends PothosSchema {
|
|||||||
where: { id: args.documentId },
|
where: { id: args.documentId },
|
||||||
})
|
})
|
||||||
if (!document) throw new Error('Document not found')
|
if (!document) throw new Error('Document not found')
|
||||||
if (document.ownerId !== ctx.http?.me?.id)
|
if (document.ownerId !== ctx.http?.me?.id) throw new Error('User is not owner of document')
|
||||||
throw new Error('User is not owner of document')
|
|
||||||
return await this.prisma.documentCollaborator.create({
|
return await this.prisma.documentCollaborator.create({
|
||||||
data: {
|
data: {
|
||||||
documentId: args.documentId,
|
documentId: args.documentId,
|
||||||
@@ -342,17 +324,16 @@ export class DocumentSchema extends PothosSchema {
|
|||||||
if (!document.isPublic) {
|
if (!document.isPublic) {
|
||||||
if (
|
if (
|
||||||
document.ownerId !== ctx.websocket?.me?.id &&
|
document.ownerId !== ctx.websocket?.me?.id &&
|
||||||
!document.collaborators.some(
|
!document.collaborators.some((c) => c.userId === ctx.websocket?.me?.id && c.writable)
|
||||||
(c) => c.userId === ctx.websocket?.me?.id && c.writable,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
throw new Error('User is not owner or collaborator of document')
|
throw new Error('User is not owner or collaborator of document')
|
||||||
}
|
}
|
||||||
return pubSub.asyncIterator([
|
return pubSub.asyncIterator([
|
||||||
`${DocumentEvent.CHANGED}.${documentId}`,
|
`${DocumentEvent.CHANGED}.${documentId}`,
|
||||||
`${DocumentEvent.CREATED}.${documentId}`,
|
|
||||||
`${DocumentEvent.DELETED}.${documentId}`,
|
`${DocumentEvent.DELETED}.${documentId}`,
|
||||||
`${DocumentEvent.SAVED}.${documentId}`,
|
`${DocumentEvent.SAVED}.${documentId}`,
|
||||||
|
`${DocumentEvent.PAGE_CREATED}.${documentId}`,
|
||||||
|
`${DocumentEvent.PAGE_DELETED}.${documentId}`,
|
||||||
`${DocumentEvent.ACTIVE_DOCUMENT_ID_CHANGED}.${documentId}`,
|
`${DocumentEvent.ACTIVE_DOCUMENT_ID_CHANGED}.${documentId}`,
|
||||||
]) as unknown as AsyncIterable<DocumentDelta>
|
]) as unknown as AsyncIterable<DocumentDelta>
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,64 +3,12 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'
|
|||||||
import Delta, { Op } from 'quill-delta'
|
import Delta, { Op } from 'quill-delta'
|
||||||
import { MinioService } from '../Minio/minio.service'
|
import { MinioService } from '../Minio/minio.service'
|
||||||
import { DocumentDelta } from './document.type'
|
import { DocumentDelta } from './document.type'
|
||||||
import { JSDOM } from 'jsdom'
|
|
||||||
import { Logger } from '@nestjs/common'
|
import { Logger } from '@nestjs/common'
|
||||||
|
import { PrismaService } from 'src/Prisma/prisma.service'
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DocumentService {
|
export class DocumentService {
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
||||||
private quill: any
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
private readonly minio: MinioService,
|
private readonly minio: MinioService,
|
||||||
public document: JSDOM,
|
) {}
|
||||||
) {
|
|
||||||
;(async () => {
|
|
||||||
await this.loadQuill()
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
|
|
||||||
private async loadQuill() {
|
|
||||||
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
||||||
const { window } = new JSDOM('<!doctype html><html><body></body></html>')
|
|
||||||
const { navigator } = window
|
|
||||||
const { Node } = window
|
|
||||||
global.window = window
|
|
||||||
global.document = window.document
|
|
||||||
global.navigator = navigator
|
|
||||||
global.Node = Node
|
|
||||||
}
|
|
||||||
|
|
||||||
const { default: Quill } = await import('quill')
|
|
||||||
this.quill = Quill
|
|
||||||
}
|
|
||||||
// TODO: maybe never do :)
|
|
||||||
async handleOnChange(documentId: string, delta: DocumentDelta) {}
|
|
||||||
|
|
||||||
async handleOnSave(documentId: string) {}
|
|
||||||
|
|
||||||
async handleOnSync(documentId: string) {}
|
|
||||||
|
|
||||||
async requestSync(documentId: string, page?: number) {
|
|
||||||
// using pubsub to broadcast to all clients
|
|
||||||
// this.pubSub.publish(`document:sync:${documentId}`, {
|
|
||||||
// documentId,
|
|
||||||
// page,
|
|
||||||
// })
|
|
||||||
}
|
|
||||||
|
|
||||||
async clientRequestSave(documentId: string) {
|
|
||||||
// using pubsub to broadcast to all clients
|
|
||||||
// this.pubSub.publish(`document:save:${documentId}`, {
|
|
||||||
// documentId,
|
|
||||||
// })
|
|
||||||
}
|
|
||||||
|
|
||||||
async serverRequestSave(documentId: string) {}
|
|
||||||
|
|
||||||
async generatePreviewImage(documentId: string) {}
|
|
||||||
|
|
||||||
async allPageToDelta(documentId: string) {}
|
|
||||||
|
|
||||||
async toDocx(documentId: string) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// epess/documents/<id>/<page>
|
|
||||||
|
|||||||
@@ -36,10 +36,7 @@ export class MinioService {
|
|||||||
}
|
}
|
||||||
let url = null
|
let url = null
|
||||||
// check if presignUrl is provided and not expired else get new presignUrl
|
// check if presignUrl is provided and not expired else get new presignUrl
|
||||||
if (
|
if (presignUrl && !DateTimeUtils.isExpired(presignUrl.split('&X-Amz-Date=')[1])) {
|
||||||
presignUrl &&
|
|
||||||
!DateTimeUtils.isExpired(presignUrl.split('&X-Amz-Date=')[1])
|
|
||||||
) {
|
|
||||||
url = presignUrl
|
url = presignUrl
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
@@ -81,14 +78,30 @@ export class MinioService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async deleteFile(id: string, category: string) {
|
async deleteFile(id: string, category: string) {
|
||||||
return await this.minioClient.removeObject(
|
return await this.minioClient.removeObject(this.configService.get('BUCKET_NAME') ?? 'epess', `${category}/${id}`)
|
||||||
this.configService.get('BUCKET_NAME') ?? 'epess',
|
}
|
||||||
`${category}/${id}`,
|
// create a folder for a document pattern epess/documents/<id>/<page>
|
||||||
)
|
async createDocumentFolder(id: string) {
|
||||||
|
return await this.minioClient.putObject(this.configService.get('BUCKET_NAME') ?? 'epess', `documents/${id}`, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
async streamFile(id: string, category: string) {}
|
async upsertDocumentFolder(id: string, page: string) {
|
||||||
|
return await this.minioClient.putObject(
|
||||||
|
this.configService.get('BUCKET_NAME') ?? 'epess',
|
||||||
|
`documents/${id}/${page}`,
|
||||||
|
'',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// export document to docx format by get all pages and convert to docx
|
||||||
|
async exportDocument(id: string) {
|
||||||
|
// get all pages
|
||||||
|
const pages = await this.minioClient.listObjects(
|
||||||
|
this.configService.get('BUCKET_NAME') ?? 'epess',
|
||||||
|
`documents/${id}`,
|
||||||
|
)
|
||||||
|
// convert to docx
|
||||||
|
// const docx = await this.convertToDocx(pages)
|
||||||
|
}
|
||||||
fileName() {
|
fileName() {
|
||||||
// generate a unique file name using uuid
|
// generate a unique file name using uuid
|
||||||
return uuidv4()
|
return uuidv4()
|
||||||
|
|||||||
@@ -4,15 +4,10 @@ import { Injectable, Logger } from '@nestjs/common'
|
|||||||
import { PrismaService } from 'src/Prisma/prisma.service'
|
import { PrismaService } from 'src/Prisma/prisma.service'
|
||||||
|
|
||||||
import { AppConfigService } from 'src/AppConfig/appconfig.service'
|
import { AppConfigService } from 'src/AppConfig/appconfig.service'
|
||||||
import {
|
import { PreviewScheduleType, ScheduleConfigType, ScheduleConfigTypeForCenter, ScheduleSlotType } from './schedule.d'
|
||||||
PreviewScheduleType,
|
|
||||||
ScheduleConfigType,
|
|
||||||
ScheduleConfigTypeForCenter,
|
|
||||||
ScheduleSlotType,
|
|
||||||
} from './schedule.d'
|
|
||||||
import { Config, Schedule, ScheduleDate } from '@prisma/client'
|
import { Config, Schedule, ScheduleDate } from '@prisma/client'
|
||||||
import { DateTime, Settings, Zone } from 'luxon'
|
import { DateTime, Settings, Zone } from 'luxon'
|
||||||
import * as _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { ScheduleDateInput } from './schedule'
|
import { ScheduleDateInput } from './schedule'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -22,9 +17,7 @@ export class ScheduleService {
|
|||||||
private readonly appConfigService: AppConfigService,
|
private readonly appConfigService: AppConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async createSchedulePreviewForSingleDay(
|
async createSchedulePreviewForSingleDay(scheduleConfig: ScheduleConfigType): Promise<PreviewScheduleType> {
|
||||||
scheduleConfig: ScheduleConfigType,
|
|
||||||
): Promise<PreviewScheduleType> {
|
|
||||||
// generate Slot By config
|
// generate Slot By config
|
||||||
const slots = this.generateSlots(scheduleConfig)
|
const slots = this.generateSlots(scheduleConfig)
|
||||||
|
|
||||||
@@ -35,12 +28,8 @@ export class ScheduleService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create preview for center require scheduleConfigInput: { startDate: "2024-11-02T00:00:00.000Z", endDate: "2024-11-22T00:00:00.000Z", slots: [1, 3], days: [2, 5] }
|
// create preview for center require scheduleConfigInput: { startDate: "2024-11-02T00:00:00.000Z", endDate: "2024-11-22T00:00:00.000Z", slots: [1, 3], days: [2, 5] }
|
||||||
async createSchedulePreviewForCenter(
|
async createSchedulePreviewForCenter(scheduleConfig: ScheduleConfigTypeForCenter): Promise<PreviewScheduleType> {
|
||||||
scheduleConfig: ScheduleConfigTypeForCenter,
|
const config: ScheduleConfigType = (await this.appConfigService.getVisibleConfigs()).reduce((acc, curr) => {
|
||||||
): Promise<PreviewScheduleType> {
|
|
||||||
const config: ScheduleConfigType = (
|
|
||||||
await this.appConfigService.getVisibleConfigs()
|
|
||||||
).reduce((acc, curr) => {
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
acc[curr.key] = curr.value
|
acc[curr.key] = curr.value
|
||||||
return acc
|
return acc
|
||||||
@@ -54,9 +43,7 @@ export class ScheduleService {
|
|||||||
|
|
||||||
async generateScheduleDates(schedule: Schedule): Promise<ScheduleDate[]> {
|
async generateScheduleDates(schedule: Schedule): Promise<ScheduleDate[]> {
|
||||||
// generate schedule dates based on data and config
|
// generate schedule dates based on data and config
|
||||||
const config: ScheduleConfigType = (
|
const config: ScheduleConfigType = (await this.appConfigService.getVisibleConfigs()).reduce((acc, curr) => {
|
||||||
await this.appConfigService.getVisibleConfigs()
|
|
||||||
).reduce((acc, curr) => {
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
acc[curr.key] = curr.value
|
acc[curr.key] = curr.value
|
||||||
return acc
|
return acc
|
||||||
@@ -72,11 +59,7 @@ export class ScheduleService {
|
|||||||
const scheduleDates: ScheduleDateInput[] = []
|
const scheduleDates: ScheduleDateInput[] = []
|
||||||
|
|
||||||
// loop each day from scheduleStart to scheduleEnd
|
// loop each day from scheduleStart to scheduleEnd
|
||||||
for (
|
for (let date = scheduleStart; date <= scheduleEnd; date = date.plus({ days: 1 })) {
|
||||||
let date = scheduleStart;
|
|
||||||
date <= scheduleEnd;
|
|
||||||
date = date.plus({ days: 1 })
|
|
||||||
) {
|
|
||||||
// Check if the current date matches one of the specified days of the week
|
// Check if the current date matches one of the specified days of the week
|
||||||
if (daysOfWeeks.includes(date.weekday)) {
|
if (daysOfWeeks.includes(date.weekday)) {
|
||||||
// loop through slots
|
// loop through slots
|
||||||
@@ -85,10 +68,7 @@ export class ScheduleService {
|
|||||||
slot,
|
slot,
|
||||||
slotDuration.toString(),
|
slotDuration.toString(),
|
||||||
slotBreakDuration.toString(),
|
slotBreakDuration.toString(),
|
||||||
DateTimeUtils.getATimeWithDateB(
|
DateTimeUtils.getATimeWithDateB(DateTime.fromISO(config.dayStartTime), date).toISO() ?? '',
|
||||||
DateTime.fromISO(config.dayStartTime),
|
|
||||||
date,
|
|
||||||
).toISO() ?? '',
|
|
||||||
)
|
)
|
||||||
scheduleDates.push({
|
scheduleDates.push({
|
||||||
scheduleId: schedule.id,
|
scheduleId: schedule.id,
|
||||||
@@ -103,10 +83,9 @@ export class ScheduleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const scheduleDatesCreated =
|
const scheduleDatesCreated = await this.prisma.scheduleDate.createManyAndReturn({
|
||||||
await this.prisma.scheduleDate.createManyAndReturn({
|
data: scheduleDates,
|
||||||
data: scheduleDates,
|
})
|
||||||
})
|
|
||||||
|
|
||||||
return scheduleDatesCreated
|
return scheduleDatesCreated
|
||||||
}
|
}
|
||||||
@@ -137,11 +116,7 @@ query CenterPreviewSchedule {
|
|||||||
const scheduleStart = DateTime.fromISO(_scheduleConfig.startDate)
|
const scheduleStart = DateTime.fromISO(_scheduleConfig.startDate)
|
||||||
const scheduleEnd = DateTime.fromISO(_scheduleConfig.endDate)
|
const scheduleEnd = DateTime.fromISO(_scheduleConfig.endDate)
|
||||||
// loop each day from scheduleStart to scheduleEnd
|
// loop each day from scheduleStart to scheduleEnd
|
||||||
for (
|
for (let date = scheduleStart; date <= scheduleEnd; date = date.plus({ days: 1 })) {
|
||||||
let date = scheduleStart;
|
|
||||||
date <= scheduleEnd;
|
|
||||||
date = date.plus({ days: 1 })
|
|
||||||
) {
|
|
||||||
// Check if the current date matches one of the specified days of the week
|
// Check if the current date matches one of the specified days of the week
|
||||||
if (daysOfWeeks.includes(date.weekday)) {
|
if (daysOfWeeks.includes(date.weekday)) {
|
||||||
// loop through slots
|
// loop through slots
|
||||||
@@ -151,10 +126,7 @@ query CenterPreviewSchedule {
|
|||||||
slot,
|
slot,
|
||||||
_config.slotDuration,
|
_config.slotDuration,
|
||||||
_config.slotBreakDuration,
|
_config.slotBreakDuration,
|
||||||
DateTimeUtils.getATimeWithDateB(
|
DateTimeUtils.getATimeWithDateB(DateTime.fromISO(_config.dayStartTime), date).toISO() ?? '',
|
||||||
DateTime.fromISO(_config.dayStartTime),
|
|
||||||
date,
|
|
||||||
).toISO() ?? '',
|
|
||||||
)
|
)
|
||||||
// if the slot is not overlapping with mid day break time, add it to the slots
|
// if the slot is not overlapping with mid day break time, add it to the slots
|
||||||
if (
|
if (
|
||||||
@@ -213,24 +185,11 @@ query CenterPreviewSchedule {
|
|||||||
return slots
|
return slots
|
||||||
}
|
}
|
||||||
|
|
||||||
isOverLapping(
|
isOverLapping(startTime1: DateTime, endTime1: DateTime, startTime2: DateTime, endTime2: DateTime) {
|
||||||
startTime1: DateTime,
|
return Math.max(startTime1.toMillis(), startTime2.toMillis()) < Math.min(endTime1.toMillis(), endTime2.toMillis())
|
||||||
endTime1: DateTime,
|
|
||||||
startTime2: DateTime,
|
|
||||||
endTime2: DateTime,
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
Math.max(startTime1.toMillis(), startTime2.toMillis()) <
|
|
||||||
Math.min(endTime1.toMillis(), endTime2.toMillis())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateNumberOfSlots(
|
calculateNumberOfSlots(startTime: string, endTime: string, slotDuration: string, slotBreakDuration: string) {
|
||||||
startTime: string,
|
|
||||||
endTime: string,
|
|
||||||
slotDuration: string,
|
|
||||||
slotBreakDuration: string,
|
|
||||||
) {
|
|
||||||
const _startTime = DateTimeUtils.toTime(startTime)
|
const _startTime = DateTimeUtils.toTime(startTime)
|
||||||
const _endTime = DateTimeUtils.toTime(endTime)
|
const _endTime = DateTimeUtils.toTime(endTime)
|
||||||
const _slotDuration = parseInt(slotDuration) // minutes
|
const _slotDuration = parseInt(slotDuration) // minutes
|
||||||
@@ -247,20 +206,12 @@ query CenterPreviewSchedule {
|
|||||||
second: _endTime.second,
|
second: _endTime.second,
|
||||||
})
|
})
|
||||||
|
|
||||||
const totalMinutes =
|
const totalMinutes = (endDate.toMillis() - startDate.toMillis()) / (60 * 1000)
|
||||||
(endDate.toMillis() - startDate.toMillis()) / (60 * 1000)
|
const numberOfSlots = Math.floor(totalMinutes / (_slotDuration + _slotBreakDuration))
|
||||||
const numberOfSlots = Math.floor(
|
|
||||||
totalMinutes / (_slotDuration + _slotBreakDuration),
|
|
||||||
)
|
|
||||||
return numberOfSlots
|
return numberOfSlots
|
||||||
}
|
}
|
||||||
|
|
||||||
getSlotStartAndEndTime(
|
getSlotStartAndEndTime(slotNumber: number, slotDuration: string, slotBreakDuration: string, dayStartTime: string) {
|
||||||
slotNumber: number,
|
|
||||||
slotDuration: string,
|
|
||||||
slotBreakDuration: string,
|
|
||||||
dayStartTime: string,
|
|
||||||
) {
|
|
||||||
const durationInMinutes = parseInt(slotDuration)
|
const durationInMinutes = parseInt(slotDuration)
|
||||||
const breakDurationInMinutes = parseInt(slotBreakDuration)
|
const breakDurationInMinutes = parseInt(slotBreakDuration)
|
||||||
const initialStartTime = DateTime.fromISO(dayStartTime)
|
const initialStartTime = DateTime.fromISO(dayStartTime)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { DevtoolsModule } from '@nestjs/devtools-integration'
|
||||||
import { ClerkModule } from './Clerk/clerk.module'
|
import { ClerkModule } from './Clerk/clerk.module'
|
||||||
import { ConfigModule } from '@nestjs/config'
|
import { ConfigModule } from '@nestjs/config'
|
||||||
import { GraphqlModule } from './Graphql/graphql.module'
|
import { GraphqlModule } from './Graphql/graphql.module'
|
||||||
@@ -9,6 +10,10 @@ import { CronModule } from './Cron/cron.module'
|
|||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
DevtoolsModule.register({
|
||||||
|
http: process.env.NODE_ENV !== 'production',
|
||||||
|
port: 8000,
|
||||||
|
}),
|
||||||
ConfigModule.forRoot({
|
ConfigModule.forRoot({
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
import { Injectable } from '@nestjs/common'
|
import { Injectable } from '@nestjs/common'
|
||||||
import * as _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import {
|
import { DateTime, Settings, HourNumbers, MinuteNumbers, SecondNumbers, DayNumbers, WeekdayNumbers } from 'luxon'
|
||||||
DateTime,
|
|
||||||
Settings,
|
|
||||||
HourNumbers,
|
|
||||||
MinuteNumbers,
|
|
||||||
SecondNumbers,
|
|
||||||
DayNumbers,
|
|
||||||
WeekdayNumbers,
|
|
||||||
} from 'luxon'
|
|
||||||
|
|
||||||
Settings.defaultLocale = 'en-US'
|
Settings.defaultLocale = 'en-US'
|
||||||
Settings.defaultZone = 'utc'
|
Settings.defaultZone = 'utc'
|
||||||
@@ -52,22 +44,11 @@ export class DateTimeUtils {
|
|||||||
return DateTime.fromISO(expires) < DateTime.now()
|
return DateTime.fromISO(expires) < DateTime.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
static isOverlap(
|
static isOverlap(startA: DateTime, endA: DateTime, startB: DateTime, endB: DateTime): boolean {
|
||||||
startA: DateTime,
|
return this.getOverlapRange(startA, endA, startB, endB).start < this.getOverlapRange(startA, endA, startB, endB).end
|
||||||
endA: DateTime,
|
|
||||||
startB: DateTime,
|
|
||||||
endB: DateTime,
|
|
||||||
): boolean {
|
|
||||||
return (
|
|
||||||
this.getOverlapRange(startA, endA, startB, endB).start <
|
|
||||||
this.getOverlapRange(startA, endA, startB, endB).end
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static isOverlaps(
|
static isOverlaps(listA: { start: DateTime; end: DateTime }[], listB: { start: DateTime; end: DateTime }[]): boolean {
|
||||||
listA: { start: DateTime; end: DateTime }[],
|
|
||||||
listB: { start: DateTime; end: DateTime }[],
|
|
||||||
): boolean {
|
|
||||||
for (const a of listA) {
|
for (const a of listA) {
|
||||||
for (const b of listB) {
|
for (const b of listB) {
|
||||||
if (this.isOverlap(a.start, a.end, b.start, b.end)) {
|
if (this.isOverlap(a.start, a.end, b.start, b.end)) {
|
||||||
|
|||||||
37
src/main.ts
37
src/main.ts
@@ -10,39 +10,22 @@ import { readFileSync } from 'node:fs'
|
|||||||
import { json } from 'express'
|
import { json } from 'express'
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule)
|
const app = await NestFactory.create(AppModule, {})
|
||||||
// load private key and public key
|
// load private key and public key
|
||||||
const privateKey = readFileSync(
|
const privateKey = readFileSync(path.join(__dirname, 'KeyStore', 'private_key.pem'), 'utf8')
|
||||||
path.join(__dirname, 'KeyStore', 'private_key.pem'),
|
const publicKey = readFileSync(path.join(__dirname, 'KeyStore', 'public_key.pem'), 'utf8')
|
||||||
'utf8',
|
|
||||||
)
|
|
||||||
const publicKey = readFileSync(
|
|
||||||
path.join(__dirname, 'KeyStore', 'public_key.pem'),
|
|
||||||
'utf8',
|
|
||||||
)
|
|
||||||
// set private key and public key to env
|
// set private key and public key to env
|
||||||
process.env.JWT_RS256_PRIVATE_KEY = privateKey
|
process.env.JWT_RS256_PRIVATE_KEY = privateKey
|
||||||
process.env.JWT_RS256_PUBLIC_KEY = publicKey
|
process.env.JWT_RS256_PUBLIC_KEY = publicKey
|
||||||
|
|
||||||
Logger.log(
|
Logger.log(`Private key: ${privateKey.slice(0, 10).replace(/\n/g, '')}...`, 'Bootstrap')
|
||||||
`Private key: ${privateKey.slice(0, 10).replace(/\n/g, '')}...`,
|
Logger.log(`Public key: ${publicKey.slice(0, 10).replace(/\n/g, '')}...`, 'Bootstrap')
|
||||||
'Bootstrap',
|
|
||||||
)
|
|
||||||
Logger.log(
|
|
||||||
`Public key: ${publicKey.slice(0, 10).replace(/\n/g, '')}...`,
|
|
||||||
'Bootstrap',
|
|
||||||
)
|
|
||||||
|
|
||||||
const corsOrigin = (process.env.CORS_ORIGIN ?? '').split(',') // split by comma to array
|
const corsOrigin = (process.env.CORS_ORIGIN ?? '').split(',') // split by comma to array
|
||||||
app.enableCors({
|
app.enableCors({
|
||||||
origin: corsOrigin,
|
origin: corsOrigin,
|
||||||
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
|
||||||
allowedHeaders: [
|
allowedHeaders: ['Content-Type', '*', 'x-apollo-operation-name', 'x-session-id'],
|
||||||
'Content-Type',
|
|
||||||
'*',
|
|
||||||
'x-apollo-operation-name',
|
|
||||||
'x-session-id',
|
|
||||||
],
|
|
||||||
credentials: true,
|
credentials: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -63,8 +46,7 @@ async function bootstrap() {
|
|||||||
get: {
|
get: {
|
||||||
tags: ['GraphQL'],
|
tags: ['GraphQL'],
|
||||||
summary: 'GraphQL Playground',
|
summary: 'GraphQL Playground',
|
||||||
description:
|
description: 'Access the GraphQL Playground to interact with the GraphQL API.',
|
||||||
'Access the GraphQL Playground to interact with the GraphQL API.',
|
|
||||||
responses: {
|
responses: {
|
||||||
'200': {
|
'200': {
|
||||||
description: 'GraphQL Playground',
|
description: 'GraphQL Playground',
|
||||||
@@ -90,10 +72,7 @@ async function bootstrap() {
|
|||||||
)
|
)
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
Logger.error(
|
Logger.error(`Error in file upload middleware: ${error.message}`, 'Bootstrap')
|
||||||
`Error in file upload middleware: ${error.message}`,
|
|
||||||
'Bootstrap',
|
|
||||||
)
|
|
||||||
// Optionally, you can handle the error further or rethrow it
|
// Optionally, you can handle the error further or rethrow it
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user