@knovator/pagecreator-node 1.2.2 → 1.2.4
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 +458 -458
- package/package.json +3 -2
- package/src/constants/index.d.ts +20 -20
- package/src/constants/index.js +33 -33
- package/src/controllers/PageController.d.ts +4 -4
- package/src/controllers/PageController.js +83 -83
- package/src/controllers/TabController.d.ts +4 -4
- package/src/controllers/TabController.js +45 -45
- package/src/controllers/UserController.d.ts +2 -2
- package/src/controllers/UserController.js +64 -64
- package/src/controllers/WidgetController.d.ts +10 -10
- package/src/controllers/WidgetController.js +328 -328
- package/src/index.d.ts +8 -8
- package/src/index.js +47 -47
- package/src/models/Item.d.ts +30 -30
- package/src/models/Item.js +49 -49
- package/src/models/Page.d.ts +30 -30
- package/src/models/Page.js +23 -22
- package/src/models/Page.js.map +1 -1
- package/src/models/SrcSet.d.ts +30 -30
- package/src/models/SrcSet.js +16 -16
- package/src/models/Tab.d.ts +30 -30
- package/src/models/Tab.js +27 -27
- package/src/models/Widget.d.ts +30 -30
- package/src/models/Widget.js +62 -62
- package/src/models/index.d.ts +6 -6
- package/src/models/index.js +18 -18
- package/src/plugins/softDelete.d.ts +7 -7
- package/src/plugins/softDelete.js +63 -63
- package/src/routes/PageRoute.d.ts +3 -3
- package/src/routes/PageRoute.js +29 -29
- package/src/routes/UserRoute.d.ts +3 -3
- package/src/routes/UserRoute.js +22 -22
- package/src/routes/WidgetRoute.d.ts +3 -3
- package/src/routes/WidgetRoute.js +67 -67
- package/src/services/dataService.d.ts +9 -9
- package/src/services/dataService.js +402 -402
- package/src/services/dbService.d.ts +16 -16
- package/src/services/dbService.js +88 -87
- package/src/services/dbService.js.map +1 -1
- package/src/types/IRequest.d.ts +6 -6
- package/src/types/IRequest.js +2 -2
- package/src/types/IResponse.d.ts +6 -6
- package/src/types/IResponse.js +2 -2
- package/src/types/Router.d.ts +4 -4
- package/src/types/Router.js +2 -2
- package/src/types/common.d.ts +143 -142
- package/src/types/common.js +2 -2
- package/src/types/enums.d.ts +14 -14
- package/src/types/enums.js +20 -20
- package/src/types/index.d.ts +5 -5
- package/src/types/index.js +8 -8
- package/src/utils/defaults.d.ts +7 -7
- package/src/utils/defaults.js +40 -40
- package/src/utils/helper.d.ts +7 -7
- package/src/utils/helper.js +224 -224
- package/src/utils/redis.d.ts +3 -3
- package/src/utils/redis.js +58 -58
- package/src/utils/responseHandlers.d.ts +6 -6
- package/src/utils/responseHandlers.js +56 -56
- package/src/utils/validate.d.ts +4 -4
- package/src/utils/validate.js +23 -23
- package/src/utils/validations/page.d.ts +7 -7
- package/src/utils/validations/page.js +52 -50
- package/src/utils/validations/page.js.map +1 -1
- package/src/utils/validations/tab.d.ts +6 -6
- package/src/utils/validations/tab.js +25 -25
- package/src/utils/validations/user.d.ts +3 -3
- package/src/utils/validations/user.js +12 -12
- package/src/utils/validations/widget.d.ts +9 -9
- package/src/utils/validations/widget.js +152 -152
package/README.md
CHANGED
|
@@ -1,458 +1,458 @@
|
|
|
1
|
-
<!-- Improved compatibility of back to top link: See: https://github.com/othneildrew/Best-README-Template/pull/73 -->
|
|
2
|
-
|
|
3
|
-
<a name="readme-top"></a>
|
|
4
|
-
|
|
5
|
-
[![Contributors][contributors-shield]][contributors-url]
|
|
6
|
-
[![Forks][forks-shield]][forks-url]
|
|
7
|
-
[![Stargazers][stars-shield]][stars-url]
|
|
8
|
-
[![Issues][issues-shield]][issues-url]
|
|
9
|
-
[![MIT License][license-shield]][license-url]
|
|
10
|
-
|
|
11
|
-
<!-- PROJECT LOGO -->
|
|
12
|
-
<br />
|
|
13
|
-
<div align="center">
|
|
14
|
-
<!-- <a href="https://github.com/knovator/pagecreator">
|
|
15
|
-
<img src="images/logo.png" alt="Logo" width="80" height="80">
|
|
16
|
-
</a> -->
|
|
17
|
-
|
|
18
|
-
<h3 align="center">@knovator/pagecreator-node</h3>
|
|
19
|
-
|
|
20
|
-
<p align="center">
|
|
21
|
-
Plug & Play functionality to Build dynamic pages on the fly.
|
|
22
|
-
<br />
|
|
23
|
-
<a href="https://github.com/knovator/pagecreator"><strong>Explore the docs »</strong></a>
|
|
24
|
-
<br />
|
|
25
|
-
<br />
|
|
26
|
-
<a href="https://github.com/knovator/pagecreator">View Demo</a>
|
|
27
|
-
·
|
|
28
|
-
<a href="https://github.com/knovator/pagecreator/issues">Report Bug</a>
|
|
29
|
-
·
|
|
30
|
-
<a href="https://github.com/knovator/pagecreator/issues">Request Feature</a>
|
|
31
|
-
</p>
|
|
32
|
-
</div>
|
|
33
|
-
|
|
34
|
-
<!-- TABLE OF CONTENTS -->
|
|
35
|
-
<details>
|
|
36
|
-
<summary>Table of Contents</summary>
|
|
37
|
-
<ol>
|
|
38
|
-
<li>
|
|
39
|
-
<a href="#about-the-project">About The Project</a>
|
|
40
|
-
<ul>
|
|
41
|
-
<li><a href="#built-with">Built With</a></li>
|
|
42
|
-
</ul>
|
|
43
|
-
</li>
|
|
44
|
-
<li>
|
|
45
|
-
<a href="#getting-started">Getting Started</a>
|
|
46
|
-
<ul>
|
|
47
|
-
<li><a href="#prerequisites">Prerequisites</a></li>
|
|
48
|
-
<li><a href="#installation">Installation</a></li>
|
|
49
|
-
</ul>
|
|
50
|
-
</li>
|
|
51
|
-
<li><a href="#usage">Usage</a></li>
|
|
52
|
-
<li><a href="#routes-infomration">Routes Infomration</a></li>
|
|
53
|
-
<li><a href="#contributing">Contributing</a></li>
|
|
54
|
-
<li><a href="#license">License</a></li>
|
|
55
|
-
<li><a href="#contact">Contact</a></li>
|
|
56
|
-
</ol>
|
|
57
|
-
</details>
|
|
58
|
-
|
|
59
|
-
<!-- ABOUT THE PROJECT -->
|
|
60
|
-
|
|
61
|
-
## About The Project
|
|
62
|
-
|
|
63
|
-
`@knovator/pagecreator-node` is built with intent to build pages that are depend on backend data, and admin can change how page will look like.
|
|
64
|
-
|
|
65
|
-
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
66
|
-
|
|
67
|
-
### Built With
|
|
68
|
-
|
|
69
|
-
- [Typescript](https://www.typescriptlang.org/)
|
|
70
|
-
- [mongoose](https://mongoosejs.com/)
|
|
71
|
-
- [express](https://expressjs.com/)
|
|
72
|
-
- [mongoose-paginate-v2](https://www.npmjs.com/package/mongoose-paginate-v2)
|
|
73
|
-
- [joi](https://www.npmjs.com/package/joi)
|
|
74
|
-
|
|
75
|
-
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
76
|
-
|
|
77
|
-
<!-- GETTING STARTED -->
|
|
78
|
-
|
|
79
|
-
## Getting Started
|
|
80
|
-
|
|
81
|
-
To integrate `@knovator/pagecreator-node`, you should be having basic nodejs application up and running with express (optionally using mongoose for mongodb database). `@knovator/pagecreator-node` provides routes for `widget`, `page` and `user` to use in application.
|
|
82
|
-
|
|
83
|
-
### Prerequisites
|
|
84
|
-
|
|
85
|
-
- It's good start to have `nodejs` application up and running with `express`. Good to have used [i18next](https://www.npmjs.com/package/i18next) to add message in response codes.
|
|
86
|
-
- `routes` uses `mongoose` connection established by application, so it's required to connect to database before using package. For example,
|
|
87
|
-
|
|
88
|
-
```js
|
|
89
|
-
// db.js
|
|
90
|
-
const mongoose = require('mongoose');
|
|
91
|
-
|
|
92
|
-
mongoose
|
|
93
|
-
.connect('mongodb://localhost:27017/knovator')
|
|
94
|
-
.then(() => console.info('Database connected'))
|
|
95
|
-
.catch((err) => {
|
|
96
|
-
console.error('DB Error', err);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
module.exports = mongoose;
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
- Image upload route for `upload` & `remove` is needed to declare externally. Example,
|
|
103
|
-
|
|
104
|
-
```js
|
|
105
|
-
// fileRoute.js
|
|
106
|
-
const express = require('express');
|
|
107
|
-
const router = express.Router();
|
|
108
|
-
|
|
109
|
-
router.post(`/files/upload`, (req, res) => {
|
|
110
|
-
// TO DO: some file storage operation
|
|
111
|
-
let uri = '/image.jpg';
|
|
112
|
-
let id = '62c54b15524b6b59d2313c02';
|
|
113
|
-
res.json({
|
|
114
|
-
code: 'SUCCESS',
|
|
115
|
-
data: { id, uri },
|
|
116
|
-
message: 'File uploaded successfully',
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
router.delete(`/files/remove/:id`, (req, res) => {
|
|
121
|
-
// TO DO: some file remove operation
|
|
122
|
-
res.json({
|
|
123
|
-
code: 'SUCCESS',
|
|
124
|
-
data: {},
|
|
125
|
-
message: 'File removed successfully',
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
module.exports = router;
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
**Sample App file**
|
|
133
|
-
|
|
134
|
-
```js
|
|
135
|
-
require('./src/db');
|
|
136
|
-
require('./src/models/file');
|
|
137
|
-
|
|
138
|
-
const cors = require('cors');
|
|
139
|
-
const express = require('express');
|
|
140
|
-
const fileRoutes = require('./fileRoute.js');
|
|
141
|
-
const PORT = 8080;
|
|
142
|
-
|
|
143
|
-
const app = express();
|
|
144
|
-
app.use(cors());
|
|
145
|
-
app.use(express.static('public'));
|
|
146
|
-
app.use(fileRoutes);
|
|
147
|
-
|
|
148
|
-
// ...
|
|
149
|
-
app.listen(PORT, () => {
|
|
150
|
-
console.log(`App started on ${PORT}`);
|
|
151
|
-
});
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### Installation
|
|
155
|
-
|
|
156
|
-
1. Add pagecreator package,
|
|
157
|
-
```sh
|
|
158
|
-
npm install @knovator/pagecreator-node
|
|
159
|
-
# or
|
|
160
|
-
yarn add @knovator/pagecreator-node
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
164
|
-
|
|
165
|
-
<!-- USAGE EXAMPLES -->
|
|
166
|
-
|
|
167
|
-
## Usage
|
|
168
|
-
|
|
169
|
-
App/Main file is a good place to use `@knovator/pagecreator-node`
|
|
170
|
-
|
|
171
|
-
```js
|
|
172
|
-
const {
|
|
173
|
-
setConfig,
|
|
174
|
-
WidgetRoutes,
|
|
175
|
-
ItemRoutes,
|
|
176
|
-
FileUploadRoute,
|
|
177
|
-
PageRoutes,
|
|
178
|
-
UserRoutes,
|
|
179
|
-
} = require('@knovator/pagecreator-node');
|
|
180
|
-
|
|
181
|
-
setConfig({
|
|
182
|
-
collections: [
|
|
183
|
-
{
|
|
184
|
-
title: 'Notifications',
|
|
185
|
-
collectionName: 'notifications',
|
|
186
|
-
filters: { isDeleted: false, isActive: true },
|
|
187
|
-
searchColumns: ['name', 'code'],
|
|
188
|
-
},
|
|
189
|
-
],
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
app.use('/widgets', WidgetRoutes);
|
|
193
|
-
app.use('/items', ItemRoutes);
|
|
194
|
-
app.use('/media', FileUploadRoute);
|
|
195
|
-
app.use('/pages', PageRoutes);
|
|
196
|
-
app.use('/users', UserRoutes);
|
|
197
|
-
|
|
198
|
-
app.listen(PORT, () => {
|
|
199
|
-
console.log(`App started on ${PORT}`);
|
|
200
|
-
});
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
Through `setConfig` function e can set `logger`, `collections` and `catchAsync` functions as parameters. By `collections`, we can add reference of application collections.
|
|
204
|
-
|
|
205
|
-
- `handleUpdateData` is used to handle update redis cache when data is updated in database. It takes `collectionName` and `_id` as parameters.
|
|
206
|
-
```js
|
|
207
|
-
import { handleUpdateData } from '@knovator/pagecreator-node';
|
|
208
|
-
|
|
209
|
-
handleUpdateData('notifications', '62c54b15524b6b59d2313c02');
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### parameter explanations
|
|
213
|
-
|
|
214
|
-
- `logger`
|
|
215
|
-
- Provides ability to add logging for Database and Validation
|
|
216
|
-
```js
|
|
217
|
-
// default
|
|
218
|
-
console;
|
|
219
|
-
```
|
|
220
|
-
- `catchAsync`
|
|
221
|
-
- Wraps functions to handle async errors
|
|
222
|
-
```js
|
|
223
|
-
// default
|
|
224
|
-
function catchAsync(fn) {
|
|
225
|
-
return function (req, res, next) {
|
|
226
|
-
Promise.resolve(fn(req, res, next)).catch((err) => {
|
|
227
|
-
// this.logger.error(err.message);
|
|
228
|
-
res.status(internalServerError).json({
|
|
229
|
-
code: RESPONSE_CODE.ERROR,
|
|
230
|
-
message: err.message,
|
|
231
|
-
data: {},
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
```
|
|
237
|
-
- `collections`
|
|
238
|
-
- Array of collection items to add reference of collections in package.
|
|
239
|
-
- `redis`
|
|
240
|
-
- Redis URL string or connection object to wrap user APIs into redis cache.
|
|
241
|
-
- i.e. `redis://localhost:6379` or `{ HOST: 'localhost', PORT: 6379, PASSWORD: "test", USER: "test", DB: 0 }`
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
#### Collection Item Format
|
|
245
|
-
|
|
246
|
-
| Code | Description |
|
|
247
|
-
| -------------- | -------------------------------------------------------------------------------------- |
|
|
248
|
-
| title | Title of collection name to show in UI |
|
|
249
|
-
| collectionName | Collection name specified in database |
|
|
250
|
-
| filters | Filter object to apply while getting data, like `{ isDeleted: false, isActive: true }` |
|
|
251
|
-
| searchColumns | Array of fields to to perform search |
|
|
252
|
-
| aggregations | Array of aggregation items you want to apply while retriving items |
|
|
253
|
-
| customWidgetTypes | Array of widget types to add, like `{ label: "", value: "", imageOnly: true, collectionsOnly: true; }` |
|
|
254
|
-
|
|
255
|
-
**Example**,
|
|
256
|
-
|
|
257
|
-
```js
|
|
258
|
-
setConfig({
|
|
259
|
-
collections: [
|
|
260
|
-
{
|
|
261
|
-
title: 'Notifications',
|
|
262
|
-
collectionName: 'notifications',
|
|
263
|
-
filters: { isDeleted: false, isActive: true },
|
|
264
|
-
searchColumns: ['name', 'code'],
|
|
265
|
-
aggregations: [
|
|
266
|
-
{
|
|
267
|
-
$lookup: {
|
|
268
|
-
from: 'file',
|
|
269
|
-
let: {
|
|
270
|
-
id: '$fileId',
|
|
271
|
-
},
|
|
272
|
-
pipeline: [
|
|
273
|
-
{
|
|
274
|
-
$match: {
|
|
275
|
-
$expr: {
|
|
276
|
-
$eq: ['$_id', '$$id'],
|
|
277
|
-
},
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
{
|
|
281
|
-
$project: {
|
|
282
|
-
_id: 1,
|
|
283
|
-
nm: 1,
|
|
284
|
-
uri: 1,
|
|
285
|
-
mimeType: 1,
|
|
286
|
-
},
|
|
287
|
-
},
|
|
288
|
-
],
|
|
289
|
-
as: 'fileId',
|
|
290
|
-
}
|
|
291
|
-
},
|
|
292
|
-
{
|
|
293
|
-
$project: {
|
|
294
|
-
_id: 1,
|
|
295
|
-
nm: 1,
|
|
296
|
-
fileId: 1,
|
|
297
|
-
}
|
|
298
|
-
},
|
|
299
|
-
{
|
|
300
|
-
$match: {
|
|
301
|
-
deletedAt: { $exists: false },
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
]
|
|
305
|
-
},
|
|
306
|
-
],
|
|
307
|
-
});
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
## Routes Infomration
|
|
311
|
-
|
|
312
|
-
Response follows following structure
|
|
313
|
-
|
|
314
|
-
```js
|
|
315
|
-
{
|
|
316
|
-
code: RESPONSE_CODES,
|
|
317
|
-
message: "" // if internationalized is applied
|
|
318
|
-
data: {}
|
|
319
|
-
}
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
### Response Codes
|
|
323
|
-
|
|
324
|
-
| Code | Description |
|
|
325
|
-
| ------- | ------------------------------------ |
|
|
326
|
-
| SUCCESS | When request fullfiled without error |
|
|
327
|
-
| ERROR | When request fullfiled with error |
|
|
328
|
-
|
|
329
|
-
### Custom Validation messages
|
|
330
|
-
|
|
331
|
-
| Message | Description |
|
|
332
|
-
| ---------------------------------- | ----------------------------------------------- |
|
|
333
|
-
| Widget with same code is available | When widget with same code is exist in database |
|
|
334
|
-
|
|
335
|
-
### HTTP Status Codes
|
|
336
|
-
|
|
337
|
-
| HTTP | Description |
|
|
338
|
-
| ---- | ------------------------------------ |
|
|
339
|
-
| 200 | When request fullfiled without error |
|
|
340
|
-
| 201 | When document is created |
|
|
341
|
-
| 500 | When internal server occurred |
|
|
342
|
-
| 422 | When Validation error occurred |
|
|
343
|
-
| 404 | When Resource is not found |
|
|
344
|
-
|
|
345
|
-
### Routes
|
|
346
|
-
|
|
347
|
-
This are the routes that gets integrated by `@knovator/pagecreator-node`,
|
|
348
|
-
|
|
349
|
-
#### Widget
|
|
350
|
-
|
|
351
|
-
| Route | Method | Description |
|
|
352
|
-
| ------------------ | ---------- | -------------------------------------------------------- |
|
|
353
|
-
| `/widget-types` | **GET** | Get widget-types like `Image` and provided `collections` |
|
|
354
|
-
| `/selection-types` | **GET** | Get Selection types like `Fixed Card` and `Carousel` |
|
|
355
|
-
| `/list` | **POST** | List widget data in pagination |
|
|
356
|
-
| `/` | **POST** | Create `widget` |
|
|
357
|
-
| `/:id` | **PUT** | Update `widget` |
|
|
358
|
-
| `/:id` | **PATCH** | Partial Update `widget` |
|
|
359
|
-
| `/:id` | **DELETE** | Delete widget whose `id` send in body |
|
|
360
|
-
| `/collection-data` | **POST** | Get collection data |
|
|
361
|
-
|
|
362
|
-
#### Page
|
|
363
|
-
|
|
364
|
-
| Route | Method | Description |
|
|
365
|
-
| ------- | ---------- | ----------------------------------- |
|
|
366
|
-
| `/list` | **POST** | List page data in pagination |
|
|
367
|
-
| `/` | **POST** | Create `page` |
|
|
368
|
-
| `/:id` | **PUT** | Update `page` |
|
|
369
|
-
| `/:id` | **DELETE** | Delete page whose `id` send in body |
|
|
370
|
-
|
|
371
|
-
#### User
|
|
372
|
-
|
|
373
|
-
| Route | Method | Description |
|
|
374
|
-
| -------------- | -------- | -------------------------------------------------------- |
|
|
375
|
-
| `/widget-data` | **POST** | Get widget-data data for specified widget `code` in body |
|
|
376
|
-
| `/page-data` | **POST** | Get page-data data for specified page `code` in body |
|
|
377
|
-
|
|
378
|
-
### `descriptor` codes & `i18n` code for messages
|
|
379
|
-
|
|
380
|
-
Nextjs [i18n](https://www.npmjs.com/package/i18next) package adds facility for internationalization in nodejs application, and it's used in following manner
|
|
381
|
-
|
|
382
|
-
```js
|
|
383
|
-
// usage
|
|
384
|
-
req?.i18n?.(CODE);
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
| CODE | Description |
|
|
388
|
-
| -------------------------- | ------------------------------------------------------------ |
|
|
389
|
-
| `widget.getItemsTypes` | While fetching widget types |
|
|
390
|
-
| `widget.getWidgetTypes` | While fetching selection types |
|
|
391
|
-
| `widget.getAll` | While fetching widgets |
|
|
392
|
-
| `widget.create` | While creating widget |
|
|
393
|
-
| `widget.update` | While updating widget |
|
|
394
|
-
| `widget.partialUpdate` | While doing partialUpdate for widget, like toggle `IsActive` |
|
|
395
|
-
| `widget.delete` | While deleting widget |
|
|
396
|
-
| `widget.getCollectionData` | While getting widget `collection-data` |
|
|
397
|
-
| `page.getAll` | While getting pages in pagination |
|
|
398
|
-
| `page.create` | While creating page |
|
|
399
|
-
| `page.update` | While updating page |
|
|
400
|
-
| `page.delete` | While deleting page |
|
|
401
|
-
| `user.widgetNotFound` | While widget is not found |
|
|
402
|
-
| `user.pageNotFound` | While page is not found |
|
|
403
|
-
| `user.getWidgetData` | While getting widget data |
|
|
404
|
-
| `user.getPageData` | While getting page data |
|
|
405
|
-
|
|
406
|
-
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
407
|
-
|
|
408
|
-
<!-- CONTRIBUTING -->
|
|
409
|
-
|
|
410
|
-
## Contributing
|
|
411
|
-
|
|
412
|
-
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
|
|
413
|
-
|
|
414
|
-
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".
|
|
415
|
-
Don't forget to give the project a star! Thanks again!
|
|
416
|
-
|
|
417
|
-
1. Fork the Project
|
|
418
|
-
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
|
|
419
|
-
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
|
|
420
|
-
4. Push to the Branch (`git push origin feature/AmazingFeature`)
|
|
421
|
-
5. Open a Pull Request
|
|
422
|
-
|
|
423
|
-
<p align="right">(<a href="#top">back to top</a>)</p>
|
|
424
|
-
|
|
425
|
-
<!-- LICENSE -->
|
|
426
|
-
|
|
427
|
-
## License
|
|
428
|
-
|
|
429
|
-
Distributed under the MIT License. See `LICENSE.txt` for more information.
|
|
430
|
-
|
|
431
|
-
<p align="right">(<a href="#top">back to top</a>)</p>
|
|
432
|
-
|
|
433
|
-
<!-- CONTACT -->
|
|
434
|
-
|
|
435
|
-
## Contact
|
|
436
|
-
|
|
437
|
-
Knovator Technologies
|
|
438
|
-
|
|
439
|
-
- Twitter [@knovator](https://twitter.com/knovator)
|
|
440
|
-
- Web [https://knovator.com/](https://knovator.com/)
|
|
441
|
-
|
|
442
|
-
Project Link: [https://github.com/knovator/pagecreator](https://github.com/knovator/pagecreator)
|
|
443
|
-
|
|
444
|
-
<p align="right">(<a href="#top">back to top</a>)</p>
|
|
445
|
-
|
|
446
|
-
<!-- MARKDOWN LINKS & IMAGES -->
|
|
447
|
-
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
|
|
448
|
-
|
|
449
|
-
[contributors-shield]: https://img.shields.io/github/contributors/knovator/pagecreator.svg?style=for-the-badge
|
|
450
|
-
[contributors-url]: https://github.com/knovator/pagecreator/graphs/contributors
|
|
451
|
-
[forks-shield]: https://img.shields.io/github/forks/knovator/pagecreator.svg?style=for-the-badge
|
|
452
|
-
[forks-url]: https://github.com/knovator/pagecreator/network/members
|
|
453
|
-
[stars-shield]: https://img.shields.io/github/stars/knovator/pagecreator.svg?style=for-the-badge
|
|
454
|
-
[stars-url]: https://github.com/knovator/pagecreator/stargazers
|
|
455
|
-
[issues-shield]: https://img.shields.io/github/issues/knovator/pagecreator.svg?style=for-the-badge
|
|
456
|
-
[issues-url]: https://github.com/knovator/pagecreator/issues
|
|
457
|
-
[license-shield]: https://img.shields.io/github/license/knovator/pagecreator.svg?style=for-the-badge
|
|
458
|
-
[license-url]: https://github.com/knovator/pagecreator/blob/master/LICENSE.txt
|
|
1
|
+
<!-- Improved compatibility of back to top link: See: https://github.com/othneildrew/Best-README-Template/pull/73 -->
|
|
2
|
+
|
|
3
|
+
<a name="readme-top"></a>
|
|
4
|
+
|
|
5
|
+
[![Contributors][contributors-shield]][contributors-url]
|
|
6
|
+
[![Forks][forks-shield]][forks-url]
|
|
7
|
+
[![Stargazers][stars-shield]][stars-url]
|
|
8
|
+
[![Issues][issues-shield]][issues-url]
|
|
9
|
+
[![MIT License][license-shield]][license-url]
|
|
10
|
+
|
|
11
|
+
<!-- PROJECT LOGO -->
|
|
12
|
+
<br />
|
|
13
|
+
<div align="center">
|
|
14
|
+
<!-- <a href="https://github.com/knovator/pagecreator">
|
|
15
|
+
<img src="images/logo.png" alt="Logo" width="80" height="80">
|
|
16
|
+
</a> -->
|
|
17
|
+
|
|
18
|
+
<h3 align="center">@knovator/pagecreator-node</h3>
|
|
19
|
+
|
|
20
|
+
<p align="center">
|
|
21
|
+
Plug & Play functionality to Build dynamic pages on the fly.
|
|
22
|
+
<br />
|
|
23
|
+
<a href="https://github.com/knovator/pagecreator"><strong>Explore the docs »</strong></a>
|
|
24
|
+
<br />
|
|
25
|
+
<br />
|
|
26
|
+
<a href="https://github.com/knovator/pagecreator">View Demo</a>
|
|
27
|
+
·
|
|
28
|
+
<a href="https://github.com/knovator/pagecreator/issues">Report Bug</a>
|
|
29
|
+
·
|
|
30
|
+
<a href="https://github.com/knovator/pagecreator/issues">Request Feature</a>
|
|
31
|
+
</p>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<!-- TABLE OF CONTENTS -->
|
|
35
|
+
<details>
|
|
36
|
+
<summary>Table of Contents</summary>
|
|
37
|
+
<ol>
|
|
38
|
+
<li>
|
|
39
|
+
<a href="#about-the-project">About The Project</a>
|
|
40
|
+
<ul>
|
|
41
|
+
<li><a href="#built-with">Built With</a></li>
|
|
42
|
+
</ul>
|
|
43
|
+
</li>
|
|
44
|
+
<li>
|
|
45
|
+
<a href="#getting-started">Getting Started</a>
|
|
46
|
+
<ul>
|
|
47
|
+
<li><a href="#prerequisites">Prerequisites</a></li>
|
|
48
|
+
<li><a href="#installation">Installation</a></li>
|
|
49
|
+
</ul>
|
|
50
|
+
</li>
|
|
51
|
+
<li><a href="#usage">Usage</a></li>
|
|
52
|
+
<li><a href="#routes-infomration">Routes Infomration</a></li>
|
|
53
|
+
<li><a href="#contributing">Contributing</a></li>
|
|
54
|
+
<li><a href="#license">License</a></li>
|
|
55
|
+
<li><a href="#contact">Contact</a></li>
|
|
56
|
+
</ol>
|
|
57
|
+
</details>
|
|
58
|
+
|
|
59
|
+
<!-- ABOUT THE PROJECT -->
|
|
60
|
+
|
|
61
|
+
## About The Project
|
|
62
|
+
|
|
63
|
+
`@knovator/pagecreator-node` is built with intent to build pages that are depend on backend data, and admin can change how page will look like.
|
|
64
|
+
|
|
65
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
66
|
+
|
|
67
|
+
### Built With
|
|
68
|
+
|
|
69
|
+
- [Typescript](https://www.typescriptlang.org/)
|
|
70
|
+
- [mongoose](https://mongoosejs.com/)
|
|
71
|
+
- [express](https://expressjs.com/)
|
|
72
|
+
- [mongoose-paginate-v2](https://www.npmjs.com/package/mongoose-paginate-v2)
|
|
73
|
+
- [joi](https://www.npmjs.com/package/joi)
|
|
74
|
+
|
|
75
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
76
|
+
|
|
77
|
+
<!-- GETTING STARTED -->
|
|
78
|
+
|
|
79
|
+
## Getting Started
|
|
80
|
+
|
|
81
|
+
To integrate `@knovator/pagecreator-node`, you should be having basic nodejs application up and running with express (optionally using mongoose for mongodb database). `@knovator/pagecreator-node` provides routes for `widget`, `page` and `user` to use in application.
|
|
82
|
+
|
|
83
|
+
### Prerequisites
|
|
84
|
+
|
|
85
|
+
- It's good start to have `nodejs` application up and running with `express`. Good to have used [i18next](https://www.npmjs.com/package/i18next) to add message in response codes.
|
|
86
|
+
- `routes` uses `mongoose` connection established by application, so it's required to connect to database before using package. For example,
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
// db.js
|
|
90
|
+
const mongoose = require('mongoose');
|
|
91
|
+
|
|
92
|
+
mongoose
|
|
93
|
+
.connect('mongodb://localhost:27017/knovator')
|
|
94
|
+
.then(() => console.info('Database connected'))
|
|
95
|
+
.catch((err) => {
|
|
96
|
+
console.error('DB Error', err);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
module.exports = mongoose;
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
- Image upload route for `upload` & `remove` is needed to declare externally. Example,
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
// fileRoute.js
|
|
106
|
+
const express = require('express');
|
|
107
|
+
const router = express.Router();
|
|
108
|
+
|
|
109
|
+
router.post(`/files/upload`, (req, res) => {
|
|
110
|
+
// TO DO: some file storage operation
|
|
111
|
+
let uri = '/image.jpg';
|
|
112
|
+
let id = '62c54b15524b6b59d2313c02';
|
|
113
|
+
res.json({
|
|
114
|
+
code: 'SUCCESS',
|
|
115
|
+
data: { id, uri },
|
|
116
|
+
message: 'File uploaded successfully',
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
router.delete(`/files/remove/:id`, (req, res) => {
|
|
121
|
+
// TO DO: some file remove operation
|
|
122
|
+
res.json({
|
|
123
|
+
code: 'SUCCESS',
|
|
124
|
+
data: {},
|
|
125
|
+
message: 'File removed successfully',
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
module.exports = router;
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Sample App file**
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
require('./src/db');
|
|
136
|
+
require('./src/models/file');
|
|
137
|
+
|
|
138
|
+
const cors = require('cors');
|
|
139
|
+
const express = require('express');
|
|
140
|
+
const fileRoutes = require('./fileRoute.js');
|
|
141
|
+
const PORT = 8080;
|
|
142
|
+
|
|
143
|
+
const app = express();
|
|
144
|
+
app.use(cors());
|
|
145
|
+
app.use(express.static('public'));
|
|
146
|
+
app.use(fileRoutes);
|
|
147
|
+
|
|
148
|
+
// ...
|
|
149
|
+
app.listen(PORT, () => {
|
|
150
|
+
console.log(`App started on ${PORT}`);
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Installation
|
|
155
|
+
|
|
156
|
+
1. Add pagecreator package,
|
|
157
|
+
```sh
|
|
158
|
+
npm install @knovator/pagecreator-node
|
|
159
|
+
# or
|
|
160
|
+
yarn add @knovator/pagecreator-node
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
164
|
+
|
|
165
|
+
<!-- USAGE EXAMPLES -->
|
|
166
|
+
|
|
167
|
+
## Usage
|
|
168
|
+
|
|
169
|
+
App/Main file is a good place to use `@knovator/pagecreator-node`
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
const {
|
|
173
|
+
setConfig,
|
|
174
|
+
WidgetRoutes,
|
|
175
|
+
ItemRoutes,
|
|
176
|
+
FileUploadRoute,
|
|
177
|
+
PageRoutes,
|
|
178
|
+
UserRoutes,
|
|
179
|
+
} = require('@knovator/pagecreator-node');
|
|
180
|
+
|
|
181
|
+
setConfig({
|
|
182
|
+
collections: [
|
|
183
|
+
{
|
|
184
|
+
title: 'Notifications',
|
|
185
|
+
collectionName: 'notifications',
|
|
186
|
+
filters: { isDeleted: false, isActive: true },
|
|
187
|
+
searchColumns: ['name', 'code'],
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
app.use('/widgets', WidgetRoutes);
|
|
193
|
+
app.use('/items', ItemRoutes);
|
|
194
|
+
app.use('/media', FileUploadRoute);
|
|
195
|
+
app.use('/pages', PageRoutes);
|
|
196
|
+
app.use('/users', UserRoutes);
|
|
197
|
+
|
|
198
|
+
app.listen(PORT, () => {
|
|
199
|
+
console.log(`App started on ${PORT}`);
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Through `setConfig` function e can set `logger`, `collections` and `catchAsync` functions as parameters. By `collections`, we can add reference of application collections.
|
|
204
|
+
|
|
205
|
+
- `handleUpdateData` is used to handle update redis cache when data is updated in database. It takes `collectionName` and `_id` as parameters.
|
|
206
|
+
```js
|
|
207
|
+
import { handleUpdateData } from '@knovator/pagecreator-node';
|
|
208
|
+
|
|
209
|
+
handleUpdateData('notifications', '62c54b15524b6b59d2313c02');
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### parameter explanations
|
|
213
|
+
|
|
214
|
+
- `logger`
|
|
215
|
+
- Provides ability to add logging for Database and Validation
|
|
216
|
+
```js
|
|
217
|
+
// default
|
|
218
|
+
console;
|
|
219
|
+
```
|
|
220
|
+
- `catchAsync`
|
|
221
|
+
- Wraps functions to handle async errors
|
|
222
|
+
```js
|
|
223
|
+
// default
|
|
224
|
+
function catchAsync(fn) {
|
|
225
|
+
return function (req, res, next) {
|
|
226
|
+
Promise.resolve(fn(req, res, next)).catch((err) => {
|
|
227
|
+
// this.logger.error(err.message);
|
|
228
|
+
res.status(internalServerError).json({
|
|
229
|
+
code: RESPONSE_CODE.ERROR,
|
|
230
|
+
message: err.message,
|
|
231
|
+
data: {},
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
- `collections`
|
|
238
|
+
- Array of collection items to add reference of collections in package.
|
|
239
|
+
- `redis`
|
|
240
|
+
- Redis URL string or connection object to wrap user APIs into redis cache.
|
|
241
|
+
- i.e. `redis://localhost:6379` or `{ HOST: 'localhost', PORT: 6379, PASSWORD: "test", USER: "test", DB: 0 }`
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
#### Collection Item Format
|
|
245
|
+
|
|
246
|
+
| Code | Description |
|
|
247
|
+
| -------------- | -------------------------------------------------------------------------------------- |
|
|
248
|
+
| title | Title of collection name to show in UI |
|
|
249
|
+
| collectionName | Collection name specified in database |
|
|
250
|
+
| filters | Filter object to apply while getting data, like `{ isDeleted: false, isActive: true }` |
|
|
251
|
+
| searchColumns | Array of fields to to perform search |
|
|
252
|
+
| aggregations | Array of aggregation items you want to apply while retriving items |
|
|
253
|
+
| customWidgetTypes | Array of widget types to add, like `{ label: "", value: "", imageOnly: true, collectionsOnly: true; }` |
|
|
254
|
+
|
|
255
|
+
**Example**,
|
|
256
|
+
|
|
257
|
+
```js
|
|
258
|
+
setConfig({
|
|
259
|
+
collections: [
|
|
260
|
+
{
|
|
261
|
+
title: 'Notifications',
|
|
262
|
+
collectionName: 'notifications',
|
|
263
|
+
filters: { isDeleted: false, isActive: true },
|
|
264
|
+
searchColumns: ['name', 'code'],
|
|
265
|
+
aggregations: [
|
|
266
|
+
{
|
|
267
|
+
$lookup: {
|
|
268
|
+
from: 'file',
|
|
269
|
+
let: {
|
|
270
|
+
id: '$fileId',
|
|
271
|
+
},
|
|
272
|
+
pipeline: [
|
|
273
|
+
{
|
|
274
|
+
$match: {
|
|
275
|
+
$expr: {
|
|
276
|
+
$eq: ['$_id', '$$id'],
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
$project: {
|
|
282
|
+
_id: 1,
|
|
283
|
+
nm: 1,
|
|
284
|
+
uri: 1,
|
|
285
|
+
mimeType: 1,
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
as: 'fileId',
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
$project: {
|
|
294
|
+
_id: 1,
|
|
295
|
+
nm: 1,
|
|
296
|
+
fileId: 1,
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
$match: {
|
|
301
|
+
deletedAt: { $exists: false },
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
]
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Routes Infomration
|
|
311
|
+
|
|
312
|
+
Response follows following structure
|
|
313
|
+
|
|
314
|
+
```js
|
|
315
|
+
{
|
|
316
|
+
code: RESPONSE_CODES,
|
|
317
|
+
message: "" // if internationalized is applied
|
|
318
|
+
data: {}
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Response Codes
|
|
323
|
+
|
|
324
|
+
| Code | Description |
|
|
325
|
+
| ------- | ------------------------------------ |
|
|
326
|
+
| SUCCESS | When request fullfiled without error |
|
|
327
|
+
| ERROR | When request fullfiled with error |
|
|
328
|
+
|
|
329
|
+
### Custom Validation messages
|
|
330
|
+
|
|
331
|
+
| Message | Description |
|
|
332
|
+
| ---------------------------------- | ----------------------------------------------- |
|
|
333
|
+
| Widget with same code is available | When widget with same code is exist in database |
|
|
334
|
+
|
|
335
|
+
### HTTP Status Codes
|
|
336
|
+
|
|
337
|
+
| HTTP | Description |
|
|
338
|
+
| ---- | ------------------------------------ |
|
|
339
|
+
| 200 | When request fullfiled without error |
|
|
340
|
+
| 201 | When document is created |
|
|
341
|
+
| 500 | When internal server occurred |
|
|
342
|
+
| 422 | When Validation error occurred |
|
|
343
|
+
| 404 | When Resource is not found |
|
|
344
|
+
|
|
345
|
+
### Routes
|
|
346
|
+
|
|
347
|
+
This are the routes that gets integrated by `@knovator/pagecreator-node`,
|
|
348
|
+
|
|
349
|
+
#### Widget
|
|
350
|
+
|
|
351
|
+
| Route | Method | Description |
|
|
352
|
+
| ------------------ | ---------- | -------------------------------------------------------- |
|
|
353
|
+
| `/widget-types` | **GET** | Get widget-types like `Image` and provided `collections` |
|
|
354
|
+
| `/selection-types` | **GET** | Get Selection types like `Fixed Card` and `Carousel` |
|
|
355
|
+
| `/list` | **POST** | List widget data in pagination |
|
|
356
|
+
| `/` | **POST** | Create `widget` |
|
|
357
|
+
| `/:id` | **PUT** | Update `widget` |
|
|
358
|
+
| `/:id` | **PATCH** | Partial Update `widget` |
|
|
359
|
+
| `/:id` | **DELETE** | Delete widget whose `id` send in body |
|
|
360
|
+
| `/collection-data` | **POST** | Get collection data |
|
|
361
|
+
|
|
362
|
+
#### Page
|
|
363
|
+
|
|
364
|
+
| Route | Method | Description |
|
|
365
|
+
| ------- | ---------- | ----------------------------------- |
|
|
366
|
+
| `/list` | **POST** | List page data in pagination |
|
|
367
|
+
| `/` | **POST** | Create `page` |
|
|
368
|
+
| `/:id` | **PUT** | Update `page` |
|
|
369
|
+
| `/:id` | **DELETE** | Delete page whose `id` send in body |
|
|
370
|
+
|
|
371
|
+
#### User
|
|
372
|
+
|
|
373
|
+
| Route | Method | Description |
|
|
374
|
+
| -------------- | -------- | -------------------------------------------------------- |
|
|
375
|
+
| `/widget-data` | **POST** | Get widget-data data for specified widget `code` in body |
|
|
376
|
+
| `/page-data` | **POST** | Get page-data data for specified page `code` in body |
|
|
377
|
+
|
|
378
|
+
### `descriptor` codes & `i18n` code for messages
|
|
379
|
+
|
|
380
|
+
Nextjs [i18n](https://www.npmjs.com/package/i18next) package adds facility for internationalization in nodejs application, and it's used in following manner
|
|
381
|
+
|
|
382
|
+
```js
|
|
383
|
+
// usage
|
|
384
|
+
req?.i18n?.(CODE);
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
| CODE | Description |
|
|
388
|
+
| -------------------------- | ------------------------------------------------------------ |
|
|
389
|
+
| `widget.getItemsTypes` | While fetching widget types |
|
|
390
|
+
| `widget.getWidgetTypes` | While fetching selection types |
|
|
391
|
+
| `widget.getAll` | While fetching widgets |
|
|
392
|
+
| `widget.create` | While creating widget |
|
|
393
|
+
| `widget.update` | While updating widget |
|
|
394
|
+
| `widget.partialUpdate` | While doing partialUpdate for widget, like toggle `IsActive` |
|
|
395
|
+
| `widget.delete` | While deleting widget |
|
|
396
|
+
| `widget.getCollectionData` | While getting widget `collection-data` |
|
|
397
|
+
| `page.getAll` | While getting pages in pagination |
|
|
398
|
+
| `page.create` | While creating page |
|
|
399
|
+
| `page.update` | While updating page |
|
|
400
|
+
| `page.delete` | While deleting page |
|
|
401
|
+
| `user.widgetNotFound` | While widget is not found |
|
|
402
|
+
| `user.pageNotFound` | While page is not found |
|
|
403
|
+
| `user.getWidgetData` | While getting widget data |
|
|
404
|
+
| `user.getPageData` | While getting page data |
|
|
405
|
+
|
|
406
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
407
|
+
|
|
408
|
+
<!-- CONTRIBUTING -->
|
|
409
|
+
|
|
410
|
+
## Contributing
|
|
411
|
+
|
|
412
|
+
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
|
|
413
|
+
|
|
414
|
+
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".
|
|
415
|
+
Don't forget to give the project a star! Thanks again!
|
|
416
|
+
|
|
417
|
+
1. Fork the Project
|
|
418
|
+
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
|
|
419
|
+
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
|
|
420
|
+
4. Push to the Branch (`git push origin feature/AmazingFeature`)
|
|
421
|
+
5. Open a Pull Request
|
|
422
|
+
|
|
423
|
+
<p align="right">(<a href="#top">back to top</a>)</p>
|
|
424
|
+
|
|
425
|
+
<!-- LICENSE -->
|
|
426
|
+
|
|
427
|
+
## License
|
|
428
|
+
|
|
429
|
+
Distributed under the MIT License. See `LICENSE.txt` for more information.
|
|
430
|
+
|
|
431
|
+
<p align="right">(<a href="#top">back to top</a>)</p>
|
|
432
|
+
|
|
433
|
+
<!-- CONTACT -->
|
|
434
|
+
|
|
435
|
+
## Contact
|
|
436
|
+
|
|
437
|
+
Knovator Technologies
|
|
438
|
+
|
|
439
|
+
- Twitter [@knovator](https://twitter.com/knovator)
|
|
440
|
+
- Web [https://knovator.com/](https://knovator.com/)
|
|
441
|
+
|
|
442
|
+
Project Link: [https://github.com/knovator/pagecreator](https://github.com/knovator/pagecreator)
|
|
443
|
+
|
|
444
|
+
<p align="right">(<a href="#top">back to top</a>)</p>
|
|
445
|
+
|
|
446
|
+
<!-- MARKDOWN LINKS & IMAGES -->
|
|
447
|
+
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
|
|
448
|
+
|
|
449
|
+
[contributors-shield]: https://img.shields.io/github/contributors/knovator/pagecreator.svg?style=for-the-badge
|
|
450
|
+
[contributors-url]: https://github.com/knovator/pagecreator/graphs/contributors
|
|
451
|
+
[forks-shield]: https://img.shields.io/github/forks/knovator/pagecreator.svg?style=for-the-badge
|
|
452
|
+
[forks-url]: https://github.com/knovator/pagecreator/network/members
|
|
453
|
+
[stars-shield]: https://img.shields.io/github/stars/knovator/pagecreator.svg?style=for-the-badge
|
|
454
|
+
[stars-url]: https://github.com/knovator/pagecreator/stargazers
|
|
455
|
+
[issues-shield]: https://img.shields.io/github/issues/knovator/pagecreator.svg?style=for-the-badge
|
|
456
|
+
[issues-url]: https://github.com/knovator/pagecreator/issues
|
|
457
|
+
[license-shield]: https://img.shields.io/github/license/knovator/pagecreator.svg?style=for-the-badge
|
|
458
|
+
[license-url]: https://github.com/knovator/pagecreator/blob/master/LICENSE.txt
|