webspresso 0.0.75 → 0.0.76
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
CHANGED
|
@@ -2165,10 +2165,34 @@ const { app } = createApp({
|
|
|
2165
2165
|
```
|
|
2166
2166
|
|
|
2167
2167
|
- **ORM:** `zdb.file({ maxLength: 2048, nullable: true })` — string column for the stored public URL or path; migrations use `table.string(..., maxLength)`.
|
|
2168
|
-
- **Admin:**
|
|
2168
|
+
- **Admin forms:** columns with `zdb.file()` automatically render a drag-and-drop upload widget (or a manual URL text field when `uploadUrl` is not configured). Optional `ui: { label, hint, accept, maxBytes }` on the column customizes the widget. For existing `zdb.string()` columns you can use `admin.customFields: { columnName: { type: 'file-upload' } }` instead of changing the schema type.
|
|
2169
|
+
- **Admin:** the panel reads **`settings.uploadUrl`** from the registry (set automatically when `uploadPlugin` is registered **before** `adminPanelPlugin`, or pass **`adminPanelPlugin({ uploadUrl: '/api/upload' })`**). File fields (`type: 'file'` or `customFields` type `file-upload`) POST to that URL with credentials; the saved record stores the returned **`url`** / **`publicUrl`** string.
|
|
2169
2170
|
- **Response:** `{ url, publicUrl, key? }` — clients typically persist **`url`** / **`publicUrl`** in the model.
|
|
2170
2171
|
- **Custom storage:** `uploadPlugin({ provider: { async put({ buffer, originalName, mimeType, size, req }) { return { publicUrl: '...' }; } } })`.
|
|
2171
2172
|
|
|
2173
|
+
**Admin model example** (`zdb.file()` picks up the upload widget from schema; no extra `customFields` needed):
|
|
2174
|
+
|
|
2175
|
+
```javascript
|
|
2176
|
+
const { defineModel, zdb } = require('webspresso');
|
|
2177
|
+
|
|
2178
|
+
const Post = defineModel({
|
|
2179
|
+
name: 'Post',
|
|
2180
|
+
table: 'posts',
|
|
2181
|
+
schema: zdb.schema({
|
|
2182
|
+
id: zdb.id(),
|
|
2183
|
+
title: zdb.string(),
|
|
2184
|
+
cover_image: zdb.file({
|
|
2185
|
+
maxLength: 2048,
|
|
2186
|
+
nullable: true,
|
|
2187
|
+
ui: { label: 'Cover', accept: 'image/*', hint: 'JPEG or PNG' },
|
|
2188
|
+
}),
|
|
2189
|
+
}),
|
|
2190
|
+
admin: { enabled: true, label: 'Posts' },
|
|
2191
|
+
});
|
|
2192
|
+
```
|
|
2193
|
+
|
|
2194
|
+
For a plain string column, use `admin.customFields: { attachment: { type: 'file-upload' } }` instead of `zdb.file()`.
|
|
2195
|
+
|
|
2172
2196
|
### Health check plugin
|
|
2173
2197
|
|
|
2174
2198
|
Exposes a lightweight **GET** endpoint for load balancers and orchestrators (Kubernetes, Docker healthcheck, etc.). **Enabled by default** in all environments; set `enabled: false` to turn it off.
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Reset admin user password via CLI
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const inquirer = require('inquirer');
|
|
7
|
+
const { hash } = require('../../core/auth/hash');
|
|
7
8
|
const { loadDbConfig, createDbInstance } = require('../utils/db');
|
|
8
9
|
|
|
9
10
|
function registerCommand(program) {
|
|
@@ -32,17 +33,14 @@ function registerCommand(program) {
|
|
|
32
33
|
// Get email (interactive if not provided)
|
|
33
34
|
let email = options.email;
|
|
34
35
|
if (!email) {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
resolve(answer.trim());
|
|
44
|
-
});
|
|
45
|
-
});
|
|
36
|
+
const answers = await inquirer.prompt([
|
|
37
|
+
{
|
|
38
|
+
type: 'input',
|
|
39
|
+
name: 'email',
|
|
40
|
+
message: 'Enter admin email:',
|
|
41
|
+
},
|
|
42
|
+
]);
|
|
43
|
+
email = answers.email.trim();
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
if (!email) {
|
|
@@ -70,49 +68,15 @@ function registerCommand(program) {
|
|
|
70
68
|
// Get new password (interactive if not provided)
|
|
71
69
|
let password = options.password;
|
|
72
70
|
if (!password) {
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
let pwd = '';
|
|
83
|
-
process.stdin.setRawMode(true);
|
|
84
|
-
process.stdin.resume();
|
|
85
|
-
process.stdin.on('data', (char) => {
|
|
86
|
-
char = char.toString();
|
|
87
|
-
if (char === '\n' || char === '\r') {
|
|
88
|
-
process.stdin.setRawMode(false);
|
|
89
|
-
process.stdin.pause();
|
|
90
|
-
console.log(); // New line after password
|
|
91
|
-
resolve(pwd);
|
|
92
|
-
} else if (char === '\u0003') {
|
|
93
|
-
// Ctrl+C
|
|
94
|
-
process.exit();
|
|
95
|
-
} else if (char === '\u007F') {
|
|
96
|
-
// Backspace
|
|
97
|
-
if (pwd.length > 0) {
|
|
98
|
-
pwd = pwd.slice(0, -1);
|
|
99
|
-
process.stdout.write('\b \b');
|
|
100
|
-
}
|
|
101
|
-
} else {
|
|
102
|
-
pwd += char;
|
|
103
|
-
process.stdout.write('*');
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
rl.close();
|
|
108
|
-
} else {
|
|
109
|
-
password = await new Promise((resolve) => {
|
|
110
|
-
rl.question('Enter new password: ', (answer) => {
|
|
111
|
-
rl.close();
|
|
112
|
-
resolve(answer);
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
}
|
|
71
|
+
const answers = await inquirer.prompt([
|
|
72
|
+
{
|
|
73
|
+
type: 'password',
|
|
74
|
+
name: 'password',
|
|
75
|
+
message: 'Enter new password:',
|
|
76
|
+
mask: '*',
|
|
77
|
+
},
|
|
78
|
+
]);
|
|
79
|
+
password = answers.password;
|
|
116
80
|
}
|
|
117
81
|
|
|
118
82
|
if (!password || password.length < 6) {
|
|
@@ -121,8 +85,8 @@ function registerCommand(program) {
|
|
|
121
85
|
process.exit(1);
|
|
122
86
|
}
|
|
123
87
|
|
|
124
|
-
// Hash the password
|
|
125
|
-
const hashedPassword = await
|
|
88
|
+
// Hash the password (same rounds as admin panel setup)
|
|
89
|
+
const hashedPassword = await hash(password, 10);
|
|
126
90
|
|
|
127
91
|
// Update the password
|
|
128
92
|
await db('admin_users')
|
package/package.json
CHANGED
|
@@ -169,6 +169,7 @@ Analytics plugin adds `fsy.analyticsHead`, `fsy.verificationTags`, etc., when co
|
|
|
169
169
|
- **Relations:** `belongsTo`, `hasMany`, `hasOne` with `model: () => OtherModel`.
|
|
170
170
|
- **Scopes:** `softDelete`, `timestamps`, optional `tenant` column.
|
|
171
171
|
- **`hidden`:** columns never exposed in admin/API (e.g. `password_hash`).
|
|
172
|
+
- **`zdb.file()`:** varchar column for uploaded asset URL/path; admin forms render a file upload widget when `uploadPlugin` is registered (or `adminPanelPlugin({ uploadUrl })`). Optional `ui: { label, accept, maxBytes }`. For `zdb.string()` columns use `admin.customFields: { col: { type: 'file-upload' } }`.
|
|
172
173
|
- **Nanoid PK:** `zdb.nanoid()` / `zdb.nanoid({ maxLength: 12 })` — string primary key; migrations use `string(length)`. On **`create()`**, omitting the PK auto-fills a URL-safe id (built-in generator, same alphabet as `nanoid`). Use **`zdb.foreignNanoid('table', { maxLength })`** when the parent uses nanoid PKs; **`generateNanoid`** is exported from `webspresso` for manual ids. In API **`schema`**, use **`z.nanoid()`** / **`z.nanoid(12)`** / **`z.nanoid({ maxLength })`** (the `z` from `schema: ({ z })` is extended by Webspresso). **`zodNanoid`** / **`extendZ`** are also exported for non-route use.
|
|
173
174
|
|
|
174
175
|
**Database:** `createDatabase({ client, connection, models: './models' })` — auto-loads `models/*.js` (ignore `_prefix`).
|
|
@@ -197,7 +198,7 @@ Pass **`db`** into **`createApp({ db })`** so **`ctx.db`** works in pages and pl
|
|
|
197
198
|
| `adminPanelPlugin` | SPA admin CRUD — needs **`db`**; optional **`uploadUrl`** (or infer from **`uploadPlugin`**); optional **`userManagement: { enabled, model, fields }`** + **`auth`** (same **`AuthManager`** as **`createApp({ auth })`**) for site-user CRUD + remember-me session UI — see **Session authentication** above |
|
|
198
199
|
| `dataExchangePlugin` | Admin-only **Excel export** + **CSV/XLSX import** under `${adminPath}/api/data-exchange/*`; register **after** `adminPanelPlugin` with same `db` / `adminPath`; optional `maxRows`, `maxFileBytes`; adds UI buttons + bulk `export-xlsx` |
|
|
199
200
|
| `redirectPlugin` | Configurable **301–308** redirects in `register()` — runs **before** file-based SSR routes; `rules` (`from` path or `RegExp`, `to`, `status`, `methods`), `preserveQuery`, `allowExternal`, `trailingSlash`, `defaultMethods`; docs **[`doc/index.html#plugins-redirect`](../../../doc/index.html#plugins-redirect)**, README **Redirect plugin** |
|
|
200
|
-
| `uploadPlugin` | `POST` multipart (`multer`), `createLocalFileProvider` or custom `provider`; set **`mimeAllowlist`** / **`maxBytes`** in production |
|
|
201
|
+
| `uploadPlugin` | `POST` multipart (`multer`), `createLocalFileProvider` or custom `provider`; set **`mimeAllowlist`** / **`maxBytes`** in production; pairs with admin **`zdb.file()`** / **`customFields.file-upload`** when registered before **`adminPanelPlugin`** |
|
|
201
202
|
| `siteAnalyticsPlugin` | Self-hosted page views + admin charts |
|
|
202
203
|
| `auditLogPlugin` | Admin mutation audit trail |
|
|
203
204
|
| `recaptchaPlugin` | v2/v3 + middleware |
|