@docbox-nz/hapi-gateway 0.1.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/.eslintignore +6 -0
- package/.eslintrc +131 -0
- package/.prettierignore +11 -0
- package/.prettierrc +6 -0
- package/LICENSE.md +21 -0
- package/README.md +152 -0
- package/cspell.json +6 -0
- package/dist/api/adminService.d.ts +16 -0
- package/dist/api/boxService.d.ts +43 -0
- package/dist/api/client.d.ts +45 -0
- package/dist/api/docboxFile.d.ts +15 -0
- package/dist/api/fileService.d.ts +251 -0
- package/dist/api/folderService.d.ts +45 -0
- package/dist/api/linkService.d.ts +115 -0
- package/dist/api/taskService.d.ts +23 -0
- package/dist/api/utils.d.ts +1 -0
- package/dist/error.d.ts +11 -0
- package/dist/index.browser.cjs +988 -0
- package/dist/index.browser.cjs.map +1 -0
- package/dist/index.browser.esm.js +984 -0
- package/dist/index.browser.esm.js.map +1 -0
- package/dist/index.browser.js +4390 -0
- package/dist/index.browser.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.node.cjs +22660 -0
- package/dist/index.node.cjs.map +1 -0
- package/dist/index.node.esm.js +22657 -0
- package/dist/index.node.esm.js.map +1 -0
- package/dist/index.node.js +20809 -0
- package/dist/index.node.js.map +1 -0
- package/dist/options.d.ts +79 -0
- package/dist/types/box.d.ts +59 -0
- package/dist/types/file.d.ts +371 -0
- package/dist/types/folder.d.ts +153 -0
- package/dist/types/index.d.ts +22 -0
- package/dist/types/link.d.ts +136 -0
- package/dist/types/search.d.ts +177 -0
- package/dist/types/shared.d.ts +94 -0
- package/dist/types/user.d.ts +20 -0
- package/package.json +56 -0
- package/rollup.config.js +36 -0
- package/src/error.ts +46 -0
- package/src/index.ts +244 -0
- package/src/options.ts +95 -0
- package/tsconfig.json +15 -0
package/.eslintignore
ADDED
package/.eslintrc
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
{
|
|
2
|
+
"root": true,
|
|
3
|
+
"env": {
|
|
4
|
+
"browser": true,
|
|
5
|
+
"es2021": true,
|
|
6
|
+
},
|
|
7
|
+
"plugins": [
|
|
8
|
+
"perfectionist",
|
|
9
|
+
"unused-imports",
|
|
10
|
+
"@typescript-eslint",
|
|
11
|
+
"prettier"
|
|
12
|
+
],
|
|
13
|
+
"extends": [
|
|
14
|
+
"airbnb",
|
|
15
|
+
"airbnb-typescript",
|
|
16
|
+
"airbnb/hooks",
|
|
17
|
+
"prettier"
|
|
18
|
+
],
|
|
19
|
+
"parser": "@typescript-eslint/parser",
|
|
20
|
+
"parserOptions": {
|
|
21
|
+
"ecmaVersion": "latest",
|
|
22
|
+
"sourceType": "module",
|
|
23
|
+
"project": "./tsconfig.json",
|
|
24
|
+
},
|
|
25
|
+
"settings": {
|
|
26
|
+
"import/resolver": {
|
|
27
|
+
"typescript": {
|
|
28
|
+
"alwaysTryTypes": true,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
"rules": {
|
|
33
|
+
"max-classes-per-file": 10,
|
|
34
|
+
"no-return-await": 0,
|
|
35
|
+
"@typescript-eslint/return-await": 0,
|
|
36
|
+
"no-await-in-loop": 0,
|
|
37
|
+
"no-restricted-syntax": [
|
|
38
|
+
"error",
|
|
39
|
+
"ForInStatement",
|
|
40
|
+
"LabeledStatement",
|
|
41
|
+
"WithStatement"
|
|
42
|
+
],
|
|
43
|
+
"no-alert": 0,
|
|
44
|
+
"camelcase": 0,
|
|
45
|
+
"no-console": 0,
|
|
46
|
+
"no-continue": 0,
|
|
47
|
+
"no-unused-vars": 0,
|
|
48
|
+
"no-param-reassign": 0,
|
|
49
|
+
"no-underscore-dangle": 0,
|
|
50
|
+
"no-restricted-exports": 0,
|
|
51
|
+
"no-promise-executor-return": 0,
|
|
52
|
+
"import/prefer-default-export": 0,
|
|
53
|
+
"@typescript-eslint/naming-convention": 0,
|
|
54
|
+
"jsx-a11y/control-has-associated-label": 0,
|
|
55
|
+
"@typescript-eslint/no-use-before-define": 0,
|
|
56
|
+
"@typescript-eslint/no-shadow": 0,
|
|
57
|
+
"prefer-destructuring": [
|
|
58
|
+
1,
|
|
59
|
+
{
|
|
60
|
+
"object": true,
|
|
61
|
+
"array": false,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
"@typescript-eslint/no-unused-vars": [
|
|
65
|
+
1,
|
|
66
|
+
{
|
|
67
|
+
"args": "none",
|
|
68
|
+
"argsIgnorePattern": "^_",
|
|
69
|
+
"varsIgnorePattern": "^_",
|
|
70
|
+
"caughtErrorsIgnorePattern": "^_",
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
// unused-imports
|
|
74
|
+
// https://www.npmjs.com/package/eslint-plugin-unused-imports
|
|
75
|
+
"unused-imports/no-unused-imports": 1,
|
|
76
|
+
"unused-imports/no-unused-vars": [
|
|
77
|
+
0,
|
|
78
|
+
{
|
|
79
|
+
"vars": "all",
|
|
80
|
+
"varsIgnorePattern": "^_",
|
|
81
|
+
"args": "after-used",
|
|
82
|
+
"argsIgnorePattern": "^_",
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
// perfectionist
|
|
86
|
+
// https://eslint-plugin-perfectionist.azat.io/
|
|
87
|
+
"perfectionist/sort-named-imports": [
|
|
88
|
+
1,
|
|
89
|
+
{
|
|
90
|
+
"order": "asc",
|
|
91
|
+
"type": "line-length",
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
"perfectionist/sort-named-exports": [
|
|
95
|
+
1,
|
|
96
|
+
{
|
|
97
|
+
"order": "asc",
|
|
98
|
+
"type": "line-length",
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
"perfectionist/sort-exports": [
|
|
102
|
+
1,
|
|
103
|
+
{
|
|
104
|
+
"order": "asc",
|
|
105
|
+
"type": "line-length",
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
"perfectionist/sort-imports": [
|
|
109
|
+
1,
|
|
110
|
+
{
|
|
111
|
+
"order": "asc",
|
|
112
|
+
"type": "line-length",
|
|
113
|
+
"newlines-between": "always",
|
|
114
|
+
"groups": [
|
|
115
|
+
[
|
|
116
|
+
"builtin",
|
|
117
|
+
"external"
|
|
118
|
+
],
|
|
119
|
+
[
|
|
120
|
+
"parent",
|
|
121
|
+
"sibling",
|
|
122
|
+
"index"
|
|
123
|
+
],
|
|
124
|
+
],
|
|
125
|
+
"internal-pattern": [
|
|
126
|
+
"src/**"
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
}
|
package/.prettierignore
ADDED
package/.prettierrc
ADDED
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Provida Ltd.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# 📦 @docbox-nz/hapi-gateway
|
|
2
|
+
|
|
3
|
+
Docbox Gateway implementation for hapi
|
|
4
|
+
|
|
5
|
+
Provides routes for forwarding requests to docbox and methods for enforcing access control over said routes
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
- Hapi.js v20.1.0 or newer
|
|
9
|
+
- Node.js 20+
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
To install the gateway use the following command (adjusted for your desired package manager):
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm i @docbox-nz/hapi-gateway
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
You will also need to ensure that you have `@hapi/hapi` installed this plugin supports versions `^20.1.0 || ^21.0.0`
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## Plugin setup
|
|
23
|
+
|
|
24
|
+
For a simple installation where not much additional logic is required, you can add the following to your server setup
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
import { DocboxGateway } from "@docbox-nz/hapi-gateway";
|
|
28
|
+
|
|
29
|
+
server.register({
|
|
30
|
+
plugin: DocboxGateway,
|
|
31
|
+
options: {
|
|
32
|
+
// Prefix all routes with /data
|
|
33
|
+
basePath: '/data',
|
|
34
|
+
// Base request options for docbox routes
|
|
35
|
+
baseRouteOptions: { },
|
|
36
|
+
// URL for the docbox API
|
|
37
|
+
docboxBaseURL: "http://docbox-server",
|
|
38
|
+
|
|
39
|
+
// Get the tenant for a request
|
|
40
|
+
getRequestTenant(request) {
|
|
41
|
+
// ..derive your target tenant
|
|
42
|
+
// this can be hard-coded for single tenant setups
|
|
43
|
+
return { id: '81284060-9542-4d66-9731-49ca3eb93bf9', env: "Development"}
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// Get the user for a request
|
|
47
|
+
getRequestUser(request) {
|
|
48
|
+
// ..derive your request user
|
|
49
|
+
return {
|
|
50
|
+
id: "dd5ba8b5-7da2-4e8b-b255-9641faece80e",
|
|
51
|
+
name: "Example User",
|
|
52
|
+
imageId: "https://example.com/example.png"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
// Check request read access
|
|
57
|
+
async isAllowedRead(request, scope, path) {
|
|
58
|
+
// ..assert request is allowed to access scope
|
|
59
|
+
return true;
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// Check request write access
|
|
63
|
+
async isAllowedWrite(request, scope, path) {
|
|
64
|
+
// ..assert request is allowed to access scope
|
|
65
|
+
return true;
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Complex Routing Setup
|
|
72
|
+
|
|
73
|
+
If you have a more complex routing setup for your hapi app, you can use the `createDocboxRoutes` function to create a docbox setup and obtain a list of routes that you can manually add using `server.route`
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
import { createDocboxRoutes } from "@docbox-nz/hapi-gateway";
|
|
77
|
+
|
|
78
|
+
const routes = createDocboxRoutes({
|
|
79
|
+
// Prefix all routes with /data
|
|
80
|
+
basePath: '/data',
|
|
81
|
+
// Base request options for docbox routes
|
|
82
|
+
baseRouteOptions: { },
|
|
83
|
+
// URL for the docbox API
|
|
84
|
+
docboxBaseURL: "http://docbox-server",
|
|
85
|
+
|
|
86
|
+
// Get the tenant for a request
|
|
87
|
+
getRequestTenant(request) {
|
|
88
|
+
// ..derive your target tenant
|
|
89
|
+
// this can be hard-coded for single tenant setups
|
|
90
|
+
return { id: '81284060-9542-4d66-9731-49ca3eb93bf9', env: "Development"}
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
// Get the user for a request
|
|
94
|
+
getRequestUser(request) {
|
|
95
|
+
// ..derive your request user
|
|
96
|
+
return {
|
|
97
|
+
id: "dd5ba8b5-7da2-4e8b-b255-9641faece80e",
|
|
98
|
+
name: "Example User",
|
|
99
|
+
imageId: "https://example.com/example.png"
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
// Check request read access
|
|
104
|
+
async isAllowedRead(request, scope, path) {
|
|
105
|
+
// ..assert request is allowed to access scope
|
|
106
|
+
return true;
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
// Check request write access
|
|
110
|
+
async isAllowedWrite(request, scope, path) {
|
|
111
|
+
// ..assert request is allowed to access scope
|
|
112
|
+
return true;
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
module.exports = routes;
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
## Example access control
|
|
121
|
+
|
|
122
|
+
Access control is enforced on a scope level, you should check the `scope` (Document box scope) and decide whether the request should be allowed based
|
|
123
|
+
on some information extracted from the request (or some other available context)
|
|
124
|
+
|
|
125
|
+
> [!NOTE]
|
|
126
|
+
>
|
|
127
|
+
> The `isAllowedRead` and `isAllowedWrite` functions can be sync or async, this allows you to perform additional logic such as checking your database for permission requirements
|
|
128
|
+
>
|
|
129
|
+
> `isAllowedWrite` also enforces the access control for creating new document boxes. Requests to `POST /box` are document box creation requests.
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
|
|
133
|
+
function isAllowedWrite(request, scope, path) {
|
|
134
|
+
|
|
135
|
+
// Get authenticated user scopes
|
|
136
|
+
const userScopes = request.auth.credentials.scope
|
|
137
|
+
|
|
138
|
+
// User is trying to access the "admin-area" scope
|
|
139
|
+
if (scope === "admin-area") {
|
|
140
|
+
|
|
141
|
+
// Ensure the user has the required admin scope
|
|
142
|
+
if (userScopes.includes('admin')) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Deny by default
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
```
|
package/cspell.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DocboxAdminSearchRequest, DocboxSearchResponseAdmin } from '../types';
|
|
2
|
+
import { DocboxClient } from './client';
|
|
3
|
+
export declare class AdminService {
|
|
4
|
+
private client;
|
|
5
|
+
constructor(client: DocboxClient);
|
|
6
|
+
/**
|
|
7
|
+
* Perform an admin search across multiple document boxes.
|
|
8
|
+
*
|
|
9
|
+
* This function will only work on your server handling authentication
|
|
10
|
+
* you should not allow the frontend to access these endpoints
|
|
11
|
+
*
|
|
12
|
+
* @param data Search request data
|
|
13
|
+
* @returns The search results
|
|
14
|
+
*/
|
|
15
|
+
search(data: DocboxAdminSearchRequest): Promise<DocboxSearchResponseAdmin>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { DocboxSearchRequest, DocboxSearchResponse, DocumentBoxResponse, DocumentBoxScope, DocumentBoxStats } from '../types';
|
|
2
|
+
import { DocboxClient } from './client';
|
|
3
|
+
export declare class BoxService {
|
|
4
|
+
private client;
|
|
5
|
+
constructor(client: DocboxClient);
|
|
6
|
+
/**
|
|
7
|
+
* Get a document box for a scope
|
|
8
|
+
*
|
|
9
|
+
* @param scope Scope of the document box to get
|
|
10
|
+
* @param createIfMissing Whether to create the document box if it doesn't exist
|
|
11
|
+
*/
|
|
12
|
+
get(scope: DocumentBoxScope, createIfMissing?: boolean): Promise<DocumentBoxResponse>;
|
|
13
|
+
/**
|
|
14
|
+
* Creates a new document box optional returning an existing one if there is one available
|
|
15
|
+
*
|
|
16
|
+
* @param scope Scope for the document box to create
|
|
17
|
+
* @param allowExisting Whether to allow returning an existing document box (If one is present)
|
|
18
|
+
* @returns The created document box
|
|
19
|
+
*/
|
|
20
|
+
create(scope: DocumentBoxScope, allowExisting?: boolean): Promise<DocumentBoxResponse>;
|
|
21
|
+
/**
|
|
22
|
+
* Delete a specific document vox
|
|
23
|
+
*
|
|
24
|
+
* @param scope Scope of the document box to delete
|
|
25
|
+
* @returns
|
|
26
|
+
*/
|
|
27
|
+
delete(scope: DocumentBoxScope): Promise<any>;
|
|
28
|
+
/**
|
|
29
|
+
* Search within the provided document box
|
|
30
|
+
*
|
|
31
|
+
* @param scope The scope of the document box to search
|
|
32
|
+
* @param data Search request data
|
|
33
|
+
* @returns The search results
|
|
34
|
+
*/
|
|
35
|
+
search(scope: DocumentBoxScope, data: DocboxSearchRequest): Promise<DocboxSearchResponse>;
|
|
36
|
+
/**
|
|
37
|
+
* Get statistics for the provided document box
|
|
38
|
+
*
|
|
39
|
+
* @param scope The scope of the document box
|
|
40
|
+
* @returns The box stats
|
|
41
|
+
*/
|
|
42
|
+
stats(scope: DocumentBoxScope): Promise<DocumentBoxStats>;
|
|
43
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AxiosRequestConfig, AxiosInstance } from 'axios';
|
|
2
|
+
import { FileService } from './fileService';
|
|
3
|
+
import { BoxService as DocumentBoxService } from './boxService';
|
|
4
|
+
import { LinkService } from './linkService';
|
|
5
|
+
import { FolderService } from './folderService';
|
|
6
|
+
import { TaskService } from './taskService';
|
|
7
|
+
import { AdminService } from './adminService';
|
|
8
|
+
export declare class DocboxClient {
|
|
9
|
+
client: AxiosInstance;
|
|
10
|
+
/**
|
|
11
|
+
* Document box related operations
|
|
12
|
+
*/
|
|
13
|
+
documentBox: DocumentBoxService;
|
|
14
|
+
/**
|
|
15
|
+
* Task related operations
|
|
16
|
+
*/
|
|
17
|
+
task: TaskService;
|
|
18
|
+
/**
|
|
19
|
+
* File related operations
|
|
20
|
+
*/
|
|
21
|
+
file: FileService;
|
|
22
|
+
/**
|
|
23
|
+
* Link related operations
|
|
24
|
+
*/
|
|
25
|
+
link: LinkService;
|
|
26
|
+
/**
|
|
27
|
+
* Folder related operations
|
|
28
|
+
*/
|
|
29
|
+
folder: FolderService;
|
|
30
|
+
/**
|
|
31
|
+
* Admin related operations
|
|
32
|
+
*/
|
|
33
|
+
admin: AdminService;
|
|
34
|
+
/**
|
|
35
|
+
* Creates a new docbox
|
|
36
|
+
*
|
|
37
|
+
* @param client
|
|
38
|
+
*/
|
|
39
|
+
constructor(client: AxiosInstance);
|
|
40
|
+
httpGet<T = any, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<T>;
|
|
41
|
+
httpPost<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<T>;
|
|
42
|
+
httpPut<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<T>;
|
|
43
|
+
httpPatch<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<T>;
|
|
44
|
+
httpDelete<T = any, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<T>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { DocFile } from '../types/file';
|
|
2
|
+
declare let FileGlobal: {
|
|
3
|
+
new (fileBits: BlobPart[], fileName: string, options?: FilePropertyBag): File;
|
|
4
|
+
prototype: File;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Wrapper around the browser File type to allow using a docbox file
|
|
8
|
+
* in place when handling already uploaded files in a form
|
|
9
|
+
*/
|
|
10
|
+
export declare class DocboxFile extends FileGlobal {
|
|
11
|
+
file: DocFile;
|
|
12
|
+
get size(): number;
|
|
13
|
+
constructor(file: DocFile);
|
|
14
|
+
}
|
|
15
|
+
export {};
|