mirror of https://github.com/nocodb/nocodb
Muhammed Mustafa
2 years ago
25 changed files with 233413 additions and 138 deletions
@ -1,13 +1,16 @@
|
||||
import { Request, Router } from 'express'; |
||||
import { TestResetService } from '../../services/test/TestResetService'; |
||||
|
||||
export async function reset(_: Request<any, any>, res) { |
||||
const service = new TestResetService(); |
||||
export async function reset(req: Request<any, any>, res) { |
||||
console.log('resetting id', req.body); |
||||
const service = new TestResetService({ |
||||
parallelId: req.body.parallelId, |
||||
}); |
||||
|
||||
res.json(await service.process()); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get('/api/v1/meta/test/reset', reset); |
||||
router.post('/api/v1/meta/test/reset', reset); |
||||
export default router; |
||||
|
@ -1,17 +0,0 @@
|
||||
import axios from 'axios'; |
||||
|
||||
const defaultUserArgs = { |
||||
email: 'user@nocodb.com', |
||||
password: 'Password123.', |
||||
}; |
||||
|
||||
const createUser = async () => { |
||||
const response = await axios.post( |
||||
'http://localhost:8080/api/v1/auth/user/signup', |
||||
defaultUserArgs |
||||
); |
||||
|
||||
return { token: response.data.token }; |
||||
}; |
||||
|
||||
export default createUser; |
@ -1,43 +1,77 @@
|
||||
import Noco from '../../../Noco'; |
||||
|
||||
import Knex from 'knex'; |
||||
import NocoCache from '../../../cache/NocoCache'; |
||||
import axios from 'axios'; |
||||
import Project from '../../../models/Project'; |
||||
import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2'; |
||||
import createProjects from './createProjects'; |
||||
import createUser from './createUser'; |
||||
import resetMeta from './resetMeta'; |
||||
import { isMysqlSakilaToBeReset, resetMysqlSakila } from './resetMysqlSakila'; |
||||
import resetMetaSakilaSqliteProject from './resetMetaSakilaSqliteProject'; |
||||
|
||||
const loginRootUser = async () => { |
||||
const response = await axios.post( |
||||
'http://localhost:8080/api/v1/auth/user/signin', |
||||
{ email: 'user@nocodb.com', password: 'Password123.' } |
||||
); |
||||
|
||||
return response.data.token; |
||||
}; |
||||
|
||||
const projectTitleByType = { |
||||
sqlite3: 'sampleREST', |
||||
}; |
||||
|
||||
export class TestResetService { |
||||
private knex: Knex | null = null; |
||||
|
||||
constructor() { |
||||
private readonly parallelId; |
||||
constructor({ parallelId }: { parallelId: string }) { |
||||
this.knex = Noco.ncMeta.knex; |
||||
this.parallelId = parallelId; |
||||
} |
||||
|
||||
async process() { |
||||
try { |
||||
await NcConnectionMgrv2.destroyAll(); |
||||
const token = await loginRootUser(); |
||||
|
||||
// if (await isPgSakilaToBeReset()) {
|
||||
// await resetPgSakila();
|
||||
// }
|
||||
const { project } = await this.resetProject({ |
||||
metaKnex: this.knex, |
||||
token, |
||||
type: 'sqlite3', |
||||
parallelId: this.parallelId, |
||||
}); |
||||
|
||||
if (await isMysqlSakilaToBeReset()) { |
||||
await resetMysqlSakila(); |
||||
return { token, project }; |
||||
} catch (e) { |
||||
console.error('TestResetService:process', e); |
||||
return { error: e }; |
||||
} |
||||
} |
||||
|
||||
await resetMeta(this.knex); |
||||
async resetProject({ |
||||
metaKnex, |
||||
token, |
||||
type, |
||||
parallelId, |
||||
}: { |
||||
metaKnex: Knex; |
||||
token: string; |
||||
type: string; |
||||
parallelId: string; |
||||
}) { |
||||
const title = `${projectTitleByType[type]}${parallelId}`; |
||||
const project = await Project.getByTitle(title); |
||||
|
||||
await NocoCache.destroy(); |
||||
if (project) { |
||||
const bases = await project.getBases(); |
||||
await Project.delete(project.id); |
||||
|
||||
const { token } = await createUser(); |
||||
const projects = await createProjects(token); |
||||
if (bases.length > 0) await NcConnectionMgrv2.deleteAwait(bases[0]); |
||||
} |
||||
|
||||
return { token, projects }; |
||||
} catch (e) { |
||||
console.error('cleanupMeta', e); |
||||
return { error: e }; |
||||
if (type == 'sqlite3') { |
||||
await resetMetaSakilaSqliteProject({ token, metaKnex, title }); |
||||
} |
||||
|
||||
return { |
||||
project: await Project.getByTitle(title), |
||||
}; |
||||
} |
||||
} |
||||
|
@ -1,59 +0,0 @@
|
||||
import Model from '../../../models/Model'; |
||||
import Project from '../../../models/Project'; |
||||
import { orderedMetaTables, sakilaTableNames } from '../../../utils/globals'; |
||||
|
||||
const disableForeignKeyChecks = async (knex) => { |
||||
await knex.raw('PRAGMA foreign_keys = OFF'); |
||||
}; |
||||
|
||||
const enableForeignKeyChecks = async (knex) => { |
||||
await knex.raw(`PRAGMA foreign_keys = ON;`); |
||||
}; |
||||
|
||||
const dropTablesAllNonExternalProjects = async (knex) => { |
||||
const projects = await Project.list({}); |
||||
const userCreatedTableNames: string[] = []; |
||||
await Promise.all( |
||||
projects |
||||
.filter((project) => project.is_meta) |
||||
.map(async (project) => { |
||||
await project.getBases(); |
||||
const base = project.bases && project.bases[0]; |
||||
if (!base) return; |
||||
|
||||
const models = await Model.list({ |
||||
project_id: project.id, |
||||
base_id: base.id!, |
||||
}); |
||||
models.forEach((model) => { |
||||
if (!sakilaTableNames.includes(model.table_name)) { |
||||
userCreatedTableNames.push(model.table_name); |
||||
} |
||||
}); |
||||
}) |
||||
); |
||||
|
||||
await disableForeignKeyChecks(knex); |
||||
|
||||
for (const tableName of userCreatedTableNames) { |
||||
await knex.raw(`DROP TABLE ${tableName}`); |
||||
} |
||||
|
||||
await enableForeignKeyChecks(knex); |
||||
}; |
||||
|
||||
const resetMeta = async (knex) => { |
||||
await dropTablesAllNonExternalProjects(knex); |
||||
|
||||
await disableForeignKeyChecks(knex); |
||||
for (const tableName of orderedMetaTables) { |
||||
try { |
||||
await knex.raw(`DELETE FROM ${tableName}`); |
||||
} catch (e) { |
||||
console.error('cleanupMetaTables', e); |
||||
} |
||||
} |
||||
await enableForeignKeyChecks(knex); |
||||
}; |
||||
|
||||
export default resetMeta; |
@ -0,0 +1,140 @@
|
||||
import axios from 'axios'; |
||||
import Knex from 'knex'; |
||||
|
||||
import fs from 'fs'; |
||||
import Audit from '../../../models/Audit'; |
||||
import { sakilaTableNames } from '../../../utils/globals'; |
||||
|
||||
const sqliteSakilaSqlViews = [ |
||||
'actor_info', |
||||
'customer_list', |
||||
'film_list', |
||||
'nice_but_slower_film_list', |
||||
'sales_by_film_category', |
||||
'sales_by_store', |
||||
'staff_list', |
||||
]; |
||||
|
||||
const dropTablesAndViews = async (metaKnex: Knex, prefix: string) => { |
||||
for (const view of sqliteSakilaSqlViews) { |
||||
try { |
||||
await metaKnex.raw(`DROP VIEW IF EXISTS ${prefix}${view}`); |
||||
} catch (e) { |
||||
console.log('Error dropping sqlite view', e); |
||||
} |
||||
} |
||||
|
||||
for (const table of sakilaTableNames) { |
||||
try { |
||||
await metaKnex.raw(`DROP TABLE IF EXISTS ${prefix}${table}`); |
||||
} catch (e) { |
||||
console.log('Error dropping sqlite table', e); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
const isMetaSakilaSqliteToBeReset = async (metaKnex: Knex, project: any) => { |
||||
const tablesInDb: Array<string> = await metaKnex.raw( |
||||
`SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '${project.prefix}%'` |
||||
); |
||||
|
||||
if ( |
||||
tablesInDb.length === 0 || |
||||
(tablesInDb.length > 0 && !tablesInDb.includes(`${project.prefix}actor`)) |
||||
) { |
||||
return true; |
||||
} |
||||
|
||||
const audits = await Audit.projectAuditList(project.id, {}); |
||||
|
||||
return audits?.length > 0; |
||||
}; |
||||
|
||||
const resetMetaSakilaSqlite = async (metaKnex: Knex, prefix: string) => { |
||||
await dropTablesAndViews(metaKnex, prefix); |
||||
|
||||
const testsDir = __dirname.replace( |
||||
'/src/lib/services/test/TestResetService', |
||||
'/tests' |
||||
); |
||||
|
||||
const trx = await metaKnex.transaction(); |
||||
|
||||
try { |
||||
const schemaFile = fs |
||||
.readFileSync( |
||||
`${testsDir}/sqlite-sakila-db/03-sqlite-prefix-sakila-schema.sql` |
||||
) |
||||
.toString() |
||||
.replace(/prefix___/g, prefix); |
||||
|
||||
const dataFile = fs |
||||
.readFileSync( |
||||
`${testsDir}/sqlite-sakila-db/04-sqlite-prefix-sakila-insert-data.sql` |
||||
) |
||||
.toString() |
||||
.replace(/prefix___/g, prefix); |
||||
|
||||
const schemaSqlQueries = schemaFile.split(';'); |
||||
for (const sqlQuery of schemaSqlQueries) { |
||||
if (sqlQuery.trim().length > 0) { |
||||
await trx.raw( |
||||
sqlQuery |
||||
.trim() |
||||
.replace(/WHERE rowid = new.rowid/g, 'WHERE rowid = new.rowid;') |
||||
); |
||||
} |
||||
} |
||||
|
||||
const dataSqlQueries = dataFile.split(';'); |
||||
for (const sqlQuery of dataSqlQueries) { |
||||
if (sqlQuery.trim().length > 0) { |
||||
await trx.raw(sqlQuery.trim()); |
||||
} |
||||
} |
||||
await trx.commit(); |
||||
} catch (e) { |
||||
console.log('Error resetting sqlite db', e); |
||||
await trx.rollback(e); |
||||
} |
||||
}; |
||||
|
||||
const resetMetaSakilaSqliteProject = async ({ |
||||
metaKnex, |
||||
token, |
||||
title, |
||||
}: { |
||||
metaKnex: Knex; |
||||
token: string; |
||||
title: string; |
||||
}) => { |
||||
const response = await axios.post( |
||||
'http://localhost:8080/api/v1/db/meta/projects/', |
||||
{ title }, |
||||
{ |
||||
headers: { |
||||
'xc-auth': token, |
||||
}, |
||||
} |
||||
); |
||||
if (response.status !== 200) { |
||||
console.error('Error creating project', response.data); |
||||
} |
||||
const project = response.data; |
||||
|
||||
if (await isMetaSakilaSqliteToBeReset(metaKnex, project)) { |
||||
await resetMetaSakilaSqlite(metaKnex, project.prefix); |
||||
} |
||||
|
||||
await axios.post( |
||||
`http://localhost:8080/api/v1/db/meta/projects/${project.id}/meta-diff`, |
||||
{}, |
||||
{ |
||||
headers: { |
||||
'xc-auth': token, |
||||
}, |
||||
} |
||||
); |
||||
}; |
||||
|
||||
export default resetMetaSakilaSqliteProject; |
@ -1,21 +0,0 @@
|
||||
import fs from 'fs'; |
||||
import Knex from 'knex'; |
||||
|
||||
const setupSakilaSqlite = async (metaKnex: Knex) => { |
||||
const testsDir = __dirname.replace( |
||||
'/src/lib/services/test/TestResetService', |
||||
'/tests' |
||||
); |
||||
|
||||
const schemaFile = fs |
||||
.readFileSync(`${testsDir}/pg-sakila-db/01-sqlite-sakila-schema.sql`) |
||||
.toString(); |
||||
const dataFile = fs |
||||
.readFileSync(`${testsDir}/pg-sakila-db/02-sqlite-sakila-insert-data.sql`) |
||||
.toString(); |
||||
|
||||
await metaKnex.raw(schemaFile); |
||||
await metaKnex.raw(dataFile); |
||||
}; |
||||
|
||||
export default setupSakilaSqlite; |
@ -0,0 +1,182 @@
|
||||
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, |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,94 @@
|
||||
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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,128 @@
|
||||
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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,193 @@
|
||||
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"> </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"> </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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,231 @@
|
||||
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"> </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"> </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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,230 @@
|
||||
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"> </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"> </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/>. |
||||
* |
||||
*/ |
@ -0,0 +1,604 @@
|
||||
/* |
||||
|
||||
Sakila for SQLite is a port of the Sakila example database available for MySQL, which was originally developed by Mike Hillyer of the MySQL AB documentation team. |
||||
This project is designed to help database administrators to decide which database to use for development of new products |
||||
The user can run the same SQL against different kind of databases and compare the performance |
||||
|
||||
License: BSD |
||||
Copyright DB Software Laboratory |
||||
http://www.etl-tools.com |
||||
|
||||
*/ |
||||
|
||||
|
||||
CREATE TABLE prefix___actor ( |
||||
actor_id numeric NOT NULL , |
||||
first_name VARCHAR(45) NOT NULL, |
||||
last_name VARCHAR(45) NOT NULL, |
||||
last_update TIMESTAMP NOT NULL, |
||||
PRIMARY KEY (actor_id) |
||||
) ; |
||||
|
||||
CREATE INDEX prefix___idx_actor_last_name ON prefix___actor(last_name) |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___actor_trigger_ai AFTER INSERT ON prefix___actor |
||||
BEGIN |
||||
UPDATE prefix___actor SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___actor_trigger_au AFTER UPDATE ON prefix___actor |
||||
BEGIN |
||||
UPDATE prefix___actor SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
-- |
||||
-- Table structure for table country |
||||
-- |
||||
|
||||
CREATE TABLE prefix___country ( |
||||
country_id SMALLINT NOT NULL, |
||||
country VARCHAR(50) NOT NULL, |
||||
last_update TIMESTAMP, |
||||
PRIMARY KEY (country_id) |
||||
) |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___country_trigger_ai AFTER INSERT ON prefix___country |
||||
BEGIN |
||||
UPDATE prefix___country SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___country_trigger_au AFTER UPDATE ON prefix___country |
||||
BEGIN |
||||
UPDATE prefix___country SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
-- |
||||
-- Table structure for table city |
||||
-- |
||||
|
||||
CREATE TABLE prefix___city ( |
||||
city_id int NOT NULL, |
||||
city VARCHAR(50) NOT NULL, |
||||
country_id SMALLINT NOT NULL, |
||||
last_update TIMESTAMP NOT NULL, |
||||
PRIMARY KEY (city_id), |
||||
CONSTRAINT prefix___fk_city_country FOREIGN KEY (country_id) REFERENCES prefix___country (country_id) ON DELETE NO ACTION ON UPDATE CASCADE |
||||
) |
||||
; |
||||
CREATE INDEX prefix___idx_fk_country_id ON prefix___city(country_id) |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___city_trigger_ai AFTER INSERT ON prefix___city |
||||
BEGIN |
||||
UPDATE prefix___city SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___city_trigger_au AFTER UPDATE ON prefix___city |
||||
BEGIN |
||||
UPDATE prefix___city SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
-- |
||||
-- Table structure for table address |
||||
-- |
||||
|
||||
CREATE TABLE prefix___address ( |
||||
address_id int NOT NULL, |
||||
address VARCHAR(50) NOT NULL, |
||||
address2 VARCHAR(50) DEFAULT NULL, |
||||
district VARCHAR(20) NOT NULL, |
||||
city_id INT NOT NULL, |
||||
postal_code VARCHAR(10) DEFAULT NULL, |
||||
phone VARCHAR(20) NOT NULL, |
||||
last_update TIMESTAMP NOT NULL, |
||||
PRIMARY KEY (address_id), |
||||
CONSTRAINT prefix___fk_address_city FOREIGN KEY (city_id) REFERENCES prefix___city (city_id) ON DELETE NO ACTION ON UPDATE CASCADE |
||||
) |
||||
; |
||||
|
||||
CREATE INDEX prefix___idx_fk_city_id ON prefix___address(city_id) |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___address_trigger_ai AFTER INSERT ON prefix___address |
||||
BEGIN |
||||
UPDATE prefix___address SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___address_trigger_au AFTER UPDATE ON prefix___address |
||||
BEGIN |
||||
UPDATE prefix___address SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
-- |
||||
-- Table structure for table language |
||||
-- |
||||
|
||||
CREATE TABLE prefix___language ( |
||||
language_id SMALLINT NOT NULL , |
||||
name CHAR(20) NOT NULL, |
||||
last_update TIMESTAMP NOT NULL, |
||||
PRIMARY KEY (language_id) |
||||
) |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___language_trigger_ai AFTER INSERT ON prefix___language |
||||
BEGIN |
||||
UPDATE prefix___language SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___language_trigger_au AFTER UPDATE ON prefix___language |
||||
BEGIN |
||||
UPDATE prefix___language SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
-- |
||||
-- Table structure for table category |
||||
-- |
||||
|
||||
CREATE TABLE prefix___category ( |
||||
category_id SMALLINT NOT NULL, |
||||
name VARCHAR(25) NOT NULL, |
||||
last_update TIMESTAMP NOT NULL, |
||||
PRIMARY KEY (category_id) |
||||
); |
||||
|
||||
CREATE TRIGGER prefix___category_trigger_ai AFTER INSERT ON prefix___category |
||||
BEGIN |
||||
UPDATE prefix___category SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___category_trigger_au AFTER UPDATE ON prefix___category |
||||
BEGIN |
||||
UPDATE prefix___category SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
-- |
||||
-- Table structure for table customer |
||||
-- |
||||
|
||||
CREATE TABLE prefix___customer ( |
||||
customer_id INT NOT NULL, |
||||
store_id INT NOT NULL, |
||||
first_name VARCHAR(45) NOT NULL, |
||||
last_name VARCHAR(45) NOT NULL, |
||||
email VARCHAR(50) DEFAULT NULL, |
||||
address_id INT NOT NULL, |
||||
active CHAR(1) DEFAULT 'Y' NOT NULL, |
||||
create_date TIMESTAMP NOT NULL, |
||||
last_update TIMESTAMP NOT NULL, |
||||
PRIMARY KEY (customer_id), |
||||
CONSTRAINT prefix___fk_customer_store FOREIGN KEY (store_id) REFERENCES prefix___store (store_id) ON DELETE NO ACTION ON UPDATE CASCADE, |
||||
CONSTRAINT prefix___fk_customer_address FOREIGN KEY (address_id) REFERENCES prefix___address (address_id) ON DELETE NO ACTION ON UPDATE CASCADE |
||||
) |
||||
; |
||||
|
||||
CREATE INDEX prefix___idx_customer_fk_store_id ON prefix___customer(store_id) |
||||
; |
||||
CREATE INDEX prefix___idx_customer_fk_address_id ON prefix___customer(address_id) |
||||
; |
||||
CREATE INDEX prefix___idx_customer_last_name ON prefix___customer(last_name) |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___customer_trigger_ai AFTER INSERT ON prefix___customer |
||||
BEGIN |
||||
UPDATE prefix___customer SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___customer_trigger_au AFTER UPDATE ON prefix___customer |
||||
BEGIN |
||||
UPDATE prefix___customer SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
-- |
||||
-- Table structure for table film |
||||
-- |
||||
|
||||
CREATE TABLE prefix___film ( |
||||
film_id int NOT NULL, |
||||
title VARCHAR(255) NOT NULL, |
||||
description BLOB SUB_TYPE TEXT DEFAULT NULL, |
||||
release_year VARCHAR(4) DEFAULT NULL, |
||||
language_id SMALLINT NOT NULL, |
||||
original_language_id SMALLINT DEFAULT NULL, |
||||
rental_duration SMALLINT DEFAULT 3 NOT NULL, |
||||
rental_rate DECIMAL(4,2) DEFAULT 4.99 NOT NULL, |
||||
length SMALLINT DEFAULT NULL, |
||||
replacement_cost DECIMAL(5,2) DEFAULT 19.99 NOT NULL, |
||||
rating VARCHAR(10) DEFAULT 'G', |
||||
special_features VARCHAR(100) DEFAULT NULL, |
||||
last_update TIMESTAMP NOT NULL, |
||||
PRIMARY KEY (film_id), |
||||
CONSTRAINT CHECK_special_features CHECK(special_features is null or |
||||
special_features like '%Trailers%' or |
||||
special_features like '%Commentaries%' or |
||||
special_features like '%Deleted Scenes%' or |
||||
special_features like '%Behind the Scenes%'), |
||||
CONSTRAINT CHECK_special_rating CHECK(rating in ('G','PG','PG-13','R','NC-17')), |
||||
CONSTRAINT prefix___fk_film_language FOREIGN KEY (language_id) REFERENCES prefix___language (language_id) , |
||||
CONSTRAINT prefix___fk_film_language_original FOREIGN KEY (original_language_id) REFERENCES prefix___language (language_id) |
||||
) |
||||
; |
||||
CREATE INDEX prefix___idx_fk_language_id ON prefix___film(language_id) |
||||
; |
||||
CREATE INDEX prefix___idx_fk_original_language_id ON prefix___film(original_language_id) |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___film_trigger_ai AFTER INSERT ON prefix___film |
||||
BEGIN |
||||
UPDATE prefix___film SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___film_trigger_au AFTER UPDATE ON prefix___film |
||||
BEGIN |
||||
UPDATE prefix___film SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
-- |
||||
-- Table structure for table film_actor |
||||
-- |
||||
|
||||
CREATE TABLE prefix___film_actor ( |
||||
actor_id INT NOT NULL, |
||||
film_id INT NOT NULL, |
||||
last_update TIMESTAMP NOT NULL, |
||||
PRIMARY KEY (actor_id,film_id), |
||||
CONSTRAINT prefix___fk_film_actor_actor FOREIGN KEY (actor_id) REFERENCES prefix___actor (actor_id) ON DELETE NO ACTION ON UPDATE CASCADE, |
||||
CONSTRAINT prefix___fk_film_actor_film FOREIGN KEY (film_id) REFERENCES prefix___film (film_id) ON DELETE NO ACTION ON UPDATE CASCADE |
||||
) |
||||
; |
||||
|
||||
CREATE INDEX prefix___idx_fk_film_actor_film ON prefix___film_actor(film_id) |
||||
; |
||||
|
||||
CREATE INDEX prefix___idx_fk_film_actor_actor ON prefix___film_actor(actor_id) |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___film_actor_trigger_ai AFTER INSERT ON prefix___film_actor |
||||
BEGIN |
||||
UPDATE prefix___film_actor SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___film_actor_trigger_au AFTER UPDATE ON prefix___film_actor |
||||
BEGIN |
||||
UPDATE prefix___film_actor SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
|
||||
-- |
||||
-- Table structure for table film_category |
||||
-- |
||||
|
||||
CREATE TABLE prefix___film_category ( |
||||
film_id INT NOT NULL, |
||||
category_id SMALLINT NOT NULL, |
||||
last_update TIMESTAMP NOT NULL, |
||||
PRIMARY KEY (film_id, category_id), |
||||
CONSTRAINT prefix___fk_film_category_film FOREIGN KEY (film_id) REFERENCES prefix___film (film_id) ON DELETE NO ACTION ON UPDATE CASCADE, |
||||
CONSTRAINT prefix___fk_film_category_category FOREIGN KEY (category_id) REFERENCES prefix___category (category_id) ON DELETE NO ACTION ON UPDATE CASCADE |
||||
) |
||||
; |
||||
|
||||
CREATE INDEX prefix___idx_fk_film_category_film ON prefix___film_category(film_id) |
||||
; |
||||
|
||||
CREATE INDEX prefix___idx_fk_film_category_category ON prefix___film_category(category_id) |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___film_category_trigger_ai AFTER INSERT ON prefix___film_category |
||||
BEGIN |
||||
UPDATE prefix___film_category SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___film_category_trigger_au AFTER UPDATE ON prefix___film_category |
||||
BEGIN |
||||
UPDATE prefix___film_category SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
-- |
||||
-- Table structure for table film_text |
||||
-- |
||||
|
||||
CREATE TABLE prefix___film_text ( |
||||
film_id SMALLINT NOT NULL, |
||||
title VARCHAR(255) NOT NULL, |
||||
description BLOB SUB_TYPE TEXT, |
||||
PRIMARY KEY (film_id) |
||||
) |
||||
; |
||||
|
||||
-- |
||||
-- Table structure for table inventory |
||||
-- |
||||
|
||||
CREATE TABLE prefix___inventory ( |
||||
inventory_id INT NOT NULL, |
||||
film_id INT NOT NULL, |
||||
store_id INT NOT NULL, |
||||
last_update TIMESTAMP NOT NULL, |
||||
PRIMARY KEY (inventory_id), |
||||
CONSTRAINT prefix___fk_inventory_store FOREIGN KEY (store_id) REFERENCES prefix___store (store_id) ON DELETE NO ACTION ON UPDATE CASCADE, |
||||
CONSTRAINT prefix___fk_inventory_film FOREIGN KEY (film_id) REFERENCES prefix___film (film_id) ON DELETE NO ACTION ON UPDATE CASCADE |
||||
) |
||||
; |
||||
|
||||
CREATE INDEX prefix___idx_fk_film_id ON prefix___inventory(film_id) |
||||
; |
||||
|
||||
CREATE INDEX prefix___idx_fk_film_id_store_id ON prefix___inventory(store_id,film_id) |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___inventory_trigger_ai AFTER INSERT ON prefix___inventory |
||||
BEGIN |
||||
UPDATE prefix___inventory SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___inventory_trigger_au AFTER UPDATE ON prefix___inventory |
||||
BEGIN |
||||
UPDATE prefix___inventory SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
-- |
||||
-- Table structure for table staff |
||||
-- |
||||
|
||||
CREATE TABLE prefix___staff ( |
||||
staff_id SMALLINT NOT NULL, |
||||
first_name VARCHAR(45) NOT NULL, |
||||
last_name VARCHAR(45) NOT NULL, |
||||
address_id INT NOT NULL, |
||||
picture BLOB DEFAULT NULL, |
||||
email VARCHAR(50) DEFAULT NULL, |
||||
store_id INT NOT NULL, |
||||
active SMALLINT DEFAULT 1 NOT NULL, |
||||
username VARCHAR(16) NOT NULL, |
||||
password VARCHAR(40) DEFAULT NULL, |
||||
last_update TIMESTAMP NOT NULL, |
||||
PRIMARY KEY (staff_id), |
||||
CONSTRAINT prefix___fk_staff_store FOREIGN KEY (store_id) REFERENCES prefix___store (store_id) ON DELETE NO ACTION ON UPDATE CASCADE, |
||||
CONSTRAINT prefix___fk_staff_address FOREIGN KEY (address_id) REFERENCES prefix___address (address_id) ON DELETE NO ACTION ON UPDATE CASCADE |
||||
) |
||||
; |
||||
CREATE INDEX prefix___idx_fk_staff_store_id ON prefix___staff(store_id) |
||||
; |
||||
|
||||
CREATE INDEX prefix___idx_fk_staff_address_id ON prefix___staff(address_id) |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___staff_trigger_ai AFTER INSERT ON prefix___staff |
||||
BEGIN |
||||
UPDATE prefix___staff SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___staff_trigger_au AFTER UPDATE ON prefix___staff |
||||
BEGIN |
||||
UPDATE prefix___staff SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
-- |
||||
-- Table structure for table store |
||||
-- |
||||
|
||||
CREATE TABLE prefix___store ( |
||||
store_id INT NOT NULL, |
||||
manager_staff_id SMALLINT NOT NULL, |
||||
address_id INT NOT NULL, |
||||
last_update TIMESTAMP NOT NULL, |
||||
PRIMARY KEY (store_id), |
||||
CONSTRAINT prefix___fk_store_staff FOREIGN KEY (manager_staff_id) REFERENCES prefix___staff (staff_id) , |
||||
CONSTRAINT prefix___fk_store_address FOREIGN KEY (address_id) REFERENCES prefix___address (address_id) |
||||
) |
||||
; |
||||
|
||||
CREATE INDEX prefix___idx_store_fk_manager_staff_id ON prefix___store(manager_staff_id) |
||||
; |
||||
|
||||
CREATE INDEX prefix___idx_fk_store_address ON prefix___store(address_id) |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___store_trigger_ai AFTER INSERT ON prefix___store |
||||
BEGIN |
||||
UPDATE prefix___store SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___store_trigger_au AFTER UPDATE ON prefix___store |
||||
BEGIN |
||||
UPDATE prefix___store SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
-- |
||||
-- Table structure for table payment |
||||
-- |
||||
|
||||
CREATE TABLE prefix___payment ( |
||||
payment_id int NOT NULL, |
||||
customer_id INT NOT NULL, |
||||
staff_id SMALLINT NOT NULL, |
||||
rental_id INT DEFAULT NULL, |
||||
amount DECIMAL(5,2) NOT NULL, |
||||
payment_date TIMESTAMP NOT NULL, |
||||
last_update TIMESTAMP NOT NULL, |
||||
PRIMARY KEY (payment_id), |
||||
CONSTRAINT prefix___fk_payment_rental FOREIGN KEY (rental_id) REFERENCES prefix___rental (rental_id) ON DELETE SET NULL ON UPDATE CASCADE, |
||||
CONSTRAINT prefix___fk_payment_customer FOREIGN KEY (customer_id) REFERENCES prefix___customer (customer_id) , |
||||
CONSTRAINT prefix___fk_payment_staff FOREIGN KEY (staff_id) REFERENCES prefix___staff (staff_id) |
||||
) |
||||
; |
||||
CREATE INDEX prefix___idx_fk_staff_id ON prefix___payment(staff_id) |
||||
; |
||||
CREATE INDEX prefix___idx_fk_customer_id ON prefix___payment(customer_id) |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___payment_trigger_ai AFTER INSERT ON prefix___payment |
||||
BEGIN |
||||
UPDATE prefix___payment SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___payment_trigger_au AFTER UPDATE ON prefix___payment |
||||
BEGIN |
||||
UPDATE prefix___payment SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TABLE prefix___rental ( |
||||
rental_id INT NOT NULL, |
||||
rental_date TIMESTAMP NOT NULL, |
||||
inventory_id INT NOT NULL, |
||||
customer_id INT NOT NULL, |
||||
return_date TIMESTAMP DEFAULT NULL, |
||||
staff_id SMALLINT NOT NULL, |
||||
last_update TIMESTAMP NOT NULL, |
||||
PRIMARY KEY (rental_id), |
||||
CONSTRAINT prefix___fk_rental_staff FOREIGN KEY (staff_id) REFERENCES prefix___staff (staff_id) , |
||||
CONSTRAINT prefix___fk_rental_inventory FOREIGN KEY (inventory_id) REFERENCES prefix___inventory (inventory_id) , |
||||
CONSTRAINT prefix___fk_rental_customer FOREIGN KEY (customer_id) REFERENCES prefix___customer (customer_id) |
||||
) |
||||
; |
||||
CREATE INDEX prefix___idx_rental_fk_inventory_id ON prefix___rental(inventory_id) |
||||
; |
||||
CREATE INDEX prefix___idx_rental_fk_customer_id ON prefix___rental(customer_id) |
||||
; |
||||
CREATE INDEX prefix___idx_rental_fk_staff_id ON prefix___rental(staff_id) |
||||
; |
||||
CREATE UNIQUE INDEX prefix___idx_rental_uq ON prefix___rental (rental_date,inventory_id,customer_id) |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___rental_trigger_ai AFTER INSERT ON prefix___rental |
||||
BEGIN |
||||
UPDATE prefix___rental SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
CREATE TRIGGER prefix___rental_trigger_au AFTER UPDATE ON prefix___rental |
||||
BEGIN |
||||
UPDATE prefix___rental SET last_update = DATETIME('NOW') WHERE rowid = new.rowid |
||||
END |
||||
; |
||||
|
||||
|
||||
-- |
||||
-- View structure for view customer_list |
||||
-- |
||||
|
||||
CREATE VIEW prefix___customer_list |
||||
AS |
||||
SELECT cu.customer_id AS ID, |
||||
cu.first_name||' '||cu.last_name AS name, |
||||
a.address AS address, |
||||
a.postal_code AS zip_code, |
||||
a.phone AS phone, |
||||
prefix___city.city AS city, |
||||
prefix___country.country AS country, |
||||
case when cu.active=1 then 'active' else '' end AS notes, |
||||
cu.store_id AS SID |
||||
FROM prefix___customer AS cu JOIN prefix___address AS a ON cu.address_id = a.address_id JOIN prefix___city ON a.city_id = prefix___city.city_id |
||||
JOIN prefix___country ON prefix___city.country_id = prefix___country.country_id |
||||
; |
||||
-- |
||||
-- View structure for view film_list |
||||
-- |
||||
|
||||
CREATE VIEW prefix___film_list |
||||
AS |
||||
SELECT prefix___film.film_id AS FID, |
||||
prefix___film.title AS title, |
||||
prefix___film.description AS description, |
||||
prefix___category.name AS category, |
||||
prefix___film.rental_rate AS price, |
||||
prefix___film.length AS length, |
||||
prefix___film.rating AS rating, |
||||
prefix___actor.first_name||' '||prefix___actor.last_name AS actors |
||||
FROM prefix___category LEFT JOIN prefix___film_category ON prefix___category.category_id = prefix___film_category.category_id LEFT JOIN prefix___film ON prefix___film_category.film_id = prefix___film.film_id |
||||
JOIN prefix___film_actor ON prefix___film.film_id = prefix___film_actor.film_id |
||||
JOIN prefix___actor ON prefix___film_actor.actor_id = prefix___actor.actor_id |
||||
; |
||||
|
||||
-- |
||||
-- View structure for view staff_list |
||||
-- |
||||
|
||||
CREATE VIEW prefix___staff_list |
||||
AS |
||||
SELECT s.staff_id AS ID, |
||||
s.first_name||' '||s.last_name AS name, |
||||
a.address AS address, |
||||
a.postal_code AS zip_code, |
||||
a.phone AS phone, |
||||
prefix___city.city AS city, |
||||
prefix___country.country AS country, |
||||
s.store_id AS SID |
||||
FROM prefix___staff AS s JOIN prefix___address AS a ON s.address_id = a.address_id JOIN prefix___city ON a.city_id = prefix___city.city_id |
||||
JOIN prefix___country ON prefix___city.country_id = prefix___country.country_id |
||||
; |
||||
-- |
||||
-- View structure for view sales_by_store |
||||
-- |
||||
|
||||
CREATE VIEW prefix___sales_by_store |
||||
AS |
||||
SELECT |
||||
s.store_id |
||||
,c.city||','||cy.country AS store |
||||
,m.first_name||' '||m.last_name AS manager |
||||
,SUM(p.amount) AS total_sales |
||||
FROM prefix___payment AS p |
||||
INNER JOIN prefix___rental AS r ON p.rental_id = r.rental_id |
||||
INNER JOIN prefix___inventory AS i ON r.inventory_id = i.inventory_id |
||||
INNER JOIN prefix___store AS s ON i.store_id = s.store_id |
||||
INNER JOIN prefix___address AS a ON s.address_id = a.address_id |
||||
INNER JOIN prefix___city AS c ON a.city_id = c.city_id |
||||
INNER JOIN prefix___country AS cy ON c.country_id = cy.country_id |
||||
INNER JOIN prefix___staff AS m ON s.manager_staff_id = m.staff_id |
||||
GROUP BY |
||||
s.store_id |
||||
, c.city||','||cy.country |
||||
, m.first_name||' '||m.last_name |
||||
; |
||||
-- |
||||
-- View structure for view sales_by_film_category |
||||
-- |
||||
-- Note that total sales will add up to >100% because |
||||
-- some titles belong to more than 1 category |
||||
-- |
||||
|
||||
CREATE VIEW prefix___sales_by_film_category |
||||
AS |
||||
SELECT |
||||
c.name AS category |
||||
, SUM(p.amount) AS total_sales |
||||
FROM prefix___payment AS p |
||||
INNER JOIN prefix___rental AS r ON p.rental_id = r.rental_id |
||||
INNER JOIN prefix___inventory AS i ON r.inventory_id = i.inventory_id |
||||
INNER JOIN prefix___film AS f ON i.film_id = f.film_id |
||||
INNER JOIN prefix___film_category AS fc ON f.film_id = fc.film_id |
||||
INNER JOIN prefix___category AS c ON fc.category_id = c.category_id |
||||
GROUP BY c.name |
||||
; |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue