pinstripe 0.12.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +1 -1
- package/lib/command.js +12 -0
- package/lib/commands/generate_project.js +18 -0
- package/lib/commands/generate_static_site.js +8 -8
- package/lib/commands/generate_view.js +4 -3
- package/lib/commands/list_commands.js +2 -4
- package/lib/commands/list_migrations.js +2 -4
- package/lib/commands/list_models.js +2 -4
- package/lib/commands/list_node_wrappers.js +2 -4
- package/lib/commands/list_services.js +2 -4
- package/lib/commands/list_views.js +2 -4
- package/lib/commands/purge_old_sessions.js +12 -0
- package/lib/commands/start_server.js +10 -8
- package/lib/database/adapters/mysql.js +55 -11
- package/lib/database/adapters/sqlite.js +87 -28
- package/lib/database/constants.js +0 -4
- package/lib/database/row.js +5 -3
- package/lib/database/table.js +13 -5
- package/lib/database/union.js +13 -6
- package/lib/database.js +12 -1
- package/lib/extensions/_importer.js +2 -0
- package/lib/extensions/multi-tenant/database/table.js +18 -0
- package/lib/extensions/multi-tenant/database.js +6 -0
- package/lib/extensions/multi-tenant/index.js +4 -0
- package/lib/extensions/multi-tenant/migrations/1627976174_create_tenant_table_and_add_tenant_id_to_existing_tables.js +16 -0
- package/lib/extensions/multi-tenant/migrations/_importer.js +2 -0
- package/lib/extensions/multi-tenant/models/_importer.js +2 -0
- package/lib/extensions/multi-tenant/models/tenant.js +7 -0
- package/lib/extensions/multi-tenant/services/_importer.js +2 -0
- package/lib/extensions/multi-tenant/services/tenant.js +7 -0
- package/lib/import_all.js +1 -0
- package/lib/initialize.client.js +4 -27
- package/lib/migrations/1628057822_create_session.js +1 -0
- package/lib/node_wrapper.js +55 -6
- package/lib/node_wrappers/anchor.client.js +2 -0
- package/lib/node_wrappers/document.client.js +1 -1
- package/lib/node_wrappers/form.client.js +12 -1
- package/lib/node_wrappers/helpers.js +2 -2
- package/lib/node_wrappers/markdown_editor/line_inserter.client.js +1 -1
- package/lib/node_wrappers/markdown_editor.client.js +9 -2
- package/lib/node_wrappers/overlay.client.js +15 -2
- package/lib/services/app.js +2 -0
- package/lib/services/bot.js +69 -0
- package/lib/services/command_names.js +6 -0
- package/lib/services/config.js +0 -1
- package/lib/services/fetch.js +15 -5
- package/lib/services/format_date.js +6 -0
- package/lib/services/migration_names.js +6 -0
- package/lib/services/model_names.js +6 -0
- package/lib/services/node_wrapper_names.js +6 -0
- package/lib/services/params.js +6 -1
- package/lib/services/render_form.js +71 -63
- package/lib/services/render_frame.js +11 -0
- package/lib/services/render_list.js +3 -3
- package/lib/services/render_markdown.js +1 -1
- package/lib/services/render_table.js +8 -8
- package/lib/services/send_mail.js +32 -0
- package/lib/services/service_names.js +6 -0
- package/lib/services/session.js +8 -2
- package/lib/services/stylesheets.js +16 -0
- package/lib/services/view_names.js +6 -0
- package/lib/url.js +2 -2
- package/lib/views/{_layout.js → main/_layout.js} +4 -2
- package/lib/views/{blocks → main/blocks}/default.js +6 -8
- package/lib/views/{blocks → main/blocks}/help.js +0 -0
- package/lib/views/{bundle.js.js → main/bundle.js.js} +1 -1
- package/lib/views/{sign_in.js → main/sign_in.js} +2 -6
- package/lib/views/{sign_out.js → main/sign_out.js} +0 -0
- package/lib/views/main/stylesheets/components/button.css.js +56 -0
- package/lib/views/main/stylesheets/components/card.css.js +41 -0
- package/lib/views/main/stylesheets/components/form.css.js +11 -0
- package/lib/views/main/stylesheets/components/frame.css.js +10 -0
- package/lib/views/main/stylesheets/components/input.css.js +54 -0
- package/lib/views/main/stylesheets/components/label.css.js +14 -0
- package/lib/views/main/stylesheets/components/markdown_editor.css.js +53 -0
- package/lib/views/main/stylesheets/components/modal.css.js +77 -0
- package/lib/views/main/stylesheets/components/overlay.css.js +17 -0
- package/lib/views/main/stylesheets/components/pagination.css.js +80 -0
- package/lib/views/main/stylesheets/components/progress_bar.css.js +32 -0
- package/lib/views/main/stylesheets/components/textarea.css.js +17 -0
- package/lib/views/main/stylesheets/global.css +120 -0
- package/lib/views/main/stylesheets/reset.css +74 -0
- package/lib/views/main/stylesheets/vars.css +25 -0
- package/package.json +8 -5
- package/pinstripe_development.db +0 -0
- package/lib/views/base.css.js +0 -657
package/cli.js
CHANGED
package/lib/command.js
CHANGED
|
@@ -19,6 +19,18 @@ export const Command = Base.extend().include({
|
|
|
19
19
|
|
|
20
20
|
run(name = 'list-commands', ...args){
|
|
21
21
|
return this.create(name, ...args).run();
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
get schedules(){
|
|
25
|
+
if(!this.hasOwnProperty('_schedules')){
|
|
26
|
+
this._schedules = [];
|
|
27
|
+
}
|
|
28
|
+
return this._schedules;
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
schedule(...args){
|
|
32
|
+
this.schedules.push(args);
|
|
33
|
+
return this;
|
|
22
34
|
}
|
|
23
35
|
});
|
|
24
36
|
},
|
|
@@ -36,11 +36,29 @@ export default async ({
|
|
|
36
36
|
await generateFile(`lib/index.js`, () => {
|
|
37
37
|
line();
|
|
38
38
|
line(`import { importAll } from 'pinstripe';`);
|
|
39
|
+
dependencies.forEach((dependency) => {
|
|
40
|
+
if(dependency != 'pinstripe') line(`import '${dependency}';`);
|
|
41
|
+
});
|
|
39
42
|
line();
|
|
40
43
|
line(`importAll(import.meta.url);`);
|
|
41
44
|
line();
|
|
42
45
|
});
|
|
43
46
|
|
|
47
|
+
await generateFile(`README.md`, () => {
|
|
48
|
+
line();
|
|
49
|
+
line(`# ${name}`);
|
|
50
|
+
line();
|
|
51
|
+
line('## Getting started');
|
|
52
|
+
line();
|
|
53
|
+
line('```bash');
|
|
54
|
+
indent(() => {
|
|
55
|
+
line('pinstripe init-database');
|
|
56
|
+
line('pinstripe start-server');
|
|
57
|
+
});
|
|
58
|
+
line('```');
|
|
59
|
+
line();
|
|
60
|
+
});
|
|
61
|
+
|
|
44
62
|
spawnSync('yarn', [ 'add', ...dependencies ], {
|
|
45
63
|
stdio: 'inherit'
|
|
46
64
|
});
|
|
@@ -8,12 +8,12 @@ export default {
|
|
|
8
8
|
|
|
9
9
|
async run(){
|
|
10
10
|
this.pages = {};
|
|
11
|
-
const
|
|
12
|
-
return `/${path.replace(/(^|\/)index$/, '')}`.replace(/^\/+/, '/');
|
|
11
|
+
const urls = Object.keys(View.classes).filter(path => !path.match(/(^|\/)_/)).map(path => {
|
|
12
|
+
return Url.fromString(`/${path.replace(/(^|\/)index$/, '')}`.replace(/^\/+/, '/'));
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
while(
|
|
16
|
-
await this.crawlPage({
|
|
15
|
+
while(urls.length){
|
|
16
|
+
await this.crawlPage({ _url: urls.shift() });
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
const pages = Object.values(this.pages).filter(page => page.status == 200 && Object.keys(page.params).length == 1);
|
|
@@ -25,7 +25,7 @@ export default {
|
|
|
25
25
|
const { params, headers } = pages.shift();
|
|
26
26
|
const contentType = headers['content-type'];
|
|
27
27
|
|
|
28
|
-
const path = params.
|
|
28
|
+
const path = params._url.path;
|
|
29
29
|
let filePath = path.replace(/^\//, '');
|
|
30
30
|
if(filePath.match(/(^|\/)$/)){
|
|
31
31
|
filePath = `${filePath}index`;
|
|
@@ -34,7 +34,7 @@ export default {
|
|
|
34
34
|
filePath = `${filePath}.${mimeTypes.extension(contentType)}`
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
const data = (await this.fetch({
|
|
37
|
+
const data = (await this.fetch({ _url: Url.fromString(path) }))[2];
|
|
38
38
|
|
|
39
39
|
await generateFile(filePath, () => {
|
|
40
40
|
echo(data.join(''));
|
|
@@ -58,7 +58,7 @@ export default {
|
|
|
58
58
|
const urls = this.extractUrls(virtualDom);
|
|
59
59
|
while(urls.length){
|
|
60
60
|
const url = urls.shift();
|
|
61
|
-
await this.crawlPage({ ...url.params,
|
|
61
|
+
await this.crawlPage({ ...url.params, _url: url });
|
|
62
62
|
}
|
|
63
63
|
},
|
|
64
64
|
|
|
@@ -68,7 +68,7 @@ export default {
|
|
|
68
68
|
['src', 'href'].forEach(name => {
|
|
69
69
|
const value = attributes[name];
|
|
70
70
|
if(!value) return;
|
|
71
|
-
const url = Url.fromString(value
|
|
71
|
+
const url = Url.fromString(value);
|
|
72
72
|
if(url.host != 'localhost') return;
|
|
73
73
|
out.push(url);
|
|
74
74
|
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
export default async ({
|
|
3
3
|
cliUtils: { extractArg },
|
|
4
|
-
fsBuilder: { inProjectRootDir, generateFile, line, indent }
|
|
4
|
+
fsBuilder: { inProjectRootDir, generateFile, line, indent },
|
|
5
|
+
app
|
|
5
6
|
}) => {
|
|
6
7
|
let name = extractArg('').replace(/^\//, '');
|
|
7
8
|
if(name == ''){
|
|
@@ -10,7 +11,7 @@ export default async ({
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
if(!name.match(/\.[^\/]+$/)){
|
|
13
|
-
name = `${name}.
|
|
14
|
+
name = `${name}.js`;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
await inProjectRootDir(async () => {
|
|
@@ -21,7 +22,7 @@ export default async ({
|
|
|
21
22
|
line();
|
|
22
23
|
});
|
|
23
24
|
|
|
24
|
-
await generateFile(`lib/views/${name}`, () => {
|
|
25
|
+
await generateFile(`lib/views/${await app}/${name}`, () => {
|
|
25
26
|
line();
|
|
26
27
|
line('export default ({ renderHtml, params }) => renderHtml(`');
|
|
27
28
|
indent(() => {
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export default () => {
|
|
4
|
+
export default ({ commandNames }) => {
|
|
7
5
|
console.log('');
|
|
8
6
|
console.log('The following commands are available:');
|
|
9
7
|
console.log('');
|
|
10
|
-
|
|
8
|
+
commandNames.forEach(commandName => {
|
|
11
9
|
console.log(` * ${chalk.green(commandName)}`);
|
|
12
10
|
});
|
|
13
11
|
console.log('');
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export default () => {
|
|
4
|
+
export default ({ migrationNames }) => {
|
|
7
5
|
console.log('');
|
|
8
6
|
console.log('The following migrations are available:');
|
|
9
7
|
console.log('');
|
|
10
|
-
|
|
8
|
+
migrationNames.forEach(migrationName => {
|
|
11
9
|
console.log(` * ${chalk.green(migrationName)}`);
|
|
12
10
|
});
|
|
13
11
|
console.log('');
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export default () => {
|
|
4
|
+
export default ({ modelNames }) => {
|
|
7
5
|
console.log('');
|
|
8
6
|
console.log('The following models are available:');
|
|
9
7
|
console.log('');
|
|
10
|
-
|
|
8
|
+
modelNames.forEach(modelName => {
|
|
11
9
|
console.log(` * ${chalk.green(modelName)}`);
|
|
12
10
|
});
|
|
13
11
|
console.log('');
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export default () => {
|
|
4
|
+
export default ({ nodeWrapperNames }) => {
|
|
7
5
|
console.log('');
|
|
8
6
|
console.log('The following node wrappers are available:');
|
|
9
7
|
console.log('');
|
|
10
|
-
|
|
8
|
+
nodeWrapperNames.forEach(nodeWrapperName => {
|
|
11
9
|
console.log(` * ${chalk.green(nodeWrapperName)}`);
|
|
12
10
|
});
|
|
13
11
|
console.log('');
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export default () => {
|
|
4
|
+
export default ({ serviceNames }) => {
|
|
7
5
|
console.log('');
|
|
8
6
|
console.log('The following services are available:');
|
|
9
7
|
console.log('');
|
|
10
|
-
|
|
8
|
+
serviceNames.forEach(serviceName => {
|
|
11
9
|
console.log(` * ${chalk.green(serviceName)}`);
|
|
12
10
|
});
|
|
13
11
|
console.log('');
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export default () => {
|
|
4
|
+
export default ({ viewNames }) => {
|
|
7
5
|
console.log('');
|
|
8
6
|
console.log('The following views are available:');
|
|
9
7
|
console.log('');
|
|
10
|
-
|
|
8
|
+
viewNames.forEach(viewName => {
|
|
11
9
|
console.log(` * ${chalk.green(viewName)}`);
|
|
12
10
|
});
|
|
13
11
|
console.log('');
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
import http from 'http';
|
|
3
3
|
import Busboy from 'busboy';
|
|
4
|
-
import {
|
|
5
|
-
const { parse: parseQueryString } = qs;
|
|
4
|
+
import { Url } from '../url.js';
|
|
6
5
|
|
|
7
|
-
export default async ({ fetch }) => {
|
|
6
|
+
export default async ({ fetch, bot }) => {
|
|
8
7
|
const host = process.env.HOST || '127.0.0.1';
|
|
9
8
|
const port = process.env.PORT || 3000;
|
|
10
9
|
|
|
@@ -27,20 +26,23 @@ export default async ({ fetch }) => {
|
|
|
27
26
|
}).listen(port, host, () => {
|
|
28
27
|
console.log(`Pinstripe running at http://${host}:${port}/`)
|
|
29
28
|
});
|
|
29
|
+
|
|
30
|
+
bot.start();
|
|
31
|
+
|
|
32
|
+
return new Promise(() => {});
|
|
30
33
|
};
|
|
31
34
|
|
|
32
35
|
const extractParams = async (request) => {
|
|
33
36
|
const { method, url, headers } = request;
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
const queryString = parseQueryString(matches ? matches[2] : "");
|
|
37
|
+
const _url = Url.fromString(url);
|
|
38
|
+
if(headers['x-host']) _url.host = headers['x-host'];
|
|
37
39
|
const body = method.match(/^POST|PUT|PATCH$/) ? await parseBody(request) : {};
|
|
38
40
|
|
|
39
41
|
return {
|
|
40
|
-
...
|
|
42
|
+
..._url.params,
|
|
41
43
|
...body,
|
|
42
44
|
_method: method,
|
|
43
|
-
|
|
45
|
+
_url,
|
|
44
46
|
_headers: headers
|
|
45
47
|
};
|
|
46
48
|
};
|
|
@@ -207,7 +207,7 @@ Adapter.register('mysql').include({
|
|
|
207
207
|
const table = this._table;
|
|
208
208
|
const database = table._database;
|
|
209
209
|
|
|
210
|
-
await table.create();
|
|
210
|
+
if(!await table.exists()) await table.create();
|
|
211
211
|
|
|
212
212
|
if(!await this.exists()){
|
|
213
213
|
await database.run`
|
|
@@ -281,9 +281,20 @@ Adapter.register('mysql').include({
|
|
|
281
281
|
return this;
|
|
282
282
|
},
|
|
283
283
|
|
|
284
|
-
_generateInsertSql(){
|
|
284
|
+
async _generateInsertSql(){
|
|
285
285
|
this._fields['id'] = crypto.randomUUID();
|
|
286
286
|
this._alteredFields['id'] = this._fields['id'];
|
|
287
|
+
|
|
288
|
+
const database = await this._database;
|
|
289
|
+
const { isMultiTenant } = database;
|
|
290
|
+
const isScopedToTenant = isMultiTenant && !this.constructor.name.match(/^(pinstripe[A-Z]|tenant$)/);
|
|
291
|
+
const tenant = isScopedToTenant && database._environment ? await database._environment.tenant : undefined;
|
|
292
|
+
if(tenant){
|
|
293
|
+
this._fields['tenantId'] = tenant.id;
|
|
294
|
+
this._alteredFields['tenantId'] = tenant.id;
|
|
295
|
+
} else if(isScopedToTenant) {
|
|
296
|
+
return this._database.renderSql`select NULL`;
|
|
297
|
+
}
|
|
287
298
|
|
|
288
299
|
return this._database.renderSql`
|
|
289
300
|
insert into ${this._adapter.escapeIdentifier(Inflector.pluralize(this.constructor.name))}(
|
|
@@ -352,7 +363,7 @@ Adapter.register('mysql').include({
|
|
|
352
363
|
},
|
|
353
364
|
|
|
354
365
|
async first(options = {}){
|
|
355
|
-
return (await this.all({ ...options, limit: Sql.fromString('
|
|
366
|
+
return (await this.all({ ...options, limit: Sql.fromString('1'), offset: Sql.fromString('0') })).pop();
|
|
356
367
|
},
|
|
357
368
|
|
|
358
369
|
async count(options = {}){
|
|
@@ -486,12 +497,32 @@ Adapter.register('mysql').include({
|
|
|
486
497
|
out.push(this.renderSql` from ${joinRoot._fromSql}`);
|
|
487
498
|
}
|
|
488
499
|
|
|
500
|
+
const { isMultiTenant } = this._database;
|
|
501
|
+
const isScopedToTenant = isMultiTenant && !this.constructor.name.match(/^(pinstripe[A-Z]|tenants$)/);
|
|
502
|
+
const tenant = isScopedToTenant ? await this._database._environment.tenant : undefined;
|
|
503
|
+
|
|
489
504
|
if (options.hasOwnProperty('where')){
|
|
490
505
|
if(options.where){
|
|
491
506
|
out.push(this.renderSql` where ${options.where}`);
|
|
507
|
+
if(tenant){
|
|
508
|
+
out.push(this.renderSql` and ${this.tenantId} = uuid_to_bin(${tenant.id})`);
|
|
509
|
+
} else if(isScopedToTenant){
|
|
510
|
+
out.push(this.renderSql` and 1 = 2`);
|
|
511
|
+
}
|
|
512
|
+
} else if(isScopedToTenant){
|
|
513
|
+
out.push(this.renderSql` where 1 = 2`);
|
|
492
514
|
}
|
|
493
515
|
} else if(joinRoot._whereSql.length) {
|
|
494
516
|
out.push(this.renderSql` where ${joinRoot._whereSql}`);
|
|
517
|
+
if(tenant){
|
|
518
|
+
out.push(this.renderSql` and ${this.tenantId} = uuid_to_bin(${tenant.id})`);
|
|
519
|
+
} else if(isScopedToTenant){
|
|
520
|
+
out.push(this.renderSql` and 1 = 2`);
|
|
521
|
+
}
|
|
522
|
+
} else if(tenant){
|
|
523
|
+
out.push(this.renderSql` where ${this.tenantId} = uuid_to_bin(${tenant.id})`);
|
|
524
|
+
} else if(isScopedToTenant){
|
|
525
|
+
out.push(this.renderSql` where 1 = 2`);
|
|
495
526
|
}
|
|
496
527
|
|
|
497
528
|
if(options.hasOwnProperty('orderBy')){
|
|
@@ -506,9 +537,18 @@ Adapter.register('mysql').include({
|
|
|
506
537
|
if(options.limit){
|
|
507
538
|
out.push(this.renderSql` limit ${options.limit}`);
|
|
508
539
|
}
|
|
509
|
-
} else if(joinRoot.
|
|
510
|
-
const {
|
|
511
|
-
out.push(this.renderSql` limit ${
|
|
540
|
+
} else if(joinRoot._pagination) {
|
|
541
|
+
const { pageSize } = joinRoot._pagination;
|
|
542
|
+
out.push(this.renderSql` limit ${pageSize}`);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if(options.hasOwnProperty('offset')){
|
|
546
|
+
if(options.offset){
|
|
547
|
+
out.push(this.renderSql` offset ${options.offset}`);
|
|
548
|
+
}
|
|
549
|
+
} else if(joinRoot._pagination || joinRoot._skipCount) {
|
|
550
|
+
const { page = 1, pageSize = 10 } = joinRoot._pagination || {};
|
|
551
|
+
out.push(this.renderSql` offset ${((page - 1) * pageSize) + joinRoot._skipCount}`);
|
|
512
552
|
}
|
|
513
553
|
|
|
514
554
|
return this.renderSql`${out}`;
|
|
@@ -559,10 +599,16 @@ Adapter.register('mysql').include({
|
|
|
559
599
|
out.push(this.renderSql` order by ${this._orderBySql}`);
|
|
560
600
|
}
|
|
561
601
|
|
|
562
|
-
if(this.
|
|
563
|
-
const {
|
|
564
|
-
out.push(this.renderSql` limit ${
|
|
602
|
+
if(this._pagination) {
|
|
603
|
+
const { pageSize } = this._pagination;
|
|
604
|
+
out.push(this.renderSql` limit ${pageSize}`);
|
|
565
605
|
}
|
|
606
|
+
|
|
607
|
+
if(this._pagination || this._skipCount) {
|
|
608
|
+
const { page = 1, pageSize = 10 } = this._pagination || {};
|
|
609
|
+
out.push(this.renderSql` offset ${((page - 1) * pageSize) + this._skipCount}`);
|
|
610
|
+
}
|
|
611
|
+
|
|
566
612
|
return this._database.renderSql`${out}`;
|
|
567
613
|
}
|
|
568
614
|
}
|
|
@@ -582,8 +628,6 @@ const TYPE_TO_COLUMN_TYPE_MAP = {
|
|
|
582
628
|
integer: "int(11)",
|
|
583
629
|
string: "varchar(255)",
|
|
584
630
|
text: "longtext",
|
|
585
|
-
time: "time",
|
|
586
|
-
timestamp: "datetime"
|
|
587
631
|
};
|
|
588
632
|
|
|
589
633
|
const COLUMN_TYPE_TO_TYPE_MAP = (() => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import sqlite from 'sqlite3';
|
|
3
3
|
import * as crypto from 'crypto';
|
|
4
|
-
import { unlink } from 'fs';
|
|
4
|
+
import { existsSync, unlink } from 'fs';
|
|
5
5
|
import { promisify } from 'util';
|
|
6
6
|
|
|
7
7
|
import { Adapter } from '../adapter.js';
|
|
@@ -14,14 +14,15 @@ import { Table } from '../table.js';
|
|
|
14
14
|
import { Row } from '../row.js';
|
|
15
15
|
|
|
16
16
|
let schemaCache = {};
|
|
17
|
+
let connection;
|
|
17
18
|
|
|
18
19
|
Adapter.register('sqlite').include({
|
|
19
20
|
get connection(){
|
|
20
|
-
if(!
|
|
21
|
+
if(!connection){
|
|
21
22
|
const { filename } = this.config;
|
|
22
|
-
|
|
23
|
+
connection = new sqlite.Database(filename);
|
|
23
24
|
}
|
|
24
|
-
return
|
|
25
|
+
return connection;
|
|
25
26
|
},
|
|
26
27
|
|
|
27
28
|
escapeValue(value){
|
|
@@ -51,13 +52,15 @@ Adapter.register('sqlite').include({
|
|
|
51
52
|
},
|
|
52
53
|
|
|
53
54
|
async drop() {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
if(connection){
|
|
56
|
+
await new Promise((resolve, reject) => {
|
|
57
|
+
connection.close(error => error ? reject(error) : resolve());
|
|
58
|
+
});
|
|
59
|
+
connection = undefined;
|
|
60
|
+
}
|
|
58
61
|
schemaCache = {};
|
|
59
62
|
this._sessionCache = {};
|
|
60
|
-
await promisify(unlink)(this._adapter.config.filename);
|
|
63
|
+
if(existsSync(this._adapter.config.filename)) await promisify(unlink)(this._adapter.config.filename);
|
|
61
64
|
},
|
|
62
65
|
|
|
63
66
|
async tables(){
|
|
@@ -80,9 +83,7 @@ Adapter.register('sqlite').include({
|
|
|
80
83
|
},
|
|
81
84
|
|
|
82
85
|
async destroy() {
|
|
83
|
-
|
|
84
|
-
this._adapter.connection.close(error => error ? reject(error) : resolve());
|
|
85
|
-
});
|
|
86
|
+
// do nothing
|
|
86
87
|
},
|
|
87
88
|
|
|
88
89
|
async _fetchRows(sql){
|
|
@@ -138,7 +139,19 @@ Adapter.register('sqlite').include({
|
|
|
138
139
|
while(rows.length){
|
|
139
140
|
const { _type, ...fields } = rows.shift();
|
|
140
141
|
if(_type !== undefined){
|
|
141
|
-
|
|
142
|
+
const table = await this[Inflector.pluralize(_type)];
|
|
143
|
+
const mappedFields = {};
|
|
144
|
+
const names = Object.keys(fields);
|
|
145
|
+
while(names.length){
|
|
146
|
+
const name = names.shift();
|
|
147
|
+
const column = await table[name];
|
|
148
|
+
if(column && ['date', 'datetime'].includes(await column.type())){
|
|
149
|
+
mappedFields[name] = new Date(fields[name]);
|
|
150
|
+
} else {
|
|
151
|
+
mappedFields[name] = fields[name];
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
out.push(await Row.create(_type, this, mappedFields));
|
|
142
155
|
} else {
|
|
143
156
|
out.push(fields);
|
|
144
157
|
}
|
|
@@ -169,7 +182,7 @@ Adapter.register('sqlite').include({
|
|
|
169
182
|
const table = this._table;
|
|
170
183
|
const database = table._database;
|
|
171
184
|
|
|
172
|
-
await table.create();
|
|
185
|
+
if(!await table.exists()) await table.create();
|
|
173
186
|
|
|
174
187
|
if(!await this.exists()){
|
|
175
188
|
let defaultSql = options.default;
|
|
@@ -252,10 +265,21 @@ Adapter.register('sqlite').include({
|
|
|
252
265
|
return this;
|
|
253
266
|
},
|
|
254
267
|
|
|
255
|
-
_generateInsertSql(){
|
|
268
|
+
async _generateInsertSql(){
|
|
256
269
|
this._fields['id'] = crypto.randomUUID();
|
|
257
270
|
this._alteredFields['id'] = this._fields['id'];
|
|
258
|
-
|
|
271
|
+
|
|
272
|
+
const database = await this._database;
|
|
273
|
+
const { isMultiTenant } = database;
|
|
274
|
+
const isScopedToTenant = isMultiTenant && !this.constructor.name.match(/^(pinstripe[A-Z]|tenant$)/);
|
|
275
|
+
const tenant = isScopedToTenant && database._environment ? await database._environment.tenant : undefined;
|
|
276
|
+
if(tenant){
|
|
277
|
+
this._fields['tenantId'] = tenant.id;
|
|
278
|
+
this._alteredFields['tenantId'] = tenant.id;
|
|
279
|
+
} else if(isScopedToTenant) {
|
|
280
|
+
return this._database.renderSql`select NULL`;
|
|
281
|
+
}
|
|
282
|
+
|
|
259
283
|
return this._database.renderSql`
|
|
260
284
|
insert into ${this._adapter.escapeIdentifier(Inflector.pluralize(this.constructor.name))}(
|
|
261
285
|
${Object.keys(this._alteredFields).map((key, i) =>
|
|
@@ -323,7 +347,7 @@ Adapter.register('sqlite').include({
|
|
|
323
347
|
},
|
|
324
348
|
|
|
325
349
|
async first(options = {}){
|
|
326
|
-
return (await this.all({ ...options, limit: Sql.fromString('
|
|
350
|
+
return (await this.all({ ...options, limit: Sql.fromString('1'), offset: Sql.fromString('0') })).pop();
|
|
327
351
|
},
|
|
328
352
|
|
|
329
353
|
async count(options = {}){
|
|
@@ -345,6 +369,8 @@ Adapter.register('sqlite').include({
|
|
|
345
369
|
type = 'primary_key';
|
|
346
370
|
} else if(name == 'id'){
|
|
347
371
|
type = 'alternate_key';
|
|
372
|
+
} else if(name.match(/.+Id$/)){
|
|
373
|
+
type = 'foreign_key';
|
|
348
374
|
} else {
|
|
349
375
|
type = COLUMN_TYPE_TO_TYPE_MAP[row.type] || 'string';
|
|
350
376
|
}
|
|
@@ -444,23 +470,43 @@ Adapter.register('sqlite').include({
|
|
|
444
470
|
}
|
|
445
471
|
out.push(this.renderSql`${Inflector.singularize(this.constructor.name)} as \`_type\``);
|
|
446
472
|
}
|
|
447
|
-
|
|
448
|
-
const joinRoot = this._joinRoot;
|
|
449
473
|
|
|
474
|
+
const joinRoot = this._joinRoot;
|
|
450
475
|
if(options.hasOwnProperty('from')){
|
|
451
476
|
if(options.from){
|
|
452
477
|
out.push(this.renderSql` from ${options.from}`);
|
|
453
478
|
}
|
|
454
479
|
} else {
|
|
455
480
|
out.push(this.renderSql` from ${joinRoot._fromSql}`);
|
|
481
|
+
|
|
456
482
|
}
|
|
483
|
+
|
|
484
|
+
const { isMultiTenant } = this._database;
|
|
485
|
+
const isScopedToTenant = isMultiTenant && !this.constructor.name.match(/^(pinstripe[A-Z]|tenants$)/);
|
|
486
|
+
const tenant = isScopedToTenant ? await this._database._environment.tenant : undefined;
|
|
457
487
|
|
|
458
488
|
if (options.hasOwnProperty('where')){
|
|
459
489
|
if(options.where){
|
|
460
490
|
out.push(this.renderSql` where ${options.where}`);
|
|
491
|
+
if(tenant){
|
|
492
|
+
out.push(this.renderSql` and ${this.tenantId} = ${tenant.id}`);
|
|
493
|
+
} else if(isScopedToTenant){
|
|
494
|
+
out.push(this.renderSql` where 1 = 2`);
|
|
495
|
+
}
|
|
496
|
+
} else if(isScopedToTenant){
|
|
497
|
+
out.push(this.renderSql` where 1 = 2`);
|
|
461
498
|
}
|
|
462
499
|
} else if(joinRoot._whereSql.length) {
|
|
463
500
|
out.push(this.renderSql` where ${joinRoot._whereSql}`);
|
|
501
|
+
if(tenant){
|
|
502
|
+
out.push(this.renderSql` and ${this.tenantId} = ${tenant.id}`);
|
|
503
|
+
} else if(isScopedToTenant){
|
|
504
|
+
out.push(this.renderSql` and 1 = 2`);
|
|
505
|
+
}
|
|
506
|
+
} else if(tenant){
|
|
507
|
+
out.push(this.renderSql` where ${this.tenantId} = ${tenant.id}`);
|
|
508
|
+
} else if(isScopedToTenant){
|
|
509
|
+
out.push(this.renderSql` where 1 = 2`);
|
|
464
510
|
}
|
|
465
511
|
|
|
466
512
|
if(options.hasOwnProperty('orderBy')){
|
|
@@ -475,9 +521,18 @@ Adapter.register('sqlite').include({
|
|
|
475
521
|
if(options.limit){
|
|
476
522
|
out.push(this.renderSql` limit ${options.limit}`);
|
|
477
523
|
}
|
|
478
|
-
} else if(joinRoot.
|
|
479
|
-
const {
|
|
480
|
-
out.push(this.renderSql` limit ${
|
|
524
|
+
} else if(joinRoot._pagination) {
|
|
525
|
+
const { pageSize } = joinRoot._pagination;
|
|
526
|
+
out.push(this.renderSql` limit ${pageSize}`);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if(options.hasOwnProperty('offset')){
|
|
530
|
+
if(options.offset){
|
|
531
|
+
out.push(this.renderSql` offset ${options.offset}`);
|
|
532
|
+
}
|
|
533
|
+
} else if(joinRoot._pagination || joinRoot._skipCount) {
|
|
534
|
+
const { page = 1, pageSize = 10 } = joinRoot._pagination || {};
|
|
535
|
+
out.push(this.renderSql` offset ${((page - 1) * pageSize) + joinRoot._skipCount}`);
|
|
481
536
|
}
|
|
482
537
|
|
|
483
538
|
return this.renderSql`${out}`;
|
|
@@ -527,11 +582,17 @@ Adapter.register('sqlite').include({
|
|
|
527
582
|
if(this._orderBySql.length){
|
|
528
583
|
out.push(this.renderSql` order by ${this._orderBySql}`);
|
|
529
584
|
}
|
|
530
|
-
|
|
531
|
-
if(this.
|
|
532
|
-
const {
|
|
533
|
-
out.push(this.renderSql` limit ${
|
|
585
|
+
|
|
586
|
+
if(this._pagination) {
|
|
587
|
+
const { pageSize } = this._pagination;
|
|
588
|
+
out.push(this.renderSql` limit ${pageSize}`);
|
|
534
589
|
}
|
|
590
|
+
|
|
591
|
+
if(this._pagination || this._skipCount) {
|
|
592
|
+
const { page = 1, pageSize = 10 } = this._pagination || {};
|
|
593
|
+
out.push(this.renderSql` offset ${((page - 1) * pageSize) + this._skipCount}`);
|
|
594
|
+
}
|
|
595
|
+
|
|
535
596
|
return this._database.renderSql`${out}`;
|
|
536
597
|
}
|
|
537
598
|
}
|
|
@@ -551,8 +612,6 @@ const TYPE_TO_COLUMN_TYPE_MAP = {
|
|
|
551
612
|
integer: "integer",
|
|
552
613
|
string: "varchar",
|
|
553
614
|
text: "text",
|
|
554
|
-
time: "time",
|
|
555
|
-
timestamp: "datetime"
|
|
556
615
|
};
|
|
557
616
|
|
|
558
617
|
const COLUMN_TYPE_TO_TYPE_MAP = (() => {
|
|
@@ -7,8 +7,6 @@ export const ALLOWED_TABLE_ADAPTER_COLUMN_TYPES = [
|
|
|
7
7
|
'float',
|
|
8
8
|
'integer',
|
|
9
9
|
'string',
|
|
10
|
-
'time',
|
|
11
|
-
'timestamp'
|
|
12
10
|
];
|
|
13
11
|
|
|
14
12
|
export const TYPE_TO_DEFAULT_VALUE_MAP = {
|
|
@@ -32,8 +30,6 @@ export const COLUMN_TYPE_TO_FORM_FIELD_TYPE_MAP = {
|
|
|
32
30
|
integer: "number",
|
|
33
31
|
string: "text",
|
|
34
32
|
text: "textarea",
|
|
35
|
-
time: "datetime-local",
|
|
36
|
-
timestamp: "datetime-local"
|
|
37
33
|
};
|
|
38
34
|
|
|
39
35
|
export const RESERVED_WORDS = [
|