+
+
diff --git a/packages/nc-gui/components/general/Modal.vue b/packages/nc-gui/components/general/Modal.vue
index 069559f70f..28279420ed 100644
--- a/packages/nc-gui/components/general/Modal.vue
+++ b/packages/nc-gui/components/general/Modal.vue
@@ -3,7 +3,7 @@ const props = withDefaults(
defineProps<{
visible: boolean
width?: string | number
- size?: 'small' | 'medium' | 'large'
+ size?: 'small' | 'medium' | 'large' | 'xl'
destroyOnClose?: boolean
maskClosable?: boolean
closable?: boolean
@@ -40,6 +40,10 @@ const width = computed(() => {
return '80rem'
}
+ if (props.size === 'xl') {
+ return '80rem'
+ }
+
return 'max(30vw, 600px)'
})
@@ -55,6 +59,9 @@ const height = computed(() => {
if (props.size === 'large') {
return '80vh'
}
+ if (props.size === 'xl') {
+ return '90vh'
+ }
return 'auto'
})
@@ -75,7 +82,7 @@ const visible = useVModel(props, 'visible', emits)
:mask-closable="maskClosable"
@keydown.esc="visible = false"
>
-
+
diff --git a/packages/nc-gui/components/nc/DatePicker.vue b/packages/nc-gui/components/nc/DatePicker.vue
new file mode 100644
index 0000000000..7ccc09b174
--- /dev/null
+++ b/packages/nc-gui/components/nc/DatePicker.vue
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
+
diff --git a/packages/nc-gui/components/nc/DateWeekSelector.vue b/packages/nc-gui/components/nc/DateWeekSelector.vue
index 6e0b91b258..40505eb96c 100644
--- a/packages/nc-gui/components/nc/DateWeekSelector.vue
+++ b/packages/nc-gui/components/nc/DateWeekSelector.vue
@@ -13,19 +13,23 @@ interface Props {
start: dayjs.Dayjs
end: dayjs.Dayjs
} | null
+ isCellInputField?: boolean
+ pickerType?: 'date' | 'time' | 'year' | 'month'
}
const props = withDefaults(defineProps
(), {
size: 'medium',
selectedDate: null,
isMondayFirst: true,
- pageDate: dayjs(),
+ pageDate: () => dayjs(),
isWeekPicker: false,
- activeDates: [] as Array,
+ activeDates: () => [] as Array,
selectedWeek: null,
hideCalendar: false,
+ isCellInputField: false,
+ pickerType: 'date',
})
-const emit = defineEmits(['update:selectedDate', 'update:pageDate', 'update:selectedWeek'])
+const emit = defineEmits(['update:selectedDate', 'update:pageDate', 'update:selectedWeek', 'update:pickerType'])
// Page date is the date we use to manage which month/date that is currently being displayed
const pageDate = useVModel(props, 'pageDate', emit)
@@ -35,6 +39,8 @@ const activeDates = useVModel(props, 'activeDates', emit)
const selectedWeek = useVModel(props, 'selectedWeek', emit)
+const pickerType = useVModel(props, 'pickerType', emit)
+
const days = computed(() => {
if (props.isMondayFirst) {
return ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su']
@@ -47,6 +53,14 @@ const currentMonthYear = computed(() => {
return dayjs(pageDate.value).format('MMMM YYYY')
})
+const currentMonth = computed(() => {
+ return dayjs(pageDate.value).format('MMMM')
+})
+
+const currentYear = computed(() => {
+ return dayjs(pageDate.value).format('YYYY')
+})
+
const selectWeek = (date: dayjs.Dayjs) => {
const dayOffset = +props.isMondayFirst
const dayOfWeek = (date.day() - dayOffset + 7) % 7
@@ -102,6 +116,9 @@ const isDayInPagedMonth = (date: dayjs.Dayjs) => {
const handleSelectDate = (date: dayjs.Dayjs) => {
if (props.isWeekPicker) {
selectWeek(date)
+ } else if (props.isCellInputField) {
+ selectedDate.value = date
+ emit('update:selectedDate', date)
} else {
if (!isDayInPagedMonth(date)) {
pageDate.value = date
@@ -137,17 +154,30 @@ const paginate = (action: 'next' | 'prev') => {
-
Hi,
- I invited you to be "<%- roles -%>" of the NocoDB base "<%- baseName %>". - Click the button below to to accept my invitation.
+ You have been invited to become "<%- roles -%>" of the NocoDB base "<%- baseName %>". + Click the button below to accept the invitation.- Thanks regards <%- adminEmail %>.
+ Have a nice day,<%- adminEmail %> diff --git a/packages/nocodb/src/controllers/auth/ui/emailTemplates/verify.ts b/packages/nocodb/src/modules/auth/ui/emailTemplates/verify.ts similarity index 98% rename from packages/nocodb/src/controllers/auth/ui/emailTemplates/verify.ts rename to packages/nocodb/src/modules/auth/ui/emailTemplates/verify.ts index 11702cc659..cc22d866a2 100644 --- a/packages/nocodb/src/controllers/auth/ui/emailTemplates/verify.ts +++ b/packages/nocodb/src/modules/auth/ui/emailTemplates/verify.ts @@ -132,7 +132,7 @@ export default `
Hi,
- Please verify your email address by clicking the following button.
+ Please verify your e-mail address by clicking the following button.- Thanks regards NocoDB.
+ Thank you and have a nice day,<%- adminEmail %> diff --git a/packages/nocodb/src/modules/datas/datas.module.ts b/packages/nocodb/src/modules/datas/datas.module.ts deleted file mode 100644 index bb6eb86161..0000000000 --- a/packages/nocodb/src/modules/datas/datas.module.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Module } from '@nestjs/common'; -import { MulterModule } from '@nestjs/platform-express'; -import multer from 'multer'; -import { NC_ATTACHMENT_FIELD_SIZE } from '~/constants'; -import { DataTableController } from '~/controllers/data-table.controller'; -import { DataTableService } from '~/services/data-table.service'; -import { DataAliasController } from '~/controllers/data-alias.controller'; -import { PublicDatasExportController } from '~/controllers/public-datas-export.controller'; -import { PublicDatasController } from '~/controllers/public-datas.controller'; -import { DatasService } from '~/services/datas.service'; -import { DatasController } from '~/controllers/datas.controller'; -import { BulkDataAliasController } from '~/controllers/bulk-data-alias.controller'; -import { DataAliasExportController } from '~/controllers/data-alias-export.controller'; -import { DataAliasNestedController } from '~/controllers/data-alias-nested.controller'; -import { OldDatasController } from '~/controllers/old-datas/old-datas.controller'; -import { BulkDataAliasService } from '~/services/bulk-data-alias.service'; -import { DataAliasNestedService } from '~/services/data-alias-nested.service'; -import { OldDatasService } from '~/controllers/old-datas/old-datas.service'; -import { PublicDatasExportService } from '~/services/public-datas-export.service'; -import { PublicDatasService } from '~/services/public-datas.service'; -import { CalendarDatasController } from '~/controllers/calendars-datas.controller'; -import { CalendarDatasService } from '~/services/calendar-datas.service'; - -export const dataModuleMetadata = { - imports: [ - MulterModule.register({ - storage: multer.diskStorage({}), - limits: { - fieldSize: NC_ATTACHMENT_FIELD_SIZE, - }, - }), - ], - controllers: [ - ...(process.env.NC_WORKER_CONTAINER !== 'true' - ? [ - DataTableController, - DatasController, - CalendarDatasController, - BulkDataAliasController, - DataAliasController, - DataAliasNestedController, - DataAliasExportController, - OldDatasController, - PublicDatasController, - PublicDatasExportController, - ] - : []), - ], - providers: [ - DataTableService, - DatasService, - BulkDataAliasService, - DataAliasNestedService, - CalendarDatasService, - OldDatasService, - PublicDatasService, - PublicDatasExportService, - ], - exports: [ - DatasService, - BulkDataAliasService, - CalendarDatasService, - DataAliasNestedService, - OldDatasService, - PublicDatasService, - PublicDatasExportService, - ], -}; - -@Module(dataModuleMetadata) -export class DatasModule {} diff --git a/packages/nocodb/src/modules/global/global.module.ts b/packages/nocodb/src/modules/global/global.module.ts deleted file mode 100644 index 1f4abce3d9..0000000000 --- a/packages/nocodb/src/modules/global/global.module.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { forwardRef, Global, Module } from '@nestjs/common'; -import { ExtractJwt } from 'passport-jwt'; -import type { Provider } from '@nestjs/common'; -import { InitMetaServiceProvider } from '~/modules/global/init-meta-service.provider'; -import { EventEmitterModule } from '~/modules/event-emitter/event-emitter.module'; -import { SocketGateway } from '~/gateways/socket.gateway'; -import { GlobalGuard } from '~/guards/global/global.guard'; -import { MetaService } from '~/meta/meta.service'; -import Noco from '~/Noco'; -import { AppHooksService } from '~/services/app-hooks/app-hooks.service'; -import { JwtStrategy } from '~/strategies/jwt.strategy'; -import { UsersService } from '~/services/users/users.service'; -import { TelemetryService } from '~/services/telemetry.service'; -import { AppHooksListenerService } from '~/services/app-hooks-listener.service'; -import { UsersModule } from '~/modules/users/users.module'; - -export const JwtStrategyProvider: Provider = { - provide: JwtStrategy, - useFactory: async (usersService: UsersService, metaService: MetaService) => { - const config = metaService.config; - - await Noco.initJwt(); - - const options = { - // ignoreExpiration: false, - jwtFromRequest: ExtractJwt.fromHeader('xc-auth'), - // expiresIn: '10h', - passReqToCallback: true, - secretOrKey: config.auth.jwt.secret, - ...config.auth.jwt.options, - }; - - return new JwtStrategy(options, usersService); - }, - inject: [UsersService, MetaService], -}; - -export const globalModuleMetadata = { - imports: [EventEmitterModule, forwardRef(() => UsersModule)], - providers: [ - InitMetaServiceProvider, - AppHooksService, - JwtStrategyProvider, - GlobalGuard, - SocketGateway, - AppHooksService, - AppHooksListenerService, - TelemetryService, - ], - exports: [ - MetaService, - JwtStrategyProvider, - GlobalGuard, - AppHooksService, - AppHooksListenerService, - TelemetryService, - ...(process.env.NC_WORKER_CONTAINER !== 'true' ? [SocketGateway] : []), - ], -}; - -@Global() -@Module(globalModuleMetadata) -export class GlobalModule {} diff --git a/packages/nocodb/src/modules/jobs/fallback/fallback-queue.service.ts b/packages/nocodb/src/modules/jobs/fallback/fallback-queue.service.ts index 49a52c8ad2..04b47e041a 100644 --- a/packages/nocodb/src/modules/jobs/fallback/fallback-queue.service.ts +++ b/packages/nocodb/src/modules/jobs/fallback/fallback-queue.service.ts @@ -6,6 +6,7 @@ import { AtImportProcessor } from '~/modules/jobs/jobs/at-import/at-import.proce import { MetaSyncProcessor } from '~/modules/jobs/jobs/meta-sync/meta-sync.processor'; import { SourceCreateProcessor } from '~/modules/jobs/jobs/source-create/source-create.processor'; import { SourceDeleteProcessor } from '~/modules/jobs/jobs/source-delete/source-delete.processor'; +import { WebhookHandlerProcessor } from '~/modules/jobs/jobs/webhook-handler/webhook-handler.processor'; import { JobsEventService } from '~/modules/jobs/fallback/jobs-event.service'; import { JobStatus, JobTypes } from '~/interface/Jobs'; @@ -31,6 +32,7 @@ export class QueueService { protected readonly metaSyncProcessor: MetaSyncProcessor, protected readonly sourceCreateProcessor: SourceCreateProcessor, protected readonly sourceDeleteProcessor: SourceDeleteProcessor, + protected readonly webhookHandlerProcessor: WebhookHandlerProcessor, ) { this.emitter.on(JobStatus.ACTIVE, (data: { job: Job }) => { const job = this.queueMemory.find((job) => job.id === data.job.id); @@ -88,6 +90,10 @@ export class QueueService { this: this.sourceDeleteProcessor, fn: this.sourceDeleteProcessor.job, }, + [JobTypes.HandleWebhook]: { + this: this.webhookHandlerProcessor, + fn: this.webhookHandlerProcessor.job, + }, }; async jobWrapper(job: Job) { @@ -123,7 +129,7 @@ export class QueueService { QueueService.queueIdCounter = index; } - add(name: string, data: any) { + add(name: string, data: any, _opts = {}) { const id = `${this.queueIndex++}`; const job = { id: `${id}`, name, status: JobStatus.WAITING, data }; this.queueMemory.push(job); diff --git a/packages/nocodb/src/modules/jobs/fallback/jobs.service.ts b/packages/nocodb/src/modules/jobs/fallback/jobs.service.ts index 594e806a8e..c8f1676723 100644 --- a/packages/nocodb/src/modules/jobs/fallback/jobs.service.ts +++ b/packages/nocodb/src/modules/jobs/fallback/jobs.service.ts @@ -1,11 +1,14 @@ import { Injectable } from '@nestjs/common'; +import type { OnModuleInit } from '@nestjs/common'; import { QueueService } from '~/modules/jobs/fallback/fallback-queue.service'; import { JobStatus } from '~/interface/Jobs'; @Injectable() -export class JobsService { +export class JobsService implements OnModuleInit { constructor(private readonly fallbackQueueService: QueueService) {} + async onModuleInit() {} + async add(name: string, data: any) { return this.fallbackQueueService.add(name, data); } diff --git a/packages/nocodb/src/modules/jobs/jobs-service.interface.ts b/packages/nocodb/src/modules/jobs/jobs-service.interface.ts new file mode 100644 index 0000000000..7f083582f2 --- /dev/null +++ b/packages/nocodb/src/modules/jobs/jobs-service.interface.ts @@ -0,0 +1,13 @@ +import type Bull from 'bull'; +import type { JobStatus } from '~/interface/Jobs'; + +export interface IJobsService { + jobsQueue: Bull.Queue; + toggleQueue(): Promise
Hi,
- I invited you to be "<%- roles -%>" of the NocoDB base "<%- baseName %>". - Click the button below to to accept my invitation.
+ You have been invited to become "<%- roles -%>" of the NocoDB base "<%- baseName %>". + Click the button below to accept the invitation.- Thanks regards <%- adminEmail %>.
+ Have a nice day,<%- adminEmail %> diff --git a/packages/nocodb/src/services/base-users/ui/emailTemplates/verify.ts b/packages/nocodb/src/services/base-users/ui/emailTemplates/verify.ts index 11702cc659..fbc923516d 100644 --- a/packages/nocodb/src/services/base-users/ui/emailTemplates/verify.ts +++ b/packages/nocodb/src/services/base-users/ui/emailTemplates/verify.ts @@ -132,7 +132,7 @@ export default `
Hi,
- Please verify your email address by clicking the following button.
+ Please verify your e-mail address by clicking the following button.- Thanks regards NocoDB.
+ Thank you and have a nice day,<%- adminEmail %> diff --git a/packages/nocodb/src/services/bulk-data-alias.service.ts b/packages/nocodb/src/services/bulk-data-alias.service.ts index b53b6890a3..0f0216d17a 100644 --- a/packages/nocodb/src/services/bulk-data-alias.service.ts +++ b/packages/nocodb/src/services/bulk-data-alias.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; -import type { PathParams } from '~/modules/datas/helpers'; +import type { PathParams } from '~/helpers/dataHelpers'; import type { BaseModelSqlv2 } from '~/db/BaseModelSqlv2'; -import { getViewAndModelByAliasOrId } from '~/modules/datas/helpers'; +import { getViewAndModelByAliasOrId } from '~/helpers/dataHelpers'; import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2'; import { Model, Source } from '~/models'; diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index a471021c9b..f2e56b8119 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -452,10 +452,16 @@ export class ColumnsService { }), ); - const data = await sqlClient.raw('SELECT DISTINCT ?? FROM ??', [ - column.column_name, - baseModel.getTnPath(table.table_name), - ]); + const data = await baseModel.execAndParse( + baseModel.dbDriver.raw('SELECT DISTINCT ?? FROM ??', [ + column.column_name, + baseModel.getTnPath(table.table_name), + ]), + null, + { + raw: true, + }, + ); if (data.length) { const existingOptions = colBody.colOptions.options.map( diff --git a/packages/nocodb/src/services/comments.service.ts b/packages/nocodb/src/services/comments.service.ts new file mode 100644 index 0000000000..4f4843afd6 --- /dev/null +++ b/packages/nocodb/src/services/comments.service.ts @@ -0,0 +1,118 @@ +import { Injectable } from '@nestjs/common'; +import { AppEvents } from 'nocodb-sdk'; +import { Base, Model } from '../models'; +import type { + CommentReqType, + CommentUpdateReqType, + UserType, +} from 'nocodb-sdk'; +import type { NcRequest } from '~/interface/config'; +import { NcError } from '~/helpers/catchError'; +import { validatePayload } from '~/helpers'; +import { AppHooksService } from '~/services/app-hooks/app-hooks.service'; +import Comment from '~/models/Comment'; + +@Injectable() +export class CommentsService { + constructor(protected readonly appHooksService: AppHooksService) {} + + async commentRow(param: { + body: CommentReqType; + user: UserType; + req: NcRequest; + }) { + validatePayload('swagger.json#/components/schemas/CommentReq', param.body); + + const res = await Comment.insert({ + ...param.body, + created_by: param.user?.id, + created_by_email: param.user?.email, + }); + + const model = await Model.getByIdOrName({ id: param.body.fk_model_id }); + + this.appHooksService.emit(AppEvents.COMMENT_CREATE, { + base: await Base.getByTitleOrId(model.base_id), + model: model, + user: param.user, + comment: param.body.comment, + rowId: param.body.row_id, + req: param.req, + }); + + return res; + } + + async commentDelete(param: { commentId: string; user: UserType }) { + const comment = await Comment.get(param.commentId); + + if (comment.created_by !== param.user.id || comment.is_deleted) { + NcError.unauthorized('Unauthorized access'); + } + + const res = await Comment.delete(param.commentId); + + const model = await Model.getByIdOrName({ id: comment.fk_model_id }); + + this.appHooksService.emit(AppEvents.COMMENT_DELETE, { + base: await Base.getByTitleOrId(model.base_id), + model: model, + user: param.user, + comment: comment.comment, + rowId: comment.row_id, + req: {}, + }); + return res; + } + + async commentList(param: { + query: { + row_id: string; + fk_model_id: string; + }; + }) { + return await Comment.list(param.query); + } + + async commentsCount(param: { fk_model_id: string; ids: string[] }) { + return await Comment.commentsCount({ + fk_model_id: param.fk_model_id as string, + ids: param.ids as string[], + }); + } + + async commentUpdate(param: { + commentId: string; + user: UserType; + body: CommentUpdateReqType; + req: NcRequest; + }) { + validatePayload( + 'swagger.json#/components/schemas/CommentUpdateReq', + param.body, + ); + + const comment = await Comment.get(param.commentId); + + if (comment.created_by !== param.user.id || comment.is_deleted) { + NcError.unauthorized('Unauthorized access'); + } + + const res = await Comment.update(param.commentId, { + comment: param.body.comment, + }); + + const model = await Model.getByIdOrName({ id: param.body.fk_model_id }); + + this.appHooksService.emit(AppEvents.COMMENT_UPDATE, { + base: await Base.getByTitleOrId(model.base_id), + model: model, + user: param.user, + comment: param.body.comment, + rowId: comment.row_id, + req: param.req, + }); + + return res; + } +} diff --git a/packages/nocodb/src/services/data-alias-nested.service.ts b/packages/nocodb/src/services/data-alias-nested.service.ts index 7f87ecf876..91ebd75b79 100644 --- a/packages/nocodb/src/services/data-alias-nested.service.ts +++ b/packages/nocodb/src/services/data-alias-nested.service.ts @@ -1,12 +1,12 @@ import { Injectable } from '@nestjs/common'; import { UITypes } from 'nocodb-sdk'; -import type { PathParams } from '~/modules/datas/helpers'; +import type { PathParams } from '~/helpers/dataHelpers'; import { NcError } from '~/helpers/catchError'; import { PagedResponseImpl } from '~/helpers/PagedResponse'; import { getColumnByIdOrName, getViewAndModelByAliasOrId, -} from '~/modules/datas/helpers'; +} from '~/helpers/dataHelpers'; import { Model, Source } from '~/models'; import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2'; diff --git a/packages/nocodb/src/services/datas.service.ts b/packages/nocodb/src/services/datas.service.ts index 9d97ee8d55..510f23f21d 100644 --- a/packages/nocodb/src/services/datas.service.ts +++ b/packages/nocodb/src/services/datas.service.ts @@ -4,8 +4,8 @@ import * as XLSX from 'xlsx'; import papaparse from 'papaparse'; import { nocoExecute } from 'nc-help'; import type { BaseModelSqlv2 } from '~/db/BaseModelSqlv2'; -import type { PathParams } from '~/modules/datas/helpers'; -import { getDbRows, getViewAndModelByAliasOrId } from '~/modules/datas/helpers'; +import type { PathParams } from '~/helpers/dataHelpers'; +import { getDbRows, getViewAndModelByAliasOrId } from '~/helpers/dataHelpers'; import { Base, Column, Model, Source, View } from '~/models'; import { NcBaseError, NcError } from '~/helpers/catchError'; import getAst from '~/helpers/getAst'; diff --git a/packages/nocodb/src/services/hook-handler.service.ts b/packages/nocodb/src/services/hook-handler.service.ts index d4191ff008..ad59fd5e93 100644 --- a/packages/nocodb/src/services/hook-handler.service.ts +++ b/packages/nocodb/src/services/hook-handler.service.ts @@ -1,28 +1,30 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Inject, Injectable, Logger } from '@nestjs/common'; import { UITypes, ViewTypes } from 'nocodb-sdk'; import ejs from 'ejs'; +import type { FormColumnType, HookType } from 'nocodb-sdk'; +import type { ColumnType } from 'nocodb-sdk'; import type { OnModuleDestroy, OnModuleInit } from '@nestjs/common'; -import type { UserType } from 'nocodb-sdk'; import NcPluginMgrv2 from '~/helpers/NcPluginMgrv2'; -import { - _transformSubmittedFormDataForEmail, - invokeWebhook, -} from '~/helpers/webhookHelpers'; +import { _transformSubmittedFormDataForEmail } from '~/helpers/webhookHelpers'; import { IEventEmitter } from '~/modules/event-emitter/event-emitter.interface'; import formSubmissionEmailTemplate from '~/utils/common/formSubmissionEmailTemplate'; import { FormView, Hook, Model, View } from '~/models'; +import { JobTypes } from '~/interface/Jobs'; +import { IJobsService } from '~/modules/jobs/jobs-service.interface'; export const HANDLE_WEBHOOK = '__nc_handleHooks'; @Injectable() export class HookHandlerService implements OnModuleInit, OnModuleDestroy { + private logger = new Logger(HookHandlerService.name); private unsubscribe: () => void; constructor( @Inject('IEventEmitter') private readonly eventEmitter: IEventEmitter, + @Inject('JobsService') private readonly jobsService: IJobsService, ) {} - private async handleHooks({ + public async handleHooks({ hookName, prevData, newData, @@ -30,14 +32,6 @@ export class HookHandlerService implements OnModuleInit, OnModuleDestroy { viewId, modelId, tnPath, - }: { - hookName; - prevData; - newData; - user: UserType; - viewId: string; - modelId: string; - tnPath: string; }): Promise