kreftjs 0.0.2-beta → 0.0.4-beta
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/README.md +12 -12
- package/bin/add.js +14 -14
- package/bin/artisan-more.js +41 -41
- package/bin/artisan.js +41 -41
- package/bin/backend/command.js +1 -1
- package/bin/backend/factory.js +1 -1
- package/bin/backend/index.js +3 -3
- package/bin/backend/model.js +2 -2
- package/bin/backend/notifications.js +1 -1
- package/bin/backend/queue.js +2 -2
- package/bin/backend/schedule.js +2 -2
- package/bin/backend/seo-pages.js +1 -1
- package/bin/backend/storage.js +1 -1
- package/bin/backend/support.js +4 -4
- package/bin/backend/typescript.js +1 -1
- package/bin/build.js +11 -11
- package/bin/db-api.js +3 -3
- package/bin/db.js +1 -1
- package/bin/deps.js +6 -6
- package/bin/electron.js +12 -12
- package/bin/env.js +2 -2
- package/bin/guard.js +2 -2
- package/bin/kreftjs-config.js +1 -1
- package/bin/kreftjs.js +105 -109
- package/bin/layout.js +1 -1
- package/bin/no-auth.js +4 -4
- package/bin/prerender.js +4 -4
- package/bin/server-routes.js +2 -2
- package/bin/ssr.js +2 -2
- package/bin/terminal.js +1 -1
- package/bin/to-ts.js +1 -1
- package/bin/update.js +17 -17
- package/bin/urls-file.js +1 -1
- package/bin/vite.js +11 -11
- package/bin/win-spawn.js +39 -0
- package/docs/Docs.kreftjs +1 -1
- package/docs/README.md +140 -123
- package/kreftjs.d.ts +1 -1
- package/kreftjs.js +18 -18
- package/package.json +3 -4
- package/templates/app/README.md +20 -20
- package/templates/app/backend/database/migrations/0001_01_01_000000_create_users_table.js +1 -1
- package/templates/app/backend/database/migrations/0001_01_01_000002_create_jobs_table.js +1 -1
- package/templates/app/backend/database/seeders/DatabaseSeeder.js +1 -1
- package/templates/app/backend/routes/api.js +1 -1
- package/templates/app/backend/routes/console.js +2 -2
- package/templates/app/frontend/errors/ErrorMonitor.kreftjs +2 -2
- package/templates/app/frontend/index.html +1 -1
- package/templates/app/frontend/kreftjs.d.ts +1 -1
- package/templates/app/frontend/kreftjs.js +18 -18
- package/templates/app/frontend/pages/Dashboard.kreftjs +1 -1
- package/templates/app/frontend/pages/ErrorMonitor.kreftjs +1 -1
- package/templates/app/frontend/pages/Home.kreftjs +2 -2
- package/templates/errors/ErrorMonitor.kreftjs +1 -1
package/README.md
CHANGED
|
@@ -22,8 +22,8 @@ action add() { count++ }
|
|
|
22
22
|
You need [Node.js](https://nodejs.org) 22.5 or newer.
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
npm i -g kreftjs # once: makes the `
|
|
26
|
-
|
|
25
|
+
npm i -g kreftjs # once: makes the `kreft` command available everywhere
|
|
26
|
+
kreft new my-app # asks: JavaScript or TypeScript?
|
|
27
27
|
cd my-app
|
|
28
28
|
npm run dev
|
|
29
29
|
```
|
|
@@ -38,7 +38,7 @@ Open the address in the terminal (normally `http://localhost:8000`). A new proje
|
|
|
38
38
|
| **Router** | Nested routes, layouts, guards, error pages, clean URLs. |
|
|
39
39
|
| **Backend** | `Route`, controllers, form requests, policies, models with relations, soft deletes and scopes, migrations, seeders, factories, jobs, mail, notifications, cache, Redis, file storage, scheduling. |
|
|
40
40
|
| **Authentication** | Cookie sessions with CSRF protection, API tokens with permissions, device list, two-factor codes, email verification, account lockout. |
|
|
41
|
-
| **Tooling** | `
|
|
41
|
+
| **Tooling** | `kreft dev` with hot reload, DevTools, an error monitor, `kreft build` (CSP-safe), prerendering, TypeScript, Vite and Electron support, a safer `kreft add`, an MCP server for AI agents. |
|
|
42
42
|
|
|
43
43
|
## Learn more
|
|
44
44
|
|
|
@@ -49,7 +49,7 @@ Open the address in the terminal (normally `http://localhost:8000`). A new proje
|
|
|
49
49
|
| Add an API and a database | [Routing](docs/README.md#routing), [Models](docs/README.md#models-and-the-database), [Migrations](docs/README.md#migrations-seeders-and-factories) |
|
|
50
50
|
| Understand sign-in | [Authentication](docs/README.md#authentication) |
|
|
51
51
|
| Go to production | [Build and deploy](docs/README.md#build-and-deploy), [Production checklist](docs/README.md#production-checklist) |
|
|
52
|
-
| Look up a command | [Command line](docs/README.md#command-line), then `
|
|
52
|
+
| Look up a command | [Command line](docs/README.md#command-line), then `kreft list` |
|
|
53
53
|
|
|
54
54
|
## The demo
|
|
55
55
|
|
|
@@ -60,9 +60,9 @@ In this repository, `npm run dev` serves live examples of every feature at **htt
|
|
|
60
60
|
```text
|
|
61
61
|
kreftjs.js, kreftjs.d.ts the browser runtime and its TypeScript types
|
|
62
62
|
server.js, server.d.ts the backend entry point: require('kreftjs/server')
|
|
63
|
-
bin/ the `
|
|
63
|
+
bin/ the `kreft` command and the backend modules (bin/backend/)
|
|
64
64
|
tools/ hot reload client, DevTools, Vite plugin
|
|
65
|
-
templates/app/ the project that `
|
|
65
|
+
templates/app/ the project that `kreft new` creates
|
|
66
66
|
editors/vscode/ syntax colours and snippets for .kreftjs files
|
|
67
67
|
demo/ the interactive demo
|
|
68
68
|
docs/ the manual (README.md) and its viewer (Docs.kreftjs)
|
|
@@ -71,23 +71,23 @@ test/ security regression tests: npm test
|
|
|
71
71
|
|
|
72
72
|
## Security
|
|
73
73
|
|
|
74
|
-
Views are escaped, logins use HttpOnly cookies with CSRF protection, passwords use scrypt, and `
|
|
74
|
+
Views are escaped, logins use HttpOnly cookies with CSRF protection, passwords use scrypt, and `kreft dev` only answers your own computer. The manual has a [Security overview](docs/README.md#security-overview) and a [Production checklist](docs/README.md#production-checklist). Run `npm test` to run the security regression tests.
|
|
75
75
|
|
|
76
76
|
## Migrating from Som.js
|
|
77
77
|
|
|
78
|
-
Kreft is Som.js under a new name: the package is `kreftjs` (version 0.0.
|
|
78
|
+
Kreft is Som.js under a new name: the package is `kreftjs` (version 0.0.4-beta), the command is `kreft` (`kreftjs` works too), the global is `Kreftjs` and component files end in `.kreftjs`. The old names keep working, so an existing project runs without edits. New files and everything the tools write use the new names. Move over when it suits you. (`kreft` was an earlier spelling of the new name; it is accepted in the same places.)
|
|
79
79
|
|
|
80
80
|
| Som.js | Kreft | Still works? |
|
|
81
81
|
|---|---|---|
|
|
82
|
-
| `npm i -g somjs` | `npm i -g kreftjs` |
|
|
83
|
-
| `som dev`, `som build`, `som make:page` … | `
|
|
82
|
+
| `npm i -g somjs` | `npm i -g kreftjs` | `kreftjs` is another name for `kreft`. Kreft does not install a `som` command, so it does not clash with an old Som.js (remove that with `npm rm -g somjs`). |
|
|
83
|
+
| `som dev`, `som build`, `som make:page` … | `kreft dev`, `kreft build`, `kreft make:page` … | Replace `som` with `kreftjs`. |
|
|
84
84
|
| `Counter.som` (and `Counter.kreft`) | `Counter.kreftjs` | Yes. All three extensions compile; a name that is missing with one extension is tried with the others (`layouts/app.kreftjs` finds `layouts/app.som`). |
|
|
85
85
|
| `s-if`, `s-each`, `s-model` … (every `s-` directive) | `k-if`, `k-each`, `k-model` … | Yes. Both prefixes are read in templates. |
|
|
86
86
|
| `<som-view>`, `<som-link>`, `<som-wait>`, `<som-component>`, `<som-yield>`, `<som-section>`, `<som-portal>` | `<kreft-view>`, `<kreft-link>` … | Yes. Both tag names work. |
|
|
87
87
|
| `Som` (global object) | `Kreftjs` | `Som` and `Kreft` point to the same object. `Som` is deprecated. |
|
|
88
88
|
| `Som.compileSom(source)` | `Kreftjs.compile(source)` | `compileSom` is kept as an alias. |
|
|
89
89
|
| `window.somDesktop` | `window.kreftDesktop` | Same object under both names. |
|
|
90
|
-
| `som.js`, `som.d.ts` in your project | `kreftjs.js`, `kreftjs.d.ts` | An existing `som.js` (or `kreft.js`) keeps working; `
|
|
90
|
+
| `som.js`, `som.d.ts` in your project | `kreftjs.js`, `kreftjs.d.ts` | An existing `som.js` (or `kreft.js`) keeps working; `kreft update` rewrites it in place with the new runtime. |
|
|
91
91
|
| `som.config.json`, `config/som.json` | `kreftjs.config.json`, `config/kreftjs.json` | Yes: `kreft.config.json` and `som.config.json` are read too (the new name wins). |
|
|
92
92
|
| `require('somjs/server')` | `require('kreftjs/server')` | Also accepted: `kreft/server`, `@kreft/server`, `somjs/server`, `som/server`, `@somjs/server`. |
|
|
93
93
|
| `som_session`, `som_2fa`, `som_bypass` cookies | `kreft_session`, `kreft_2fa`, `kreft_bypass` | `app/Auth/Session.js` belongs to your project, so an existing project keeps its `som_` names until you edit that file. The framework's CSRF check and the maintenance-mode bypass accept both. New projects use the new names. |
|
|
@@ -95,7 +95,7 @@ Kreft is Som.js under a new name: the package is `kreftjs` (version 0.0.2-beta),
|
|
|
95
95
|
| `window.__SOM_ENV__`, `__SOM_DEV__`, `__SOM_LANG__`, `__SOM_VITE__` | `__KREFT_ENV__` … | Read by the runtime under both names. |
|
|
96
96
|
| `/__som/…` dev-server addresses | `/__kreft/…` | The dev server answers both. |
|
|
97
97
|
| Vite: `import som from 'somjs/vite'` | `import kreftjs from 'kreftjs/vite'` | The plugin also exports `kreft`, `som` and `somPlugin`, and handles `.kreft` and `.som` files. |
|
|
98
|
-
| VS Code extension `.som` language | `.kreftjs` language | The new extension colours `.kreft` and `.som` files as well. Run `
|
|
98
|
+
| VS Code extension `.som` language | `.kreftjs` language | The new extension colours `.kreft` and `.som` files as well. Run `kreft vscode` to install it. |
|
|
99
99
|
|
|
100
100
|
Kept on purpose, with a TODO for 3.1: the `som_migrations` table (renaming it would lose the migration history of every deployed database), the `som:` prefix of `saved`/`Kreftjs.storage` keys in `localStorage`, and the `som:queue:` / `som:delayed:` Redis keys of the queue. Renaming them needs a migration with a fallback read, so it waits for a minor release.
|
|
101
101
|
|
package/bin/add.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
*
|
|
2
|
+
* kreft add <packages...> / kreft install <packages...>
|
|
3
3
|
*
|
|
4
4
|
* Enterprise-Grade Security Gatekeeper & Package Installer for Kreft:
|
|
5
5
|
*
|
|
@@ -43,7 +43,7 @@ const fs = require('fs');
|
|
|
43
43
|
const path = require('path');
|
|
44
44
|
const https = require('https');
|
|
45
45
|
const readline = require('readline');
|
|
46
|
-
const {
|
|
46
|
+
const { runSync } = require('./win-spawn.js');
|
|
47
47
|
const { layout } = require('./layout.js');
|
|
48
48
|
const pkg = require('../package.json');
|
|
49
49
|
|
|
@@ -63,7 +63,7 @@ const BUILTIN_ALTERNATIVES = {
|
|
|
63
63
|
dotenv: 'Kreft automatically parses .env into process.env & Kreftjs.env natively out of the box.',
|
|
64
64
|
'body-parser': 'Kreft natively parses JSON, form-data, and raw payloads in request.js.',
|
|
65
65
|
cors: 'Kreft routes and dev server include built-in CORS origin and preflight validation.',
|
|
66
|
-
nodemon: 'Kreft includes built-in file watching, hot reloading, and dev tools (
|
|
66
|
+
nodemon: 'Kreft includes built-in file watching, hot reloading, and dev tools (kreft dev).',
|
|
67
67
|
sqlite3: 'Kreft includes a native zero-dependency SQLite driver (built-in). Use better-sqlite3 only if you need C-bindings.',
|
|
68
68
|
'cookie-parser': 'Kreft natively parses, signs, and sets cookies on request/response objects.',
|
|
69
69
|
multer: 'Kreft natively handles multipart file uploads via request.file() and request.files().',
|
|
@@ -449,7 +449,7 @@ module.exports = async function add(args = []) {
|
|
|
449
449
|
installArgs.push(...targets);
|
|
450
450
|
|
|
451
451
|
console.log(` ${cyan('Executing:')} npm ${installArgs.join(' ')}\n`);
|
|
452
|
-
const res =
|
|
452
|
+
const res = runSync(npmCmd, installArgs, { stdio: 'inherit', cwd: project });
|
|
453
453
|
|
|
454
454
|
if (res.status !== 0) {
|
|
455
455
|
console.error(`\n ${red('✖ Failed to install packages.')}`);
|
|
@@ -739,7 +739,7 @@ function getInstalledPackageJson(projectDir, pkgName) {
|
|
|
739
739
|
|
|
740
740
|
function getAuditReport(npmCmd, projectDir) {
|
|
741
741
|
try {
|
|
742
|
-
const res =
|
|
742
|
+
const res = runSync(npmCmd, ['audit', '--json'], { cwd: projectDir, encoding: 'utf8' });
|
|
743
743
|
if (res.stdout) {
|
|
744
744
|
return JSON.parse(res.stdout);
|
|
745
745
|
}
|
|
@@ -889,7 +889,7 @@ function displayPackageInfoCards(projectDir, evaluationReports, auditData) {
|
|
|
889
889
|
type: 'update',
|
|
890
890
|
icon: yellow('🔔 Update Available:'),
|
|
891
891
|
message: `Version v${latestVersion} is out! (Installed: v${installedVersion})`,
|
|
892
|
-
action: `Run "
|
|
892
|
+
action: `Run "kreft update ${name}" to upgrade to the latest version.`,
|
|
893
893
|
});
|
|
894
894
|
} else {
|
|
895
895
|
notifications.push({
|
|
@@ -1029,18 +1029,18 @@ function runSecurityAudit(npmCmd, project, auditData) {
|
|
|
1029
1029
|
|
|
1030
1030
|
function printHelp() {
|
|
1031
1031
|
console.log(`
|
|
1032
|
-
${bold('
|
|
1032
|
+
${bold('kreft add <packages...>') } (alias: ${dim('kreft install')}, ${dim('kreft i')})
|
|
1033
1033
|
|
|
1034
1034
|
Enterprise-Grade Security Gatekeeper & Package Installer for Kreft.
|
|
1035
1035
|
|
|
1036
1036
|
Usage:
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1037
|
+
kreft add <packages...> Install package(s) with full security gatekeeper verification
|
|
1038
|
+
kreft add pg mysql2 Install verified database drivers
|
|
1039
|
+
kreft add -D vitest Install as devDependency
|
|
1040
|
+
kreft add --check <pkg> Inspect package security report without installing (alias: --dry-run)
|
|
1041
|
+
kreft add -y <pkg> Auto-confirm unverified packages in CI/CD (alias: --yes)
|
|
1042
|
+
kreft add --allow-scripts <pkg> Allow lifecycle scripts (postinstall/build)
|
|
1043
|
+
kreft add --force <pkg> Bypass security warnings & redundancy checks
|
|
1044
1044
|
|
|
1045
1045
|
Security Protections & Verification Rules:
|
|
1046
1046
|
1. Zero-Dependency Redundancy Guard:
|
package/bin/artisan-more.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Full-stack CLI commands:
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
4
|
+
* kreft list every command (yours too)
|
|
5
|
+
* kreft about versions, settings, folders
|
|
6
|
+
* kreft tinker a console with your models, DB, Cache… (await works)
|
|
7
|
+
* kreft key:generate a new APP_KEY in .env
|
|
8
|
+
* kreft down [--secret=x] / kreft up maintenance mode
|
|
9
|
+
* kreft db:wipe drop every table
|
|
10
|
+
* kreft migrate:refresh [--seed] undo every migration, then run them again
|
|
11
|
+
* kreft migrate:reset undo every migration
|
|
12
|
+
* kreft queue:work [--queue=a,b] [--once] [--stop-when-empty]
|
|
13
|
+
* kreft queue:failed / queue:retry <id|all> / queue:flush / queue:table
|
|
14
|
+
* kreft schedule:work / schedule:run / schedule:list
|
|
15
|
+
* kreft cache:clear / cache:table
|
|
16
|
+
* kreft notifications:table
|
|
17
|
+
* kreft event:list
|
|
18
|
+
* kreft make:job, make:event, make:listener, make:mail, make:notification, make:policy, make:request,
|
|
19
19
|
* make:resource, make:factory, make:rule, make:command, make:observer
|
|
20
|
-
* Your own commands: app/Console/Commands/*.js (
|
|
20
|
+
* Your own commands: app/Console/Commands/*.js (kreft make:command SendReport)
|
|
21
21
|
*/
|
|
22
22
|
const fs = require('fs');
|
|
23
23
|
const path = require('path');
|
|
@@ -42,7 +42,7 @@ async function withApp(o, ctx, fn) {
|
|
|
42
42
|
|
|
43
43
|
/* ------------------------------------------------------------------ built-in migration tables */
|
|
44
44
|
const tableStubs = {
|
|
45
|
-
jobs: `// The queue's tables: jobs waiting (
|
|
45
|
+
jobs: `// The queue's tables: jobs waiting (kreft queue:work, or kreft dev, runs them) and jobs that failed
|
|
46
46
|
module.exports = {
|
|
47
47
|
up(schema) {
|
|
48
48
|
schema.create('jobs', (table) => {
|
|
@@ -110,7 +110,7 @@ function tableCommand(kind, fileName) {
|
|
|
110
110
|
const exists = fs.existsSync(dir) && fs.readdirSync(dir).some((f) => f.includes(fileName));
|
|
111
111
|
if (exists) return console.log(` There is already a ${fileName} migration.`);
|
|
112
112
|
write(path.join(dir, `${nextStamp(dir)}_${fileName}.js`), tableStubs[kind]);
|
|
113
|
-
console.log(dim(' Then:
|
|
113
|
+
console.log(dim(' Then: kreft migrate'));
|
|
114
114
|
};
|
|
115
115
|
}
|
|
116
116
|
commands['queue:table'] = tableCommand('jobs', 'create_jobs_table');
|
|
@@ -124,7 +124,7 @@ commands['queue:work'] = async function (args) {
|
|
|
124
124
|
const queues = (raw.split('=')[1] || 'default').split(',');
|
|
125
125
|
await withApp(o, ctx, async () => {
|
|
126
126
|
const q = require('./backend/queue.js');
|
|
127
|
-
if (!(await q.tablesReady())) return console.error(' No jobs table. Make it with:
|
|
127
|
+
if (!(await q.tablesReady())) return console.error(' No jobs table. Make it with: kreft queue:table, then kreft migrate');
|
|
128
128
|
console.log(` Working on the ${queues.join(', ')} queue${queues.length > 1 ? 's' : ''}. Stop with Ctrl+C.\n`);
|
|
129
129
|
await q.work({ queues, once: o.flags.has('once'), stopWhenEmpty: o.flags.has('stop-when-empty'), log: console.log });
|
|
130
130
|
});
|
|
@@ -135,13 +135,13 @@ commands['queue:failed'] = async function (args) {
|
|
|
135
135
|
const rows = await db.query('SELECT id, queue, payload, exception, failed_at FROM failed_jobs ORDER BY id DESC LIMIT 50');
|
|
136
136
|
if (!rows.length) return console.log(' No failed jobs.');
|
|
137
137
|
for (const r of rows) console.log(` ${String(r.id).padEnd(5)} ${String(r.failed_at).slice(0, 19)} ${JSON.parse(r.payload).class} ${dim(String(r.exception).split('\n')[0].slice(0, 90))}`);
|
|
138
|
-
console.log(dim('\n Try one again:
|
|
138
|
+
console.log(dim('\n Try one again: kreft queue:retry <id> (or all)'));
|
|
139
139
|
});
|
|
140
140
|
};
|
|
141
141
|
commands['queue:retry'] = async function (args) {
|
|
142
142
|
const o = options(args), ctx = context(o);
|
|
143
143
|
const which = o._[0];
|
|
144
|
-
if (!which) return console.error('
|
|
144
|
+
if (!which) return console.error(' kreft queue:retry <id> or kreft queue:retry all');
|
|
145
145
|
await withDb(o, ctx, async (db) => {
|
|
146
146
|
const { Builder } = require('./backend/query.js');
|
|
147
147
|
const rows = which === 'all' ? await new Builder('failed_jobs').get() : await new Builder('failed_jobs').where('id', which).get();
|
|
@@ -169,10 +169,10 @@ commands['queue:stop'] = async function (args) {
|
|
|
169
169
|
q.stop(target);
|
|
170
170
|
if (target === 'all' || target === '*') {
|
|
171
171
|
console.log(` ${red('STOPPED')} All running queues have been stopped.`);
|
|
172
|
-
console.log(dim(' Resume anytime with:
|
|
172
|
+
console.log(dim(' Resume anytime with: kreft queue:resume --all'));
|
|
173
173
|
} else {
|
|
174
174
|
console.log(` ${yellow('STOPPED')} Queue "${target}" has been stopped.`);
|
|
175
|
-
console.log(dim(` Resume with:
|
|
175
|
+
console.log(dim(` Resume with: kreft queue:resume ${target}`));
|
|
176
176
|
}
|
|
177
177
|
};
|
|
178
178
|
commands['queue:pause'] = commands['queue:stop'];
|
|
@@ -417,7 +417,7 @@ commands['sitemap'] = async function (args) {
|
|
|
417
417
|
}
|
|
418
418
|
bar.complete(`${routes.length} URLs indexed`);
|
|
419
419
|
|
|
420
|
-
if (/^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:|\/|$)/.test(baseUrl)) console.log(` WARN The sitemap uses ${baseUrl}. Search engines need your real address:
|
|
420
|
+
if (/^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:|\/|$)/.test(baseUrl)) console.log(` WARN The sitemap uses ${baseUrl}. Search engines need your real address: kreft sitemap --url https://example.com (or set APP_URL)`);
|
|
421
421
|
const { SEO } = require('./backend/support.js');
|
|
422
422
|
const xml = SEO.sitemap(routes, { baseUrl });
|
|
423
423
|
const robots = SEO.robots({ allow: '/', sitemapUrl: `${baseUrl.replace(/\/+$/, '')}/sitemap.xml` });
|
|
@@ -602,7 +602,7 @@ commands.down = function (args) {
|
|
|
602
602
|
const data = { time: Math.floor(Date.now() / 1000), retry: Number(get('retry')) || null, message: get('message') || 'We are making the site better. Please come back soon.', secret };
|
|
603
603
|
fs.mkdirSync(path.dirname(downFile(ctx)), { recursive: true });
|
|
604
604
|
fs.writeFileSync(downFile(ctx), JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
605
|
-
console.log(` ${yellow('DOWN')} The app is in maintenance mode: visitors see a 503 page.${secret ? `\n You can still use it at: /${secret} (once; a cookie remembers you)` : ''}\n Back with:
|
|
605
|
+
console.log(` ${yellow('DOWN')} The app is in maintenance mode: visitors see a 503 page.${secret ? `\n You can still use it at: /${secret} (once; a cookie remembers you)` : ''}\n Back with: kreft up`);
|
|
606
606
|
};
|
|
607
607
|
commands.up = function (args) {
|
|
608
608
|
const o = options(args), ctx = context(o);
|
|
@@ -621,7 +621,7 @@ commands.about = function (args) {
|
|
|
621
621
|
row('Node.js', process.version);
|
|
622
622
|
row('App name', process.env.APP_NAME || '-');
|
|
623
623
|
row('App URL', process.env.APP_URL || '-');
|
|
624
|
-
row('APP_KEY', process.env.APP_KEY ? green('set') : red('missing (
|
|
624
|
+
row('APP_KEY', process.env.APP_KEY ? green('set') : red('missing (kreft key:generate)'));
|
|
625
625
|
row('TypeScript', ok(has(path.join(ctx.project, 'tsconfig.json'))));
|
|
626
626
|
row('Maintenance mode', has(downFile(ctx)) ? yellow('down') : 'live');
|
|
627
627
|
console.log('\n Drivers');
|
|
@@ -653,7 +653,7 @@ commands.tinker = async function (args) {
|
|
|
653
653
|
const stub = {
|
|
654
654
|
job: (n) => `const { Job } = require('kreftjs/server')
|
|
655
655
|
|
|
656
|
-
// dispatch(new ${n}(…)) puts it on the queue;
|
|
656
|
+
// dispatch(new ${n}(…)) puts it on the queue; kreft queue:work (or kreft dev) runs handle()
|
|
657
657
|
class ${n} extends Job {
|
|
658
658
|
static tries = 3 // try again after an error, up to 3 times
|
|
659
659
|
static backoff = 10 // seconds between tries
|
|
@@ -728,7 +728,7 @@ class ${n} extends Notification {
|
|
|
728
728
|
}
|
|
729
729
|
|
|
730
730
|
via(notifiable) {
|
|
731
|
-
return ['mail', 'database'] // 'database' needs the notifications table:
|
|
731
|
+
return ['mail', 'database'] // 'database' needs the notifications table: kreft notifications:table
|
|
732
732
|
}
|
|
733
733
|
|
|
734
734
|
toMail(notifiable) {
|
|
@@ -894,21 +894,21 @@ function makeIn(folder, suffix, build, usage) {
|
|
|
894
894
|
return build(o, ctx, dirs, name, path.join(ctx.base, ...folder.split('/'), ...dirs, name + '.js'));
|
|
895
895
|
};
|
|
896
896
|
}
|
|
897
|
-
make.job = makeIn('app/Jobs', '', (o, c, d, n, f) => write(f, stub.job(n)), '
|
|
898
|
-
make.event = makeIn('app/Events', '', (o, c, d, n, f) => write(f, stub.event(n)), '
|
|
899
|
-
make.listener = makeIn('app/Listeners', '', (o, c, d, n, f) => write(f, stub.listener(n, o.event)), '
|
|
900
|
-
make.mail = makeIn('app/Mail', 'Mail', (o, c, d, n, f) => write(f, stub.mail(n)), '
|
|
901
|
-
make.notification = makeIn('app/Notifications', '', (o, c, d, n, f) => write(f, stub.notification(n)), '
|
|
902
|
-
make.policy = makeIn('app/Policies', 'Policy', (o, c, d, n, f) => write(f, stub.policy(n, o.model || n.replace(/Policy$/, ''))), '
|
|
903
|
-
make.request = make.request || makeIn('app/Http/Requests', 'Request', (o, c, d, n, f) => write(f, stub.request(n)), '
|
|
904
|
-
make.resource = make.resource || makeIn('app/Http/Resources', 'Resource', (o, c, d, n, f) => write(f, stub.resource(n)), '
|
|
905
|
-
make.rule = make.rule || makeIn('app/Rules', '', (o, c, d, n, f) => write(f, stub.rule(n)), '
|
|
906
|
-
make.observer = makeIn('app/Observers', 'Observer', (o, c, d, n, f) => write(f, stub.observer(n, o.model || n.replace(/Observer$/, ''))), '
|
|
907
|
-
make.command = makeIn('app/Console/Commands', '', (o, c, d, n, f) => write(f, stub.command(n, o.command || n.replace(/Command$/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase().replace(/-/, ':'))), '
|
|
897
|
+
make.job = makeIn('app/Jobs', '', (o, c, d, n, f) => write(f, stub.job(n)), 'kreft make:job SendWelcomeEmail');
|
|
898
|
+
make.event = makeIn('app/Events', '', (o, c, d, n, f) => write(f, stub.event(n)), 'kreft make:event OrderShipped');
|
|
899
|
+
make.listener = makeIn('app/Listeners', '', (o, c, d, n, f) => write(f, stub.listener(n, o.event)), 'kreft make:listener SendShipmentNotice --event OrderShipped');
|
|
900
|
+
make.mail = makeIn('app/Mail', 'Mail', (o, c, d, n, f) => write(f, stub.mail(n)), 'kreft make:mail WelcomeMail');
|
|
901
|
+
make.notification = makeIn('app/Notifications', '', (o, c, d, n, f) => write(f, stub.notification(n)), 'kreft make:notification InvoicePaid');
|
|
902
|
+
make.policy = makeIn('app/Policies', 'Policy', (o, c, d, n, f) => write(f, stub.policy(n, o.model || n.replace(/Policy$/, ''))), 'kreft make:policy PostPolicy [--model Post]');
|
|
903
|
+
make.request = make.request || makeIn('app/Http/Requests', 'Request', (o, c, d, n, f) => write(f, stub.request(n)), 'kreft make:request StorePostRequest');
|
|
904
|
+
make.resource = make.resource || makeIn('app/Http/Resources', 'Resource', (o, c, d, n, f) => write(f, stub.resource(n)), 'kreft make:resource PostResource');
|
|
905
|
+
make.rule = make.rule || makeIn('app/Rules', '', (o, c, d, n, f) => write(f, stub.rule(n)), 'kreft make:rule Uppercase');
|
|
906
|
+
make.observer = makeIn('app/Observers', 'Observer', (o, c, d, n, f) => write(f, stub.observer(n, o.model || n.replace(/Observer$/, ''))), 'kreft make:observer PostObserver [--model Post]');
|
|
907
|
+
make.command = makeIn('app/Console/Commands', '', (o, c, d, n, f) => write(f, stub.command(n, o.command || n.replace(/Command$/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase().replace(/-/, ':'))), 'kreft make:command SendReport [--command report:send]');
|
|
908
908
|
make.factory = function (args) {
|
|
909
909
|
const o = options(args), ctx = context(o);
|
|
910
910
|
let { name } = nested(o._[0]);
|
|
911
|
-
if (!name) return console.error('
|
|
911
|
+
if (!name) return console.error(' kreft make:factory PostFactory [--model Post]');
|
|
912
912
|
if (!name.endsWith('Factory')) name += 'Factory';
|
|
913
913
|
const model = o.model ? nested(o.model) : { dirs: [], name: name.replace(/Factory$/, '') };
|
|
914
914
|
write(path.join(ctx.base, 'database', 'factories', name + '.js'), stub.factory(name, model.name, '../../app/Models/' + [...model.dirs, model.name].join('/')));
|
|
@@ -979,7 +979,7 @@ commands.list = function (args) {
|
|
|
979
979
|
const groups = {};
|
|
980
980
|
for (const c of builtIn) (groups[c.includes(':') ? c.split(':')[0] : ''] = groups[c.includes(':') ? c.split(':')[0] : ''] || []).push(c);
|
|
981
981
|
console.log(`\n Kreft ${pkg.version}\n\n Commands:`);
|
|
982
|
-
console.log(' new dev serve build update db vscode (
|
|
982
|
+
console.log(' new dev serve build update db vscode (kreft help for their options)');
|
|
983
983
|
console.log(' ' + (groups[''] || []).join(' '));
|
|
984
984
|
for (const [g, list] of Object.entries(groups)) if (g) console.log(` ${g}\n ${list.join(' ')}`);
|
|
985
985
|
const mine = customCommands(ctx.base);
|
package/bin/artisan.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* The backend commands:
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
4
|
+
* kreft migrate run new migrations
|
|
5
|
+
* kreft migrate:rollback [--step 2] undo the last batch (or the last 2 migrations)
|
|
6
|
+
* kreft migrate:fresh [--seed] drop every table, migrate again (and seed)
|
|
7
|
+
* kreft migrate:status which migrations ran
|
|
8
|
+
* kreft db:seed [--class PostSeeder] run database/seeders/DatabaseSeeder.js (or another seeder)
|
|
9
|
+
* kreft route:list the routes in routes/api.js
|
|
10
|
+
* kreft make:model Post [-m -f -s -c -p -r] [--route=file] [--auth] [--all]
|
|
11
|
+
* kreft make:controller PostController [--api] [--model Post] [--route=file] [--auth]
|
|
12
|
+
* kreft make:page Post [--model Post] [--route=file] [--auth]
|
|
13
|
+
* kreft make:migration create_posts_table
|
|
14
|
+
* kreft make:seeder PostSeeder
|
|
15
|
+
* kreft make:factory PostFactory
|
|
16
|
+
* kreft make:middleware EnsureIsAdmin
|
|
17
17
|
*
|
|
18
18
|
* Every command takes --db <url> (or DATABASE_URL in .env) and --backend <folder> (default: the project).
|
|
19
19
|
*/
|
|
@@ -168,13 +168,13 @@ async function runSeed(db, b, ctx, className) {
|
|
|
168
168
|
// an older project: database/seed.sql
|
|
169
169
|
const sql = path.join(ctx.base, 'database', 'seed.sql');
|
|
170
170
|
if (!className && fs.existsSync(sql)) { await db.exec(fs.readFileSync(sql, 'utf8')); return console.log(' Seeded from database/seed.sql'); }
|
|
171
|
-
console.log(` No database/seeders/${className || 'DatabaseSeeder'}.js. Make one with:
|
|
171
|
+
console.log(` No database/seeders/${className || 'DatabaseSeeder'}.js. Make one with: kreft make:seeder ${className || 'DatabaseSeeder'}`);
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
/* ------------------------------------------------------------------ make:… */
|
|
175
175
|
const pascal = (s) => s.replace(/(^|[-_\s])(\w)/g, (_, __, c) => c.toUpperCase()).replace(/[^\w]/g, '');
|
|
176
176
|
// 'User/ProfileController', 'user\\profile-controller' or 'User.ProfileController' → { dirs: ['User'], name: 'ProfileController' }
|
|
177
|
-
// (folders are made when they don't exist yet, e.g.
|
|
177
|
+
// (folders are made when they don't exist yet, e.g. kreft make:controller User/ProfileController)
|
|
178
178
|
function nested(raw) {
|
|
179
179
|
const parts = String(raw || '').split(/[\\/]+/).map((p) => pascal(p)).filter(Boolean);
|
|
180
180
|
const name = parts.pop() || '';
|
|
@@ -298,7 +298,7 @@ module.exports = ${name}
|
|
|
298
298
|
migration: (name) => {
|
|
299
299
|
const create = name.match(/^create_(\w+?)_table$/);
|
|
300
300
|
const change = name.match(/^\w+_(?:to|from|in)_(\w+?)_table$/);
|
|
301
|
-
if (create) return `//
|
|
301
|
+
if (create) return `// kreft migrate runs up(); kreft migrate:rollback runs down()
|
|
302
302
|
module.exports = {
|
|
303
303
|
up(schema) {
|
|
304
304
|
schema.create('${create[1]}', (table) => {
|
|
@@ -313,7 +313,7 @@ module.exports = {
|
|
|
313
313
|
}
|
|
314
314
|
`;
|
|
315
315
|
const t = change ? change[1] : 'table_name';
|
|
316
|
-
return `//
|
|
316
|
+
return `// kreft migrate runs up(); kreft migrate:rollback runs down()
|
|
317
317
|
module.exports = {
|
|
318
318
|
up(schema) {
|
|
319
319
|
schema.table('${t}', (table) => {
|
|
@@ -331,7 +331,7 @@ module.exports = {
|
|
|
331
331
|
},
|
|
332
332
|
seeder: (name, model, modelReq) => `const { Seeder, DB } = require('kreftjs/server')
|
|
333
333
|
${model ? `const ${model} = require('${modelReq || '../../app/Models/' + model}')\n` : ''}
|
|
334
|
-
//
|
|
334
|
+
// kreft db:seed --class ${name} (or this.call(${name}) from DatabaseSeeder)
|
|
335
335
|
class ${name} extends Seeder {
|
|
336
336
|
async run() {
|
|
337
337
|
${model ? ` // await ${model}.factory().count(10).create()\n // await ${model}.create({ … })\n` : name === 'DatabaseSeeder' ? ' // await this.call([UserSeeder, PostSeeder])\n' : " // await DB.table('posts').insert({ … })\n"} }
|
|
@@ -1186,15 +1186,15 @@ const make = {
|
|
|
1186
1186
|
model(args) {
|
|
1187
1187
|
const o = options(args), ctx = context(o);
|
|
1188
1188
|
let { dirs, name, rel } = nested(o._[0]);
|
|
1189
|
-
if (!name) return console.error('
|
|
1189
|
+
if (!name) return console.error(' kreft make:model Post [-m -f -s -c -p -r] [--route=file] [--auth] [--all] [--folder=Admin] (Shop/Product: app/Models/Shop/Product.js)');
|
|
1190
1190
|
const all = o.flags.has('a') || o.flags.has('all');
|
|
1191
1191
|
const auth = o.flags.has('auth');
|
|
1192
1192
|
const hasRoute = all || o.flags.has('r') || o.flags.has('route') || Boolean(o.route);
|
|
1193
1193
|
|
|
1194
1194
|
// Support placing all auxiliary files (controllers, pages, routes) in a specific folder [except model]:
|
|
1195
|
-
// e.g.
|
|
1196
|
-
//
|
|
1197
|
-
//
|
|
1195
|
+
// e.g. kreft make:model Post --all --folder=Admin
|
|
1196
|
+
// kreft make:model Post --all Admin
|
|
1197
|
+
// kreft make:model Admin/Post --all --flat-model
|
|
1198
1198
|
const targetFolder = o.folder || o.subfolder || o['in'] || o.group || o.path || (o._[1] && !o._[1].startsWith('-') ? o._[1] : null);
|
|
1199
1199
|
const flatModel = o.flags.has('flat-model') || o.flags.has('except-model') || o.flags.has('no-model-dir') || Boolean(targetFolder);
|
|
1200
1200
|
|
|
@@ -1222,7 +1222,7 @@ const make = {
|
|
|
1222
1222
|
controller(args) {
|
|
1223
1223
|
const o = options(args), ctx = context(o);
|
|
1224
1224
|
let { dirs, name } = nested(o._[0]);
|
|
1225
|
-
if (!name) return console.error('
|
|
1225
|
+
if (!name) return console.error(' kreft make:controller PostController [--api] [--model Post] [--route=file] [--auth]');
|
|
1226
1226
|
if (!/Controller$/.test(name)) name += 'Controller';
|
|
1227
1227
|
const m = o.model ? nested(o.model) : o.flags.has('api') || o.flags.has('resource') ? { dirs: [], name: name.replace(/Controller$/, '') } : null;
|
|
1228
1228
|
const model = m ? m.name : null;
|
|
@@ -1250,7 +1250,7 @@ const make = {
|
|
|
1250
1250
|
page(args) {
|
|
1251
1251
|
const o = options(args), ctx = context(o);
|
|
1252
1252
|
let { dirs, name } = nested(o._[0]);
|
|
1253
|
-
if (!name) return console.error('
|
|
1253
|
+
if (!name) return console.error(' kreft make:page Post [--model Post] [--route=file] [--auth]');
|
|
1254
1254
|
const m = o.model ? nested(o.model) : { dirs, name };
|
|
1255
1255
|
const modelDirs = m.dirs && m.dirs.length ? m.dirs : dirs;
|
|
1256
1256
|
const endpoint = [...(modelDirs.length ? modelDirs.map((d) => snake(d).replace(/_/g, '-')) : []), plural(snake(m.name)).replace(/_/g, '-')].join('/');
|
|
@@ -1270,7 +1270,7 @@ const make = {
|
|
|
1270
1270
|
factory(args) {
|
|
1271
1271
|
const o = options(args), ctx = context(o);
|
|
1272
1272
|
let { dirs, name } = nested(o._[0]);
|
|
1273
|
-
if (!name) return console.error('
|
|
1273
|
+
if (!name) return console.error(' kreft make:factory PostFactory [--model Post]');
|
|
1274
1274
|
if (!name.endsWith('Factory')) name += 'Factory';
|
|
1275
1275
|
const m = o.model ? nested(o.model) : { dirs: [], name: name.replace(/Factory$/, '') };
|
|
1276
1276
|
const modelReq = '../../app/Models/' + [...m.dirs, m.name].join('/');
|
|
@@ -1279,14 +1279,14 @@ const make = {
|
|
|
1279
1279
|
migration(args) {
|
|
1280
1280
|
const o = options(args), ctx = context(o);
|
|
1281
1281
|
const name = snake(o._[0] || '').replace(/[^a-z0-9_]/g, '_');
|
|
1282
|
-
if (!name) return console.error('
|
|
1282
|
+
if (!name) return console.error(' kreft make:migration create_posts_table');
|
|
1283
1283
|
const dir = migrationsOf(o, ctx);
|
|
1284
1284
|
write(path.join(dir, `${nextStamp(dir)}_${name}.js`), stubs.migration(name));
|
|
1285
1285
|
},
|
|
1286
1286
|
seeder(args) {
|
|
1287
1287
|
const o = options(args), ctx = context(o);
|
|
1288
1288
|
let { dirs, name } = nested(o._[0]);
|
|
1289
|
-
if (!name) return console.error('
|
|
1289
|
+
if (!name) return console.error(' kreft make:seeder PostSeeder');
|
|
1290
1290
|
if (!/Seeder$/.test(name)) name += 'Seeder';
|
|
1291
1291
|
const m = o.model ? nested(o.model) : null;
|
|
1292
1292
|
const modelReq = m ? up(2 + dirs.length) + ['app', 'Models', ...m.dirs, m.name].join('/') : null;
|
|
@@ -1295,14 +1295,14 @@ const make = {
|
|
|
1295
1295
|
middleware(args) {
|
|
1296
1296
|
const o = options(args), ctx = context(o);
|
|
1297
1297
|
const { dirs, name } = nested(o._[0]);
|
|
1298
|
-
if (!name) return console.error('
|
|
1298
|
+
if (!name) return console.error(' kreft make:middleware EnsureIsAdmin');
|
|
1299
1299
|
write(path.join(ctx.base, 'app', 'Http', 'Middleware', ...dirs, name + '.js'), stubs.middleware(name));
|
|
1300
1300
|
if (dirs.length) console.log(dim(` In a folder, give it a name in app/Http/Kernel.js: aliases: { '${snake(name).replace(/_/g, '-')}': 'Middleware/${[...dirs, name].join('/')}.js' }`));
|
|
1301
1301
|
},
|
|
1302
1302
|
request(args) {
|
|
1303
1303
|
const o = options(args), ctx = context(o);
|
|
1304
1304
|
let { dirs, name } = nested(o._[0]);
|
|
1305
|
-
if (!name) return console.error('
|
|
1305
|
+
if (!name) return console.error(' kreft make:request StorePostRequest [--model Post]');
|
|
1306
1306
|
if (!/Request$/.test(name)) name += 'Request';
|
|
1307
1307
|
const reqDir = path.join(ctx.base, 'app', 'Http', 'Requests');
|
|
1308
1308
|
if (!fs.existsSync(reqDir)) fs.mkdirSync(reqDir, { recursive: true });
|
|
@@ -1317,7 +1317,7 @@ const make = {
|
|
|
1317
1317
|
resource(args) {
|
|
1318
1318
|
const o = options(args), ctx = context(o);
|
|
1319
1319
|
let { dirs, name } = nested(o._[0]);
|
|
1320
|
-
if (!name) return console.error('
|
|
1320
|
+
if (!name) return console.error(' kreft make:resource PostResource');
|
|
1321
1321
|
if (!/Resource$/.test(name)) name += 'Resource';
|
|
1322
1322
|
const resDir = path.join(ctx.base, 'app', 'Http', 'Resources');
|
|
1323
1323
|
if (!fs.existsSync(resDir)) fs.mkdirSync(resDir, { recursive: true });
|
|
@@ -1331,7 +1331,7 @@ const make = {
|
|
|
1331
1331
|
rule(args) {
|
|
1332
1332
|
const o = options(args), ctx = context(o);
|
|
1333
1333
|
let { dirs, name } = nested(o._[0]);
|
|
1334
|
-
if (!name) return console.error('
|
|
1334
|
+
if (!name) return console.error(' kreft make:rule Uppercase');
|
|
1335
1335
|
const ruleDir = path.join(ctx.base, 'app', 'Rules');
|
|
1336
1336
|
if (!fs.existsSync(ruleDir)) fs.mkdirSync(ruleDir, { recursive: true });
|
|
1337
1337
|
const isTs = isTsProject(path.join(ruleDir, ...dirs, name + '.js'));
|
|
@@ -1345,7 +1345,7 @@ const make = {
|
|
|
1345
1345
|
const o = options(args), ctx = context(o);
|
|
1346
1346
|
const locale = (o._[0] || '').toLowerCase().trim();
|
|
1347
1347
|
if (!locale || /[\/\\]|\.\./.test(locale)) {
|
|
1348
|
-
console.error('
|
|
1348
|
+
console.error(' kreft make:lang <locale> (e.g. kreft make:lang ar)');
|
|
1349
1349
|
return;
|
|
1350
1350
|
}
|
|
1351
1351
|
// Place in project root lang/ (matching standard Laravel 9/10/11) or backend/lang/ if already existing
|
|
@@ -1494,7 +1494,7 @@ const make = {
|
|
|
1494
1494
|
console.log(dim(` Use in frontend: { __('auth.failed') } or { t('Sign In') }`));
|
|
1495
1495
|
},
|
|
1496
1496
|
};
|
|
1497
|
-
//
|
|
1497
|
+
// kreft make:errors — the error pages (errors/404.kreftjs, 403, 500…)
|
|
1498
1498
|
make.errors = function (args) {
|
|
1499
1499
|
const o = options(args), ctx = context(o);
|
|
1500
1500
|
const from = path.join(__dirname, '..', 'templates', 'errors');
|
|
@@ -1511,12 +1511,12 @@ make.errors = function (args) {
|
|
|
1511
1511
|
}
|
|
1512
1512
|
};
|
|
1513
1513
|
|
|
1514
|
-
//
|
|
1515
|
-
//
|
|
1514
|
+
// kreft make:routes admin → frontend/routes/admin.kreftjs (pages), listed in Kreftjs.router({ routes: […] }) in index.html
|
|
1515
|
+
// kreft make:routes admin --api → backend/routes/admin.js (API), loaded by routes/api.js
|
|
1516
1516
|
make.routes = function (args) {
|
|
1517
1517
|
const o = options(args), ctx = context(o);
|
|
1518
1518
|
const name = String(o._[0] || '').replace(/\.(kreftjs|kreft|som|js)$/, '').replace(/[^\w-]/g, '');
|
|
1519
|
-
if (!name) return console.error('
|
|
1519
|
+
if (!name) return console.error(' kreft make:routes admin (pages)\n kreft make:routes admin --api (API)');
|
|
1520
1520
|
if (o.flags.has('api')) {
|
|
1521
1521
|
const file = path.join(ctx.base, 'routes', name + '.js');
|
|
1522
1522
|
if (!write(file, stubs.apiRoutes(name))) return;
|
|
@@ -1549,12 +1549,12 @@ make.routes = function (args) {
|
|
|
1549
1549
|
console.log(` ${green('UPDATED')} ${path.relative(process.cwd(), index)} (${entry} in Kreftjs.router)`);
|
|
1550
1550
|
};
|
|
1551
1551
|
|
|
1552
|
-
//
|
|
1553
|
-
//
|
|
1552
|
+
// kreft make:admin you@example.com → that user may open the admin panel (/admin)
|
|
1553
|
+
// kreft make:admin you@example.com --remove
|
|
1554
1554
|
commands['make:admin'] = async function (args) {
|
|
1555
1555
|
const o = options(args), ctx = context(o);
|
|
1556
1556
|
const email = String(o._[0] || '').trim().toLowerCase(); // Register keeps emails in lowercase
|
|
1557
|
-
if (!email) return console.error('
|
|
1557
|
+
if (!email) return console.error(' kreft make:admin you@example.com (--remove takes the rights away)');
|
|
1558
1558
|
await withDb(o, ctx, async (db) => {
|
|
1559
1559
|
const value = o.flags.has('remove') ? 0 : 1;
|
|
1560
1560
|
const r = await db.run(`UPDATE ${db.q('users')} SET ${db.q('is_admin')} = ? WHERE LOWER(${db.q('email')}) = ?`, [db.kind === 'postgres' ? Boolean(value) : value, email]);
|
|
@@ -1615,8 +1615,8 @@ async function removeModel(args) {
|
|
|
1615
1615
|
}
|
|
1616
1616
|
|
|
1617
1617
|
if (!targetModelName) {
|
|
1618
|
-
console.error(' Usage:
|
|
1619
|
-
console.error('
|
|
1618
|
+
console.error(' Usage: kreft remove:model <Name> [--all] [--controller] [--request] [--route] [--migration] [--seeder] [--factory] [--page] [--folder=Folder]');
|
|
1619
|
+
console.error(' kreft remove model <Name> [options]');
|
|
1620
1620
|
console.error(' kreft:remove model <Name> [options]');
|
|
1621
1621
|
return;
|
|
1622
1622
|
}
|
package/bin/backend/command.js
CHANGED
package/bin/backend/factory.js
CHANGED
|
@@ -144,7 +144,7 @@ class Factory {
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
|
-
if (!def) throw new Error(`No factory for ${Model && Model.name ? Model.name : 'Model'}. Make one:
|
|
147
|
+
if (!def) throw new Error(`No factory for ${Model && Model.name ? Model.name : 'Model'}. Make one: kreft make:factory ${(Model && Model.name) || 'Model'}Factory`);
|
|
148
148
|
return new Factory(def);
|
|
149
149
|
}
|
|
150
150
|
constructor(def) {
|
package/bin/backend/index.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* app/Http/Controllers/PostController.js
|
|
6
6
|
* app/Http/Middleware/Authenticate.js 'auth' in a route finds Authenticate.js (or Auth.js)
|
|
7
7
|
* app/Models/Post.js
|
|
8
|
-
* database/migrations/*.js
|
|
9
|
-
* database/seeders/DatabaseSeeder.js
|
|
8
|
+
* database/migrations/*.js kreft migrate, kreft migrate:rollback, kreft migrate:fresh --seed
|
|
9
|
+
* database/seeders/DatabaseSeeder.js kreft db:seed
|
|
10
10
|
*
|
|
11
11
|
* In your files: const { Route, Model, DB, abort, response } = require('kreftjs/server')
|
|
12
12
|
*/
|
|
@@ -171,7 +171,7 @@ function createBackend(baseDir, { db = null, production = false, log = console,
|
|
|
171
171
|
const known = { auth: 'Authenticate', guest: 'RedirectIfAuthenticated', verified: 'EnsureEmailIsVerified', 'password.confirm': 'RequirePassword', admin: 'EnsureIsAdmin', role: 'EnsureHasRole', abilities: 'CheckAbilities', ability: 'CheckForAnyAbility' }[name];
|
|
172
172
|
const names = [name, p, p + 'Middleware', known].filter(Boolean);
|
|
173
173
|
const file = names.map((n) => findFile(path.join(dir, n))).find(Boolean);
|
|
174
|
-
if (!file) throw new Error(`No middleware "${name}". Make it with:
|
|
174
|
+
if (!file) throw new Error(`No middleware "${name}". Make it with: kreft make:middleware ${p}`);
|
|
175
175
|
if (!isInsideDir(file, path.resolve(baseDir, 'app'))) throw new Error(`Forbidden middleware path.`);
|
|
176
176
|
return { fn: await loadModuleAsync(file), args };
|
|
177
177
|
}
|