jxp 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.sample +31 -6
- package/README.md +27 -13
- package/dist/bin/init_db.d.ts +3 -0
- package/dist/bin/init_db.d.ts.map +1 -0
- package/dist/bin/init_db.js +48 -0
- package/dist/bin/init_db.js.map +1 -0
- package/dist/bin/server.d.ts +4 -0
- package/dist/bin/server.d.ts.map +1 -0
- package/dist/bin/server.js +132 -0
- package/dist/bin/server.js.map +1 -0
- package/dist/bin/setup.d.ts +3 -0
- package/dist/bin/setup.d.ts.map +1 -0
- package/dist/bin/setup.js +185 -0
- package/dist/bin/setup.js.map +1 -0
- package/dist/globals.d.ts +14 -0
- package/dist/globals.d.ts.map +1 -0
- package/dist/globals.js +3 -0
- package/dist/globals.js.map +1 -0
- package/dist/libs/aggregate_guard.d.ts +5 -0
- package/dist/libs/aggregate_guard.d.ts.map +1 -0
- package/dist/libs/aggregate_guard.js +58 -0
- package/dist/libs/aggregate_guard.js.map +1 -0
- package/dist/libs/bulkwrite_guard.d.ts +5 -0
- package/dist/libs/bulkwrite_guard.d.ts.map +1 -0
- package/dist/libs/bulkwrite_guard.js +44 -0
- package/dist/libs/bulkwrite_guard.js.map +1 -0
- package/dist/libs/cache.d.ts +2 -0
- package/dist/libs/cache.d.ts.map +1 -0
- package/dist/libs/cache.js +94 -0
- package/dist/libs/cache.js.map +1 -0
- package/dist/libs/call_guard.d.ts +3 -0
- package/dist/libs/call_guard.d.ts.map +1 -0
- package/dist/libs/call_guard.js +17 -0
- package/dist/libs/call_guard.js.map +1 -0
- package/dist/libs/connection_string.d.ts +3 -0
- package/dist/libs/connection_string.d.ts.map +1 -0
- package/dist/libs/connection_string.js +4 -0
- package/dist/libs/connection_string.js.map +1 -0
- package/dist/libs/datamunging.d.ts +2 -0
- package/dist/libs/datamunging.d.ts.map +1 -0
- package/dist/libs/datamunging.js +320 -0
- package/dist/libs/datamunging.js.map +1 -0
- package/dist/libs/docs.d.ts +2 -0
- package/dist/libs/docs.d.ts.map +1 -0
- package/dist/libs/docs.js +307 -0
- package/dist/libs/docs.js.map +1 -0
- package/dist/libs/groups.d.ts +2 -0
- package/dist/libs/groups.d.ts.map +1 -0
- package/dist/libs/groups.js +136 -0
- package/dist/libs/groups.js.map +1 -0
- package/dist/libs/jxp.d.ts +4 -0
- package/dist/libs/jxp.d.ts.map +1 -0
- package/dist/libs/jxp.js +1157 -0
- package/dist/libs/jxp.js.map +1 -0
- package/dist/libs/link_index.d.ts +14 -0
- package/dist/libs/link_index.d.ts.map +1 -0
- package/dist/libs/link_index.js +37 -0
- package/dist/libs/link_index.js.map +1 -0
- package/dist/libs/load-config.d.ts +16 -0
- package/dist/libs/load-config.d.ts.map +1 -0
- package/dist/libs/load-config.js +155 -0
- package/dist/libs/load-config.js.map +1 -0
- package/dist/libs/login.d.ts +2 -0
- package/dist/libs/login.d.ts.map +1 -0
- package/dist/libs/login.js +244 -0
- package/dist/libs/login.js.map +1 -0
- package/dist/libs/modeldir.d.ts +5 -0
- package/dist/libs/modeldir.d.ts.map +1 -0
- package/dist/libs/modeldir.js +15 -0
- package/dist/libs/modeldir.js.map +1 -0
- package/dist/libs/query_limits.d.ts +2 -0
- package/dist/libs/query_limits.d.ts.map +1 -0
- package/dist/libs/query_limits.js +84 -0
- package/dist/libs/query_limits.js.map +1 -0
- package/dist/libs/query_manipulation.d.ts +3 -0
- package/dist/libs/query_manipulation.d.ts.map +1 -0
- package/{libs → dist/libs}/query_manipulation.js +22 -21
- package/dist/libs/query_manipulation.js.map +1 -0
- package/dist/libs/query_sanitize.d.ts +5 -0
- package/dist/libs/query_sanitize.d.ts.map +1 -0
- package/dist/libs/query_sanitize.js +109 -0
- package/dist/libs/query_sanitize.js.map +1 -0
- package/dist/libs/response_sanitize.d.ts +7 -0
- package/dist/libs/response_sanitize.d.ts.map +1 -0
- package/dist/libs/response_sanitize.js +57 -0
- package/dist/libs/response_sanitize.js.map +1 -0
- package/dist/libs/safe_error.d.ts +3 -0
- package/dist/libs/safe_error.d.ts.map +1 -0
- package/dist/libs/safe_error.js +14 -0
- package/dist/libs/safe_error.js.map +1 -0
- package/dist/libs/schema.d.ts +14 -0
- package/dist/libs/schema.d.ts.map +1 -0
- package/dist/libs/schema.js +98 -0
- package/dist/libs/schema.js.map +1 -0
- package/dist/libs/schema_description.d.ts +3 -0
- package/dist/libs/schema_description.d.ts.map +1 -0
- package/{libs → dist/libs}/schema_description.js +12 -21
- package/dist/libs/schema_description.js.map +1 -0
- package/dist/libs/security.d.ts +2 -0
- package/dist/libs/security.d.ts.map +1 -0
- package/dist/libs/security.js +428 -0
- package/dist/libs/security.js.map +1 -0
- package/dist/libs/setup.d.ts +2 -0
- package/dist/libs/setup.d.ts.map +1 -0
- package/dist/libs/setup.js +87 -0
- package/dist/libs/setup.js.map +1 -0
- package/dist/libs/startup-deprecations.d.ts +2 -0
- package/dist/libs/startup-deprecations.d.ts.map +1 -0
- package/dist/libs/startup-deprecations.js +21 -0
- package/dist/libs/startup-deprecations.js.map +1 -0
- package/dist/libs/startup.d.ts +16 -0
- package/dist/libs/startup.d.ts.map +1 -0
- package/dist/libs/startup.js +63 -0
- package/dist/libs/startup.js.map +1 -0
- package/dist/libs/ws.d.ts +2 -0
- package/dist/libs/ws.d.ts.map +1 -0
- package/{libs → dist/libs}/ws.js +91 -73
- package/dist/libs/ws.js.map +1 -0
- package/dist/models/apikey_model.d.ts +10 -0
- package/dist/models/apikey_model.d.ts.map +1 -0
- package/dist/models/apikey_model.js +14 -0
- package/dist/models/apikey_model.js.map +1 -0
- package/dist/models/link_model.d.ts +9 -0
- package/dist/models/link_model.d.ts.map +1 -0
- package/dist/models/link_model.js +20 -0
- package/dist/models/link_model.js.map +1 -0
- package/dist/models/refreshtoken_model.d.ts +11 -0
- package/dist/models/refreshtoken_model.d.ts.map +1 -0
- package/dist/models/refreshtoken_model.js +22 -0
- package/dist/models/refreshtoken_model.js.map +1 -0
- package/dist/models/test_model.d.ts +24 -0
- package/dist/models/test_model.d.ts.map +1 -0
- package/dist/models/test_model.js +44 -0
- package/dist/models/test_model.js.map +1 -0
- package/dist/models/token_model.d.ts +14 -0
- package/dist/models/token_model.d.ts.map +1 -0
- package/dist/models/token_model.js +25 -0
- package/dist/models/token_model.js.map +1 -0
- package/dist/models/user_model.d.ts +15 -0
- package/dist/models/user_model.d.ts.map +1 -0
- package/dist/models/user_model.js +38 -0
- package/dist/models/user_model.js.map +1 -0
- package/dist/models/usergroups_model.d.ts +9 -0
- package/dist/models/usergroups_model.d.ts.map +1 -0
- package/dist/models/usergroups_model.js +18 -0
- package/dist/models/usergroups_model.js.map +1 -0
- package/dist/types/jxp-config.d.ts +134 -0
- package/dist/types/jxp-config.d.ts.map +1 -0
- package/dist/types/jxp-config.js +3 -0
- package/dist/types/jxp-config.js.map +1 -0
- package/dist/types/schema-fields.d.ts +43 -0
- package/dist/types/schema-fields.d.ts.map +1 -0
- package/dist/types/schema-fields.js +3 -0
- package/dist/types/schema-fields.js.map +1 -0
- package/docs/aggregations.md +40 -21
- package/docs/api.md +55 -15
- package/docs/authentication.md +60 -37
- package/docs/bulk_writes.md +11 -8
- package/docs/caching.md +28 -11
- package/docs/changelog.md +237 -31
- package/docs/configuration.md +99 -118
- package/docs/hooks.md +12 -8
- package/docs/index.md +17 -17
- package/docs/installation.md +23 -18
- package/docs/permissions.md +33 -19
- package/docs/queries.md +9 -5
- package/docs/schemas.md +21 -10
- package/docs/special.md +4 -4
- package/docs/typescript.md +86 -0
- package/docs/websocket.md +99 -1
- package/mkdocs.yml +1 -0
- package/package.json +121 -69
- package/templates/api.pug +32 -0
- package/templates/app-server.js +43 -0
- package/templates/app.env.sample +8 -0
- package/templates/assets/api-console.js +133 -0
- package/templates/assets/docs.css +227 -0
- package/templates/index.pug +62 -2
- package/templates/layout.pug +17 -11
- package/templates/md.pug +1 -1
- package/templates/model.pug +117 -68
- package/templates/navbar.pug +14 -21
- package/templates/sidebar.pug +30 -0
- package/.env +0 -9
- package/.eslintrc.json +0 -20
- package/.jshintrc +0 -4
- package/.vscode/launch.json +0 -34
- package/.vscode/settings.json +0 -3
- package/Dockerfile +0 -7
- package/bin/init_db.js +0 -54
- package/bin/server.js +0 -131
- package/bin/setup.js +0 -190
- package/config_sample.json +0 -10
- package/docker-compose.yml +0 -32
- package/libs/cache.js +0 -92
- package/libs/connection_string.js +0 -24
- package/libs/datamunging.js +0 -335
- package/libs/docs.js +0 -162
- package/libs/groups.js +0 -133
- package/libs/jxp.js +0 -1208
- package/libs/login.js +0 -222
- package/libs/modeldir.js +0 -14
- package/libs/schema.js +0 -99
- package/libs/security.js +0 -410
- package/libs/setup.js +0 -90
- package/models/apikey_model.js +0 -10
- package/models/link_model.js +0 -17
- package/models/refreshtoken_model.js +0 -20
- package/models/test_model.js +0 -50
- package/models/token_model.js +0 -23
- package/models/user_model.js +0 -38
- package/models/usergroups_model.js +0 -15
- package/setup.sh +0 -3
- package/test/.eslintrc.json +0 -13
- package/test/auth.js +0 -190
- package/test/init.js +0 -92
- package/test/setup.test.js +0 -62
- package/test/test.js +0 -1527
- package/test/wstest.js.old +0 -124
package/docs/installation.md
CHANGED
|
@@ -4,27 +4,31 @@ JXP would typically run as a stand-alone server, although you can include it as
|
|
|
4
4
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
7
|
-
JXP runs on [Node
|
|
7
|
+
JXP runs on [Node.js](https://nodejs.org/en/). **Node.js 22 or newer is required** (`engines.node` is `>=22.0.0` in the package). We recommend the current Node 22 LTS release.
|
|
8
8
|
|
|
9
|
-
JXP requires a [
|
|
9
|
+
JXP requires a [MongoDB](https://www.mongodb.com/) database server to connect to. You can host your own, or connect to [MongoDB Atlas](https://www.mongodb.com/cloud/atlas), which has a free tier if you don't want Mongo running locally.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Optional in-process response caching is available via environment variables (see [Caching](caching.md)). No external Memcache server is required.
|
|
12
12
|
|
|
13
|
-
If you want to send
|
|
13
|
+
If you want to send forgotten-password links, configure SMTP on your `JXP()` options object (see [Configuration](configuration.md#smtp-and-password-recovery)).
|
|
14
|
+
|
|
15
|
+
### JXP 4
|
|
16
|
+
|
|
17
|
+
JXP 4 is implemented in TypeScript and published as compiled JavaScript in `dist/`. The npm package includes a prebuilt `dist/`; `npm install jxp` does not compile from source. Cloning the repo and running `npm install` builds automatically when `src/` is present. Consumer apps still define models as `*_model.js` files in `MODEL_DIR`.
|
|
14
18
|
|
|
15
19
|
## Running on Docker
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
The repository includes `Dockerfile` and `docker-compose.yml`. Review those files and align environment variables with [Configuration](configuration.md) (`MONGO_CONNECTION_STRING` or `MONGODB_*` vars) before running `docker compose up -d`.
|
|
18
22
|
|
|
19
23
|
## Installing the easy way
|
|
20
24
|
|
|
21
|
-
JXP has a helper that will set up and configure an instance for you.
|
|
25
|
+
JXP has a helper that will set up and configure an instance for you.
|
|
22
26
|
|
|
23
27
|
First, install JXP globally:
|
|
24
28
|
|
|
25
29
|
`npm install --global jxp`
|
|
26
30
|
|
|
27
|
-
Now run `jxp-setup <directory>` and follow the prompts. This will install the necessary files, give you a few models to get started, and help you set up an admin username and password.
|
|
31
|
+
Now run `jxp-setup <directory>` and follow the prompts. This will install the necessary files, give you a few models to get started, and help you set up an admin username and password.
|
|
28
32
|
|
|
29
33
|
Once the setup is complete, use `npm start` to start the server.
|
|
30
34
|
|
|
@@ -42,7 +46,8 @@ Then include in your project:
|
|
|
42
46
|
|
|
43
47
|
NOTE: All the `/setup` endpoints will only run if the user table is empty to ensure that you can't overwrite an existing installation.
|
|
44
48
|
|
|
45
|
-
You can set up a first user using the `/setup` endpoint, with the following default properties that you can override:
|
|
49
|
+
You can set up a first user using the `/setup` endpoint (GET or POST), with the following default properties that you can override:
|
|
50
|
+
|
|
46
51
|
```js
|
|
47
52
|
{
|
|
48
53
|
email: "admin@example.com",
|
|
@@ -51,7 +56,8 @@ You can set up a first user using the `/setup` endpoint, with the following defa
|
|
|
51
56
|
}
|
|
52
57
|
```
|
|
53
58
|
|
|
54
|
-
Response:
|
|
59
|
+
Response:
|
|
60
|
+
|
|
55
61
|
```js
|
|
56
62
|
{
|
|
57
63
|
status: "success",
|
|
@@ -61,7 +67,7 @@ Response:
|
|
|
61
67
|
}
|
|
62
68
|
```
|
|
63
69
|
|
|
64
|
-
You can scaffold an entire system by using the `/setup/data` endpoint. This writes directly to the database, and doesn't go through the API, so be careful
|
|
70
|
+
You can scaffold an entire system by using the `/setup/data` endpoint. This writes directly to the database, and doesn't go through the API, so be careful — features like automatic password encryption will not take effect. You also need to use the collection names, not the model names, eg. the `user` model becomes `users`.
|
|
65
71
|
|
|
66
72
|
```js
|
|
67
73
|
{
|
|
@@ -76,22 +82,21 @@ You can scaffold an entire system by using the `/setup/data` endpoint. This writ
|
|
|
76
82
|
```
|
|
77
83
|
|
|
78
84
|
Response:
|
|
85
|
+
|
|
79
86
|
```js
|
|
80
87
|
{
|
|
81
|
-
status:
|
|
88
|
+
status: "success",
|
|
82
89
|
results: {
|
|
83
90
|
users: {
|
|
84
|
-
|
|
85
|
-
ops: [Array],
|
|
91
|
+
acknowledged: true,
|
|
86
92
|
insertedCount: 2,
|
|
87
|
-
insertedIds:
|
|
93
|
+
insertedIds: { ... }
|
|
88
94
|
},
|
|
89
95
|
tests: {
|
|
90
|
-
|
|
91
|
-
ops: [Array],
|
|
96
|
+
acknowledged: true,
|
|
92
97
|
insertedCount: 1,
|
|
93
|
-
insertedIds:
|
|
98
|
+
insertedIds: { ... }
|
|
94
99
|
}
|
|
95
100
|
}
|
|
96
101
|
}
|
|
97
|
-
```
|
|
102
|
+
```
|
package/docs/permissions.md
CHANGED
|
@@ -3,29 +3,46 @@
|
|
|
3
3
|
## Access Control
|
|
4
4
|
|
|
5
5
|
You can set permissions on each model for user groups which you can define yourself. There are also a few special groups:
|
|
6
|
+
|
|
6
7
|
* `all` -- All users, whether they authenticate or not.
|
|
7
|
-
* `owner` -- The user who created an item. This requires the model to have a `_owner_id` property (
|
|
8
|
+
* `owner` -- The user who created an item. This requires the model to have a `_owner_id` property (added automatically by JXPSchema).
|
|
8
9
|
* `user` -- Any authenticated user.
|
|
9
10
|
* `admin` -- Any admin user.
|
|
10
11
|
|
|
11
12
|
Each group can have one, some or all of the following permissions:
|
|
13
|
+
|
|
12
14
|
* `c` -- Create -- the ability to create a new record (a POST operation)
|
|
13
15
|
* `r` -- Retrieve -- the ability to read a record or all records (a GET operation)
|
|
14
16
|
* `u` -- Update -- the ability to update an existing record (a PUT operation)
|
|
15
|
-
* `d` -- Delete -- the ability to delete an
|
|
17
|
+
* `d` -- Delete -- the ability to delete an existing record (a DELETE operation)
|
|
16
18
|
|
|
17
|
-
The permissions are defined in the
|
|
19
|
+
The permissions are defined in the schema constructor options (preferred):
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
const TestSchema = new JXPSchema({
|
|
23
|
+
foo: String,
|
|
24
|
+
}, {
|
|
25
|
+
perms: {
|
|
26
|
+
admin: "crud",
|
|
27
|
+
owner: "rud",
|
|
28
|
+
user: "cr",
|
|
29
|
+
all: "r",
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Alternatively, after creating the schema:
|
|
18
35
|
|
|
19
36
|
```js
|
|
20
37
|
TestSchema.set("_perms", {
|
|
21
|
-
admin: "crud",
|
|
38
|
+
admin: "crud",
|
|
22
39
|
owner: "rud",
|
|
23
40
|
user: "cr",
|
|
24
|
-
all: "r"
|
|
41
|
+
all: "r",
|
|
25
42
|
});
|
|
26
43
|
```
|
|
27
44
|
|
|
28
|
-
In this case, the admin and record owner have full permissions. (We don't need to set "create" for the owner, obvz.) An authenticated user can create and
|
|
45
|
+
In this case, the admin and record owner have full permissions. (We don't need to set "create" for the owner, obvz.) An authenticated user can create and retrieve records. Everyone can read everything.
|
|
29
46
|
|
|
30
47
|
To make a model completely private, just don't set the perms.
|
|
31
48
|
|
|
@@ -36,32 +53,29 @@ You can add and remove groups to a user with the `/groups/:user_id` endpoint. Th
|
|
|
36
53
|
* GET gets all the groups for the user
|
|
37
54
|
* PUT adds a group
|
|
38
55
|
* POST rewrites the user's groups
|
|
39
|
-
* DELETE deletes the matching group
|
|
56
|
+
* DELETE deletes the matching group
|
|
40
57
|
|
|
41
58
|
The field needs to be named `group`. You can even have an array of groups, eg. `group[0]`, `group[1]` etc.
|
|
42
59
|
|
|
43
60
|
***Example***
|
|
44
61
|
|
|
45
|
-
Note that you'll need to authenticate as an admin through one of the methods described
|
|
62
|
+
Note that you'll need to authenticate as an admin through one of the methods described in [Authentication](authentication.md).
|
|
46
63
|
|
|
47
64
|
Set the user's group to `test`
|
|
48
65
|
|
|
49
66
|
```
|
|
50
|
-
curl -X POST -F "group=test" "http://localhost:
|
|
67
|
+
curl -X POST -F "group=test" "http://localhost:4001/groups/5485bd62fbad8791660d2658"
|
|
51
68
|
```
|
|
52
69
|
|
|
53
|
-
Add the groups `
|
|
70
|
+
Add the groups `test0` and `test1`
|
|
54
71
|
|
|
55
72
|
```
|
|
56
|
-
curl -X PUT -F "group[0]=test0" -F "group[1]=test1" "http://localhost:
|
|
73
|
+
curl -X PUT -F "group[0]=test0" -F "group[1]=test1" "http://localhost:4001/groups/5485bd62fbad8791660d2658"
|
|
57
74
|
```
|
|
58
75
|
|
|
59
76
|
### Adding custom permission logic
|
|
60
77
|
|
|
61
|
-
Maybe you want to do
|
|
62
|
-
the user object in your model as a virtual attribute. (I suppose you could use a real Mixed attribute too.)
|
|
63
|
-
|
|
64
|
-
Eg.
|
|
78
|
+
Maybe you want to do more checks on permissions than the built-in `crud` strings. You can capture the user object in your model as a virtual attribute (JXP sets `__user` on documents during API operations):
|
|
65
79
|
|
|
66
80
|
```js
|
|
67
81
|
var sender;
|
|
@@ -71,10 +85,10 @@ LedgerSchema.virtual("__user").set(function(usr) {
|
|
|
71
85
|
});
|
|
72
86
|
```
|
|
73
87
|
|
|
74
|
-
And then later, say in your pre- or post-save
|
|
88
|
+
And then later, say in your pre- or post-save:
|
|
75
89
|
|
|
76
90
|
```js
|
|
77
|
-
(!sender.admin)
|
|
78
|
-
return next(new Error(
|
|
91
|
+
if (!sender.admin) {
|
|
92
|
+
return next(new Error("Verboten!"));
|
|
79
93
|
}
|
|
80
|
-
```
|
|
94
|
+
```
|
package/docs/queries.md
CHANGED
|
@@ -2,24 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
You can post complex queries through the `/query/<modelname>` endpoint, such as doing `$and` or `$or` queries, or regex expressions.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Dangerous operators (e.g. `$where`, `$function`) are rejected. Per-model opt-out: `advanced_queries: { query: false }` in schema options.
|
|
6
|
+
|
|
7
|
+
Wrap your MongoDB filter in a `"query"` object and POST it as JSON. URL query parameters for `GET /api/<model>` also work here: `limit`, `page`, `sort`, `populate`, `filter`, `autopopulate`, and `fields`. See [The Restful API](api.md) for those features.
|
|
6
8
|
|
|
7
9
|
Eg.
|
|
8
10
|
|
|
9
|
-
```
|
|
11
|
+
```json
|
|
10
12
|
{
|
|
11
13
|
"query": {
|
|
12
14
|
"$and": [
|
|
13
|
-
{
|
|
15
|
+
{
|
|
14
16
|
"foo": {
|
|
15
17
|
"$regex": "foo",
|
|
16
18
|
"$options": "i"
|
|
17
19
|
}
|
|
18
20
|
},
|
|
19
|
-
{
|
|
21
|
+
{
|
|
20
22
|
"bar": "Bar"
|
|
21
23
|
}
|
|
22
24
|
]
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
|
-
```
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Note: the top-level `search` query parameter does not apply to `/query` — express search conditions inside the posted `query` object instead.
|
package/docs/schemas.md
CHANGED
|
@@ -10,9 +10,9 @@ The schemas are based on Mongoose, so anything you can do with a [Mongoose schem
|
|
|
10
10
|
|
|
11
11
|
## Defining a schema
|
|
12
12
|
|
|
13
|
-
Schemas are
|
|
13
|
+
Schemas are loaded from `MODEL_DIR` (default `./dist/models` on the sample server). Each file is named `<name>_model.js`. Each schema represents a collection in Mongo, with the Mongo collection name being the plural of the schema name. Eg. the data for `user_model.js` is stored in the `users` collection in Mongo.
|
|
14
14
|
|
|
15
|
-
A typical schema looks like
|
|
15
|
+
A typical schema looks like `test_model.js` in your model directory:
|
|
16
16
|
```javascript
|
|
17
17
|
/* global JXPSchema ObjectId Mixed */
|
|
18
18
|
|
|
@@ -108,6 +108,12 @@ _owner_id: ObjectId
|
|
|
108
108
|
|
|
109
109
|
This is a link to the `user` that originally created the document. This is used for permissioning the `owner` rights.
|
|
110
110
|
|
|
111
|
+
```javascript
|
|
112
|
+
_updated_by_id: ObjectId
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Set on PUT to the user who last updated the document (populated as `_updated_by` when using `map_to`).
|
|
116
|
+
|
|
111
117
|
```javascript
|
|
112
118
|
createdAt: Date,
|
|
113
119
|
updatedAt: Date
|
|
@@ -116,25 +122,28 @@ updatedAt: Date
|
|
|
116
122
|
We always store the created and updated timestamp.
|
|
117
123
|
|
|
118
124
|
```javascript
|
|
119
|
-
|
|
125
|
+
__v: Number
|
|
120
126
|
```
|
|
121
127
|
|
|
122
|
-
|
|
128
|
+
Mongoose version key.
|
|
123
129
|
|
|
124
130
|
## Stored Procedures
|
|
125
131
|
|
|
126
|
-
You can
|
|
127
|
-
|
|
128
|
-
`/call/test/test`
|
|
132
|
+
You can expose selected static methods via the `/call` endpoint. Declare the static on the schema and list its name in **`callable_statics`** (only listed methods are callable; all others return 403).
|
|
129
133
|
|
|
130
134
|
```js
|
|
135
|
+
const TestSchema = new JXPSchema({ ... }, {
|
|
136
|
+
perms: { ... },
|
|
137
|
+
callable_statics: ["test"],
|
|
138
|
+
});
|
|
139
|
+
|
|
131
140
|
TestSchema.statics.test = function(data) {
|
|
132
141
|
console.log(data);
|
|
133
142
|
return "Testing OKAY!";
|
|
134
143
|
};
|
|
135
144
|
```
|
|
136
145
|
|
|
137
|
-
|
|
146
|
+
`/call/test/test` — GET or POST. Unlisted statics are rejected.
|
|
138
147
|
|
|
139
148
|
You can even prepopulate the function with an item:
|
|
140
149
|
|
|
@@ -147,8 +156,10 @@ TestSchema.statics.testItem = function(item, data) {
|
|
|
147
156
|
};
|
|
148
157
|
```
|
|
149
158
|
|
|
150
|
-
The static call will include the user
|
|
159
|
+
The static call will include the authenticated user on `data.__user` when a user is logged in.
|
|
160
|
+
|
|
161
|
+
You can also set permissions after construction with `TestSchema.set("_perms", { ... })`, but the usual pattern is the `perms` option in the constructor (see above).
|
|
151
162
|
|
|
152
|
-
## Pre- and post-
|
|
163
|
+
## Pre- and post-functions
|
|
153
164
|
|
|
154
165
|
You can use [Mongoose pre- and post-middleware](https://mongoosejs.com/docs/middleware.html#post) to do advanced validation or to return a calculated field with your results.
|
package/docs/special.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Special Features
|
|
2
2
|
|
|
3
|
-
## We
|
|
3
|
+
## We always encrypt passwords
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
If you send anything called `password`, it is encrypted with bcrypt automatically. The query parameter `password_override=1` skips hashing but **requires an admin user** (for setting a pre-computed hash).
|
|
6
6
|
|
|
7
7
|
## Reflection/navel gazing
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ The endpoint `/model/modelname` gives us a description of a model.
|
|
|
12
12
|
|
|
13
13
|
## Cache
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
When caching is enabled, `GET /cache/stats` returns in-process cache statistics (see [Caching](caching.md)). Use `GET /cache/clear` to flush the cache.
|
|
16
16
|
|
|
17
17
|
## Downloading data as CSV
|
|
18
18
|
|
|
@@ -30,4 +30,4 @@ You can permanently delete a document by passing the parameter `?_permaDelete=1`
|
|
|
30
30
|
|
|
31
31
|
You can delete all of the rows that reference a row you're deleting by adding `?_cascade=1` to your endpoint.
|
|
32
32
|
|
|
33
|
-
You can combine permanent and cascade deleting.
|
|
33
|
+
You can combine permanent and cascade deleting.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# TypeScript (v4)
|
|
2
|
+
|
|
3
|
+
JXP 4 is written in TypeScript and published as compiled JavaScript plus `.d.ts` declaration files.
|
|
4
|
+
|
|
5
|
+
## Building from source
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/WorkSpaceMan/jxp.git
|
|
9
|
+
cd jxp
|
|
10
|
+
npm install --legacy-peer-deps
|
|
11
|
+
npm run build # compiles src/ → dist/
|
|
12
|
+
npm test # build + mocha (requires MongoDB for integration tests)
|
|
13
|
+
npm start # runs dist/bin/server.js
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
During development:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm run dev # rebuild on src/ changes and restart the sample server
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Consuming JXP from JavaScript
|
|
23
|
+
|
|
24
|
+
No changes are required for existing `*_model.js` apps:
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
/* global JXPSchema ObjectId Mixed */
|
|
28
|
+
const MySchema = new JXPSchema({ name: String }, { perms: { admin: "crud", all: "r" } });
|
|
29
|
+
module.exports = JXPSchema.model("MyModel", MySchema);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`require("jxp")` resolves to the compiled entry at `dist/libs/jxp.js`.
|
|
33
|
+
|
|
34
|
+
Deep imports remain supported, e.g. `require("jxp/libs/query_manipulation")`.
|
|
35
|
+
|
|
36
|
+
## Editor types for JS models
|
|
37
|
+
|
|
38
|
+
Add a dev dependency on `jxp@^4` and reference globals in model files:
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
/// <reference types="jxp/globals" />
|
|
42
|
+
/* global JXPSchema ObjectId Mixed */
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Authoring models in TypeScript
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import JXPSchema from "jxp/libs/schema"; // or relative path when in-repo
|
|
49
|
+
import type { Types } from "mongoose";
|
|
50
|
+
|
|
51
|
+
export interface IWidget {
|
|
52
|
+
_id?: Types.ObjectId;
|
|
53
|
+
name?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const WidgetSchema = new JXPSchema(
|
|
57
|
+
{ name: String },
|
|
58
|
+
{ perms: { admin: "crud", all: "r" } }
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const Widget = JXPSchema.model<IWidget>("Widget", WidgetSchema);
|
|
62
|
+
export default Widget;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Compile app models to `*_model.js` in your `model_dir`, or extend the loader in a future release to accept `_model.ts` directly.
|
|
66
|
+
|
|
67
|
+
## Built-in document types
|
|
68
|
+
|
|
69
|
+
JXP ships typed built-in models (import from your compiled `dist/models` path when developing JXP itself):
|
|
70
|
+
|
|
71
|
+
| Model | Interface |
|
|
72
|
+
|-------|-----------|
|
|
73
|
+
| User | `IUser` |
|
|
74
|
+
| Token | `IToken` |
|
|
75
|
+
| RefreshToken | `IRefreshToken` |
|
|
76
|
+
| APIKey | `IAPIKey` |
|
|
77
|
+
| Usergroup | `IUserGroup` |
|
|
78
|
+
| Link | `ILink` |
|
|
79
|
+
| Test | `ITest` |
|
|
80
|
+
|
|
81
|
+
## Breaking changes in v4
|
|
82
|
+
|
|
83
|
+
- **Published package** — npm installs ship prebuilt `dist/` (not `src/`). Building from source (`npm run build`) is only required when developing from a git clone.
|
|
84
|
+
- **Entry point** — `main` is `dist/libs/jxp.js`; `types` points to matching `.d.ts`.
|
|
85
|
+
- **Node** — requires Node.js 22+.
|
|
86
|
+
- **Relative `model_dir`** — resolved from `process.cwd()` (typical for npm scripts), not from the server script path.
|
package/docs/websocket.md
CHANGED
|
@@ -1,3 +1,101 @@
|
|
|
1
1
|
# Websockets
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
JXP exposes a WebSocket server on the same HTTP port as the REST API (upgrade on the shared Restify server). Clients receive real-time notifications when documents are created, updated, or deleted, subject to the same permission checks as the REST API.
|
|
4
|
+
|
|
5
|
+
## Connecting
|
|
6
|
+
|
|
7
|
+
Connect to the WebSocket URL for your server (same host and port as the API, `ws://` or `wss://` scheme).
|
|
8
|
+
|
|
9
|
+
## Message format
|
|
10
|
+
|
|
11
|
+
Send JSON messages with:
|
|
12
|
+
|
|
13
|
+
| Field | Description |
|
|
14
|
+
|-------|-------------|
|
|
15
|
+
| `action` | One of `ping`, `auth`, `subscribe`, `unsubscribe` |
|
|
16
|
+
| `msg_id` | Client-chosen id echoed in the response (required) |
|
|
17
|
+
| `data` | Action-specific payload (optional object) |
|
|
18
|
+
|
|
19
|
+
Responses include `status` (`okay` or `error`), `msg_id`, `client_id`, and optionally `result` or `message`.
|
|
20
|
+
|
|
21
|
+
### ping
|
|
22
|
+
|
|
23
|
+
Request:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{ "action": "ping", "msg_id": "1", "data": {} }
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Response `result`: `"pong!"`
|
|
30
|
+
|
|
31
|
+
### auth
|
|
32
|
+
|
|
33
|
+
Authenticate before subscribing. Provide one of:
|
|
34
|
+
|
|
35
|
+
- `data.email` and `data.password`
|
|
36
|
+
- `data.apikey`
|
|
37
|
+
- `data.token` (Bearer access token)
|
|
38
|
+
|
|
39
|
+
Request:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"action": "auth",
|
|
44
|
+
"msg_id": "2",
|
|
45
|
+
"data": { "email": "user@example.com", "password": "secret" }
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### subscribe
|
|
50
|
+
|
|
51
|
+
Subscribe to changes on a model. Requires prior `auth`.
|
|
52
|
+
|
|
53
|
+
**Collection-wide** (post, put, delete on any document):
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"action": "subscribe",
|
|
58
|
+
"msg_id": "3",
|
|
59
|
+
"data": { "model": "test" }
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Single document** (put and delete on one id):
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"action": "subscribe",
|
|
68
|
+
"msg_id": "4",
|
|
69
|
+
"data": { "model": "test", "id": "5eb7cf838c9fba641e0e9dcb" }
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Optional `data.filter` limits which events are delivered (field values must match on the changed document).
|
|
74
|
+
|
|
75
|
+
### unsubscribe
|
|
76
|
+
|
|
77
|
+
Same `data` shape as `subscribe` (`model`, optional `id`).
|
|
78
|
+
|
|
79
|
+
## Events pushed to the client
|
|
80
|
+
|
|
81
|
+
When a subscribed change occurs, the server sends JSON with a `data` object such as:
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"client_id": "<ws client id>",
|
|
86
|
+
"data": {
|
|
87
|
+
"event": "put",
|
|
88
|
+
"modelname": "test",
|
|
89
|
+
"item": { ... },
|
|
90
|
+
"user_id": "...",
|
|
91
|
+
"user_name": "...",
|
|
92
|
+
"user_email": "..."
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`event` is one of `post`, `put`, or `del`.
|
|
98
|
+
|
|
99
|
+
## Hooks
|
|
100
|
+
|
|
101
|
+
Server `pre_hooks` on `post`, `put`, and `delete` run in the same process as the WebSocket emitter. Use hooks to enforce extra rules; subscribers only receive events the acting user was allowed to perform.
|