Browse Source

Merge pull request #5262 from nocodb/refactor/ctl-svc-layer

refactor: ctl & svc layer
pull/5252/head
Pranav C 2 years ago committed by GitHub
parent
commit
7da8114ce5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      packages/nocodb-sdk/src/lib/Api.ts
  2. 4
      packages/nocodb/src/lib/controllers/dbData/helpers.ts
  3. 12
      packages/nocodb/src/lib/controllers/dbData/oldData.ctl.ts
  4. 2
      packages/nocodb/src/lib/controllers/publicControllers/publicDataExport.ctl.ts
  5. 0
      packages/nocodb/src/lib/controllers/sync/import.ctl.ts
  6. 71
      packages/nocodb/src/lib/controllers/sync/index.ts
  7. 68
      packages/nocodb/src/lib/controllers/sync/sync.ctl.ts
  8. 446
      packages/nocodb/src/lib/controllers/user/index.ts
  9. 250
      packages/nocodb/src/lib/controllers/user/user.ctl.ts
  10. 16
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts
  11. 2
      packages/nocodb/src/lib/db/sql-mgr/v2/SqlMgrv2Trans.ts
  12. 2
      packages/nocodb/src/lib/db/sql-migrator/lib/KnexMigratorv2.ts
  13. 2
      packages/nocodb/src/lib/meta/api/helpers/populateMeta.ts
  14. 3
      packages/nocodb/src/lib/meta/api/index.ts
  15. 4
      packages/nocodb/src/lib/meta/helpers/extractProps.ts
  16. 4
      packages/nocodb/src/lib/meta/helpers/getColumnPropsFromUIDT.ts
  17. 5
      packages/nocodb/src/lib/models/Base.ts
  18. 2
      packages/nocodb/src/lib/models/Hook.ts
  19. 3
      packages/nocodb/src/lib/models/KanbanView.ts
  20. 2
      packages/nocodb/src/lib/models/KanbanViewColumn.ts
  21. 2
      packages/nocodb/src/lib/services/base.svc.ts
  22. 18
      packages/nocodb/src/lib/services/column.svc.ts
  23. 2
      packages/nocodb/src/lib/services/dbData/bulkData.ts
  24. 14
      packages/nocodb/src/lib/services/dbData/dataAliasNested.svc.ts
  25. 4
      packages/nocodb/src/lib/services/dbData/helpers.ts
  26. 42
      packages/nocodb/src/lib/services/dbData/index.ts
  27. 0
      packages/nocodb/src/lib/services/ee/orgToken.svc.ts
  28. 4
      packages/nocodb/src/lib/services/index.ts
  29. 6
      packages/nocodb/src/lib/services/public/index.ts
  30. 12
      packages/nocodb/src/lib/services/public/publicData.svc.ts
  31. 2
      packages/nocodb/src/lib/services/public/publicDataExport.svc.ts
  32. 0
      packages/nocodb/src/lib/services/public/publicMeta.svc.ts
  33. 7
      packages/nocodb/src/lib/services/sync/index.ts
  34. 55
      packages/nocodb/src/lib/services/table.svc.ts
  35. 10
      packages/nocodb/src/lib/services/user/helpers.ts
  36. 164
      packages/nocodb/src/lib/services/user/index.ts
  37. 105
      packages/nocodb/src/lib/utils/common/NcConnectionMgrv2.ts
  38. 2
      packages/nocodb/src/lib/version-upgrader/ncAttachmentUpgrader.ts
  39. 2
      packages/nocodb/src/lib/version-upgrader/ncAttachmentUpgrader_0104002.ts
  40. 44
      packages/nocodb/src/schema/swagger.json
  41. 2
      packages/nocodb/tests/unit/factory/row.ts
  42. 8
      packages/nocodb/tests/unit/model/tests/baseModelSql.test.ts
  43. 2
      tests/playwright/tests/filters.spec.ts

8
packages/nocodb-sdk/src/lib/Api.ts

@ -1960,6 +1960,14 @@ export interface SignUpReqType {
* @example password123456789
*/
password: string;
/** Model for StringOrNull */
firstname?: StringOrNullType;
/** Model for StringOrNull */
lastname?: StringOrNullType;
/** Sign Up Token. Used for invitation. */
token?: StringOrNullType;
/** Ignore Subscription */
ignore_subscribe?: BoolType;
}
/**

4
packages/nocodb/src/lib/controllers/dbData/helpers.ts

@ -57,7 +57,7 @@ export async function extractXlsxData(param: {
const baseModel = await Model.getBaseModelSQL({
id: view.model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const { offset, dbRows, elapsed } = await dataService.getDbRows({
@ -92,7 +92,7 @@ export async function extractCsvData(view: View, req: Request) {
const baseModel = await Model.getBaseModelSQL({
id: view.model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const { offset, dbRows, elapsed } = await dataService.getDbRows({

12
packages/nocodb/src/lib/controllers/dbData/oldData.ctl.ts

@ -17,7 +17,7 @@ export async function dataList(req: Request, res: Response) {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const requestObj = await getAst({
@ -50,7 +50,7 @@ export async function dataCount(req: Request, res: Response) {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const listArgs: any = { ...req.query };
@ -73,7 +73,7 @@ async function dataInsert(req: Request, res: Response) {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
res.json(await baseModel.insert(req.body, null, req));
@ -86,7 +86,7 @@ async function dataUpdate(req: Request, res: Response) {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
res.json(await baseModel.updateByPk(req.params.rowId, req.body, null, req));
@ -98,7 +98,7 @@ async function dataDelete(req: Request, res: Response) {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
res.json(await baseModel.delByPk(req.params.rowId, null, req));
@ -128,7 +128,7 @@ async function dataRead(req: Request, res: Response) {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
res.json(

2
packages/nocodb/src/lib/controllers/publicControllers/publicDataExport.ctl.ts

@ -138,7 +138,7 @@ async function getDbRows(model, view: View, req: Request) {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const requestObj = await getAst({

0
packages/nocodb/src/lib/controllers/sync/importApis.ts → packages/nocodb/src/lib/controllers/sync/import.ctl.ts

71
packages/nocodb/src/lib/controllers/sync/index.ts

@ -1,69 +1,4 @@
import { Request, Response, Router } from 'express';
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw';
import { syncService } from '../../services';
import importController from './import.ctl';
import syncSourceController from './sync.ctl';
export async function syncSourceList(req: Request, res: Response) {
// todo: pagination
res.json(
syncService.syncSourceList({
projectId: req.params.projectId,
})
);
}
export async function syncCreate(req: Request, res: Response) {
res.json(
await syncService.syncCreate({
projectId: req.params.projectId,
baseId: req.params.baseId,
userId: (req as any).user.id,
syncPayload: req.body,
})
);
}
export async function syncDelete(req: Request, res: Response<any>) {
res.json(
await syncService.syncDelete({
syncId: req.params.syncId,
})
);
}
export async function syncUpdate(req: Request, res: Response) {
res.json(
await syncService.syncUpdate({
syncId: req.params.syncId,
syncPayload: req.body,
})
);
}
const router = Router({ mergeParams: true });
router.get(
'/api/v1/db/meta/projects/:projectId/syncs',
ncMetaAclMw(syncSourceList, 'syncSourceList')
);
router.post(
'/api/v1/db/meta/projects/:projectId/syncs',
ncMetaAclMw(syncCreate, 'syncSourceCreate')
);
router.get(
'/api/v1/db/meta/projects/:projectId/syncs/:baseId',
ncMetaAclMw(syncSourceList, 'syncSourceList')
);
router.post(
'/api/v1/db/meta/projects/:projectId/syncs/:baseId',
ncMetaAclMw(syncCreate, 'syncSourceCreate')
);
router.delete(
'/api/v1/db/meta/syncs/:syncId',
ncMetaAclMw(syncDelete, 'syncSourceDelete')
);
router.patch(
'/api/v1/db/meta/syncs/:syncId',
ncMetaAclMw(syncUpdate, 'syncSourceUpdate')
);
export default router;
export { importController, syncSourceController };

68
packages/nocodb/src/lib/controllers/sync/sync.ctl.ts

@ -0,0 +1,68 @@
import { Request, Response, Router } from 'express';
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw';
import { syncService } from '../../services';
export async function syncSourceList(req: Request, res: Response) {
res.json(
await syncService.syncSourceList({
projectId: req.params.projectId,
})
);
}
export async function syncCreate(req: Request, res: Response) {
res.json(
await syncService.syncCreate({
projectId: req.params.projectId,
baseId: req.params.baseId,
userId: (req as any).user.id,
syncPayload: req.body,
})
);
}
export async function syncDelete(req: Request, res: Response<any>) {
res.json(
await syncService.syncDelete({
syncId: req.params.syncId,
})
);
}
export async function syncUpdate(req: Request, res: Response) {
res.json(
await syncService.syncUpdate({
syncId: req.params.syncId,
syncPayload: req.body,
})
);
}
const router = Router({ mergeParams: true });
router.get(
'/api/v1/db/meta/projects/:projectId/syncs',
ncMetaAclMw(syncSourceList, 'syncSourceList')
);
router.post(
'/api/v1/db/meta/projects/:projectId/syncs',
ncMetaAclMw(syncCreate, 'syncSourceCreate')
);
router.get(
'/api/v1/db/meta/projects/:projectId/syncs/:baseId',
ncMetaAclMw(syncSourceList, 'syncSourceList')
);
router.post(
'/api/v1/db/meta/projects/:projectId/syncs/:baseId',
ncMetaAclMw(syncCreate, 'syncSourceCreate')
);
router.delete(
'/api/v1/db/meta/syncs/:syncId',
ncMetaAclMw(syncDelete, 'syncSourceDelete')
);
router.patch(
'/api/v1/db/meta/syncs/:syncId',
ncMetaAclMw(syncUpdate, 'syncSourceUpdate')
);
export default router;

446
packages/nocodb/src/lib/controllers/user/index.ts

@ -1,445 +1,3 @@
import { Request, Response } from 'express';
import { TableType, validatePassword } from 'nocodb-sdk';
import { T } from 'nc-help';
import userController from './user.ctl';
const { isEmail } = require('validator');
import * as ejs from 'ejs';
import bcrypt from 'bcryptjs';
import { promisify } from 'util';
const { v4: uuidv4 } = require('uuid');
import passport from 'passport';
import { getAjvValidatorMw } from '../../meta/api/helpers';
import catchError, { NcError } from '../../meta/helpers/catchError';
import extractProjectIdAndAuthenticate from '../../meta/helpers/extractProjectIdAndAuthenticate';
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw';
import NcPluginMgrv2 from '../../meta/helpers/NcPluginMgrv2';
import { Audit, User } from '../../models';
import Noco from '../../Noco';
import { userService } from '../../services';
export async function signup(req: Request, res: Response<TableType>) {
const {
email: _email,
firstname,
lastname,
token,
ignore_subscribe,
} = req.body;
let { password } = req.body;
// validate password and throw error if password is satisfying the conditions
const { valid, error } = validatePassword(password);
if (!valid) {
NcError.badRequest(`Password : ${error}`);
}
if (!isEmail(_email)) {
NcError.badRequest(`Invalid email`);
}
const email = _email.toLowerCase();
let user = await User.getByEmail(email);
if (user) {
if (token) {
if (token !== user.invite_token) {
NcError.badRequest(`Invalid invite url`);
} else if (user.invite_token_expires < new Date()) {
NcError.badRequest(
'Expired invite url, Please contact super admin to get a new invite url'
);
}
} else {
// todo : opening up signup for timebeing
// return next(new Error(`Email '${email}' already registered`));
}
}
const salt = await promisify(bcrypt.genSalt)(10);
password = await promisify(bcrypt.hash)(password, salt);
const email_verification_token = uuidv4();
if (!ignore_subscribe) {
T.emit('evt_subscribe', email);
}
if (user) {
if (token) {
await User.update(user.id, {
firstname,
lastname,
salt,
password,
email_verification_token,
invite_token: null,
invite_token_expires: null,
email: user.email,
});
} else {
NcError.badRequest('User already exist');
}
} else {
await userService.registerNewUserIfAllowed({
firstname,
lastname,
email,
salt,
password,
email_verification_token,
});
}
user = await User.getByEmail(email);
try {
const template = (await import('./ui/emailTemplates/verify')).default;
await (
await NcPluginMgrv2.emailAdapter()
).mailSend({
to: email,
subject: 'Verify email',
html: ejs.render(template, {
verifyLink:
(req as any).ncSiteUrl +
`/email/verify/${user.email_verification_token}`,
}),
});
} catch (e) {
console.log(
'Warning : `mailSend` failed, Please configure emailClient configuration.'
);
}
await promisify((req as any).login.bind(req))(user);
const refreshToken = userService.randomTokenString();
await User.update(user.id, {
refresh_token: refreshToken,
email: user.email,
});
setTokenCookie(res, refreshToken);
user = (req as any).user;
await Audit.insert({
op_type: 'AUTHENTICATION',
op_sub_type: 'SIGNUP',
user: user.email,
description: `signed up `,
ip: (req as any).clientIp,
});
res.json({
token: userService.genJwt(user, Noco.getConfig()),
} as any);
}
async function successfulSignIn({
user,
err,
info,
req,
res,
auditDescription,
}) {
try {
if (!user || !user.email) {
if (err) {
return res.status(400).send(err);
}
if (info) {
return res.status(400).send(info);
}
return res.status(400).send({ msg: 'Your signin has failed' });
}
await promisify((req as any).login.bind(req))(user);
const refreshToken = userService.randomTokenString();
if (!user.token_version) {
user.token_version = userService.randomTokenString();
}
await User.update(user.id, {
refresh_token: refreshToken,
email: user.email,
token_version: user.token_version,
});
setTokenCookie(res, refreshToken);
await Audit.insert({
op_type: 'AUTHENTICATION',
op_sub_type: 'SIGNIN',
user: user.email,
ip: req.clientIp,
description: auditDescription,
});
res.json({
token: userService.genJwt(user, Noco.getConfig()),
} as any);
} catch (e) {
console.log(e);
throw e;
}
}
async function signin(req, res, next) {
passport.authenticate(
'local',
{ session: false },
async (err, user, info): Promise<any> =>
await successfulSignIn({
user,
err,
info,
req,
res,
auditDescription: 'signed in',
})
)(req, res, next);
}
async function googleSignin(req, res, next) {
passport.authenticate(
'google',
{
session: false,
callbackURL: req.ncSiteUrl + Noco.getConfig().dashboardPath,
},
async (err, user, info): Promise<any> =>
await successfulSignIn({
user,
err,
info,
req,
res,
auditDescription: 'signed in using Google Auth',
})
)(req, res, next);
}
function setTokenCookie(res: Response, token): void {
// create http only cookie with refresh token that expires in 7 days
const cookieOptions = {
httpOnly: true,
expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
};
res.cookie('refresh_token', token, cookieOptions);
}
async function me(req, res): Promise<any> {
res.json(req?.session?.passport?.user ?? {});
}
async function passwordChange(req: Request<any, any>, res): Promise<any> {
if (!(req as any).isAuthenticated()) {
NcError.forbidden('Not allowed');
}
await userService.passwordChange({
user: req['user'],
req,
body: req.body,
});
res.json({ msg: 'Password updated successfully' });
}
async function passwordForgot(req: Request<any, any>, res): Promise<any> {
await userService.passwordForgot({
siteUrl: (req as any).ncSiteUrl,
body: req.body,
req,
});
res.json({ msg: 'Please check your email to reset the password' });
}
async function tokenValidate(req, res): Promise<any> {
await userService.tokenValidate({
token: req.params.tokenId,
});
res.json(true);
}
async function passwordReset(req, res): Promise<any> {
await userService.passwordReset({
token: req.params.tokenId,
body: req.body,
req,
});
res.json({ msg: 'Password reset successful' });
}
async function emailVerification(req, res): Promise<any> {
await userService.emailVerification({
token: req.params.tokenId,
req,
});
res.json({ msg: 'Email verified successfully' });
}
async function refreshToken(req, res): Promise<any> {
try {
if (!req?.cookies?.refresh_token) {
return res.status(400).json({ msg: 'Missing refresh token' });
}
const user = await User.getByRefreshToken(req.cookies.refresh_token);
if (!user) {
return res.status(400).json({ msg: 'Invalid refresh token' });
}
const refreshToken = userService.randomTokenString();
await User.update(user.id, {
email: user.email,
refresh_token: refreshToken,
});
setTokenCookie(res, refreshToken);
res.json({
token: userService.genJwt(user, Noco.getConfig()),
} as any);
} catch (e) {
return res.status(400).json({ msg: e.message });
}
}
async function renderPasswordReset(req, res): Promise<any> {
try {
res.send(
ejs.render((await import('./ui/auth/resetPassword')).default, {
ncPublicUrl: process.env.NC_PUBLIC_URL || '',
token: JSON.stringify(req.params.tokenId),
baseUrl: `/`,
})
);
} catch (e) {
return res.status(400).json({ msg: e.message });
}
}
const mapRoutes = (router) => {
// todo: old api - /auth/signup?tool=1
router.post(
'/auth/user/signup',
getAjvValidatorMw('swagger.json#/components/schemas/SignUpReq'),
catchError(signup)
);
router.post(
'/auth/user/signin',
getAjvValidatorMw('swagger.json#/components/schemas/SignInReq'),
catchError(signin)
);
router.get('/auth/user/me', extractProjectIdAndAuthenticate, catchError(me));
router.post('/auth/password/forgot', catchError(passwordForgot));
router.post('/auth/token/validate/:tokenId', catchError(tokenValidate));
router.post(
'/auth/password/reset/:tokenId',
getAjvValidatorMw('swagger.json#/components/schemas/PasswordResetReq'),
catchError(passwordReset)
);
router.post('/auth/email/validate/:tokenId', catchError(emailVerification));
router.post(
'/user/password/change',
ncMetaAclMw(passwordChange, 'passwordChange')
);
router.post('/auth/token/refresh', catchError(refreshToken));
/* Google auth apis */
router.post(`/auth/google/genTokenByCode`, catchError(googleSignin));
router.get('/auth/google', (req: any, res, next) =>
passport.authenticate('google', {
scope: ['profile', 'email'],
state: req.query.state,
callbackURL: req.ncSiteUrl + Noco.getConfig().dashboardPath,
})(req, res, next)
);
// deprecated APIs
router.post(
'/api/v1/db/auth/user/signup',
getAjvValidatorMw('swagger.json#/components/schemas/SignUpReq'),
catchError(signup)
);
router.post(
'/api/v1/db/auth/user/signin',
getAjvValidatorMw('swagger.json#/components/schemas/SignInReq'),
catchError(signin)
);
router.get(
'/api/v1/db/auth/user/me',
extractProjectIdAndAuthenticate,
catchError(me)
);
router.post('/api/v1/db/auth/password/forgot', catchError(passwordForgot));
router.post(
'/api/v1/db/auth/token/validate/:tokenId',
catchError(tokenValidate)
);
router.post(
'/api/v1/db/auth/password/reset/:tokenId',
catchError(passwordReset)
);
router.post(
'/api/v1/db/auth/email/validate/:tokenId',
catchError(emailVerification)
);
router.post(
'/api/v1/db/auth/password/change',
ncMetaAclMw(passwordChange, 'passwordChange')
);
router.post('/api/v1/db/auth/token/refresh', catchError(refreshToken));
router.get(
'/api/v1/db/auth/password/reset/:tokenId',
catchError(renderPasswordReset)
);
// new API
router.post(
'/api/v1/auth/user/signup',
getAjvValidatorMw('swagger.json#/components/schemas/SignUpReq'),
catchError(signup)
);
router.post(
'/api/v1/auth/user/signin',
getAjvValidatorMw('swagger.json#/components/schemas/SignInReq'),
catchError(signin)
);
router.get(
'/api/v1/auth/user/me',
extractProjectIdAndAuthenticate,
catchError(me)
);
router.post('/api/v1/auth/password/forgot', catchError(passwordForgot));
router.post(
'/api/v1/auth/token/validate/:tokenId',
catchError(tokenValidate)
);
router.post(
'/api/v1/auth/password/reset/:tokenId',
catchError(passwordReset)
);
router.post(
'/api/v1/auth/email/validate/:tokenId',
catchError(emailVerification)
);
router.post(
'/api/v1/auth/password/change',
ncMetaAclMw(passwordChange, 'passwordChange')
);
router.post('/api/v1/auth/token/refresh', catchError(refreshToken));
// respond with password reset page
router.get('/auth/password/reset/:tokenId', catchError(renderPasswordReset));
};
export { mapRoutes as userController };
export { userController };

250
packages/nocodb/src/lib/controllers/user/userApis.ts → packages/nocodb/src/lib/controllers/user/user.ctl.ts

@ -1,139 +1,34 @@
import { Request, Response } from 'express';
import { TableType, validatePassword } from 'nocodb-sdk';
import { T } from 'nc-help';
const { isEmail } = require('validator');
import { Request } from 'express';
import * as ejs from 'ejs';
import bcrypt from 'bcryptjs';
import { promisify } from 'util';
const { v4: uuidv4 } = require('uuid');
import passport from 'passport';
import { getAjvValidatorMw } from '../../meta/api/helpers';
import catchError, { NcError } from '../../meta/helpers/catchError';
import extractProjectIdAndAuthenticate from '../../meta/helpers/extractProjectIdAndAuthenticate';
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw';
import NcPluginMgrv2 from '../../meta/helpers/NcPluginMgrv2';
import { Audit, User } from '../../models';
import Noco from '../../Noco';
import { userService } from '../../services';
import { setTokenCookie } from '../../services/user/helpers';
export async function signup(req: Request<any, any>, res): Promise<any> {
res.json(
await userService.signup({
body: req.body,
req,
res,
})
);
}
export async function signup(req: Request, res: Response<TableType>) {
const {
email: _email,
firstname,
lastname,
token,
ignore_subscribe,
} = req.body;
let { password } = req.body;
// validate password and throw error if password is satisfying the conditions
const { valid, error } = validatePassword(password);
if (!valid) {
NcError.badRequest(`Password : ${error}`);
}
if (!isEmail(_email)) {
NcError.badRequest(`Invalid email`);
}
const email = _email.toLowerCase();
let user = await User.getByEmail(email);
if (user) {
if (token) {
if (token !== user.invite_token) {
NcError.badRequest(`Invalid invite url`);
} else if (user.invite_token_expires < new Date()) {
NcError.badRequest(
'Expired invite url, Please contact super admin to get a new invite url'
);
}
} else {
// todo : opening up signup for timebeing
// return next(new Error(`Email '${email}' already registered`));
}
}
const salt = await promisify(bcrypt.genSalt)(10);
password = await promisify(bcrypt.hash)(password, salt);
const email_verification_token = uuidv4();
if (!ignore_subscribe) {
T.emit('evt_subscribe', email);
}
if (user) {
if (token) {
await User.update(user.id, {
firstname,
lastname,
salt,
password,
email_verification_token,
invite_token: null,
invite_token_expires: null,
email: user.email,
});
} else {
NcError.badRequest('User already exist');
}
} else {
await userService.registerNewUserIfAllowed({
firstname,
lastname,
email,
salt,
password,
email_verification_token,
});
}
user = await User.getByEmail(email);
try {
const template = (await import('./ui/emailTemplates/verify')).default;
await (
await NcPluginMgrv2.emailAdapter()
).mailSend({
to: email,
subject: 'Verify email',
html: ejs.render(template, {
verifyLink:
(req as any).ncSiteUrl +
`/email/verify/${user.email_verification_token}`,
}),
});
} catch (e) {
console.log(
'Warning : `mailSend` failed, Please configure emailClient configuration.'
);
}
await promisify((req as any).login.bind(req))(user);
const refreshToken = userService.randomTokenString();
await User.update(user.id, {
refresh_token: refreshToken,
email: user.email,
});
setTokenCookie(res, refreshToken);
user = (req as any).user;
await Audit.insert({
op_type: 'AUTHENTICATION',
op_sub_type: 'SIGNUP',
user: user.email,
description: `signed up `,
ip: (req as any).clientIp,
});
res.json({
token: userService.genJwt(user, Noco.getConfig()),
} as any);
export async function refreshToken(req: Request<any, any>, res): Promise<any> {
res.json(
await userService.refreshToken({
body: req.body,
req,
res,
})
);
}
async function successfulSignIn({
@ -156,6 +51,7 @@ async function successfulSignIn({
}
await promisify((req as any).login.bind(req))(user);
const refreshToken = userService.randomTokenString();
if (!user.token_version) {
@ -221,15 +117,6 @@ async function googleSignin(req, res, next) {
)(req, res, next);
}
function setTokenCookie(res: Response, token): void {
// create http only cookie with refresh token that expires in 7 days
const cookieOptions = {
httpOnly: true,
expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
};
res.cookie('refresh_token', token, cookieOptions);
}
async function me(req, res): Promise<any> {
res.json(req?.session?.passport?.user ?? {});
}
@ -284,35 +171,6 @@ async function emailVerification(req, res): Promise<any> {
res.json({ msg: 'Email verified successfully' });
}
async function refreshToken(req, res): Promise<any> {
try {
if (!req?.cookies?.refresh_token) {
return res.status(400).json({ msg: 'Missing refresh token' });
}
const user = await User.getByRefreshToken(req.cookies.refresh_token);
if (!user) {
return res.status(400).json({ msg: 'Invalid refresh token' });
}
const refreshToken = userService.randomTokenString();
await User.update(user.id, {
email: user.email,
refresh_token: refreshToken,
});
setTokenCookie(res, refreshToken);
res.json({
token: userService.genJwt(user, Noco.getConfig()),
} as any);
} catch (e) {
return res.status(400).json({ msg: e.message });
}
}
async function renderPasswordReset(req, res): Promise<any> {
try {
res.send(
@ -329,32 +187,15 @@ async function renderPasswordReset(req, res): Promise<any> {
const mapRoutes = (router) => {
// todo: old api - /auth/signup?tool=1
router.post(
'/auth/user/signup',
getAjvValidatorMw('swagger.json#/components/schemas/SignUpReq'),
catchError(signup)
);
router.post(
'/auth/user/signin',
getAjvValidatorMw('swagger.json#/components/schemas/SignInReq'),
catchError(signin)
);
router.post('/auth/user/signup', catchError(signup));
router.post('/auth/user/signin', catchError(signin));
router.get('/auth/user/me', extractProjectIdAndAuthenticate, catchError(me));
router.post(
'/auth/password/forgot',
getAjvValidatorMw('swagger.json#/components/schemas/ForgotPasswordReq'),
catchError(passwordForgot)
);
router.post('/auth/password/forgot', catchError(passwordForgot));
router.post('/auth/token/validate/:tokenId', catchError(tokenValidate));
router.post(
'/auth/password/reset/:tokenId',
getAjvValidatorMw('swagger.json#/components/schemas/PasswordResetReq'),
catchError(passwordReset)
);
router.post('/auth/password/reset/:tokenId', catchError(passwordReset));
router.post('/auth/email/validate/:tokenId', catchError(emailVerification));
router.post(
'/user/password/change',
getAjvValidatorMw('swagger.json#/components/schemas/PasswordChangeReq'),
ncMetaAclMw(passwordChange, 'passwordChange')
);
router.post('/auth/token/refresh', catchError(refreshToken));
@ -372,33 +213,20 @@ const mapRoutes = (router) => {
);
// deprecated APIs
router.post(
'/api/v1/db/auth/user/signup',
getAjvValidatorMw('swagger.json#/components/schemas/SignUpReq'),
catchError(signup)
);
router.post(
'/api/v1/db/auth/user/signin',
getAjvValidatorMw('swagger.json#/components/schemas/SignInReq'),
catchError(signin)
);
router.post('/api/v1/db/auth/user/signup', catchError(signup));
router.post('/api/v1/db/auth/user/signin', catchError(signin));
router.get(
'/api/v1/db/auth/user/me',
extractProjectIdAndAuthenticate,
catchError(me)
);
router.post(
'/api/v1/db/auth/password/forgot',
getAjvValidatorMw('swagger.json#/components/schemas/ForgotPasswordReq'),
catchError(passwordForgot)
);
router.post('/api/v1/db/auth/password/forgot', catchError(passwordForgot));
router.post(
'/api/v1/db/auth/token/validate/:tokenId',
catchError(tokenValidate)
);
router.post(
'/api/v1/db/auth/password/reset/:tokenId',
getAjvValidatorMw('swagger.json#/components/schemas/PasswordResetReq'),
catchError(passwordReset)
);
router.post(
@ -407,7 +235,6 @@ const mapRoutes = (router) => {
);
router.post(
'/api/v1/db/auth/password/change',
getAjvValidatorMw('swagger.json#/components/schemas/PasswordChangeReq'),
ncMetaAclMw(passwordChange, 'passwordChange')
);
router.post('/api/v1/db/auth/token/refresh', catchError(refreshToken));
@ -417,26 +244,14 @@ const mapRoutes = (router) => {
);
// new API
router.post(
'/api/v1/auth/user/signup',
getAjvValidatorMw('swagger.json#/components/schemas/SignUpReq'),
catchError(signup)
);
router.post(
'/api/v1/auth/user/signin',
getAjvValidatorMw('swagger.json#/components/schemas/SignInReq'),
catchError(signin)
);
router.post('/api/v1/auth/user/signup', catchError(signup));
router.post('/api/v1/auth/user/signin', catchError(signin));
router.get(
'/api/v1/auth/user/me',
extractProjectIdAndAuthenticate,
catchError(me)
);
router.post(
'/api/v1/auth/password/forgot',
getAjvValidatorMw('swagger.json#/components/schemas/ForgotPasswordReq'),
catchError(passwordForgot)
);
router.post('/api/v1/auth/password/forgot', catchError(passwordForgot));
router.post(
'/api/v1/auth/token/validate/:tokenId',
catchError(tokenValidate)
@ -451,11 +266,10 @@ const mapRoutes = (router) => {
);
router.post(
'/api/v1/auth/password/change',
getAjvValidatorMw('swagger.json#/components/schemas/PasswordChangeReq'),
ncMetaAclMw(passwordChange, 'passwordChange')
);
router.post('/api/v1/auth/token/refresh', catchError(refreshToken));
// respond with password reset page
router.get('/auth/password/reset/:tokenId', catchError(renderPasswordReset));
};
export { mapRoutes as userController };
export default mapRoutes;

16
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts

@ -576,7 +576,7 @@ const parseConditionV2 = async (
}
}
break;
case 'gt':
case 'gt': {
const gt_op = customWhereClause ? '<' : '>';
qb = qb.where(field, gt_op, val);
if (column.uidt === UITypes.Rating) {
@ -586,8 +586,9 @@ const parseConditionV2 = async (
}
}
break;
}
case 'ge':
case 'gte':
case 'gte': {
const ge_op = customWhereClause ? '<=' : '>=';
qb = qb.where(field, ge_op, val);
if (column.uidt === UITypes.Rating) {
@ -597,7 +598,8 @@ const parseConditionV2 = async (
}
}
break;
case 'lt':
}
case 'lt': {
const lt_op = customWhereClause ? '>' : '<';
qb = qb.where(field, lt_op, val);
if (column.uidt === UITypes.Rating) {
@ -607,8 +609,10 @@ const parseConditionV2 = async (
}
}
break;
}
case 'le':
case 'lte':
case 'lte': {
const le_op = customWhereClause ? '>=' : '<=';
qb = qb.where(field, le_op, val);
if (column.uidt === UITypes.Rating) {
@ -618,6 +622,7 @@ const parseConditionV2 = async (
}
}
break;
}
case 'in':
qb = qb.whereIn(
field,
@ -720,7 +725,7 @@ const parseConditionV2 = async (
case 'nbtw':
qb = qb.whereNotBetween(field, val.split(','));
break;
case 'isWithin':
case 'isWithin': {
let now = dayjs(new Date()).format(dateFormat).toString();
now = column.uidt === UITypes.Date ? now.substring(0, 10) : now;
switch (filter.comparison_sub_op) {
@ -737,6 +742,7 @@ const parseConditionV2 = async (
qb = qb.whereBetween(field, [now, val]);
break;
}
}
}
};
}

2
packages/nocodb/src/lib/db/sql-mgr/v2/SqlMgrv2Trans.ts

@ -34,7 +34,7 @@ export default class SqlMgrv2Trans extends SqlMgrv2 {
}
public async startTransaction(base: Base) {
const knex: XKnex = NcConnectionMgrv2.get(base);
const knex: XKnex = await NcConnectionMgrv2.get(base);
this.trx = await knex.transaction();
}

2
packages/nocodb/src/lib/db/sql-migrator/lib/KnexMigratorv2.ts

@ -384,7 +384,7 @@ export default class KnexMigratorv2 {
async _initDbWithSql(base: Base) {
const sqlClient = await this.getSqlClient(base);
const connectionConfig = base.getConnectionConfig();
const connectionConfig = await base.getConnectionConfig();
if (connectionConfig.client === 'oracledb') {
this.emit(
`${connectionConfig.client}: Creating DB if not exists ${connectionConfig.connection.user}`

2
packages/nocodb/src/lib/meta/api/helpers/populateMeta.ts

@ -22,7 +22,7 @@ export async function populateMeta(base: Base, project: Project): Promise<any> {
tablesCount: 0,
relationsCount: 0,
viewsCount: 0,
client: base?.getConnectionConfig()?.client,
client: (await base?.getConnectionConfig())?.client,
timeTaken: 0,
};

3
packages/nocodb/src/lib/meta/api/index.ts

@ -52,8 +52,7 @@ import passport from 'passport';
import crypto from 'crypto';
import swaggerController from '../../controllers/apiDocs';
import importController from '../../controllers/sync/importApis';
import syncSourceController from '../../controllers/sync';
import { importController, syncSourceController } from '../../controllers/sync';
import mapViewController from '../../controllers/views/mapView.ctl';
const clients: { [id: string]: Socket } = {};

4
packages/nocodb/src/lib/meta/helpers/extractProps.ts

@ -1,6 +1,6 @@
import DOMPurify from 'isomorphic-dompurify';
export function extractProps<T extends object>(
export function extractProps<T extends Record<string, any>>(
body: T,
props: string[]
): Partial<T> {
@ -11,7 +11,7 @@ export function extractProps<T extends object>(
}, {});
}
export function extractPropsAndSanitize<T extends object>(
export function extractPropsAndSanitize<T extends Record<string, any>>(
body: T,
props: string[]
): Partial<T> {

4
packages/nocodb/src/lib/meta/helpers/getColumnPropsFromUIDT.ts

@ -7,11 +7,11 @@ import {
import Base from '../../models/Base';
import Column from '../../models/Column';
export default function getColumnPropsFromUIDT(
export default async function getColumnPropsFromUIDT(
column: ColumnReqType & { altered?: number },
base: Base
) {
const sqlUi = SqlUiFactory.create(base.getConnectionConfig());
const sqlUi = SqlUiFactory.create(await base.getConnectionConfig());
const colProp = sqlUi.getDataTypeForUiType(
column as Column,

5
packages/nocodb/src/lib/models/Base.ts

@ -234,14 +234,15 @@ export default class Base implements BaseType {
}
}
public getConnectionConfig(): any {
// NC_DATA_DB is not available in community version
// make it return Promise to avoid conflicts
public getConnectionConfig(): Promise<any> {
if (this.is_meta) {
const metaConfig = Noco.getConfig()?.meta?.db;
const config = { ...metaConfig };
if (config.client === 'sqlite3') {
config.connection = metaConfig;
}
return config;
}

2
packages/nocodb/src/lib/models/Hook.ts

@ -26,7 +26,7 @@ export default class Hook implements HookType {
url?: string;
headers?: string;
condition?: BoolType;
notification?: string | object;
notification?: string | Record<string, any>;
retries?: number;
retry_interval?: number;
timeout?: number;

3
packages/nocodb/src/lib/models/KanbanView.ts

@ -1,5 +1,6 @@
import Noco from '../Noco';
import { BoolType, KanbanType, MetaType, UITypes } from 'nocodb-sdk';
import { UITypes } from 'nocodb-sdk';
import type { BoolType, KanbanType, MetaType } from 'nocodb-sdk';
import { CacheGetType, CacheScope, MetaTable } from '../utils/globals';
import View from './View';
import NocoCache from '../cache/NocoCache';

2
packages/nocodb/src/lib/models/KanbanViewColumn.ts

@ -3,7 +3,7 @@ import { CacheGetType, CacheScope, MetaTable } from '../utils/globals';
import View from './View';
import NocoCache from '../cache/NocoCache';
import { extractProps } from '../meta/helpers/extractProps';
import { BoolType, KanbanColumnType } from 'nocodb-sdk';
import type { BoolType, KanbanColumnType } from 'nocodb-sdk';
export default class KanbanViewColumn implements KanbanColumnType {
id: string;

2
packages/nocodb/src/lib/services/base.svc.ts

@ -8,7 +8,7 @@ import { populateMeta, validatePayload } from '../meta/api/helpers';
export async function baseGetWithConfig(param: { baseId: any }) {
const base = await Base.get(param.baseId);
base.config = base.getConnectionConfig();
base.config = await base.getConnectionConfig();
return base;
}

18
packages/nocodb/src/lib/services/column.svc.ts

@ -127,7 +127,7 @@ export async function columnUpdate(param: {
try {
// test the query to see if it is valid in db level
const dbDriver = NcConnectionMgrv2.get(base);
const dbDriver = await NcConnectionMgrv2.get(base);
await formulaQueryBuilderv2(colBody.formula, null, dbDriver, table);
} catch (e) {
console.error(e);
@ -167,16 +167,16 @@ export async function columnUpdate(param: {
} else if (
[UITypes.SingleSelect, UITypes.MultiSelect].includes(colBody.uidt)
) {
colBody = getColumnPropsFromUIDT(colBody, base);
colBody = await getColumnPropsFromUIDT(colBody, base);
const baseModel = await Model.getBaseModelSQL({
id: table.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
if (colBody.colOptions?.options) {
const supportedDrivers = ['mysql', 'mysql2', 'pg', 'mssql', 'sqlite3'];
const dbDriver = NcConnectionMgrv2.get(base);
const dbDriver = await NcConnectionMgrv2.get(base);
const driverType = dbDriver.clientType();
// MultiSelect to SingleSelect
@ -750,7 +750,7 @@ export async function columnUpdate(param: {
...colBody,
});
} else {
colBody = getColumnPropsFromUIDT(colBody, base);
colBody = await getColumnPropsFromUIDT(colBody, base);
const tableUpdateBody = {
...table,
tn: table.table_name,
@ -849,7 +849,7 @@ export async function columnAdd(param: {
const project = await base.getProject();
if (param.column.title || param.column.column_name) {
const dbDriver = NcConnectionMgrv2.get(base);
const dbDriver = await NcConnectionMgrv2.get(base);
const sqlClientType = dbDriver.clientType();
@ -932,7 +932,7 @@ export async function columnAdd(param: {
try {
// test the query to see if it is valid in db level
const dbDriver = NcConnectionMgrv2.get(base);
const dbDriver = await NcConnectionMgrv2.get(base);
await formulaQueryBuilderv2(colBody.formula, null, dbDriver, table);
} catch (e) {
console.error(e);
@ -947,7 +947,7 @@ export async function columnAdd(param: {
break;
default:
{
colBody = getColumnPropsFromUIDT(colBody, base);
colBody = await getColumnPropsFromUIDT(colBody, base);
if (colBody.uidt === UITypes.Duration) {
colBody.dtxp = '20';
// by default, colBody.dtxs is 2
@ -958,7 +958,7 @@ export async function columnAdd(param: {
if (
[UITypes.SingleSelect, UITypes.MultiSelect].includes(colBody.uidt)
) {
const dbDriver = NcConnectionMgrv2.get(base);
const dbDriver = await NcConnectionMgrv2.get(base);
const driverType = dbDriver.clientType();
const optionTitles = colBody.colOptions.options.map((el) =>
el.title.replace(/'/g, "''")

2
packages/nocodb/src/lib/services/dbData/bulkData.ts

@ -27,7 +27,7 @@ export async function executeBulkOperation<T extends BulkOperation>(
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
return await baseModel[param.operation].apply(null, param.options);
}

14
packages/nocodb/src/lib/services/dbData/dataAliasNested.svs.ts → packages/nocodb/src/lib/services/dbData/dataAliasNested.svc.ts

@ -25,7 +25,7 @@ export async function mmList(
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const column = await getColumnByIdOrName(param.columnName, model);
@ -63,7 +63,7 @@ export async function mmExcludedList(
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const column = await getColumnByIdOrName(param.columnName, model);
@ -105,7 +105,7 @@ export async function hmExcludedList(
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const column = await getColumnByIdOrName(param.columnName, model);
@ -147,7 +147,7 @@ export async function btExcludedList(
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const column = await getColumnByIdOrName(param.columnName, model);
@ -191,7 +191,7 @@ export async function hmList(
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const column = await getColumnByIdOrName(param.columnName, model);
@ -232,7 +232,7 @@ export async function relationDataRemove(
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const column = await getColumnByIdOrName(param.columnName, model);
@ -265,7 +265,7 @@ export async function relationDataAdd(
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const column = await getColumnByIdOrName(param.columnName, model);

4
packages/nocodb/src/lib/services/dbData/helpers.ts

@ -58,7 +58,7 @@ export async function extractXlsxData(view: View, req: Request) {
const baseModel = await Model.getBaseModelSQL({
id: view.model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const { offset, dbRows, elapsed } = await getDbRows({
@ -93,7 +93,7 @@ export async function extractCsvData(view: View, req: Request) {
const baseModel = await Model.getBaseModelSQL({
id: view.model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const { offset, dbRows, elapsed } = await getDbRows({

42
packages/nocodb/src/lib/services/dbData/index.ts

@ -30,7 +30,7 @@ export async function dataCount(param: PathParams & { query: any }) {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const countArgs: any = { ...param.query };
@ -53,7 +53,7 @@ export async function dataInsert(
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
return await baseModel.insert(param.body, null, param.cookie);
@ -68,7 +68,7 @@ export async function dataUpdate(
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
return await baseModel.updateByPk(
@ -87,7 +87,7 @@ export async function dataDelete(
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
// todo: Should have error http status code
@ -110,7 +110,7 @@ export async function getDataList(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const requestObj = await getAst({ model, query, view });
@ -158,7 +158,7 @@ export async function getFindOne(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const args: any = { ...query };
@ -192,7 +192,7 @@ export async function getDataGroupBy(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const listArgs: any = { ...query };
@ -215,7 +215,7 @@ export async function dataRead(
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const row = await baseModel.readByPk(param.rowId);
@ -242,7 +242,7 @@ export async function dataExist(
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
return await baseModel.exist(param.rowId);
@ -275,7 +275,7 @@ export async function getGroupedDataList(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const requestObj = await getAst({ model, query, view });
@ -353,7 +353,7 @@ export async function mmList(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const key = `${model.title}List`;
@ -411,7 +411,7 @@ export async function mmExcludedList(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const key = 'List';
@ -472,7 +472,7 @@ export async function hmExcludedList(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const key = 'List';
@ -533,7 +533,7 @@ export async function btExcludedList(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const key = 'List';
@ -594,7 +594,7 @@ export async function hmList(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const key = `${model.title}List`;
@ -646,7 +646,7 @@ export async function dataReadByViewId(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
return await nocoExecute(
@ -677,7 +677,7 @@ export async function dataInsertByViewId(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
return await baseModel.insert(param.body, null, param.cookie);
@ -698,7 +698,7 @@ export async function dataUpdateByViewId(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
return await baseModel.updateByPk(
@ -723,7 +723,7 @@ export async function dataDeleteByViewId(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
return await baseModel.delByPk(param.rowId, null, param.cookie);
@ -749,7 +749,7 @@ export async function relationDataDelete(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
await baseModel.removeChild({
@ -782,7 +782,7 @@ export async function relationDataAdd(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
await baseModel.addChild({

0
packages/nocodb/src/lib/services/ee/orgToken.svs.ts → packages/nocodb/src/lib/services/ee/orgToken.svc.ts

4
packages/nocodb/src/lib/services/index.ts

@ -27,7 +27,7 @@ export * as attachmentService from './attachment.svc';
export * as hookFilterService from './hookFilter.svc';
export * as dataService from './dbData';
export * as bulkDataService from './dbData/bulkData';
export * as dataAliasNestedService from './dbData/dataAliasNested.svs';
export * as dataAliasNestedService from './dbData/dataAliasNested.svc';
export * as cacheService from './cache.svc';
export * as auditService from './audit.svc';
export * as swaggerService from './apiDocs';
@ -35,4 +35,4 @@ export * as userService from './user';
export * as syncService from './sync';
export * from './public';
export * as orgTokenService from './orgToken.svc';
export * as orgTokenServiceEE from './ee/orgToken.svs';
export * as orgTokenServiceEE from './ee/orgToken.svc';

6
packages/nocodb/src/lib/services/public/index.ts

@ -1,5 +1,5 @@
import * as publicDataService from './publicData.svs';
import * as publicDataExportApis from './publicDataExport.svs';
import * as publicMetaService from './publicMeta.svs';
import * as publicDataService from './publicData.svc';
import * as publicDataExportApis from './publicDataExport.svc';
import * as publicMetaService from './publicMeta.svc';
export { publicDataService, publicDataExportApis, publicMetaService };

12
packages/nocodb/src/lib/services/public/publicData.svs.ts → packages/nocodb/src/lib/services/public/publicData.svc.ts

@ -49,7 +49,7 @@ export async function dataList(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const listArgs: any = { ...param.query };
@ -130,7 +130,7 @@ async function getGroupedDataList(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const requestObj = await getAst({ model, query: param.query, view });
@ -208,7 +208,7 @@ export async function dataInsert(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
await view.getViewWithInfo();
@ -306,7 +306,7 @@ export async function relDataList(param: {
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const requestObj = await getAst({
@ -362,7 +362,7 @@ export async function publicMmList(param: {
const baseModel = await Model.getBaseModelSQL({
id: view.fk_model_id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const key = `List`;
@ -427,7 +427,7 @@ export async function publicHmList(param: {
const baseModel = await Model.getBaseModelSQL({
id: view.fk_model_id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const key = `List`;

2
packages/nocodb/src/lib/services/public/publicDataExport.svs.ts → packages/nocodb/src/lib/services/public/publicDataExport.svc.ts

@ -45,7 +45,7 @@ export async function getDbRows(param: {
const baseModel = await Model.getBaseModelSQL({
id: param.model.id,
viewId: param.view?.id,
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
});
const requestObj = await getAst({

0
packages/nocodb/src/lib/services/public/publicMeta.svs.ts → packages/nocodb/src/lib/services/public/publicMeta.svc.ts

7
packages/nocodb/src/lib/services/sync/index.ts

@ -15,10 +15,9 @@ export async function syncCreate(param: {
projectId: string;
baseId?: string;
userId: string;
// todo: define type
syncPayload: Partial<SyncSource>;
}) {
T.emit('evt', { evt_type: 'webhooks:created' });
T.emit('evt', { evt_type: 'syncSource:created' });
const project = await Project.getWithInfo(param.projectId);
const sync = await SyncSource.insert({
@ -31,7 +30,7 @@ export async function syncCreate(param: {
}
export async function syncDelete(param: { syncId: string }) {
T.emit('evt', { evt_type: 'webhooks:deleted' });
T.emit('evt', { evt_type: 'syncSource:deleted' });
return await SyncSource.delete(param.syncId);
}
@ -39,7 +38,7 @@ export async function syncUpdate(param: {
syncId: string;
syncPayload: Partial<SyncSource>;
}) {
T.emit('evt', { evt_type: 'webhooks:updated' });
T.emit('evt', { evt_type: 'syncSource:updated' });
return await SyncSource.update(param.syncId, param.syncPayload);
}

55
packages/nocodb/src/lib/services/table.svc.ts

@ -2,6 +2,7 @@ import DOMPurify from 'isomorphic-dompurify';
import {
AuditOperationSubTypes,
AuditOperationTypes,
ColumnType,
isVirtualCol,
ModelTypes,
NormalColumnRequestType,
@ -314,6 +315,12 @@ export async function tableCreate(param: {
}) {
validatePayload('swagger.json#/components/schemas/TableReq', param.table);
const tableCreatePayLoad: Omit<TableReqType, 'columns'> & {
columns: (Omit<ColumnType, 'column_name' | 'title'> & { cn?: string })[];
} = {
...param.table,
};
const project = await Project.getWithInfo(param.projectId);
let base = project.bases[0];
@ -322,8 +329,8 @@ export async function tableCreate(param: {
}
if (
!param.table.table_name ||
(project.prefix && project.prefix === param.table.table_name)
!tableCreatePayLoad.table_name ||
(project.prefix && project.prefix === tableCreatePayLoad.table_name)
) {
NcError.badRequest(
'Missing table name `table_name` property in request body'
@ -331,15 +338,17 @@ export async function tableCreate(param: {
}
if (base.is_meta && project.prefix) {
if (!param.table.table_name.startsWith(project.prefix)) {
param.table.table_name = `${project.prefix}_${param.table.table_name}`;
if (!tableCreatePayLoad.table_name.startsWith(project.prefix)) {
tableCreatePayLoad.table_name = `${project.prefix}_${tableCreatePayLoad.table_name}`;
}
}
param.table.table_name = DOMPurify.sanitize(param.table.table_name);
tableCreatePayLoad.table_name = DOMPurify.sanitize(
tableCreatePayLoad.table_name
);
// validate table name
if (/^\s+|\s+$/.test(param.table.table_name)) {
if (/^\s+|\s+$/.test(tableCreatePayLoad.table_name)) {
NcError.badRequest(
'Leading or trailing whitespace not allowed in table names'
);
@ -347,7 +356,7 @@ export async function tableCreate(param: {
if (
!(await Model.checkTitleAvailable({
table_name: param.table.table_name,
table_name: tableCreatePayLoad.table_name,
project_id: project.id,
base_id: base.id,
}))
@ -355,9 +364,9 @@ export async function tableCreate(param: {
NcError.badRequest('Duplicate table name');
}
if (!param.table.title) {
param.table.title = getTableNameAlias(
param.table.table_name,
if (!tableCreatePayLoad.title) {
tableCreatePayLoad.title = getTableNameAlias(
tableCreatePayLoad.table_name,
project.prefix,
base
);
@ -365,7 +374,7 @@ export async function tableCreate(param: {
if (
!(await Model.checkAliasAvailable({
title: param.table.title,
title: tableCreatePayLoad.title,
project_id: project.id,
base_id: base.id,
}))
@ -387,7 +396,7 @@ export async function tableCreate(param: {
tableNameLengthLimit = 128;
}
if (param.table.table_name.length > tableNameLengthLimit) {
if (tableCreatePayLoad.table_name.length > tableNameLengthLimit) {
NcError.badRequest(`Table name exceeds ${tableNameLengthLimit} characters`);
}
@ -401,13 +410,16 @@ export async function tableCreate(param: {
}
}
param.table.columns = param.table.columns?.map((c) => ({
...getColumnPropsFromUIDT(c as any, base),
cn: c.column_name,
}));
tableCreatePayLoad.columns = await Promise.all(
param.table.columns?.map(async (c) => ({
...(await getColumnPropsFromUIDT(c as any, base)),
cn: c.column_name,
column_name: c.column_name,
}))
);
await sqlMgr.sqlOpPlus(base, 'tableCreate', {
...param.table,
tn: param.table.table_name,
...tableCreatePayLoad,
tn: tableCreatePayLoad.table_name,
});
const columns: Array<
@ -415,7 +427,8 @@ export async function tableCreate(param: {
cn: string;
system?: boolean;
}
> = (await sqlClient.columnList({ tn: param.table.table_name }))?.data?.list;
> = (await sqlClient.columnList({ tn: tableCreatePayLoad.table_name }))?.data
?.list;
const tables = await Model.list({
project_id: project.id,
@ -428,7 +441,7 @@ export async function tableCreate(param: {
op_type: AuditOperationTypes.TABLE,
op_sub_type: AuditOperationSubTypes.CREATED,
user: param.user?.email,
description: `created table ${param.table.table_name} with alias ${param.table.title} `,
description: `created table ${tableCreatePayLoad.table_name} with alias ${tableCreatePayLoad.title} `,
ip: param.req?.clientIp,
}).then(() => {});
@ -438,7 +451,7 @@ export async function tableCreate(param: {
// todo: type correction
const result = await Model.insert(project.id, base.id, {
...param.table,
...tableCreatePayLoad,
columns: columns.map((c, i) => {
const colMetaFromReq = param.table?.columns?.find(
(c1) => c.cn === c1.column_name

10
packages/nocodb/src/lib/services/user/helpers.ts

@ -2,6 +2,7 @@ import * as jwt from 'jsonwebtoken';
import crypto from 'crypto';
import User from '../../models/User';
import { NcConfig } from '../../../interface/config';
import { Response } from 'express';
export function genJwt(user: User, config: NcConfig) {
return jwt.sign(
@ -21,3 +22,12 @@ export function genJwt(user: User, config: NcConfig) {
export function randomTokenString(): string {
return crypto.randomBytes(40).toString('hex');
}
export function setTokenCookie(res: Response, token): void {
// create http only cookie with refresh token that expires in 7 days
const cookieOptions = {
httpOnly: true,
expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
};
res.cookie('refresh_token', token, cookieOptions);
}

164
packages/nocodb/src/lib/services/user/index.ts

@ -2,6 +2,7 @@ import {
PasswordChangeReqType,
PasswordForgotReqType,
PasswordResetReqType,
SignUpReqType,
UserType,
validatePassword,
} from 'nocodb-sdk';
@ -19,9 +20,10 @@ import NcPluginMgrv2 from '../../meta/helpers/NcPluginMgrv2';
import { Audit, Store, User } from '../../models';
import Noco from '../../Noco';
import { MetaTable } from '../../utils/globals';
import { randomTokenString } from './helpers';
import { genJwt, randomTokenString, setTokenCookie } from './helpers';
const { v4: uuidv4 } = require('uuid');
const { isEmail } = require('validator');
export async function registerNewUserIfAllowed({
firstname,
@ -136,7 +138,7 @@ export async function passwordForgot(param: {
req: any;
}): Promise<any> {
validatePayload(
'swagger.json#/components/schemas/ForgotPasswordReq',
'swagger.json#/components/schemas/PasswordForgotReq',
param.body
);
@ -296,5 +298,163 @@ export async function emailVerification(param: {
return true;
}
export async function refreshToken(param: {
body: SignUpReqType;
req: any;
res: any;
}): Promise<any> {
try {
if (!param.req?.cookies?.refresh_token) {
NcError.badRequest(`Missing refresh token`);
}
const user = await User.getByRefreshToken(param.req.cookies.refresh_token);
if (!user) {
NcError.badRequest(`Invalid refresh token`);
}
const refreshToken = randomTokenString();
await User.update(user.id, {
email: user.email,
refresh_token: refreshToken,
});
setTokenCookie(param.res, refreshToken);
return {
token: genJwt(user, Noco.getConfig()),
} as any;
} catch (e) {
NcError.badRequest(e.message);
}
}
export async function signup(param: {
body: SignUpReqType;
req: any;
res: any;
}): Promise<any> {
validatePayload('swagger.json#/components/schemas/SignUpReq', param.body);
const {
email: _email,
firstname,
lastname,
token,
ignore_subscribe,
} = param.req.body;
let { password } = param.req.body;
// validate password and throw error if password is satisfying the conditions
const { valid, error } = validatePassword(password);
if (!valid) {
NcError.badRequest(`Password : ${error}`);
}
if (!isEmail(_email)) {
NcError.badRequest(`Invalid email`);
}
const email = _email.toLowerCase();
let user = await User.getByEmail(email);
if (user) {
if (token) {
if (token !== user.invite_token) {
NcError.badRequest(`Invalid invite url`);
} else if (user.invite_token_expires < new Date()) {
NcError.badRequest(
'Expired invite url, Please contact super admin to get a new invite url'
);
}
} else {
// todo : opening up signup for timebeing
// return next(new Error(`Email '${email}' already registered`));
}
}
const salt = await promisify(bcrypt.genSalt)(10);
password = await promisify(bcrypt.hash)(password, salt);
const email_verification_token = uuidv4();
if (!ignore_subscribe) {
T.emit('evt_subscribe', email);
}
if (user) {
if (token) {
await User.update(user.id, {
firstname,
lastname,
salt,
password,
email_verification_token,
invite_token: null,
invite_token_expires: null,
email: user.email,
});
} else {
NcError.badRequest('User already exist');
}
} else {
await registerNewUserIfAllowed({
firstname,
lastname,
email,
salt,
password,
email_verification_token,
});
}
user = await User.getByEmail(email);
try {
const template = (await import('./ui/emailTemplates/verify')).default;
await (
await NcPluginMgrv2.emailAdapter()
).mailSend({
to: email,
subject: 'Verify email',
html: ejs.render(template, {
verifyLink:
(param.req as any).ncSiteUrl +
`/email/verify/${user.email_verification_token}`,
}),
});
} catch (e) {
console.log(
'Warning : `mailSend` failed, Please configure emailClient configuration.'
);
}
await promisify((param.req as any).login.bind(param.req))(user);
const refreshToken = randomTokenString();
await User.update(user.id, {
refresh_token: refreshToken,
email: user.email,
});
setTokenCookie(param.res, refreshToken);
user = (param.req as any).user;
await Audit.insert({
op_type: 'AUTHENTICATION',
op_sub_type: 'SIGNUP',
user: user.email,
description: `signed up `,
ip: (param.req as any).clientIp,
});
return {
token: genJwt(user, Noco.getConfig()),
} as any;
}
export * from './helpers';
export { default as initAdminFromEnv } from './initAdminFromEnv';

105
packages/nocodb/src/lib/utils/common/NcConnectionMgrv2.ts

@ -1,10 +1,5 @@
import SqlClientFactory from '../../db/sql-client/lib/SqlClientFactory';
import { XKnex } from '../../db/sql-data-mapper';
// import { NcConfig } from '../../../interface/config';
// import fs from 'fs';
// import Knex from 'knex';
// import NcMetaIO from '../meta/NcMetaIO';
import {
defaultConnectionConfig,
defaultConnectionOptions,
@ -19,12 +14,6 @@ export default class NcConnectionMgrv2 {
};
} = {};
// private static metaKnex: NcMetaIO;
//
// public static setXcMeta(ncMeta: NcMetaIO) {
// this.metaKnex = ncMeta;
// }
public static async destroyAll() {
for (const projectId in this.connectionRefs) {
for (const baseId in this.connectionRefs[projectId]) {
@ -60,7 +49,9 @@ export default class NcConnectionMgrv2 {
}
}
public static get(base: Base): XKnex {
// NC_DATA_DB is not available in community version
// make it return Promise<XKnex> to avoid conflicts
public static async get(base: Base): Promise<XKnex> {
if (base.is_meta) return Noco.ncMeta.knex;
if (this.connectionRefs?.[base.project_id]?.[base.id]) {
@ -68,76 +59,26 @@ export default class NcConnectionMgrv2 {
}
this.connectionRefs[base.project_id] =
this.connectionRefs?.[base.project_id] || {};
// if (?.prefix && this.metaKnex) {
// this.connectionRefs[projectId][env][dbAlias] = this.metaKnex?.knex;
// } else {
// const connectionConfig = this.getConnectionConfig(config, env, dbAlias);
const connectionConfig = base.getConnectionConfig();
//
// if (
// connectionConfig?.connection?.ssl &&
// typeof connectionConfig?.connection?.ssl === 'object'
// ) {
// if (
// connectionConfig.connection.ssl.caFilePath &&
// !connectionConfig.connection.ssl.ca
// ) {
// connectionConfig.connection.ssl.ca = fs
// .readFileSync(connectionConfig.connection.ssl.caFilePath)
// .toString();
// }
// if (
// connectionConfig.connection.ssl.keyFilePath &&
// !connectionConfig.connection.ssl.key
// ) {
// connectionConfig.connection.ssl.key = fs
// .readFileSync(connectionConfig.connection.ssl.keyFilePath)
// .toString();
// }
// if (
// connectionConfig.connection.ssl.certFilePath &&
// !connectionConfig.connection.ssl.cert
// ) {
// connectionConfig.connection.ssl.cert = fs
// .readFileSync(connectionConfig.connection.ssl.certFilePath)
// .toString();
// }
// }
//
// const isSqlite = connectionConfig?.client === 'sqlite3';
//
// if (connectionConfig?.connection?.port) {
// connectionConfig.connection.port = +connectionConfig.connection.port;
// }
this.connectionRefs[base.project_id][base.id] = XKnex(
// isSqlite
// ? (connectionConfig.connection as Knex.Config)
// :
{
...defaultConnectionOptions,
...connectionConfig,
connection: {
...defaultConnectionConfig,
...connectionConfig.connection,
typeCast(_field, next) {
const res = next();
if (res instanceof Buffer) {
return [...res]
.map((v) => ('00' + v.toString(16)).slice(-2))
.join('');
}
return res;
},
},
} as any
);
// if (isSqlite) {
// this.connectionRefs[projectId][env][dbAlias]
// .raw(`PRAGMA journal_mode=WAL;`)
// .then(() => {});
// }
const connectionConfig = await base.getConnectionConfig();
this.connectionRefs[base.project_id][base.id] = XKnex({
...defaultConnectionOptions,
...connectionConfig,
connection: {
...defaultConnectionConfig,
...connectionConfig.connection,
typeCast(_field, next) {
const res = next();
if (res instanceof Buffer) {
return [...res]
.map((v) => ('00' + v.toString(16)).slice(-2))
.join('');
}
return res;
},
},
} as any);
return this.connectionRefs[base.project_id][base.id];
}
@ -150,10 +91,10 @@ export default class NcConnectionMgrv2 {
// }
public static async getSqlClient(base: Base, _knex = null) {
const knex = _knex || this.get(base);
const knex = _knex || (await this.get(base));
return SqlClientFactory.create({
knex,
...base.getConnectionConfig(),
...(await base.getConnectionConfig()),
});
}
}

2
packages/nocodb/src/lib/version-upgrader/ncAttachmentUpgrader.ts

@ -66,7 +66,7 @@ export default async function ({ ncMeta }: NcUpgraderCtx) {
const knex: Knex = base.is_meta
? ncMeta.knexConnection
: NcConnectionMgrv2.get(base);
: await NcConnectionMgrv2.get(base);
const models = await base.getModels(ncMeta);
// used in timeout error message

2
packages/nocodb/src/lib/version-upgrader/ncAttachmentUpgrader_0104002.ts

@ -58,7 +58,7 @@ export default async function ({ ncMeta }: NcUpgraderCtx) {
const knex: Knex = base.is_meta
? ncMeta.knexConnection
: NcConnectionMgrv2.get(base);
: await NcConnectionMgrv2.get(base);
const models = await base.getModels(ncMeta);
// used in timeout error message

44
packages/nocodb/src/schema/swagger.json

@ -129,7 +129,11 @@
"Example 1": {
"value": {
"email": "user@example.com",
"password": "password123456789"
"password": "password123456789",
"firstname": "Alice",
"lastname": "Smith",
"token": null,
"ignore_subscribe": 0
}
}
}
@ -13359,9 +13363,15 @@
"examples": [
{
"email": "user@example.com",
"password": "password123456789"
"password": "password123456789",
"firstname": "Alice",
"lastname": "Smith",
"token": null,
"ignore_subscribe": 0
}
],
"title": "Signup Request Model",
"type": "object",
"properties": {
"email": {
"description": "Email address of the user",
@ -13374,11 +13384,35 @@
"example": "password123456789",
"minLength": 8,
"type": "string"
},
"firstname": {
"$ref": "#/components/schemas/StringOrNull",
"x-stoplight": {
"id": "lblivgs8wcsm1"
}
},
"lastname": {
"$ref": "#/components/schemas/StringOrNull",
"x-stoplight": {
"id": "d4341r35tucq3"
}
},
"token": {
"$ref": "#/components/schemas/StringOrNull",
"x-stoplight": {
"id": "otw9jgnr9n7c4"
},
"description": "Sign Up Token. Used for invitation."
},
"ignore_subscribe": {
"$ref": "#/components/schemas/Bool",
"x-stoplight": {
"id": "g7ge6mc6vdsds"
},
"description": "Ignore Subscription"
}
},
"required": ["email", "password"],
"title": "Signup Request Model",
"type": "object"
"required": ["email", "password"]
},
"Sort": {
"description": "Model for Sort",

2
packages/nocodb/tests/unit/factory/row.ts

@ -218,7 +218,7 @@ const listRow = async ({
const bases = await project.getBases();
const baseModel = await Model.getBaseModelSQL({
id: table.id,
dbDriver: NcConnectionMgrv2.get(bases[0]!),
dbDriver: await NcConnectionMgrv2.get(bases[0]!),
});
const ignorePagination = !options;

8
packages/nocodb/tests/unit/model/tests/baseModelSql.test.ts

@ -31,7 +31,7 @@ function baseModelSqlTests() {
const base = await Base.get(table.base_id);
baseModelSql = new BaseModelSqlv2({
dbDriver: NcConnectionMgrv2.get(base),
dbDriver: await NcConnectionMgrv2.get(base),
model: table,
view
})
@ -347,7 +347,7 @@ function baseModelSqlTests() {
);
const childBaseModel = new BaseModelSqlv2({
dbDriver: NcConnectionMgrv2.get(await Base.get(table.base_id)),
dbDriver: await NcConnectionMgrv2.get(await Base.get(table.base_id)),
model: childTable,
view
})
@ -406,7 +406,7 @@ function baseModelSqlTests() {
});
const childBaseModel = new BaseModelSqlv2({
dbDriver: NcConnectionMgrv2.get(await Base.get(table.base_id)),
dbDriver: await NcConnectionMgrv2.get(await Base.get(table.base_id)),
model: childTable,
view
})
@ -473,7 +473,7 @@ function baseModelSqlTests() {
});
const childBaseModel = new BaseModelSqlv2({
dbDriver: NcConnectionMgrv2.get(await Base.get(table.base_id)),
dbDriver: await NcConnectionMgrv2.get(await Base.get(table.base_id)),
model: childTable,
view
})

2
tests/playwright/tests/filters.spec.ts

@ -9,7 +9,7 @@ import { rowMixedValue } from '../setup/xcdb-records';
let dashboard: DashboardPage, toolbar: ToolbarPage;
let context: any;
let api: Api<any>;
let records = [];
let records: Record<string, any>;
const skipList = {
Number: ['is null', 'is not null'],

Loading…
Cancel
Save