diff --git a/packages/nc-gui/package-lock.json b/packages/nc-gui/package-lock.json index 5123c5beb8..4a50a2051b 100644 --- a/packages/nc-gui/package-lock.json +++ b/packages/nc-gui/package-lock.json @@ -110,7 +110,7 @@ } }, "../nocodb-sdk": { - "version": "0.107.3", + "version": "0.107.4", "license": "AGPL-3.0-or-later", "dependencies": { "axios": "^0.21.1", @@ -15245,9 +15245,9 @@ } }, "node_modules/socket.io-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", - "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", + "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -29548,9 +29548,9 @@ } }, "socket.io-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", - "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", + "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", "requires": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" diff --git a/packages/nc-lib-gui/package.json b/packages/nc-lib-gui/package.json index 4658b04c2f..34d5efbf1b 100644 --- a/packages/nc-lib-gui/package.json +++ b/packages/nc-lib-gui/package.json @@ -1,6 +1,6 @@ { "name": "nc-lib-gui", - "version": "0.107.3", + "version": "0.107.4", "description": "NocoDB GUI", "author": { "name": "NocoDB", diff --git a/packages/noco-docs/content/en/engineering/development-setup.md b/packages/noco-docs/content/en/engineering/development-setup.md index 6e773e8532..1576ba471b 100644 --- a/packages/noco-docs/content/en/engineering/development-setup.md +++ b/packages/noco-docs/content/en/engineering/development-setup.md @@ -51,4 +51,14 @@ For Playwright tests, screenshots are captured on the tests. These will provide ![Screenshot 2022-09-29 at 12 43 37 PM](https://user-images.githubusercontent.com/86527202/192965070-dc04b952-70fb-4197-b4bd-ca7eda066e60.png) +## Accessing 'Easter egg' menu + +Double click twice on empty space between `View list` & `Share` button to the left top of Grid view; following options become accessible +1. Export Cache +2. Delete Cache +3. Debug Meta +4. Toggle Beta Features + +![Screenshot 2023-05-23 at 8 35 14 PM](https://github.com/nocodb/nocodb/assets/86527202/fe2765fa-5796-4d26-8c12-e71b8226872e) + diff --git a/packages/nocodb-sdk/package-lock.json b/packages/nocodb-sdk/package-lock.json index 84bdda167b..bc7fc27f3c 100644 --- a/packages/nocodb-sdk/package-lock.json +++ b/packages/nocodb-sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "nocodb-sdk", - "version": "0.107.3", + "version": "0.107.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "nocodb-sdk", - "version": "0.107.3", + "version": "0.107.4", "license": "AGPL-3.0-or-later", "dependencies": { "axios": "^0.21.1", diff --git a/packages/nocodb-sdk/package.json b/packages/nocodb-sdk/package.json index 0ceedb126c..9709ad7d86 100644 --- a/packages/nocodb-sdk/package.json +++ b/packages/nocodb-sdk/package.json @@ -1,6 +1,6 @@ { "name": "nocodb-sdk", - "version": "0.107.3", + "version": "0.107.4", "description": "NocoDB SDK", "main": "build/main/index.js", "typings": "build/main/index.d.ts", diff --git a/packages/nocodb/Dockerfile b/packages/nocodb/Dockerfile index 7cf4135a59..4eac4af1d3 100644 --- a/packages/nocodb/Dockerfile +++ b/packages/nocodb/Dockerfile @@ -31,9 +31,7 @@ COPY ./package*.json ./ COPY ./docker/main.js ./docker/main.js #COPY ./docker/start.sh /usr/src/appEntry/start.sh COPY ./docker/start-litestream.sh /usr/src/appEntry/start.sh -COPY ./public/css/*.css ./docker/public/css/ -COPY ./public/js/*.js ./docker/public/js/ -COPY ./public/favicon.ico ./docker/public/ +COPY src/public/ ./docker/public/ # install production dependencies, # reduce node_module size with modclean & removing sqlite deps, diff --git a/packages/nocodb/Dockerfile.local b/packages/nocodb/Dockerfile.local index 07b3c07c00..390deb32dd 100644 --- a/packages/nocodb/Dockerfile.local +++ b/packages/nocodb/Dockerfile.local @@ -14,9 +14,7 @@ COPY ./package*.json ./ COPY ./docker/nc-gui/ ./docker/nc-gui/ COPY ./docker/main.js ./docker/index.js COPY ./docker/start-local.sh /usr/src/appEntry/start.sh -COPY ./public/css/*.css ./docker/public/css/ -COPY ./public/js/*.js ./docker/public/js/ -COPY ./public/favicon.ico ./docker/public/ +COPY src/public/ ./docker/public/ # install production dependencies, # reduce node_module size with modclean & removing sqlite deps, diff --git a/packages/nocodb/package-lock.json b/packages/nocodb/package-lock.json index 35a1591c88..6095a65621 100644 --- a/packages/nocodb/package-lock.json +++ b/packages/nocodb/package-lock.json @@ -1,12 +1,12 @@ { "name": "nocodb", - "version": "0.107.3", + "version": "0.107.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "nocodb", - "version": "0.107.3", + "version": "0.107.4", "license": "AGPL-3.0-or-later", "dependencies": { "@google-cloud/storage": "^5.7.2", @@ -80,7 +80,7 @@ "mysql2": "^3.2.0", "nanoid": "^3.1.20", "nc-help": "^0.2.87", - "nc-lib-gui": "0.107.3", + "nc-lib-gui": "0.107.4", "nc-plugin": "^0.1.3", "ncp": "^2.0.0", "nocodb-sdk": "file:../nocodb-sdk", @@ -190,7 +190,7 @@ } }, "../nocodb-sdk": { - "version": "0.107.3", + "version": "0.107.4", "license": "AGPL-3.0-or-later", "dependencies": { "axios": "^0.21.1", @@ -13157,9 +13157,9 @@ } }, "node_modules/nc-lib-gui": { - "version": "0.107.3", - "resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.107.3.tgz", - "integrity": "sha512-U/GEGt4AFIIA0W1uD5nzG9drWrwdZjj1V1AFTNXdXECqpW3tshev6IFJhBOPXNl7QbXR3POAfZLW3x/IUjQZ7Q==", + "version": "0.107.4", + "resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.107.4.tgz", + "integrity": "sha512-+e0jjJgrBfgLGTTShkcu1QQ2I0QxE/NUCCJ5L8KeZibm71pvgxL/P2hfatSz8PvzSDU/YwXnyXZQkeaSDBzNyA==", "dependencies": { "express": "^4.17.1" } @@ -15797,9 +15797,9 @@ } }, "node_modules/socket.io-parser": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", - "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", + "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -28442,9 +28442,9 @@ } }, "nc-lib-gui": { - "version": "0.107.3", - "resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.107.3.tgz", - "integrity": "sha512-U/GEGt4AFIIA0W1uD5nzG9drWrwdZjj1V1AFTNXdXECqpW3tshev6IFJhBOPXNl7QbXR3POAfZLW3x/IUjQZ7Q==", + "version": "0.107.4", + "resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.107.4.tgz", + "integrity": "sha512-+e0jjJgrBfgLGTTShkcu1QQ2I0QxE/NUCCJ5L8KeZibm71pvgxL/P2hfatSz8PvzSDU/YwXnyXZQkeaSDBzNyA==", "requires": { "express": "^4.17.1" } @@ -30444,9 +30444,9 @@ } }, "socket.io-parser": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", - "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", + "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", "requires": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" diff --git a/packages/nocodb/package.json b/packages/nocodb/package.json index 038dfeae89..1b8701a2e6 100644 --- a/packages/nocodb/package.json +++ b/packages/nocodb/package.json @@ -1,6 +1,6 @@ { "name": "nocodb", - "version": "0.107.3", + "version": "0.107.4", "description": "NocoDB Backend", "main": "dist/bundle.js", "author": { @@ -113,7 +113,7 @@ "mysql2": "^3.2.0", "nanoid": "^3.1.20", "nc-help": "^0.2.87", - "nc-lib-gui": "0.107.3", + "nc-lib-gui": "0.107.4", "nc-plugin": "^0.1.3", "ncp": "^2.0.0", "nocodb-sdk": "file:../nocodb-sdk", diff --git a/packages/nocodb/src/Noco.ts b/packages/nocodb/src/Noco.ts index 942ce717f6..1b93161bb7 100644 --- a/packages/nocodb/src/Noco.ts +++ b/packages/nocodb/src/Noco.ts @@ -1,5 +1,6 @@ import Sentry, { Handlers } from '@sentry/node'; import { Logger } from '@nestjs/common'; +import path from 'path'; import { NestFactory } from '@nestjs/core'; import clear from 'clear'; import * as express from 'express'; @@ -122,6 +123,7 @@ export default class Noco { const dashboardPath = process.env.NC_DASHBOARD_URL || '/dashboard'; server.use(NcToolGui.expressMiddleware(dashboardPath)); + server.use(express.static(path.join(__dirname, 'public'))); server.get('/', (_req, res) => res.redirect(dashboardPath)); this.initSentryErrorHandler(server); diff --git a/packages/nocodb/src/app.module.ts b/packages/nocodb/src/app.module.ts index e87e17a37e..f5a4e8f1b7 100644 --- a/packages/nocodb/src/app.module.ts +++ b/packages/nocodb/src/app.module.ts @@ -5,7 +5,6 @@ import { EventEmitterModule as NestJsEventEmitter } from '@nestjs/event-emitter' import { GlobalExceptionFilter } from './filters/global-exception/global-exception.filter'; import { GlobalMiddleware } from './middlewares/global/global.middleware'; import { GuiMiddleware } from './middlewares/gui/gui.middleware'; -import { PublicMiddleware } from './middlewares/public/public.middleware'; import { DatasModule } from './modules/datas/datas.module'; import { EventEmitterModule } from './modules/event-emitter/event-emitter.module'; import { AuthService } from './services/auth.service'; @@ -59,8 +58,6 @@ export class AppModule { consumer .apply(GuiMiddleware) .forRoutes({ path: '*', method: RequestMethod.GET }) - .apply(PublicMiddleware) - .forRoutes({ path: '*', method: RequestMethod.GET }) .apply(GlobalMiddleware) .forRoutes({ path: '*', method: RequestMethod.ALL }); } diff --git a/packages/nocodb/src/helpers/initAdminFromEnv.ts b/packages/nocodb/src/helpers/initAdminFromEnv.ts new file mode 100644 index 0000000000..d41b3ece8f --- /dev/null +++ b/packages/nocodb/src/helpers/initAdminFromEnv.ts @@ -0,0 +1,281 @@ +import { promisify } from 'util'; +import { v4 as uuidv4 } from 'uuid'; +import bcrypt from 'bcryptjs'; +import { validatePassword } from 'nocodb-sdk'; +import boxen from 'boxen'; +import { T } from 'nc-help'; +import { isEmail } from 'validator'; +import NocoCache from '../cache/NocoCache'; +import { ProjectUser, User } from '../models'; +import Noco from '../Noco'; +import { CacheScope, MetaTable } from '../utils/globals'; + +const rolesLevel = { owner: 0, creator: 1, editor: 2, commenter: 3, viewer: 4 }; + +export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) { + if (process.env.NC_ADMIN_EMAIL && process.env.NC_ADMIN_PASSWORD) { + if (!isEmail(process.env.NC_ADMIN_EMAIL?.trim())) { + console.log( + '\n', + boxen( + `Provided admin email '${process.env.NC_ADMIN_EMAIL}' is not valid`, + { + title: 'Invalid admin email', + padding: 1, + borderStyle: 'double', + titleAlignment: 'center', + borderColor: 'red', + }, + ), + '\n', + ); + process.exit(1); + } + + const { valid, error, hint } = validatePassword( + process.env.NC_ADMIN_PASSWORD, + ); + if (!valid) { + console.log( + '\n', + boxen(`${error}${hint ? `\n\n${hint}` : ''}`, { + title: 'Invalid admin password', + padding: 1, + borderStyle: 'double', + titleAlignment: 'center', + borderColor: 'red', + }), + '\n', + ); + process.exit(1); + } + + let ncMeta; + try { + ncMeta = await _ncMeta.startTransaction(); + const email = process.env.NC_ADMIN_EMAIL.toLowerCase().trim(); + + const salt = await promisify(bcrypt.genSalt)(10); + const password = await promisify(bcrypt.hash)( + process.env.NC_ADMIN_PASSWORD, + salt, + ); + const email_verification_token = uuidv4(); + const roles = 'user,super'; + + // if super admin not present + if (await User.isFirst(ncMeta)) { + // roles = 'owner,creator,editor' + T.emit('evt', { + evt_type: 'project:invite', + count: 1, + }); + + await User.insert( + { + firstname: '', + lastname: '', + email, + salt, + password, + email_verification_token, + roles, + }, + ncMeta, + ); + } else { + const salt = await promisify(bcrypt.genSalt)(10); + const password = await promisify(bcrypt.hash)( + process.env.NC_ADMIN_PASSWORD, + salt, + ); + const email_verification_token = uuidv4(); + const superUser = await ncMeta.metaGet2(null, null, MetaTable.USERS, { + roles: 'user,super', + }); + + if (!superUser?.id) { + const existingUserWithNewEmail = await User.getByEmail(email, ncMeta); + if (existingUserWithNewEmail?.id) { + // clear cache + await NocoCache.delAll( + CacheScope.USER, + `${existingUserWithNewEmail.email}___*`, + ); + await NocoCache.del( + `${CacheScope.USER}:${existingUserWithNewEmail.id}`, + ); + await NocoCache.del( + `${CacheScope.USER}:${existingUserWithNewEmail.email}`, + ); + + // Update email and password of super admin account + await User.update( + existingUserWithNewEmail.id, + { + salt, + email, + password, + email_verification_token, + token_version: null, + refresh_token: null, + roles, + }, + ncMeta, + ); + } else { + T.emit('evt', { + evt_type: 'project:invite', + count: 1, + }); + + await User.insert( + { + firstname: '', + lastname: '', + email, + salt, + password, + email_verification_token, + roles, + }, + ncMeta, + ); + } + } else if (email !== superUser.email) { + // update admin email and password and migrate projects + // if user already present and associated with some project + + // check user account already present with the new admin email + const existingUserWithNewEmail = await User.getByEmail(email, ncMeta); + + if (existingUserWithNewEmail?.id) { + // get all project access belongs to the existing account + // and migrate to the admin account + const existingUserProjects = await ncMeta.metaList2( + null, + null, + MetaTable.PROJECT_USERS, + { + condition: { fk_user_id: existingUserWithNewEmail.id }, + }, + ); + + for (const existingUserProject of existingUserProjects) { + const userProject = await ProjectUser.get( + existingUserProject.project_id, + superUser.id, + ncMeta, + ); + + // if admin user already have access to the project + // then update role based on the highest access level + if (userProject) { + if ( + rolesLevel[userProject.roles] > + rolesLevel[existingUserProject.roles] + ) { + await ProjectUser.update( + userProject.project_id, + superUser.id, + existingUserProject.roles, + ncMeta, + ); + } + } else { + // if super doesn't have access then add the access + await ProjectUser.insert( + { + ...existingUserProject, + fk_user_id: superUser.id, + }, + ncMeta, + ); + } + // delete the old project access entry from DB + await ProjectUser.delete( + existingUserProject.project_id, + existingUserProject.fk_user_id, + ncMeta, + ); + } + + // delete existing user + await ncMeta.metaDelete( + null, + null, + MetaTable.USERS, + existingUserWithNewEmail.id, + ); + + // clear cache + await NocoCache.delAll( + CacheScope.USER, + `${existingUserWithNewEmail.email}___*`, + ); + await NocoCache.del( + `${CacheScope.USER}:${existingUserWithNewEmail.id}`, + ); + await NocoCache.del( + `${CacheScope.USER}:${existingUserWithNewEmail.email}`, + ); + + // Update email and password of super admin account + await User.update( + superUser.id, + { + salt, + email, + password, + email_verification_token, + token_version: null, + refresh_token: null, + }, + ncMeta, + ); + } else { + // if email's are not different update the password and hash + await User.update( + superUser.id, + { + salt, + email, + password, + email_verification_token, + token_version: null, + refresh_token: null, + }, + ncMeta, + ); + } + } else { + const newPasswordHash = await promisify(bcrypt.hash)( + process.env.NC_ADMIN_PASSWORD, + superUser.salt, + ); + + if (newPasswordHash !== superUser.password) { + // if email's are same and passwords are different + // then update the password and token version + await User.update( + superUser.id, + { + salt, + password, + email_verification_token, + token_version: null, + refresh_token: null, + }, + ncMeta, + ); + } + } + } + await ncMeta.commit(); + } catch (e) { + console.log('Error occurred while updating/creating admin user'); + console.log(e); + await ncMeta.rollback(e); + } + } +} diff --git a/packages/nocodb/src/middlewares/public/public.middleware.spec.ts b/packages/nocodb/src/middlewares/public/public.middleware.spec.ts deleted file mode 100644 index 7b5e460f52..0000000000 --- a/packages/nocodb/src/middlewares/public/public.middleware.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { PublicMiddleware } from './public.middleware'; - -describe('PublicMiddleware', () => { - it('should be defined', () => { - expect(new PublicMiddleware()).toBeDefined(); - }); -}); diff --git a/packages/nocodb/src/middlewares/public/public.middleware.ts b/packages/nocodb/src/middlewares/public/public.middleware.ts deleted file mode 100644 index 4c5761065c..0000000000 --- a/packages/nocodb/src/middlewares/public/public.middleware.ts +++ /dev/null @@ -1,23 +0,0 @@ -import path, { join } from 'path'; -import { Injectable } from '@nestjs/common'; -import express from 'express'; -import isDocker from 'is-docker'; -import type { NestMiddleware } from '@nestjs/common'; - -@Injectable() -export class PublicMiddleware implements NestMiddleware { - use(req: any, res: any, next: () => void) { - // redirect root to dashboard - if (req.path === '/') { - const dashboardPath = process.env.NC_DASHBOARD_URL || '/dashboard'; - return res.redirect(dashboardPath); - } - - // serve static files from public folder - if (isDocker()) { - express.static(join(process.cwd(), 'docker', 'public'))(req, res, next); - } else { - express.static(join(process.cwd(), 'public'))(req, res, next); - } - } -} diff --git a/packages/nocodb/src/models/Base.ts b/packages/nocodb/src/models/Base.ts index 2846b3b7b4..92f04e4d18 100644 --- a/packages/nocodb/src/models/Base.ts +++ b/packages/nocodb/src/models/Base.ts @@ -91,6 +91,7 @@ export default class Base implements BaseType { base: BaseType & { id: string; projectId: string; + skipReorder?: boolean; }, ncMeta = Noco.ncMeta, ) { @@ -144,7 +145,8 @@ export default class Base implements BaseType { // call before reorder to update cache const returnBase = await this.get(oldBase.id, ncMeta); - await this.reorderBases(base.projectId, returnBase.id, ncMeta); + if (!base.skipReorder) + await this.reorderBases(base.projectId, returnBase.id, ncMeta); return returnBase; } diff --git a/packages/nocodb/src/models/Model.ts b/packages/nocodb/src/models/Model.ts index f59a77a6fb..d12427d846 100644 --- a/packages/nocodb/src/models/Model.ts +++ b/packages/nocodb/src/models/Model.ts @@ -137,7 +137,15 @@ export default class Model implements TableType { MetaTable.MODELS, insertObj, ); - + if (baseId) { + await NocoCache.appendToList( + CacheScope.MODEL, + [projectId, baseId], + `${CacheScope.MODEL}:${id}`, + ); + } + // cater cases where baseId is not required + // e.g. xcVisibilityMetaGet await NocoCache.appendToList( CacheScope.MODEL, [projectId], @@ -171,7 +179,10 @@ export default class Model implements TableType { }, ncMeta = Noco.ncMeta, ): Promise { - const cachedList = await NocoCache.getList(CacheScope.MODEL, [project_id]); + const cachedList = await NocoCache.getList(CacheScope.MODEL, [ + project_id, + base_id, + ]); let { list: modelList } = cachedList; const { isNoneList } = cachedList; if (!isNoneList && !modelList.length) { @@ -191,7 +202,11 @@ export default class Model implements TableType { model.meta = parseMetaProp(model); } - await NocoCache.setList(CacheScope.MODEL, [project_id], modelList); + await NocoCache.setList( + CacheScope.MODEL, + [project_id, base_id], + modelList, + ); } modelList.sort( (a, b) => @@ -746,13 +761,13 @@ export default class Model implements TableType { }, ncMeta = Noco.ncMeta, ) { + const cacheKey = base_id + ? `${CacheScope.MODEL}:${project_id}:${base_id}:${aliasOrId}` + : `${CacheScope.MODEL}:${project_id}:${aliasOrId}`; const modelId = project_id && aliasOrId && - (await NocoCache.get( - `${CacheScope.MODEL}:${project_id}:${aliasOrId}`, - CacheGetType.TYPE_OBJECT, - )); + (await NocoCache.get(cacheKey, CacheGetType.TYPE_OBJECT)); if (!modelId) { const model = base_id ? await ncMeta.metaGet2( @@ -798,10 +813,7 @@ export default class Model implements TableType { }, ); if (model) { - await NocoCache.set( - `${CacheScope.MODEL}:${project_id}:${aliasOrId}`, - model.id, - ); + await NocoCache.set(cacheKey, model.id); await NocoCache.set(`${CacheScope.MODEL}:${model.id}`, model); } return model && new Model(model); diff --git a/packages/nocodb/public/css/fonts.montserrat.css b/packages/nocodb/src/public/css/fonts.montserrat.css similarity index 100% rename from packages/nocodb/public/css/fonts.montserrat.css rename to packages/nocodb/src/public/css/fonts.montserrat.css diff --git a/packages/nocodb/public/css/fonts.roboto.css b/packages/nocodb/src/public/css/fonts.roboto.css similarity index 100% rename from packages/nocodb/public/css/fonts.roboto.css rename to packages/nocodb/src/public/css/fonts.roboto.css diff --git a/packages/nocodb/public/css/materialdesignicons.5.x.min.css b/packages/nocodb/src/public/css/materialdesignicons.5.x.min.css similarity index 100% rename from packages/nocodb/public/css/materialdesignicons.5.x.min.css rename to packages/nocodb/src/public/css/materialdesignicons.5.x.min.css diff --git a/packages/nocodb/public/css/swagger-ui-bundle.4.5.2.min.css b/packages/nocodb/src/public/css/swagger-ui-bundle.4.5.2.min.css similarity index 100% rename from packages/nocodb/public/css/swagger-ui-bundle.4.5.2.min.css rename to packages/nocodb/src/public/css/swagger-ui-bundle.4.5.2.min.css diff --git a/packages/nocodb/public/css/vuetify.2.x.min.css b/packages/nocodb/src/public/css/vuetify.2.x.min.css similarity index 100% rename from packages/nocodb/public/css/vuetify.2.x.min.css rename to packages/nocodb/src/public/css/vuetify.2.x.min.css diff --git a/packages/nocodb/public/favicon.ico b/packages/nocodb/src/public/favicon.ico similarity index 100% rename from packages/nocodb/public/favicon.ico rename to packages/nocodb/src/public/favicon.ico diff --git a/packages/nocodb/public/icon.png b/packages/nocodb/src/public/icon.png similarity index 100% rename from packages/nocodb/public/icon.png rename to packages/nocodb/src/public/icon.png diff --git a/packages/nocodb/public/js/axios.0.19.2.min.js b/packages/nocodb/src/public/js/axios.0.19.2.min.js similarity index 100% rename from packages/nocodb/public/js/axios.0.19.2.min.js rename to packages/nocodb/src/public/js/axios.0.19.2.min.js diff --git a/packages/nocodb/public/js/redoc.standalone.min.js b/packages/nocodb/src/public/js/redoc.standalone.min.js similarity index 100% rename from packages/nocodb/public/js/redoc.standalone.min.js rename to packages/nocodb/src/public/js/redoc.standalone.min.js diff --git a/packages/nocodb/public/js/swagger-ui-bundle.4.5.2.min.js b/packages/nocodb/src/public/js/swagger-ui-bundle.4.5.2.min.js similarity index 100% rename from packages/nocodb/public/js/swagger-ui-bundle.4.5.2.min.js rename to packages/nocodb/src/public/js/swagger-ui-bundle.4.5.2.min.js diff --git a/packages/nocodb/public/js/vue.2.6.14.min.js b/packages/nocodb/src/public/js/vue.2.6.14.min.js similarity index 100% rename from packages/nocodb/public/js/vue.2.6.14.min.js rename to packages/nocodb/src/public/js/vue.2.6.14.min.js diff --git a/packages/nocodb/public/js/vue.global.js b/packages/nocodb/src/public/js/vue.global.js similarity index 100% rename from packages/nocodb/public/js/vue.global.js rename to packages/nocodb/src/public/js/vue.global.js diff --git a/packages/nocodb/public/js/vuetify.2.x.min.js b/packages/nocodb/src/public/js/vuetify.2.x.min.js similarity index 100% rename from packages/nocodb/public/js/vuetify.2.x.min.js rename to packages/nocodb/src/public/js/vuetify.2.x.min.js diff --git a/packages/nocodb/src/services/app-init.service.ts b/packages/nocodb/src/services/app-init.service.ts index b305425347..c913fcaefb 100644 --- a/packages/nocodb/src/services/app-init.service.ts +++ b/packages/nocodb/src/services/app-init.service.ts @@ -1,9 +1,12 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { T } from 'nc-help'; import NocoCache from '../cache/NocoCache'; import { Connection } from '../connection/connection'; +import initAdminFromEnv from '../helpers/initAdminFromEnv'; import NcPluginMgrv2 from '../helpers/NcPluginMgrv2'; import { MetaService } from '../meta/meta.service'; +import { User } from '../models' import Noco from '../Noco'; +import getInstance from '../utils/getInstance'; import NcConfigFactory from '../utils/NcConfigFactory'; import NcUpgrader from '../version-upgrader/NcUpgrader'; import type { IEventEmitter } from '../modules/event-emitter/event-emitter.interface'; @@ -35,7 +38,7 @@ export const appInitServiceProvider: Provider = { metaService: MetaService, eventEmitter: IEventEmitter, ) => { - process.env.NC_VERSION = '0105004'; + process.env.NC_VERSION = '0107004'; await NocoCache.init(); @@ -54,6 +57,9 @@ export const appInitServiceProvider: Provider = { // init jwt secret await Noco.initJwt(); + // load super admin user from env if env is set + await initAdminFromEnv(metaService); + // init plugin manager await NcPluginMgrv2.init(Noco.ncMeta); await Noco.loadEEState(); @@ -61,6 +67,11 @@ export const appInitServiceProvider: Provider = { // run upgrader await NcUpgrader.upgrade({ ncMeta: Noco._ncMeta }); + T.init({ + instance: getInstance, + }); + T.emit('evt_app_started', await User.count()); + // todo: move app config to app-init service return new AppInitService(connection.config); }, diff --git a/packages/nocodb/src/strategies/authtoken.strategy/authtoken.strategy.ts b/packages/nocodb/src/strategies/authtoken.strategy/authtoken.strategy.ts index 8afc8129f6..f206777352 100644 --- a/packages/nocodb/src/strategies/authtoken.strategy/authtoken.strategy.ts +++ b/packages/nocodb/src/strategies/authtoken.strategy/authtoken.strategy.ts @@ -26,6 +26,11 @@ export class AuthTokenStrategy extends PassportStrategy(Strategy, 'authtoken') { return callback({ msg: 'User not found' }); } + Object.assign(user, { + id: dbUser.id, + roles: dbUser.roles, + }); + dbUser.is_api_token = true; if (req['ncProjectId']) { const projectUser = await ProjectUser.get( diff --git a/packages/nocodb/src/version-upgrader/NcUpgrader.ts b/packages/nocodb/src/version-upgrader/NcUpgrader.ts index c2d351dc2f..951a93099f 100644 --- a/packages/nocodb/src/version-upgrader/NcUpgrader.ts +++ b/packages/nocodb/src/version-upgrader/NcUpgrader.ts @@ -13,6 +13,7 @@ import ncProjectUpgraderV2_0090000 from './ncProjectUpgraderV2_0090000'; import ncProjectEnvUpgrader0011045 from './ncProjectEnvUpgrader0011045'; import ncProjectEnvUpgrader from './ncProjectEnvUpgrader'; import ncHookUpgrader from './ncHookUpgrader'; +import ncProjectConfigUpgrader from './ncProjectConfigUpgrader'; import type { MetaService } from '../meta/meta.service'; import type { NcConfig } from '../interface/config'; @@ -48,6 +49,7 @@ export default class NcUpgrader { { name: '0105002', handler: ncStickyColumnUpgrader }, { name: '0105003', handler: ncFilterUpgrader_0105003 }, { name: '0105004', handler: ncHookUpgrader }, + { name: '0107004', handler: ncProjectConfigUpgrader }, ]; if (!(await ctx.ncMeta.knexConnection?.schema?.hasTable?.('nc_store'))) { return; diff --git a/packages/nocodb/src/version-upgrader/ncProjectConfigUpgrader.ts b/packages/nocodb/src/version-upgrader/ncProjectConfigUpgrader.ts new file mode 100644 index 0000000000..35a7a759a2 --- /dev/null +++ b/packages/nocodb/src/version-upgrader/ncProjectConfigUpgrader.ts @@ -0,0 +1,48 @@ +import CryptoJS from 'crypto-js'; +import { Base } from '../models'; +import { MetaTable } from '../utils/globals'; +import type { NcUpgraderCtx } from './NcUpgrader'; + +const TEMP_KEY = 'temporary-key'; + +// In version 0.107.0 we were used a temporary fallback secret key for JWT token encryption and project base config encryption. +// So any project created in version 0.107.0 won't be able to decrypt the project base config. +// So we need to update the project base config with the new secret key. +// Get all the project bases and update the project config with the new secret key. +export default async function ({ ncMeta }: NcUpgraderCtx) { + const actions = []; + + // Get all the project bases + const bases = await ncMeta.metaList2(null, null, MetaTable.BASES); + + // Update the base config with the new secret key if we could decrypt the base config with the fallback secret key + for (const base of bases) { + let config; + + // Try to decrypt the base config with the fallback secret key + // if we could decrypt the base config with the fallback secret key then we will update the base config with the new secret key + // otherwise we will skip the base config update since it is already encrypted with the new secret key + try { + config = JSON.parse( + CryptoJS.AES.decrypt(base.config, TEMP_KEY).toString(CryptoJS.enc.Utf8), + ); + + // Update the base config with the new secret key + actions.push( + Base.updateBase( + base.id, + { + id: base.id, + projectId: base.project_id, + config, + skipReorder: true, + }, + ncMeta, + ), + ); + } catch (e) { + // ignore the error + } + } + await Promise.all(actions); +} diff --git a/packages/nocodb/webpack.config.js b/packages/nocodb/webpack.config.js index 9f462d4c1d..4e3789a283 100644 --- a/packages/nocodb/webpack.config.js +++ b/packages/nocodb/webpack.config.js @@ -44,7 +44,7 @@ module.exports = { plugins: [ new webpack.EnvironmentPlugin(['EE']), new CopyPlugin({ - patterns: [{ from: 'public', to: 'public' }], + patterns: [{ from: 'src/public', to: 'public' }], }), ],