Browse Source

fix: various leftover stuff

Signed-off-by: mertmit <mertmit99@gmail.com>
pull/7140/head
mertmit 10 months ago
parent
commit
3f7cdfeeaa
  1. 1
      packages/nc-gui/components/account/UsersModal.vue
  2. 1
      packages/nc-gui/components/cell/DatePicker.vue
  3. 18
      packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue
  4. 1
      packages/nocodb/src/db/BaseModelSqlv2.ts
  5. 6
      packages/nocodb/src/db/sql-client/lib/mysql/MysqlClient.ts
  6. 1
      packages/nocodb/src/helpers/index.ts
  7. 4033
      packages/nocodb/src/helpers/isDisposableEmail.ts
  8. 22
      packages/nocodb/src/helpers/sqlSanitize.ts
  9. 11
      packages/nocodb/src/interface/Jobs.ts
  10. 6
      packages/nocodb/src/modules/jobs/redis/jobs-event.service.ts
  11. 11
      packages/nocodb/src/modules/jobs/redis/jobs-redis.service.ts
  12. 21
      packages/nocodb/src/modules/jobs/redis/jobs.service.ts
  13. 2
      packages/nocodb/tests/unit/rest/tests/groupby.test.ts

1
packages/nc-gui/components/account/UsersModal.vue

@ -192,6 +192,7 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
<div class="flex flex-col w-2/4"> <div class="flex flex-col w-2/4">
<a-form-item name="role" :rules="[{ required: true, message: $t('msg.roleRequired') }]"> <a-form-item name="role" :rules="[{ required: true, message: $t('msg.roleRequired') }]">
<div class="ml-1 mb-1 text-xs text-gray-500">{{ $t('labels.selectUserRole') }}</div> <div class="ml-1 mb-1 text-xs text-gray-500">{{ $t('labels.selectUserRole') }}</div>
<a-select v-model:value="usersData.role" class="nc-user-roles" dropdown-class-name="nc-dropdown-user-role"> <a-select v-model:value="usersData.role" class="nc-user-roles" dropdown-class-name="nc-dropdown-user-role">
<a-select-option <a-select-option
class="nc-role-option" class="nc-role-option"

1
packages/nc-gui/components/cell/DatePicker.vue

@ -22,6 +22,7 @@ interface Props {
} }
const { modelValue, isPk } = defineProps<Props>() const { modelValue, isPk } = defineProps<Props>()
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const { t } = useI18n() const { t } = useI18n()

18
packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue

@ -77,7 +77,7 @@ const tempTitle = ref('')
const activeBaseId = ref('') const activeBaseId = ref('')
const isErdModalOpen = ref<boolean>(false) const isErdModalOpen = ref<Boolean>(false)
const { t } = useI18n() const { t } = useI18n()
@ -116,7 +116,7 @@ const showBaseOption = computed(() => {
return ['airtableImport', 'csvImport', 'jsonImport', 'excelImport'].some((permission) => isUIAllowed(permission)) return ['airtableImport', 'csvImport', 'jsonImport', 'excelImport'].some((permission) => isUIAllowed(permission))
}) })
function enableEditMode() { const enableEditMode = () => {
editMode.value = true editMode.value = true
tempTitle.value = base.value.title! tempTitle.value = base.value.title!
nextTick(() => { nextTick(() => {
@ -126,7 +126,7 @@ function enableEditMode() {
}) })
} }
async function updateProjectTitle() { const updateProjectTitle = async () => {
if (!tempTitle.value) return if (!tempTitle.value) return
try { try {
@ -146,7 +146,7 @@ async function updateProjectTitle() {
const { copy } = useCopy(true) const { copy } = useCopy(true)
async function copyProjectInfo() { const copyProjectInfo = async () => {
try { try {
if ( if (
await copy( await copy(
@ -168,7 +168,7 @@ defineExpose({
enableEditMode, enableEditMode,
}) })
async function setIcon(icon: string, base: BaseType) { const setIcon = async (icon: string, base: BaseType) => {
try { try {
const meta = { const meta = {
...((base.meta as object) || {}), ...((base.meta as object) || {}),
@ -249,7 +249,7 @@ async function addNewProjectChildEntity() {
} }
} }
async function onProjectClick(base: NcProject, ignoreNavigation?: boolean, toggleIsExpanded?: boolean) { const onProjectClick = async (base: NcProject, ignoreNavigation?: boolean, toggleIsExpanded?: boolean) => {
if (!base) { if (!base) {
return return
} }
@ -348,17 +348,17 @@ onKeyStroke('Escape', () => {
const isDuplicateDlgOpen = ref(false) const isDuplicateDlgOpen = ref(false)
const selectedProjectToDuplicate = ref() const selectedProjectToDuplicate = ref()
function duplicateProject(base: BaseType) { const duplicateProject = (base: BaseType) => {
selectedProjectToDuplicate.value = base selectedProjectToDuplicate.value = base
isDuplicateDlgOpen.value = true isDuplicateDlgOpen.value = true
} }
function tableDelete() { const tableDelete = () => {
isTableDeleteDialogVisible.value = true isTableDeleteDialogVisible.value = true
$e('c:table:delete') $e('c:table:delete')
} }
function projectDelete() { const projectDelete = () => {
isProjectDeleteDialogVisible.value = true isProjectDeleteDialogVisible.value = true
$e('c:project:delete') $e('c:project:delete')
} }

1
packages/nocodb/src/db/BaseModelSqlv2.ts

@ -2305,6 +2305,7 @@ class BaseModelSqlv2 {
if ('beforeInsert' in this) { if ('beforeInsert' in this) {
await this.beforeInsert(insertObj, trx, cookie); await this.beforeInsert(insertObj, trx, cookie);
} }
await this.prepareAttachmentData(insertObj); await this.prepareAttachmentData(insertObj);
let response; let response;

6
packages/nocodb/src/db/sql-client/lib/mysql/MysqlClient.ts

@ -20,9 +20,9 @@ const log = new Debug('MysqlClient');
const evt = new Emit(); const evt = new Emit();
class MysqlClient extends KnexClient { class MysqlClient extends KnexClient {
private queries: any; protected queries: any;
private _version: any; protected _version: any;
private types: any; protected types: any;
constructor(connectionConfig) { constructor(connectionConfig) {
super(connectionConfig); super(connectionConfig);

1
packages/nocodb/src/helpers/index.ts

@ -3,5 +3,6 @@ export * from './columnHelpers';
export * from './apiHelpers'; export * from './apiHelpers';
export * from './cacheHelpers'; export * from './cacheHelpers';
export * from './extractLimitAndOffset'; export * from './extractLimitAndOffset';
export * from './isDisposableEmail';
export { populateMeta }; export { populateMeta };

4033
packages/nocodb/src/helpers/isDisposableEmail.ts

File diff suppressed because it is too large Load Diff

22
packages/nocodb/src/helpers/sqlSanitize.ts

@ -1,3 +1,5 @@
import type { XKnex } from '~/db/CustomKnex';
export function sanitize(v) { export function sanitize(v) {
if (typeof v !== 'string') return v; if (typeof v !== 'string') return v;
return v?.replace(/([^\\]|^)(\?+)/g, (_, m1, m2) => { return v?.replace(/([^\\]|^)(\?+)/g, (_, m1, m2) => {
@ -9,3 +11,23 @@ export function unsanitize(v) {
if (typeof v !== 'string') return v; if (typeof v !== 'string') return v;
return v?.replace(/\\[?]/g, '?'); return v?.replace(/\\[?]/g, '?');
} }
export function sanitizeAndEscapeDots(alias: string, knex: XKnex) {
const sanitizedAlias = sanitize(alias);
// if alias does not contain any dot then return as it is
if (!knex || !sanitizedAlias.includes('.')) return sanitizedAlias;
// if alias contains dot then return knex.raw with escaped dot
switch (knex?.clientType?.()) {
case 'mysql':
case 'mysql2':
return knex.raw(
knex.raw('??', sanitizedAlias).toQuery().replace(/`\.`/g, '.'),
);
case 'pg':
return knex.raw(
knex.raw('??', sanitizedAlias).toQuery().replace(/"\."/g, '.'),
);
default:
return sanitizedAlias;
}
}

11
packages/nocodb/src/interface/Jobs.ts

@ -28,13 +28,14 @@ export enum JobEvents {
LOG = 'job.log', LOG = 'job.log',
} }
export enum InstanceTypes { export const InstanceTypes = {
PRIMARY = 'primary', PRIMARY: `${process.env.NC_ENV ?? 'default'}-primary`,
WORKER = 'worker', WORKER: `${process.env.NC_ENV ?? 'default'}-worker`,
} };
export enum WorkerCommands { export enum InstanceCommands {
RESUME_LOCAL = 'resumeLocal', RESUME_LOCAL = 'resumeLocal',
PAUSE_LOCAL = 'pauseLocal', PAUSE_LOCAL = 'pauseLocal',
RESET = 'reset', RESET = 'reset',
RELEASE = 'release',
} }

6
packages/nocodb/src/modules/jobs/redis/jobs-event.service.ts

@ -7,14 +7,18 @@ import {
import { Job } from 'bull'; import { Job } from 'bull';
import boxen from 'boxen'; import boxen from 'boxen';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';
import { Logger } from '@nestjs/common';
import { JobEvents, JOBS_QUEUE, JobStatus } from '~/interface/Jobs'; import { JobEvents, JOBS_QUEUE, JobStatus } from '~/interface/Jobs';
@Processor(JOBS_QUEUE) @Processor(JOBS_QUEUE)
export class JobsEventService { export class JobsEventService {
protected logger = new Logger(JobsEventService.name);
constructor(private eventEmitter: EventEmitter2) {} constructor(private eventEmitter: EventEmitter2) {}
@OnQueueActive() @OnQueueActive()
onActive(job: Job) { onActive(job: Job) {
this.logger.log(`Processing job ${job.id} of type ${job.name}`);
this.eventEmitter.emit(JobEvents.STATUS, { this.eventEmitter.emit(JobEvents.STATUS, {
id: job.id.toString(), id: job.id.toString(),
status: JobStatus.ACTIVE, status: JobStatus.ACTIVE,
@ -23,6 +27,7 @@ export class JobsEventService {
@OnQueueFailed() @OnQueueFailed()
onFailed(job: Job, error: Error) { onFailed(job: Job, error: Error) {
this.logger.error(`Job ${job.id} failed with error ${error.message}`);
console.error( console.error(
boxen( boxen(
`---- !! JOB FAILED !! ----\nid:${job.id}\nerror:${error.name} (${error.message})\n\nstack: ${error.stack}`, `---- !! JOB FAILED !! ----\nid:${job.id}\nerror:${error.name} (${error.message})\n\nstack: ${error.stack}`,
@ -47,6 +52,7 @@ export class JobsEventService {
@OnQueueCompleted() @OnQueueCompleted()
onCompleted(job: Job, data: any) { onCompleted(job: Job, data: any) {
this.logger.log(`Job ${job.id} completed`);
this.eventEmitter.emit(JobEvents.STATUS, { this.eventEmitter.emit(JobEvents.STATUS, {
id: job.id.toString(), id: job.id.toString(),
status: JobStatus.COMPLETED, status: JobStatus.COMPLETED,

11
packages/nocodb/src/modules/jobs/redis/jobs-redis.service.ts

@ -8,8 +8,8 @@ export class JobsRedisService {
private redisSubscriber: Redis; private redisSubscriber: Redis;
private unsubscribeCallbacks: { [key: string]: () => void } = {}; private unsubscribeCallbacks: { [key: string]: () => void } = {};
public primaryCallbacks: { [key: string]: () => void } = {}; public primaryCallbacks: { [key: string]: (...args) => void } = {};
public workerCallbacks: { [key: string]: () => void } = {}; public workerCallbacks: { [key: string]: (...args) => void } = {};
constructor() { constructor() {
this.redisClient = new Redis(process.env.NC_REDIS_JOB_URL); this.redisClient = new Redis(process.env.NC_REDIS_JOB_URL);
@ -22,10 +22,13 @@ export class JobsRedisService {
} }
const onMessage = (channel, message) => { const onMessage = (channel, message) => {
const args = message.split(':');
const command = args.shift();
if (channel === InstanceTypes.WORKER) { if (channel === InstanceTypes.WORKER) {
this.workerCallbacks[message] && this.workerCallbacks[message](); this.workerCallbacks[command] && this.workerCallbacks[command](...args);
} else if (channel === InstanceTypes.PRIMARY) { } else if (channel === InstanceTypes.PRIMARY) {
this.primaryCallbacks[message] && this.primaryCallbacks[message](); this.primaryCallbacks[command] &&
this.primaryCallbacks[command](...args);
} }
}; };

21
packages/nocodb/src/modules/jobs/redis/jobs.service.ts

@ -2,7 +2,12 @@ import { InjectQueue } from '@nestjs/bull';
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { Queue } from 'bull'; import { Queue } from 'bull';
import type { OnModuleInit } from '@nestjs/common'; import type { OnModuleInit } from '@nestjs/common';
import { JOBS_QUEUE, JobStatus, WorkerCommands } from '~/interface/Jobs'; import {
InstanceCommands,
InstanceTypes,
JOBS_QUEUE,
JobStatus,
} from '~/interface/Jobs';
import { JobsRedisService } from '~/modules/jobs/redis/jobs-redis.service'; import { JobsRedisService } from '~/modules/jobs/redis/jobs-redis.service';
@Injectable() @Injectable()
@ -19,12 +24,12 @@ export class JobsService implements OnModuleInit {
if (process.env.NC_WORKER_CONTAINER !== 'true') { if (process.env.NC_WORKER_CONTAINER !== 'true') {
await this.jobsQueue.pause(true); await this.jobsQueue.pause(true);
} else { } else {
this.jobsRedisService.workerCallbacks[WorkerCommands.RESUME_LOCAL] = this.jobsRedisService.workerCallbacks[InstanceCommands.RESUME_LOCAL] =
async () => { async () => {
this.logger.log('Resuming local queue'); this.logger.log('Resuming local queue');
await this.jobsQueue.resume(true); await this.jobsQueue.resume(true);
}; };
this.jobsRedisService.workerCallbacks[WorkerCommands.PAUSE_LOCAL] = this.jobsRedisService.workerCallbacks[InstanceCommands.PAUSE_LOCAL] =
async () => { async () => {
this.logger.log('Pausing local queue'); this.logger.log('Pausing local queue');
await this.jobsQueue.pause(true); await this.jobsQueue.pause(true);
@ -102,4 +107,14 @@ export class JobsService implements OnModuleInit {
this.logger.log('Pausing global queue'); this.logger.log('Pausing global queue');
await this.jobsQueue.pause(); await this.jobsQueue.pause();
} }
async emitWorkerCommand(command: InstanceCommands, ...args: any[]) {
const data = `${command}${args.length ? `:${args.join(':')}` : ''}`;
await this.jobsRedisService.publish(InstanceTypes.WORKER, data);
}
async emitPrimaryCommand(command: InstanceCommands, ...args: any[]) {
const data = `${command}${args.length ? `:${args.join(':')}` : ''}`;
await this.jobsRedisService.publish(InstanceTypes.PRIMARY, data);
}
} }

2
packages/nocodb/tests/unit/rest/tests/groupby.test.ts

@ -18,7 +18,7 @@ function groupByTests() {
let filmView: View; let filmView: View;
let gridViewColumns; let gridViewColumns;
before(async function () { beforeEach(async function () {
console.time('GroupBy Tests'); console.time('GroupBy Tests');
context = await init(); context = await init();

Loading…
Cancel
Save