speedly 2.0.38 → 2.0.42
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 +470 -171
- package/dist/cjs/config/init.js +2 -2
- package/dist/cjs/kit/db/db.js +16 -0
- package/dist/cjs/model/translation.d.ts +37 -25
- package/dist/cjs/util/translator.d.ts +1 -1
- package/dist/config/init.d.ts +23 -0
- package/dist/config/init.js +93 -0
- package/dist/document/document.d.ts +5 -0
- package/dist/document/document.js +270 -0
- package/dist/document/index.d.ts +2 -0
- package/dist/document/index.js +7 -0
- package/dist/document/parser.d.ts +1 -0
- package/dist/document/parser.js +11 -0
- package/dist/esm/config/init.js +2 -2
- package/dist/esm/kit/db/db.js +16 -0
- package/dist/esm/model/translation.d.ts +37 -25
- package/dist/esm/util/translator.d.ts +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +13 -0
- package/dist/{cjs → kit}/auth/auth.js +1 -1
- package/dist/{cjs → kit}/db/db.d.ts +15 -0
- package/dist/{cjs → kit}/db/db.js +44 -19
- package/dist/kit/index.d.ts +5 -0
- package/dist/kit/index.js +14 -0
- package/dist/{cjs → kit}/uploader/uploader.d.ts +2 -2
- package/dist/{esm → kit}/uploader/uploader.js +22 -17
- package/dist/model/index.d.ts +2 -0
- package/dist/model/index.js +8 -0
- package/dist/model/translation.d.ts +71 -0
- package/dist/model/translation.js +13 -0
- package/dist/modules/index.d.ts +2 -0
- package/dist/modules/index.js +8 -0
- package/dist/modules/translation/translation.routes.d.ts +2 -0
- package/dist/modules/translation/translation.routes.js +24 -0
- package/dist/modules/translation/translation.validator.d.ts +15 -0
- package/dist/modules/translation/translation.validator.js +22 -0
- package/dist/util/getConfig.d.ts +4 -0
- package/dist/util/getConfig.js +40 -0
- package/dist/util/index.d.ts +2 -0
- package/dist/util/index.js +8 -0
- package/dist/util/makeOptional.d.ts +10 -0
- package/dist/util/makeOptional.js +47 -0
- package/dist/util/strToObj.d.ts +2 -0
- package/dist/util/strToObj.js +9 -0
- package/dist/util/translator.d.ts +2 -0
- package/dist/util/translator.js +74 -0
- package/examples/blog-routes/blog/blog.routes.js +15 -0
- package/examples/blog-routes/blog/blog.validator.js +35 -0
- package/examples/blog-routes/role/role.routes.js +14 -0
- package/examples/blog-routes/role/role.validator.js +28 -0
- package/examples/blog-routes/user/user.controller.js +97 -0
- package/examples/blog-routes/user/user.routes.js +18 -0
- package/examples/blog-routes/user/user.validator.js +53 -0
- package/package.json +65 -66
- package/dist/cjs/auth/auth2.d.ts +0 -18
- package/dist/cjs/auth/auth2.js +0 -93
- package/dist/cjs/uploader/uploader.js +0 -145
- package/dist/cjs/yup.config.d.ts +0 -2
- package/dist/cjs/yup.config.js +0 -24
- package/dist/esm/auth/auth.d.ts +0 -3
- package/dist/esm/auth/auth.js +0 -38
- package/dist/esm/auth/types.d.ts +0 -19
- package/dist/esm/auth/types.js +0 -2
- package/dist/esm/db/db.d.ts +0 -182
- package/dist/esm/db/db.js +0 -594
- package/dist/esm/db/utils.d.ts +0 -3
- package/dist/esm/db/utils.js +0 -15
- package/dist/esm/uploader/uploader.d.ts +0 -24
- package/dist/esm/validator/validator.d.ts +0 -9
- package/dist/esm/validator/validator.js +0 -36
- /package/dist/{cjs → kit}/auth/auth.d.ts +0 -0
- /package/dist/{cjs → kit}/auth/types.d.ts +0 -0
- /package/dist/{cjs → kit}/auth/types.js +0 -0
- /package/dist/{cjs → kit}/db/utils.d.ts +0 -0
- /package/dist/{cjs → kit}/db/utils.js +0 -0
- /package/dist/{cjs → kit}/validator/validator.d.ts +0 -0
- /package/dist/{cjs → kit}/validator/validator.js +0 -0
package/README.md
CHANGED
|
@@ -1,171 +1,470 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
app.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
1
|
+
# speedly
|
|
2
|
+
|
|
3
|
+
A lightweight Express utility framework that bundles auth middlewares, database model handlers, file uploader helpers, request validators, an API documentation loader, and small utilities to speed up building REST APIs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install speedly
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Install peer dependencies (Express, Mongoose, and Multer are required):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install express mongoose multer
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
const speedly = require('speedly');
|
|
21
|
+
const mongoose = require('mongoose');
|
|
22
|
+
|
|
23
|
+
const app = speedly();
|
|
24
|
+
|
|
25
|
+
mongoose.connect('mongodb://localhost:27017/myapp')
|
|
26
|
+
.catch(err => { console.error('MongoDB connection error:', err); process.exit(1); });
|
|
27
|
+
|
|
28
|
+
app.listen(3000, () => {
|
|
29
|
+
console.log('Server running on http://localhost:3000');
|
|
30
|
+
console.log('API docs at http://localhost:3000/docs');
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or with ESM:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
import speedly from 'speedly';
|
|
38
|
+
import mongoose from 'mongoose';
|
|
39
|
+
|
|
40
|
+
const app = speedly();
|
|
41
|
+
|
|
42
|
+
await mongoose.connect('mongodb://localhost:27017/myapp');
|
|
43
|
+
|
|
44
|
+
app.listen(3000);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`speedly()` returns a fully configured Express app with JSON parsing, URL-encoded body parsing, static file serving, a home route, a 404 handler, an error handler, and Swagger documentation pre-wired.
|
|
48
|
+
|
|
49
|
+
### Init options
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
const app = speedly({
|
|
53
|
+
jsonParser: true, // default: true
|
|
54
|
+
urlEncodedParser: true, // default: true
|
|
55
|
+
cookieParser: true, // default: true
|
|
56
|
+
staticFiles: true, // default: true — serves /public at /static
|
|
57
|
+
homeHandler: true, // default: true — GET / returns a welcome page
|
|
58
|
+
notFoundHandler: true, // default: true
|
|
59
|
+
errorHandler: true, // default: true
|
|
60
|
+
documentation: true, // default: true — mounts Swagger UI at /docs
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## API Reference
|
|
67
|
+
|
|
68
|
+
### `speedly/kit` — auth, db, uploader, validator
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
const { auth, db, uploader, validator } = require('speedly/kit');
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### `auth`
|
|
75
|
+
|
|
76
|
+
Express middlewares for access control.
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
const { auth } = require('speedly/kit');
|
|
80
|
+
|
|
81
|
+
// Require an authenticated user
|
|
82
|
+
app.get('/profile', auth.user(), handler);
|
|
83
|
+
|
|
84
|
+
// Require an admin
|
|
85
|
+
app.delete('/post/:id', auth.admin(), handler);
|
|
86
|
+
|
|
87
|
+
// Require an admin with a specific permission
|
|
88
|
+
app.get('/users', auth.admin({ permission: 'OWNER' }), handler);
|
|
89
|
+
|
|
90
|
+
// Accept any authenticated request
|
|
91
|
+
app.get('/dashboard', auth.any(), handler);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- `auth.user()` — enforces user-level authentication.
|
|
95
|
+
- `auth.admin(config?)` — enforces admin access. Pass `{ permission }` to require a specific permission string (e.g. `'OWNER'`, `'ADMIN'`).
|
|
96
|
+
- `auth.any()` — accepts any authenticated request regardless of role.
|
|
97
|
+
|
|
98
|
+
#### `db`
|
|
99
|
+
|
|
100
|
+
Creates Express route handlers that run Mongoose operations on a model. Supports pagination, search, sort, and field selection via query params (`?search=`, `?page=`, `?limit=`, `?sort=`, `?select=`).
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
const { db } = require('speedly/kit');
|
|
104
|
+
|
|
105
|
+
// GET /posts — list all posts (supports ?search=, ?page=, ?limit=)
|
|
106
|
+
app.get('/posts', db('post').find());
|
|
107
|
+
|
|
108
|
+
// POST /posts — create a new post
|
|
109
|
+
app.post('/posts', db('post').create());
|
|
110
|
+
|
|
111
|
+
// GET /posts/:id — get post by id
|
|
112
|
+
app.get('/posts/:id', db('post').findById());
|
|
113
|
+
|
|
114
|
+
// PUT /posts/:id — update post by id
|
|
115
|
+
app.put('/posts/:id', db('post').findByIdAndUpdate());
|
|
116
|
+
|
|
117
|
+
// DELETE /posts/:id — delete post by id
|
|
118
|
+
app.delete('/posts/:id', db('post').findByIdAndDelete());
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Available methods: `.find()`, `.create()`, `.findOne()`, `.findById()`, `.findByIdAndUpdate()`, `.findByIdAndDelete()`, `.updateOne()`, `.updateMany()`, `.deleteOne()`, `.deleteMany()`, `.findOneAndUpdate()`, `.aggregate()`.
|
|
122
|
+
|
|
123
|
+
You can pass a callback to build the query dynamically from `req`:
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
// GET /posts/:slug — find by slug and increment view count
|
|
127
|
+
app.get('/posts/:slug',
|
|
128
|
+
db('post').findOneAndUpdate(
|
|
129
|
+
req => ({ slug: req.params.slug }, { $inc: { views: 1 } })
|
|
130
|
+
).populate('author_id')
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// POST /posts — create with author set from authenticated user
|
|
134
|
+
app.post('/posts',
|
|
135
|
+
auth.admin(),
|
|
136
|
+
db('post').create(req => ({ author_id: req.user._id }))
|
|
137
|
+
);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Chain `.populate()` and `.select()` to control what is returned:
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
app.get('/posts',
|
|
144
|
+
db('post').find()
|
|
145
|
+
.populate([{ path: 'author_id', select: '-password' }, 'thumbnail_id'])
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
app.get('/me',
|
|
149
|
+
auth.user(),
|
|
150
|
+
db('user').findOne(req => ({ _id: req.user._id }))
|
|
151
|
+
.populate('role_id')
|
|
152
|
+
.select('-password')
|
|
153
|
+
);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Pass `{ type: 'internal' }` to resolve speedly's built-in models:
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
app.get('/translations', db('translation', { type: 'internal' }).find());
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### `uploader`
|
|
163
|
+
|
|
164
|
+
File upload middlewares built on `multer`, with optional metadata persistence to a `media` collection.
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
const { uploader } = require('speedly/kit');
|
|
168
|
+
|
|
169
|
+
// Single file upload
|
|
170
|
+
app.post('/upload', uploader('/images').single('photo'), (req, res) => {
|
|
171
|
+
res.json({ url: req.body.photo });
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Multiple files
|
|
175
|
+
app.post('/gallery', uploader('/images').array('photos', 10), (req, res) => {
|
|
176
|
+
res.json({ urls: req.body.photos });
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Config options: `saveInDb`, `prefix`, `limit` (MB), `format` (RegExp), `path`.
|
|
181
|
+
|
|
182
|
+
#### `validator`
|
|
183
|
+
|
|
184
|
+
Request validation middleware built on `yup`. Validates `req.body`, `req.params`, and `req.query`.
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
const { validator } = require('speedly/kit');
|
|
188
|
+
const yup = require('yup');
|
|
189
|
+
|
|
190
|
+
app.post(
|
|
191
|
+
'/posts',
|
|
192
|
+
validator({
|
|
193
|
+
body: yup.object({
|
|
194
|
+
title: yup.string().required(),
|
|
195
|
+
slug: yup.string().required(),
|
|
196
|
+
status: yup.string().oneOf(['draft', 'published', 'hidden']),
|
|
197
|
+
}),
|
|
198
|
+
}),
|
|
199
|
+
handler
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
// Validate route params
|
|
203
|
+
app.put(
|
|
204
|
+
'/posts/:id',
|
|
205
|
+
validator({
|
|
206
|
+
params: yup.object({ id: yup.string().required() }),
|
|
207
|
+
body: yup.object({ title: yup.string() }),
|
|
208
|
+
}),
|
|
209
|
+
handler
|
|
210
|
+
);
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
On validation failure, calls `next({ status: 405, message: '...' })`.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
### `speedly/modules` — built-in routers
|
|
218
|
+
|
|
219
|
+
```js
|
|
220
|
+
const { translation } = require('speedly/modules');
|
|
221
|
+
|
|
222
|
+
app.use('/api/translation', translation);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Includes a `translation` router with:
|
|
226
|
+
- `GET /` — list translations (supports `?search=`, `?page=`, `?limit=`)
|
|
227
|
+
- `POST /` — create a translation
|
|
228
|
+
- `PUT /:id` — update a translation (requires admin auth)
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
### `speedly/document` — Swagger UI
|
|
233
|
+
|
|
234
|
+
Scans your module routers and mounts a Swagger UI at `/docs`.
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
const document = require('speedly/document');
|
|
238
|
+
const path = require('path');
|
|
239
|
+
|
|
240
|
+
document(app, path.join(process.cwd(), 'src/modules'));
|
|
241
|
+
// Visit http://localhost:3000/docs
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Automatically detects routes, HTTP methods, `yup` validation schemas, and auth middlewares to generate OpenAPI documentation.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
### `speedly/util` — utilities
|
|
249
|
+
|
|
250
|
+
```js
|
|
251
|
+
const { translator } = require('speedly/util');
|
|
252
|
+
|
|
253
|
+
const result = await translator('Hello', 'fa');
|
|
254
|
+
console.log(result); // translated text
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
`translator(text, lang)` — translates text to the target language, caching results in the `translation` model.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
### `speedly/model` — Mongoose models
|
|
262
|
+
|
|
263
|
+
```js
|
|
264
|
+
const { translation } = require('speedly/model');
|
|
265
|
+
|
|
266
|
+
const docs = await translation.find({ lang: 'fa' });
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Includes the `translation` model with fields: `text`, `lang`, `translatedText`, and timestamps.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Examples
|
|
274
|
+
|
|
275
|
+
### Blog API
|
|
276
|
+
|
|
277
|
+
A full blog REST API with auth-protected write routes and public read routes.
|
|
278
|
+
|
|
279
|
+
**`src/modules/blog/blog.validator.js`**
|
|
280
|
+
|
|
281
|
+
```js
|
|
282
|
+
const { validator } = require('speedly/kit');
|
|
283
|
+
const yup = require('yup');
|
|
284
|
+
|
|
285
|
+
const paramId = yup.object({
|
|
286
|
+
id: yup.string().required('id is required'),
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
const schema = yup.object({
|
|
290
|
+
title: yup.string().required(),
|
|
291
|
+
slug: yup.string().required().matches(/^[\d\w-]+$/i, 'slug can only contain letters, numbers and dashes'),
|
|
292
|
+
subTitle: yup.string(),
|
|
293
|
+
content: yup.string(),
|
|
294
|
+
status: yup.string().oneOf(['draft', 'published', 'hidden']),
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
exports.post = { body: schema };
|
|
298
|
+
exports.put = { params: paramId, body: schema };
|
|
299
|
+
exports.delete = { params: paramId };
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**`src/modules/blog/blog.routes.js`**
|
|
303
|
+
|
|
304
|
+
```js
|
|
305
|
+
const express = require('express');
|
|
306
|
+
const { auth, db, validator } = require('speedly/kit');
|
|
307
|
+
const v = require('./blog.validator');
|
|
308
|
+
|
|
309
|
+
const router = express.Router();
|
|
310
|
+
|
|
311
|
+
// Public: list all posts with author and thumbnail populated
|
|
312
|
+
router.get('/',
|
|
313
|
+
db('blog').find()
|
|
314
|
+
.populate([{ path: 'author_id', select: '-password' }, 'thumbnail_id'])
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
// Public: get single post by slug and increment view count
|
|
318
|
+
router.get('/:slug',
|
|
319
|
+
db('blog').findOneAndUpdate(
|
|
320
|
+
req => ({ slug: req.params.slug }, { $inc: { views: 1 } })
|
|
321
|
+
).populate([{ path: 'author_id', select: '-password' }, 'thumbnail_id'])
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
// Admin only: create / update / delete
|
|
325
|
+
router.post('/', auth.admin(), validator(v.post), db('blog').create(req => ({ author_id: req.user._id })));
|
|
326
|
+
router.put('/:id', auth.admin(), validator(v.put), db('blog').findByIdAndUpdate());
|
|
327
|
+
router.delete('/:id', auth.admin(), validator(v.delete), db('blog').findByIdAndDelete());
|
|
328
|
+
|
|
329
|
+
module.exports = router;
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### User & Auth Routes
|
|
333
|
+
|
|
334
|
+
Registration, login, and profile routes with OTP flow.
|
|
335
|
+
|
|
336
|
+
**`src/modules/user/user.routes.js`**
|
|
337
|
+
|
|
338
|
+
```js
|
|
339
|
+
const express = require('express');
|
|
340
|
+
const { auth, db, validator } = require('speedly/kit');
|
|
341
|
+
const yup = require('yup');
|
|
342
|
+
|
|
343
|
+
const router = express.Router();
|
|
344
|
+
|
|
345
|
+
const phoneSchema = yup.object({
|
|
346
|
+
// Iranian mobile format: 11 digits starting with 09 — adjust regex for your region
|
|
347
|
+
phone: yup.string().required().matches(/^09\d{9}$/, 'Enter an 11-digit phone number starting with 09'),
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Check phone number and send OTP
|
|
351
|
+
router.post('/check-phone',
|
|
352
|
+
validator({ body: phoneSchema }),
|
|
353
|
+
myUserController.checkPhone // your custom controller
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
// Register / login with OTP
|
|
357
|
+
router.post('/register',
|
|
358
|
+
validator({ body: phoneSchema }),
|
|
359
|
+
myUserController.register
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
// Get current user profile
|
|
363
|
+
router.get('/me',
|
|
364
|
+
auth.user(),
|
|
365
|
+
db('user').findOne(req => ({ _id: req.user._id }))
|
|
366
|
+
.populate('role_id')
|
|
367
|
+
.select('-password')
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
// Admin: list all users
|
|
371
|
+
router.get('/',
|
|
372
|
+
auth.admin({ permission: 'OWNER' }),
|
|
373
|
+
db('user').find()
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
// Admin: create / update / delete user
|
|
377
|
+
router.post('/', auth.admin(), db('user').create());
|
|
378
|
+
router.put('/:id', auth.admin(), db('user').findByIdAndUpdate());
|
|
379
|
+
router.delete('/:id', auth.admin(), db('user').findByIdAndDelete());
|
|
380
|
+
|
|
381
|
+
module.exports = router;
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### Role Management
|
|
385
|
+
|
|
386
|
+
CRUD routes for roles, restricted to users with the `OWNER` permission.
|
|
387
|
+
|
|
388
|
+
**`src/modules/role/role.routes.js`**
|
|
389
|
+
|
|
390
|
+
```js
|
|
391
|
+
const express = require('express');
|
|
392
|
+
const { auth, db, validator } = require('speedly/kit');
|
|
393
|
+
const yup = require('yup');
|
|
394
|
+
|
|
395
|
+
const router = express.Router();
|
|
396
|
+
|
|
397
|
+
const schema = yup.object({
|
|
398
|
+
title: yup.string().required(),
|
|
399
|
+
access: yup.string().required().oneOf(['OWNER', 'ADMIN', 'EXPERT']),
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
router.get('/', auth.admin({ permission: 'OWNER' }), db('role').find());
|
|
403
|
+
router.post('/', auth.admin({ permission: 'OWNER' }), validator({ body: schema }), db('role').create());
|
|
404
|
+
router.put('/:id', auth.admin({ permission: 'OWNER' }), validator({ body: schema }), db('role').findByIdAndUpdate());
|
|
405
|
+
router.delete('/:id', auth.admin({ permission: 'OWNER' }), db('role').findByIdAndDelete());
|
|
406
|
+
|
|
407
|
+
module.exports = router;
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### File Upload
|
|
411
|
+
|
|
412
|
+
```js
|
|
413
|
+
const express = require('express');
|
|
414
|
+
const { auth, uploader } = require('speedly/kit');
|
|
415
|
+
|
|
416
|
+
const router = express.Router();
|
|
417
|
+
|
|
418
|
+
// Single image upload (saves file info to the media collection)
|
|
419
|
+
router.post('/image',
|
|
420
|
+
auth.user(),
|
|
421
|
+
uploader('/images', { saveInDb: true, limit: 5 /* max file size in MB */, format: /image\/(jpeg|png|webp)/ }).single('photo'),
|
|
422
|
+
(req, res) => res.json({ url: req.body.photo })
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
// Multiple file upload
|
|
426
|
+
router.post('/gallery',
|
|
427
|
+
auth.user(),
|
|
428
|
+
uploader('/images').array('photos', 10),
|
|
429
|
+
(req, res) => res.json({ urls: req.body.photos })
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
module.exports = router;
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Putting It All Together
|
|
436
|
+
|
|
437
|
+
**`src/app.js`**
|
|
438
|
+
|
|
439
|
+
```js
|
|
440
|
+
const speedly = require('speedly');
|
|
441
|
+
const mongoose = require('mongoose');
|
|
442
|
+
const { translation } = require('speedly/modules');
|
|
443
|
+
|
|
444
|
+
const blogRoutes = require('./modules/blog/blog.routes');
|
|
445
|
+
const userRoutes = require('./modules/user/user.routes');
|
|
446
|
+
const roleRoutes = require('./modules/role/role.routes');
|
|
447
|
+
|
|
448
|
+
const app = speedly();
|
|
449
|
+
|
|
450
|
+
mongoose.connect(process.env.MONGO_URI || 'mongodb://localhost:27017/myapp');
|
|
451
|
+
|
|
452
|
+
// Built-in translation module
|
|
453
|
+
app.use('/api/translation', translation);
|
|
454
|
+
|
|
455
|
+
// Application routes
|
|
456
|
+
app.use('/api/blogs', blogRoutes);
|
|
457
|
+
app.use('/api/users', userRoutes);
|
|
458
|
+
app.use('/api/roles', roleRoutes);
|
|
459
|
+
|
|
460
|
+
app.listen(3000, () => {
|
|
461
|
+
console.log('Running on http://localhost:3000');
|
|
462
|
+
console.log('API docs: http://localhost:3000/docs');
|
|
463
|
+
});
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
## License
|
|
469
|
+
|
|
470
|
+
MIT
|
package/dist/cjs/config/init.js
CHANGED
|
@@ -47,8 +47,8 @@ function speedly(config = {}) {
|
|
|
47
47
|
app.use("/static", express_1.default.static("public"));
|
|
48
48
|
if (finalConfig.homeHandler) {
|
|
49
49
|
app.get("/", (req, res) => {
|
|
50
|
-
res.send(`<h1>Welcome to ${require(path_1.default.join(process.cwd(), "package.json")).name} App</h1>
|
|
51
|
-
<p>Your app is running successfully.</p>
|
|
50
|
+
res.send(`<h1>Welcome to ${require(path_1.default.join(process.cwd(), "package.json")).name} App</h1>
|
|
51
|
+
<p>Your app is running successfully.</p>
|
|
52
52
|
${finalConfig.documentation
|
|
53
53
|
? '<p>Visit <a href="/docs">/docs</a> for API documentation.</p>'
|
|
54
54
|
: ""}`);
|