Browse Source

feat(testing): Synced to develop regression, reverted non required backend changes and added await to expect poll

pull/3848/head
Muhammed Mustafa 2 years ago
parent
commit
ad8d8a0f76
  1. 2
      packages/nocodb/.gitignore
  2. 15
      packages/nocodb/src/lib/Noco.ts
  3. 208
      packages/nocodb/src/lib/db/sql-client/lib/sqlite/SqliteClient.ts
  4. 182
      packages/nocodb/src/lib/services/user/UserCreatorService.ts
  5. 94
      packages/nocodb/src/lib/services/user/ui/auth/emailVerify.ts
  6. 128
      packages/nocodb/src/lib/services/user/ui/auth/resetPassword.ts
  7. 193
      packages/nocodb/src/lib/services/user/ui/emailTemplates/forgotPassword.ts
  8. 231
      packages/nocodb/src/lib/services/user/ui/emailTemplates/invite.ts
  9. 230
      packages/nocodb/src/lib/services/user/ui/emailTemplates/verify.ts
  10. 6
      packages/nocodb/src/lib/utils/NcConfigFactory.ts
  11. 65
      packages/nocodb/src/run/testDocker.ts
  12. 2
      scripts/playwright/pages/Dashboard/Grid/index.ts
  13. 2
      scripts/playwright/tests/linkToAnotherRecord.spec.ts

2
packages/nocodb/.gitignore vendored

@ -18,6 +18,4 @@ noco.db*
/docker/main.js
test_meta.db
test_sakila.db
test_noco.db-shm
test_noco.db-wal
.env

15
packages/nocodb/src/lib/Noco.ts

@ -43,7 +43,6 @@ import * as http from 'http';
import weAreHiring from './utils/weAreHiring';
import getInstance from './utils/getInstance';
import initAdminFromEnv from './meta/api/userApi/initAdminFromEnv';
import UserCreatorService from './services/user/UserCreatorService';
const log = debug('nc:app');
require('dotenv').config();
@ -277,20 +276,6 @@ export default class Noco {
});
Tele.emit('evt_app_started', await User.count());
weAreHiring();
if (process.env.TEST === 'true') {
if (!(await User.getByEmail('user@nocodb.com'))) {
const service = new UserCreatorService({
email: 'user@nocodb.com',
password: 'Password123.',
ignoreSubscribe: true,
clientInfo: {} as any,
nocoConfig: this.config,
});
await service.process();
}
}
return this.router;
}

208
packages/nocodb/src/lib/db/sql-client/lib/sqlite/SqliteClient.ts

@ -1443,107 +1443,6 @@ class SqliteClient extends KnexClient {
return result;
}
private async _tableUpdate(args) {
const result = new Result();
args.table = args.tn;
const originalColumns = args.originalColumns;
args.connectionConfig = this._connectionConfig;
args.sqlClient = this.sqlClient;
let upQuery = '';
let downQuery = '';
for (let i = 0; i < args.columns.length; ++i) {
const oldColumn = lodash.find(originalColumns, {
cn: args.columns[i].cno,
});
if (args.columns[i].altered & 4) {
// col remove
upQuery += await this.alterTableRemoveColumn(
args.table,
args.columns[i],
oldColumn,
upQuery
);
downQuery += this.alterTableAddColumn(
args.table,
oldColumn,
args.columns[i],
downQuery
);
} else if (args.columns[i].altered & 2 || args.columns[i].altered & 8) {
// col edit
upQuery += this.alterTableChangeColumn(
args.table,
args.columns[i],
oldColumn,
upQuery
);
downQuery += ';';
// downQuery += this.alterTableChangeColumn(
// args.table,
// oldColumn,
// args.columns[i],
// downQuery,
// this.sqlClient
// );
} else if (args.columns[i].altered & 1) {
// col addition
upQuery += this.alterTableAddColumn(
args.table,
args.columns[i],
oldColumn,
upQuery
);
downQuery += ';';
// downQuery += alterTableRemoveColumn(
// args.table,
// args.columns[i],
// oldColumn,
// downQuery,
// this.sqlClient
// );
}
}
upQuery += this.alterTablePK(
args.columns,
args.originalColumns,
upQuery,
this.sqlClient
);
//downQuery += alterTablePK(args.originalColumns, args.columns, downQuery);
if (upQuery) {
//upQuery = `ALTER TABLE ${args.columns[0].tn} ${upQuery};`;
//downQuery = `ALTER TABLE ${args.columns[0].tn} ${downQuery};`;
}
await Promise.all(
upQuery.split(';').map(async (query) => {
if (query.trim().length) await this.sqlClient.raw(query);
})
);
// await this.sqlClient.raw(upQuery);
console.log(upQuery);
const afterUpdate = await this.afterTableUpdate(args);
result.data.object = {
upStatement: [
{ sql: this.querySeparator() + upQuery },
...afterUpdate.upStatement,
],
downStatement: [{ sql: ';' }],
};
return result;
}
/**
*
* @param {Object} - args
@ -1573,24 +1472,109 @@ class SqliteClient extends KnexClient {
*/
async tableUpdate(args) {
const _func = this.tableUpdate.name;
const result = new Result();
log.api(`${_func}:args:`, args);
// for (let retry = 0; retry < 3; retry++) {
// try {
// return await this._tableUpdate(args);
// } catch (e) {
// console.log('retrying:tableUpdate', e);
// }
// // Wait for 300ms
// await new Promise((resolve) => setTimeout(resolve, 300));
// }
try {
return await this._tableUpdate(args);
args.table = args.tn;
const originalColumns = args.originalColumns;
args.connectionConfig = this._connectionConfig;
args.sqlClient = this.sqlClient;
let upQuery = '';
let downQuery = '';
for (let i = 0; i < args.columns.length; ++i) {
const oldColumn = lodash.find(originalColumns, {
cn: args.columns[i].cno,
});
if (args.columns[i].altered & 4) {
// col remove
upQuery += await this.alterTableRemoveColumn(
args.table,
args.columns[i],
oldColumn,
upQuery
);
downQuery += this.alterTableAddColumn(
args.table,
oldColumn,
args.columns[i],
downQuery
);
} else if (args.columns[i].altered & 2 || args.columns[i].altered & 8) {
// col edit
upQuery += this.alterTableChangeColumn(
args.table,
args.columns[i],
oldColumn,
upQuery
);
downQuery += ';';
// downQuery += this.alterTableChangeColumn(
// args.table,
// oldColumn,
// args.columns[i],
// downQuery,
// this.sqlClient
// );
} else if (args.columns[i].altered & 1) {
// col addition
upQuery += this.alterTableAddColumn(
args.table,
args.columns[i],
oldColumn,
upQuery
);
downQuery += ';';
// downQuery += alterTableRemoveColumn(
// args.table,
// args.columns[i],
// oldColumn,
// downQuery,
// this.sqlClient
// );
}
}
upQuery += this.alterTablePK(
args.columns,
args.originalColumns,
upQuery,
this.sqlClient
);
//downQuery += alterTablePK(args.originalColumns, args.columns, downQuery);
if (upQuery) {
//upQuery = `ALTER TABLE ${args.columns[0].tn} ${upQuery};`;
//downQuery = `ALTER TABLE ${args.columns[0].tn} ${downQuery};`;
}
await Promise.all(
upQuery.split(';').map(async (query) => {
if (query.trim().length) await this.sqlClient.raw(query);
})
);
// await this.sqlClient.raw(upQuery);
console.log(upQuery);
const afterUpdate = await this.afterTableUpdate(args);
result.data.object = {
upStatement: [
{ sql: this.querySeparator() + upQuery },
...afterUpdate.upStatement,
],
downStatement: [{ sql: ';' }],
};
} catch (e) {
log.ppe(e, _func);
throw e;
}
return result;
}
/**

182
packages/nocodb/src/lib/services/user/UserCreatorService.ts

@ -1,182 +0,0 @@
import { validatePassword } from 'nocodb-sdk';
import { promisify } from 'util';
import { NcError } from '../../meta/helpers/catchError';
import User from '../../models/User';
const { isEmail } = require('validator');
import bcrypt from 'bcryptjs';
const { v4: uuidv4 } = require('uuid');
import { Tele } from 'nc-help';
import { randomTokenString } from '../../meta/helpers/stringHelpers';
import { genJwt } from '../../meta/api/userApi/helpers';
import Audit from '../../models/Audit';
import NcPluginMgrv2 from '../../meta/helpers/NcPluginMgrv2';
import * as ejs from 'ejs';
import { NcConfig } from '../../../interface/config';
export default class UserCreatorService {
private readonly email: string;
private readonly password: string;
private readonly firstName: string;
private readonly lastName: string;
private readonly token: string;
private readonly ignoreSubscribe: boolean;
private readonly clientInfo: any;
private readonly nocoConfig: NcConfig;
constructor(args: {
email: string;
password: string;
firstName?: string;
lastName?: string;
token?: string;
ignoreSubscribe?: boolean;
clientInfo: any;
nocoConfig: NcConfig;
}) {
this.email = args.email;
this.password = args.password;
this.firstName = args.firstName;
this.lastName = args.lastName;
this.token = args.token;
this.ignoreSubscribe = args.ignoreSubscribe;
this.clientInfo = args.clientInfo;
this.nocoConfig = args.nocoConfig;
}
async process() {
const {
email: _email,
password,
firstName,
lastName,
clientInfo,
ignoreSubscribe,
token,
nocoConfig,
} = this;
// 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();
const 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);
const passwordHash = await promisify(bcrypt.hash)(password, salt);
const email_verification_token = uuidv4();
if (!ignoreSubscribe) {
Tele.emit('evt_subscribe', email);
}
if (user) {
if (token) {
await User.update(user.id, {
firstname: firstName,
lastname: lastName,
salt,
password: passwordHash,
email_verification_token,
invite_token: null,
invite_token_expires: null,
email: user.email,
});
} else {
NcError.badRequest('User already exist');
}
} else {
let roles = 'user';
if (await User.isFirst()) {
roles = 'user,super';
// todo: update in nc_store
// roles = 'owner,creator,editor'
Tele.emit('evt', {
evt_type: 'project:invite',
count: 1,
});
} else {
if (process.env.NC_INVITE_ONLY_SIGNUP) {
NcError.badRequest('Not allowed to signup, contact super admin.');
} else {
roles = 'user_new';
}
}
const token_version = randomTokenString();
await User.insert({
firstname: firstName,
lastname: lastName,
email,
salt,
password: passwordHash,
email_verification_token,
roles,
token_version,
});
}
const createdUser = 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:
createdUser.ncSiteUrl +
`/email/verify/${createdUser.email_verification_token}`,
}),
});
} catch (e) {
console.log(
'Warning : `mailSend` failed, Please configure emailClient configuration.'
);
}
const refreshToken = randomTokenString();
await User.update(createdUser.id, {
refresh_token: refreshToken,
email: createdUser.email,
});
await Audit.insert({
op_type: 'AUTHENTICATION',
op_sub_type: 'SIGNUP',
user: createdUser.email,
description: `signed up `,
ip: clientInfo.clientIp,
});
return {
token: genJwt(createdUser, nocoConfig),
refreshToken,
};
}
}

94
packages/nocodb/src/lib/services/user/ui/auth/emailVerify.ts

@ -1,94 +0,0 @@
export default `<!DOCTYPE html>
<html>
<head>
<title>NocoDB - Verify Email</title>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.min.js" integrity="sha512-XdUZ5nrNkVySQBnnM5vzDqHai823Spoq1W3pJoQwomQja+o4Nw0Ew1ppxo5bhF2vMug6sfibhKWcNJsG8Vj9tg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<div id="app">
<v-app>
<v-container>
<v-row class="justify-center">
<v-col class="col-12 col-md-6">
<v-alert v-if="valid" type="success">
Email verified successfully!
</v-alert>
<v-alert v-else-if="errMsg" type="error">
{{errMsg}}
</v-alert>
<template v-else>
<v-skeleton-loader type="heading"></v-skeleton-loader>
</template>
</v-col>
</v-row>
</v-container>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<script>
var app = new Vue({
el: '#app',
vuetify: new Vuetify(),
data: {
valid: null,
errMsg: null,
validForm: false,
token: <%- token %>,
greeting: 'Password Reset',
formdata: {
password: '',
newPassword: ''
},
success: false
},
methods: {},
async created() {
try {
const valid = (await axios.post('<%- baseUrl %>/api/v1/auth/email/validate/' + this.token)).data;
this.valid = !!valid;
} catch (e) {
this.valid = false;
if(e.response && e.response.data && e.response.data.msg){
this.errMsg = e.response.data.msg;
}else{
this.errMsg = 'Some error occurred';
}
}
}
})
</script>
</body>
</html>`;
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
* @author Wing-Kam Wong <wingkwong.code@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

128
packages/nocodb/src/lib/services/user/ui/auth/resetPassword.ts

@ -1,128 +0,0 @@
export default `<!DOCTYPE html>
<html>
<head>
<title>NocoDB - Reset Password</title>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.min.js" integrity="sha512-XdUZ5nrNkVySQBnnM5vzDqHai823Spoq1W3pJoQwomQja+o4Nw0Ew1ppxo5bhF2vMug6sfibhKWcNJsG8Vj9tg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<div id="app">
<v-app>
<v-container>
<v-row class="justify-center">
<v-col class="col-12 col-md-6">
<v-alert v-if="success" type="success">
Password reset successful!
</v-alert>
<template v-else>
<v-form ref="form" v-model="validForm" v-if="valid === true" ref="formType" class="ma-auto"
lazy-validation>
<v-text-field
name="input-10-2"
label="New password"
type="password"
v-model="formdata.password"
:rules="[v => !!v || 'Password is required']"
></v-text-field>
<v-text-field
name="input-10-2"
type="password"
label="Confirm new password"
v-model="formdata.newPassword"
:rules="[v => !!v || 'Password is required', v => v === formdata.password || 'Password mismatch']"
></v-text-field>
<v-btn
:disabled="!validForm"
large
@click="resetPassword"
>
RESET PASSWORD
</v-btn>
</v-form>
<div v-else-if="valid === false">Not a valid url</div>
<div v-else>
<v-skeleton-loader type="actions"></v-skeleton-loader>
</div>
</template>
</v-col>
</v-row>
</v-container>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<script>
var app = new Vue({
el: '#app',
vuetify: new Vuetify(),
data: {
valid: null,
validForm: false,
token: <%- token %>,
greeting: 'Password Reset',
formdata: {
password: '',
newPassword: ''
},
success: false
},
methods: {
async resetPassword() {
if (this.$refs.form.validate()) {
try {
const res = await axios.post('<%- baseUrl %>api/v1/db/auth/password/reset/' + this.token, {
...this.formdata
});
this.success = true;
} catch (e) {
alert('Some error occured')
}
}
}
},
async created() {
try {
const valid = (await axios.post('<%- baseUrl %>api/v1/db/auth/token/validate/' + this.token)).data;
this.valid = !!valid;
} catch (e) {
this.valid = false;
}
}
})
</script>
</body>
</html>`;
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
* @author Wing-Kam Wong <wingkwong.code@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

193
packages/nocodb/src/lib/services/user/ui/emailTemplates/forgotPassword.ts

@ -1,193 +0,0 @@
export default `<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Simple Transactional Email</title>
<style>
@media only screen and (max-width: 620px) {
table[class=body] h1 {
font-size: 28px !important;
margin-bottom: 10px !important;
}
table[class=body] p,
table[class=body] ul,
table[class=body] ol,
table[class=body] td,
table[class=body] span,
table[class=body] a {
font-size: 16px !important;
}
table[class=body] .wrapper,
table[class=body] .article {
padding: 10px !important;
}
table[class=body] .content {
padding: 0 !important;
}
table[class=body] .container {
padding: 0 !important;
width: 100% !important;
}
table[class=body] .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
table[class=body] .btn table {
width: 100% !important;
}
table[class=body] .btn a {
width: 100% !important;
}
table[class=body] .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important;
}
}
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
#MessageViewBody a {
color: inherit;
text-decoration: none;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
line-height: inherit;
}
.btn-primary table td:hover {
background-color: #34495e !important;
}
.btn-primary a:hover {
background-color: #34495e !important;
border-color: #34495e !important;
}
}
</style>
</head>
<body class="" style="background-color: #f6f6f6; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">This is preheader text. Some clients will show this text as a preview.</span>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f6f6f6; width: 100%;" width="100%" bgcolor="#f6f6f6">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
<td class="container" style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; max-width: 580px; padding: 10px; width: 580px; margin: 0 auto;" width="580" valign="top">
<div class="content" style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 580px; padding: 10px;">
<!-- START CENTERED WHITE CONTAINER -->
<table role="presentation" class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background: #ffffff; border-radius: 3px; width: 100%;" width="100%">
<!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;" valign="top">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">Hi,</p>
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">To change your NocoDB account password click the following link.</p>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; box-sizing: border-box; width: 100%;" width="100%">
<tbody>
<tr>
<td align="left" style="font-family: sans-serif; font-size: 14px; vertical-align: top; padding-bottom: 15px;" valign="top">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
<tbody>
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top; border-radius: 5px; text-align: center; background-color: #3498db;" valign="top" align="center" bgcolor="#1088ff"> <a href="<%- resetLink %>" target="_blank" style="border: solid 1px rgb(23, 139, 255); border-radius: 5px; box-sizing: border-box; cursor: pointer; display: inline-block; font-size: 14px; font-weight: bold; margin: 0; padding: 12px 25px; text-decoration: none; text-transform: capitalize; background-color: rgb(23, 139, 255); border-color: #3498db; color: #ffffff;">Reset Password</a> </td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">Thanks regards NocoDB.</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
<!-- END CENTERED WHITE CONTAINER -->
<!-- START FOOTER -->
<div class="footer" style="clear: both; margin-top: 10px; text-align: center; width: 100%;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
<tr>
<td class="content-block" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;" valign="top" align="center">
<span class="apple-link" style="color: #999999; font-size: 12px; text-align: center;"></span>
<!-- <br> Don't like these emails? <a href="http://i.imgur.com/CScmqnj.gif">Unsubscribe</a>.-->
</td>
</tr>
<tr>
<td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;" valign="top" align="center">
<a href="http://nocodb.com/">NocoDB</a>
<!-- Powered by <a href="http://htmlemail.io">HTMLemail</a>.-->
</td>
</tr>
</table>
</div>
<!-- END FOOTER -->
</div>
</td>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
</tr>
</table>
</body>
</html>
`;
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

231
packages/nocodb/src/lib/services/user/ui/emailTemplates/invite.ts

@ -1,231 +0,0 @@
export default `<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Simple Transactional Email</title>
<style>
@media only screen and (max-width: 620px) {
table[class=body] h1 {
font-size: 28px !important;
margin-bottom: 10px !important;
}
table[class=body] p,
table[class=body] ul,
table[class=body] ol,
table[class=body] td,
table[class=body] span,
table[class=body] a {
font-size: 16px !important;
}
table[class=body] .wrapper,
table[class=body] .article {
padding: 10px !important;
}
table[class=body] .content {
padding: 0 !important;
}
table[class=body] .container {
padding: 0 !important;
width: 100% !important;
}
table[class=body] .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
table[class=body] .btn table {
width: 100% !important;
}
table[class=body] .btn a {
width: 100% !important;
}
table[class=body] .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important;
}
}
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
#MessageViewBody a {
color: inherit;
text-decoration: none;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
line-height: inherit;
}
.btn-primary table td:hover {
background-color: #34495e !important;
}
.btn-primary a:hover {
background-color: #34495e !important;
border-color: #34495e !important;
}
}
</style>
</head>
<body class=""
style="background-color: #f6f6f6; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
<span class="preheader"
style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">This is preheader text. Some clients will show this text as a preview.</span>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f6f6f6; width: 100%;"
width="100%" bgcolor="#f6f6f6">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
<td class="container"
style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; max-width: 580px; padding: 10px; width: 580px; margin: 0 auto;"
width="580" valign="top">
<div class="content"
style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 580px; padding: 10px;">
<!-- START CENTERED WHITE CONTAINER -->
<table role="presentation" class="main"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background: #ffffff; border-radius: 3px; width: 100%;"
width="100%">
<!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper"
style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;"
valign="top">
<table role="presentation" border="0" cellpadding="0" cellspacing="0"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;"
width="100%">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;"
valign="top">
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">
Hi,</p>
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">
I invited you to be "<%- roles -%>" of the NocoDB project "<%- projectName %>".
Click the button below to to accept my invitation.</p>
<table role="presentation" border="0" cellpadding="0" cellspacing="0"
class="btn btn-primary"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; box-sizing: border-box; width: 100%;"
width="100%">
<tbody>
<tr>
<td align="left"
style="font-family: sans-serif; font-size: 14px; vertical-align: top; padding-bottom: 15px;"
valign="top">
<table role="presentation" border="0" cellpadding="0"
cellspacing="0"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
<tbody>
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top; border-radius: 5px; text-align: center; background-color: #3498db;"
valign="top" align="center" bgcolor="#1088ff"><a
href="<%- signupLink %>" target="_blank"
style="border: solid 1px rgb(23, 139, 255); border-radius: 5px; box-sizing: border-box; cursor: pointer; display: inline-block; font-size: 14px; font-weight: bold; margin: 0; padding: 12px 25px; text-decoration: none; text-transform: capitalize; background-color: rgb(23, 139, 255); border-color: #3498db; color: #ffffff;">Signup</a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">
Thanks regards <%- adminEmail %>.</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
<!-- END CENTERED WHITE CONTAINER -->
<!-- START FOOTER -->
<div class="footer" style="clear: both; margin-top: 10px; text-align: center; width: 100%;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;"
width="100%">
<tr>
<td class="content-block"
style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;"
valign="top" align="center">
<span class="apple-link"
style="color: #999999; font-size: 12px; text-align: center;"></span>
<!-- <br> Don't like these emails? <a href="http://i.imgur.com/CScmqnj.gif">Unsubscribe</a>.-->
</td>
</tr>
<tr>
<td class="content-block powered-by"
style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;"
valign="top" align="center">
<a href="http://nocodb.com/">NocoDB</a>
<!-- Powered by <a href="http://htmlemail.io">HTMLemail</a>.-->
</td>
</tr>
</table>
</div>
<!-- END FOOTER -->
</div>
</td>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
</tr>
</table>
</body>
</html>
`;
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

230
packages/nocodb/src/lib/services/user/ui/emailTemplates/verify.ts

@ -1,230 +0,0 @@
export default `<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Simple Transactional Email</title>
<style>
@media only screen and (max-width: 620px) {
table[class=body] h1 {
font-size: 28px !important;
margin-bottom: 10px !important;
}
table[class=body] p,
table[class=body] ul,
table[class=body] ol,
table[class=body] td,
table[class=body] span,
table[class=body] a {
font-size: 16px !important;
}
table[class=body] .wrapper,
table[class=body] .article {
padding: 10px !important;
}
table[class=body] .content {
padding: 0 !important;
}
table[class=body] .container {
padding: 0 !important;
width: 100% !important;
}
table[class=body] .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
table[class=body] .btn table {
width: 100% !important;
}
table[class=body] .btn a {
width: 100% !important;
}
table[class=body] .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important;
}
}
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
#MessageViewBody a {
color: inherit;
text-decoration: none;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
line-height: inherit;
}
.btn-primary table td:hover {
background-color: #34495e !important;
}
.btn-primary a:hover {
background-color: #34495e !important;
border-color: #34495e !important;
}
}
</style>
</head>
<body class=""
style="background-color: #f6f6f6; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
<span class="preheader"
style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">This is preheader text. Some clients will show this text as a preview.</span>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f6f6f6; width: 100%;"
width="100%" bgcolor="#f6f6f6">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
<td class="container"
style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; max-width: 580px; padding: 10px; width: 580px; margin: 0 auto;"
width="580" valign="top">
<div class="content"
style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 580px; padding: 10px;">
<!-- START CENTERED WHITE CONTAINER -->
<table role="presentation" class="main"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background: #ffffff; border-radius: 3px; width: 100%;"
width="100%">
<!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper"
style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;"
valign="top">
<table role="presentation" border="0" cellpadding="0" cellspacing="0"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;"
width="100%">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;"
valign="top">
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">
Hi,</p>
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">
Please verify your email address by clicking the following button.</p>
<table role="presentation" border="0" cellpadding="0" cellspacing="0"
class="btn btn-primary"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; box-sizing: border-box; width: 100%;"
width="100%">
<tbody>
<tr>
<td align="left"
style="font-family: sans-serif; font-size: 14px; vertical-align: top; padding-bottom: 15px;"
valign="top">
<table role="presentation" border="0" cellpadding="0"
cellspacing="0"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
<tbody>
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top; border-radius: 5px; text-align: center; background-color: #3498db;"
valign="top" align="center" bgcolor="#1088ff"><a
href="<%- verifyLink %>" target="_blank"
style="border: solid 1px rgb(23, 139, 255); border-radius: 5px; box-sizing: border-box; cursor: pointer; display: inline-block; font-size: 14px; font-weight: bold; margin: 0; padding: 12px 25px; text-decoration: none; text-transform: capitalize; background-color: rgb(23, 139, 255); border-color: #3498db; color: #ffffff;">Verify</a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">
Thanks regards NocoDB.</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
<!-- END CENTERED WHITE CONTAINER -->
<!-- START FOOTER -->
<div class="footer" style="clear: both; margin-top: 10px; text-align: center; width: 100%;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;"
width="100%">
<tr>
<td class="content-block"
style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;"
valign="top" align="center">
<span class="apple-link"
style="color: #999999; font-size: 12px; text-align: center;"></span>
<!-- <br> Don't like these emails? <a href="http://i.imgur.com/CScmqnj.gif">Unsubscribe</a>.-->
</td>
</tr>
<tr>
<td class="content-block powered-by"
style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;"
valign="top" align="center">
<a href="http://nocodb.com/">NocoDB</a>
<!-- Powered by <a href="http://htmlemail.io">HTMLemail</a>.-->
</td>
</tr>
</table>
</div>
<!-- END FOOTER -->
</div>
</td>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
</tr>
</table>
</body>
</html>
`;
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

6
packages/nocodb/src/lib/utils/NcConfigFactory.ts

@ -590,10 +590,6 @@ export default class NcConfigFactory implements NcConfig {
...args.meta.db,
connection: args.meta.db,
});
if (process.env['TEST'] === 'true') {
await metaSqlClient.raw('PRAGMA journal_mode=WAL');
await metaSqlClient.raw('PRAGMA busy_timeout=60000');
}
await metaSqlClient.createDatabaseIfNotExists({
database: args.meta.db?.connection?.filename,
});
@ -630,7 +626,7 @@ export default class NcConfigFactory implements NcConfig {
db: {
client: 'sqlite3',
connection: {
filename: process.env['TEST'] !== 'true' ? 'noco.db' : 'test_noco.db',
filename: 'noco.db',
},
},
};

65
packages/nocodb/src/run/testDocker.ts

@ -0,0 +1,65 @@
import axios from 'axios';
import cors from 'cors';
import express from 'express';
import Noco from '../lib/Noco';
import User from '../lib/models/User';
process.env.NC_VERSION = '0009044';
const server = express();
server.enable('trust proxy');
server.disable('etag');
server.disable('x-powered-by');
server.use(
cors({
exposedHeaders: 'xc-db-response',
})
);
server.set('view engine', 'ejs');
process.env[`DEBUG`] = 'xc*';
(async () => {
const httpServer = server.listen(process.env.PORT || 8080, () => {
console.log(`App started successfully.\nVisit -> ${Noco.dashboardUrl}`);
});
server.use(await Noco.init({}, httpServer, server));
// Wait for 0.5 seconds for the server to start
await new Promise((resolve) => setTimeout(resolve, 500));
if (!(await User.getByEmail('user@nocodb.com'))) {
const response = await axios.post(
`http://localhost:${process.env.PORT || 8080}/api/v1/auth/user/signup`,
{
email: 'user@nocodb.com',
password: 'Password123.',
}
);
console.log(response.data);
}
})().catch((e) => console.log(e));
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

2
scripts/playwright/pages/Dashboard/Grid/index.ts

@ -66,7 +66,7 @@ export class GridPage extends BasePage {
const rowCount = await this.get().locator(".nc-grid-row").count();
await this.get().locator(".nc-grid-add-new-cell").click();
expect
await expect
.poll(async () => await this.get().locator(".nc-grid-row").count())
.toBe(rowCount + 1);

2
scripts/playwright/tests/linkToAnotherRecord.spec.ts

@ -2,7 +2,7 @@ import { test } from "@playwright/test";
import { DashboardPage } from "../pages/Dashboard";
import setup from "../setup";
test.describe("LTAR create & update", () => {
test.describe.only("LTAR create & update", () => {
let dashboard: DashboardPage;
let context: any;

Loading…
Cancel
Save