@datrix/api 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/LICENSE +21 -0
- package/README.md +233 -0
- package/dist/index.d.mts +305 -0
- package/dist/index.d.ts +305 -0
- package/dist/index.js +3354 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3314 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 datrix Contributors
|
|
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,233 @@
|
|
|
1
|
+
# @datrix/api
|
|
2
|
+
|
|
3
|
+
HTTP REST API plugin for Datrix. Turns any Datrix instance into a fully-featured REST API — auto-generates CRUD routes for every schema, handles JWT and session authentication, RBAC permissions, and optionally manages file uploads via [`@datrix/api-upload`](../api-upload/README.md).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @datrix/api
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { defineConfig } from "datrix-core"
|
|
15
|
+
import { ApiPlugin } from "@datrix/api"
|
|
16
|
+
|
|
17
|
+
export default defineConfig(() => ({
|
|
18
|
+
adapter,
|
|
19
|
+
schemas,
|
|
20
|
+
plugins: [
|
|
21
|
+
new ApiPlugin({
|
|
22
|
+
prefix: "/api", // default: "/api"
|
|
23
|
+
defaultPageSize: 25, // default: 25
|
|
24
|
+
maxPageSize: 100, // default: 100
|
|
25
|
+
maxPopulateDepth: 5, // default: 5
|
|
26
|
+
excludeSchemas: [], // always excludes _datrix and _datrix_migrations
|
|
27
|
+
}),
|
|
28
|
+
],
|
|
29
|
+
}))
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Request handling
|
|
33
|
+
|
|
34
|
+
### `handleRequest`
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { handleRequest } from "@datrix/api"
|
|
38
|
+
|
|
39
|
+
handleRequest(datrix: IDatrix, request: Request): Promise<Response>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Main entry point. Routes to the appropriate handler (auth, CRUD, or upload). Always returns a `Response`, never throws.
|
|
43
|
+
|
|
44
|
+
#### Next.js App Router
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import datrix from "@/datrix.config"
|
|
48
|
+
import { handleRequest } from "@datrix/api"
|
|
49
|
+
|
|
50
|
+
async function handler(request: Request): Promise<Response> {
|
|
51
|
+
return handleRequest(await datrix(), request)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const GET = handler
|
|
55
|
+
export const POST = handler
|
|
56
|
+
export const PATCH = handler
|
|
57
|
+
export const PUT = handler
|
|
58
|
+
export const DELETE = handler
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
#### Express
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import express from "express"
|
|
65
|
+
import { handleRequest, toWebRequest, sendWebResponse } from "@datrix/api"
|
|
66
|
+
|
|
67
|
+
const app = express()
|
|
68
|
+
app.use(express.raw({ type: "*/*" }))
|
|
69
|
+
|
|
70
|
+
app.all("*", async (req, res) => {
|
|
71
|
+
const request = toWebRequest(req)
|
|
72
|
+
const response = await handleRequest(await datrix(), request)
|
|
73
|
+
await sendWebResponse(res, response)
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`toWebRequest` / `sendWebResponse` work with any Node.js-style request/response — Fastify, Koa, raw `http.createServer`.
|
|
78
|
+
|
|
79
|
+
## Auto-generated CRUD routes
|
|
80
|
+
|
|
81
|
+
For every registered schema:
|
|
82
|
+
|
|
83
|
+
| Method | Path | Description |
|
|
84
|
+
| -------- | ------------------ | ---------------------------------------- |
|
|
85
|
+
| `GET` | `/api/:schema` | List records — pagination, filtering, sorting, populate |
|
|
86
|
+
| `GET` | `/api/:schema/:id` | Get a single record |
|
|
87
|
+
| `POST` | `/api/:schema` | Create a record |
|
|
88
|
+
| `PATCH` | `/api/:schema/:id` | Update a record |
|
|
89
|
+
| `DELETE` | `/api/:schema/:id` | Delete a record |
|
|
90
|
+
|
|
91
|
+
The `:schema` segment matches the schema's table name (e.g. schema `"product"` → `/api/products`).
|
|
92
|
+
|
|
93
|
+
### Filtering, sorting, pagination
|
|
94
|
+
|
|
95
|
+
Pass query parameters as a serialized `ParsedQuery` object. Use `queryToParams` to build them:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { queryToParams } from "@datrix/api"
|
|
99
|
+
|
|
100
|
+
const qs = queryToParams({
|
|
101
|
+
where: { status: "active" },
|
|
102
|
+
orderBy: { createdAt: "desc" },
|
|
103
|
+
page: 1,
|
|
104
|
+
pageSize: 20,
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
fetch(`/api/products?${qs}`)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Authentication
|
|
111
|
+
|
|
112
|
+
Enable by adding an `auth` block to `ApiPlugin`:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const roles = ["admin", "editor", "user"] as const
|
|
116
|
+
type Role = (typeof roles)[number]
|
|
117
|
+
|
|
118
|
+
new ApiPlugin<Role>({
|
|
119
|
+
auth: {
|
|
120
|
+
roles,
|
|
121
|
+
defaultRole: "user",
|
|
122
|
+
jwt: {
|
|
123
|
+
secret: "your-secret-key-at-least-32-characters",
|
|
124
|
+
expiresIn: "7d",
|
|
125
|
+
},
|
|
126
|
+
session: {
|
|
127
|
+
store: "memory", // or a custom SessionStore instance
|
|
128
|
+
maxAge: 86400,
|
|
129
|
+
},
|
|
130
|
+
defaultPermission: {
|
|
131
|
+
create: ["admin"],
|
|
132
|
+
read: true,
|
|
133
|
+
update: ["admin", "editor"],
|
|
134
|
+
delete: ["admin"],
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
})
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
JWT and session can be active simultaneously. Responses include both a token and a `sessionId` cookie.
|
|
141
|
+
|
|
142
|
+
A `user` schema with an `email` field must exist before enabling auth. The plugin creates an `authentication` table automatically.
|
|
143
|
+
|
|
144
|
+
### Auth endpoints
|
|
145
|
+
|
|
146
|
+
| Method | Path | Description |
|
|
147
|
+
| ------ | --------------------- | ---------------------------------------- |
|
|
148
|
+
| `POST` | `/api/auth/register` | Create a new user account |
|
|
149
|
+
| `POST` | `/api/auth/login` | Login — returns token and/or sets cookie |
|
|
150
|
+
| `POST` | `/api/auth/logout` | Invalidate session |
|
|
151
|
+
| `GET` | `/api/auth/me` | Get the currently authenticated user |
|
|
152
|
+
|
|
153
|
+
Endpoint paths are configurable via `auth.endpoints`. Registration can be disabled with `auth.endpoints.disableRegister: true`.
|
|
154
|
+
|
|
155
|
+
### Authenticated requests
|
|
156
|
+
|
|
157
|
+
```http
|
|
158
|
+
GET /api/products
|
|
159
|
+
Authorization: Bearer eyJ...
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Or send the `sessionId` cookie — the browser handles this automatically.
|
|
163
|
+
|
|
164
|
+
### Permissions
|
|
165
|
+
|
|
166
|
+
Defined per schema in `SchemaDefinition.permission`:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
defineSchema({
|
|
170
|
+
name: "product",
|
|
171
|
+
fields: { ... },
|
|
172
|
+
permission: {
|
|
173
|
+
create: ["admin", "editor"],
|
|
174
|
+
read: true, // public
|
|
175
|
+
update: ["admin", "editor"],
|
|
176
|
+
delete: ["admin"],
|
|
177
|
+
},
|
|
178
|
+
})
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Permission values: `true` (public), `false` (blocked), role array, async function, or a mixed array (OR logic). Field-level `read`/`write` permissions are also supported per field.
|
|
182
|
+
|
|
183
|
+
## File uploads
|
|
184
|
+
|
|
185
|
+
File upload support is in a separate package to keep `sharp` out of the core dependency tree. See [`@datrix/api-upload`](../api-upload/README.md) for the full setup, storage provider docs, format conversion, and resolution variants.
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { Upload, LocalStorageProvider } from "@datrix/api-upload"
|
|
189
|
+
|
|
190
|
+
new ApiPlugin({
|
|
191
|
+
upload: new Upload({
|
|
192
|
+
provider: new LocalStorageProvider({
|
|
193
|
+
basePath: "./uploads",
|
|
194
|
+
baseUrl: "https://example.com/uploads",
|
|
195
|
+
}),
|
|
196
|
+
format: "webp",
|
|
197
|
+
quality: 80,
|
|
198
|
+
resolutions: {
|
|
199
|
+
thumbnail: { width: 150, height: 150, fit: "cover" },
|
|
200
|
+
small: { width: 320 },
|
|
201
|
+
},
|
|
202
|
+
}),
|
|
203
|
+
})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Architecture
|
|
207
|
+
|
|
208
|
+
```text
|
|
209
|
+
src/
|
|
210
|
+
├── api.ts # ApiPlugin class — plugin lifecycle, schema injection, request routing
|
|
211
|
+
├── interface.ts # IApiPlugin interface — used internally to avoid circular deps
|
|
212
|
+
├── types.ts # ApiConfig type
|
|
213
|
+
├── index.ts # Public exports
|
|
214
|
+
├── helper/
|
|
215
|
+
│ └── index.ts # handleRequest, toWebRequest, sendWebResponse, queryToParams
|
|
216
|
+
├── handler/
|
|
217
|
+
│ ├── unified.ts # CRUD request handler (GET / POST / PATCH / DELETE)
|
|
218
|
+
│ ├── auth-handler.ts # Auth endpoint handlers (register, login, logout, me)
|
|
219
|
+
│ └── utils.ts # jsonResponse, datrixErrorResponse helpers
|
|
220
|
+
├── auth/
|
|
221
|
+
│ ├── manager.ts # AuthManager — coordinates JWT, session, and password
|
|
222
|
+
│ ├── jwt.ts # JwtStrategy — signing/verification (no external deps)
|
|
223
|
+
│ ├── session.ts # SessionStrategy + MemorySessionStore
|
|
224
|
+
│ ├── password.ts # PasswordManager — PBKDF2 hashing
|
|
225
|
+
│ └── types.ts # AuthConfig, SessionConfig, JwtConfig, SessionData, etc.
|
|
226
|
+
├── middleware/
|
|
227
|
+
│ ├── context.ts # RequestContext builder
|
|
228
|
+
│ ├── permission.ts # Schema and field permission evaluation
|
|
229
|
+
│ └── types.ts # RequestContext type
|
|
230
|
+
└── errors/
|
|
231
|
+
├── api-error.ts # DatrixApiError and handlerError factory
|
|
232
|
+
└── auth-error.ts # DatrixAuthError and authError factory
|
|
233
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { DefaultPermission, IAuthManager, AuthUser, LoginResult, AuthContext, IUpload, BasePlugin, IApiPlugin, Datrix, QueryContext, PluginContext, SchemaDefinition, DatrixEntry, QueryObject, PermissionAction, ParsedQuery, FallbackInput, ParserError, PermissionValue, PermissionCheckResult, FieldPermissionCheckResult, RawQueryParams, ParserOptions, FallbackWhereClause, PopulateClause, DatrixRecord, DatrixError } from '@datrix/core';
|
|
2
|
+
export { PermissionAction } from '@datrix/core';
|
|
3
|
+
|
|
4
|
+
interface PasswordHash {
|
|
5
|
+
readonly hash: string;
|
|
6
|
+
readonly salt: string;
|
|
7
|
+
}
|
|
8
|
+
interface PasswordConfig {
|
|
9
|
+
readonly iterations?: number;
|
|
10
|
+
readonly keyLength?: number;
|
|
11
|
+
readonly minLength?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface JwtPayload {
|
|
15
|
+
readonly userId: number;
|
|
16
|
+
readonly role: string;
|
|
17
|
+
readonly iat: number;
|
|
18
|
+
readonly exp: number;
|
|
19
|
+
readonly iss?: string;
|
|
20
|
+
readonly aud?: string;
|
|
21
|
+
readonly [key: string]: unknown;
|
|
22
|
+
}
|
|
23
|
+
interface SessionStore {
|
|
24
|
+
get(sessionId: string): Promise<SessionData | undefined>;
|
|
25
|
+
set(sessionId: string, data: SessionData): Promise<void>;
|
|
26
|
+
delete(sessionId: string): Promise<void>;
|
|
27
|
+
cleanup(): Promise<number>;
|
|
28
|
+
clear(): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
interface SessionData {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly userId: number;
|
|
33
|
+
readonly role: string;
|
|
34
|
+
readonly createdAt: Date;
|
|
35
|
+
readonly expiresAt: Date;
|
|
36
|
+
readonly lastAccessedAt: Date;
|
|
37
|
+
readonly [key: string]: unknown;
|
|
38
|
+
}
|
|
39
|
+
type JwtAlgorithm = "HS256" | "HS512";
|
|
40
|
+
type TimeUnit = "s" | "m" | "h" | "d";
|
|
41
|
+
type ExpiryString = `${number}${TimeUnit}`;
|
|
42
|
+
interface JwtConfig {
|
|
43
|
+
readonly secret: string;
|
|
44
|
+
readonly expiresIn?: ExpiryString | number;
|
|
45
|
+
readonly algorithm?: JwtAlgorithm;
|
|
46
|
+
readonly issuer?: string;
|
|
47
|
+
readonly audience?: string;
|
|
48
|
+
}
|
|
49
|
+
interface SessionConfig {
|
|
50
|
+
readonly store?: "memory" | SessionStore;
|
|
51
|
+
readonly maxAge?: number;
|
|
52
|
+
readonly checkPeriod?: number;
|
|
53
|
+
readonly prefix?: string;
|
|
54
|
+
}
|
|
55
|
+
interface AuthConfig<TRole extends string = string> {
|
|
56
|
+
readonly roles: readonly TRole[];
|
|
57
|
+
readonly defaultRole: TRole;
|
|
58
|
+
readonly defaultPermission?: DefaultPermission<TRole>;
|
|
59
|
+
readonly jwt?: JwtConfig;
|
|
60
|
+
readonly session?: SessionConfig;
|
|
61
|
+
readonly password?: PasswordConfig;
|
|
62
|
+
readonly authSchemaName?: string;
|
|
63
|
+
readonly userSchema?: {
|
|
64
|
+
readonly name?: string;
|
|
65
|
+
readonly email?: string;
|
|
66
|
+
};
|
|
67
|
+
readonly endpoints?: {
|
|
68
|
+
readonly login?: string;
|
|
69
|
+
readonly register?: string;
|
|
70
|
+
readonly logout?: string;
|
|
71
|
+
readonly me?: string;
|
|
72
|
+
readonly disableRegister?: boolean;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
declare class JwtStrategy {
|
|
77
|
+
private readonly secret;
|
|
78
|
+
private readonly expiresIn;
|
|
79
|
+
private readonly algorithm;
|
|
80
|
+
private readonly issuer;
|
|
81
|
+
private readonly audience;
|
|
82
|
+
constructor(config: JwtConfig);
|
|
83
|
+
sign(payload: Omit<JwtPayload, "iat" | "exp" | "iss" | "aud">): string;
|
|
84
|
+
verify(token: string): JwtPayload;
|
|
85
|
+
refresh(token: string): string;
|
|
86
|
+
decode(token: string): JwtPayload;
|
|
87
|
+
private createToken;
|
|
88
|
+
private signData;
|
|
89
|
+
private encodeBase64Url;
|
|
90
|
+
private decodeBase64Url;
|
|
91
|
+
private constantTimeCompare;
|
|
92
|
+
private parseExpiry;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
declare class SessionStrategy {
|
|
96
|
+
private readonly store;
|
|
97
|
+
private readonly maxAge;
|
|
98
|
+
private readonly checkPeriod;
|
|
99
|
+
private cleanupTimer;
|
|
100
|
+
constructor(config: SessionConfig);
|
|
101
|
+
create(userId: number, role: string, data?: Record<string, unknown>): Promise<SessionData>;
|
|
102
|
+
get(sessionId: string): Promise<SessionData>;
|
|
103
|
+
update(sessionId: string, data: Partial<Omit<SessionData, "id" | "createdAt">>): Promise<SessionData>;
|
|
104
|
+
delete(sessionId: string): Promise<void>;
|
|
105
|
+
refresh(sessionId: string): Promise<SessionData>;
|
|
106
|
+
validate(sessionId: string): Promise<boolean>;
|
|
107
|
+
startCleanup(): void;
|
|
108
|
+
stopCleanup(): void;
|
|
109
|
+
clear(): Promise<void>;
|
|
110
|
+
private generateSessionId;
|
|
111
|
+
}
|
|
112
|
+
declare class MemorySessionStore implements SessionStore {
|
|
113
|
+
readonly name: "memory";
|
|
114
|
+
private readonly sessions;
|
|
115
|
+
private readonly prefix;
|
|
116
|
+
constructor(prefix?: string);
|
|
117
|
+
get(sessionId: string): Promise<SessionData | undefined>;
|
|
118
|
+
set(sessionId: string, data: SessionData): Promise<void>;
|
|
119
|
+
delete(sessionId: string): Promise<void>;
|
|
120
|
+
cleanup(): Promise<number>;
|
|
121
|
+
clear(): Promise<void>;
|
|
122
|
+
private getKey;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
declare class AuthManager<TRole extends string = string> implements IAuthManager {
|
|
126
|
+
private readonly passwordManager;
|
|
127
|
+
private readonly jwtStrategy;
|
|
128
|
+
private readonly sessionStrategy;
|
|
129
|
+
private readonly config;
|
|
130
|
+
get authConfig(): AuthConfig<TRole>;
|
|
131
|
+
constructor(config: AuthConfig<TRole>);
|
|
132
|
+
hashPassword(password: string): Promise<PasswordHash>;
|
|
133
|
+
verifyPassword(password: string, hash: string, salt: string): Promise<boolean>;
|
|
134
|
+
login(user: AuthUser, options?: {
|
|
135
|
+
createToken?: boolean;
|
|
136
|
+
createSession?: boolean;
|
|
137
|
+
}): Promise<LoginResult>;
|
|
138
|
+
logout(sessionId: string): Promise<void>;
|
|
139
|
+
authenticate(request: Request): Promise<AuthContext | null>;
|
|
140
|
+
private extractToken;
|
|
141
|
+
private extractSessionId;
|
|
142
|
+
getJwtStrategy(): JwtStrategy | undefined;
|
|
143
|
+
getSessionStrategy(): SessionStrategy | undefined;
|
|
144
|
+
destroy(): Promise<void>;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
interface ApiConfig<TRole extends string = string> extends Record<string, unknown> {
|
|
148
|
+
readonly disabled?: boolean;
|
|
149
|
+
readonly prefix?: string;
|
|
150
|
+
readonly defaultPageSize?: number;
|
|
151
|
+
readonly maxPageSize?: number;
|
|
152
|
+
readonly maxPopulateDepth?: number;
|
|
153
|
+
readonly auth?: AuthConfig<TRole>;
|
|
154
|
+
readonly upload?: IUpload;
|
|
155
|
+
readonly excludeSchemas?: readonly string[];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
declare class ApiPlugin<TRole extends string = string> extends BasePlugin<ApiConfig<TRole>> implements IApiPlugin<TRole> {
|
|
159
|
+
readonly name = "api";
|
|
160
|
+
readonly version = "1.0.0";
|
|
161
|
+
authManager?: AuthManager;
|
|
162
|
+
user: AuthUser | null;
|
|
163
|
+
private datrixInstance?;
|
|
164
|
+
get datrix(): Datrix;
|
|
165
|
+
get upload(): IUpload | undefined;
|
|
166
|
+
setUser(user: AuthUser | null): void;
|
|
167
|
+
private get authConfig();
|
|
168
|
+
private get apiConfig();
|
|
169
|
+
private get authSchemaName();
|
|
170
|
+
private get userSchemaName();
|
|
171
|
+
private get userSchemaEmailField();
|
|
172
|
+
get authDefaultPermission(): DefaultPermission<TRole> | undefined;
|
|
173
|
+
get authDefaultRole(): TRole | undefined;
|
|
174
|
+
get excludeSchemas(): readonly string[];
|
|
175
|
+
private getTableName;
|
|
176
|
+
onCreateQueryContext(context: QueryContext): Promise<QueryContext>;
|
|
177
|
+
init(context: PluginContext): Promise<void>;
|
|
178
|
+
destroy(): Promise<void>;
|
|
179
|
+
getSchemas(): Promise<SchemaDefinition[]>;
|
|
180
|
+
onBeforeQuery<T extends DatrixEntry>(query: QueryObject<T>, context: QueryContext): Promise<QueryObject<T>>;
|
|
181
|
+
onAfterQuery<TResult>(result: TResult, context: QueryContext): Promise<TResult>;
|
|
182
|
+
private createAuthenticationRecord;
|
|
183
|
+
private syncAuthenticationEmail;
|
|
184
|
+
handleRequest(request: Request, datrix: Datrix): Promise<Response>;
|
|
185
|
+
private isAuthPath;
|
|
186
|
+
private handleAuthRequest;
|
|
187
|
+
isEnabled(): boolean;
|
|
188
|
+
isAuthEnabled(): boolean;
|
|
189
|
+
getAuthManager(): AuthManager | undefined;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
declare function handleRequest(datrix: Datrix, request: Request): Promise<Response>;
|
|
193
|
+
|
|
194
|
+
type HttpMethod = "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
|
|
195
|
+
interface RequestContext<TRole extends string = string> {
|
|
196
|
+
readonly schema: SchemaDefinition | null;
|
|
197
|
+
readonly action: PermissionAction;
|
|
198
|
+
readonly id: number | null;
|
|
199
|
+
readonly method: HttpMethod;
|
|
200
|
+
readonly query: ParsedQuery | null;
|
|
201
|
+
readonly body: FallbackInput | null;
|
|
202
|
+
readonly headers: Record<string, string>;
|
|
203
|
+
readonly url: URL;
|
|
204
|
+
readonly request: Request;
|
|
205
|
+
readonly user: AuthUser | null;
|
|
206
|
+
readonly datrix: Datrix;
|
|
207
|
+
readonly api: IApiPlugin<TRole>;
|
|
208
|
+
readonly authEnabled: boolean;
|
|
209
|
+
}
|
|
210
|
+
interface ContextBuilderOptions {
|
|
211
|
+
readonly apiPrefix?: string;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
declare class ContextBuildError extends Error {
|
|
215
|
+
readonly parserError: ParserError;
|
|
216
|
+
constructor(parserError: ParserError);
|
|
217
|
+
}
|
|
218
|
+
declare function buildRequestContext<TRole extends string = string>(request: Request, datrix: Datrix, api: IApiPlugin<TRole>, options?: ContextBuilderOptions): Promise<RequestContext<TRole>>;
|
|
219
|
+
|
|
220
|
+
declare function authenticate(request: Request, authManager?: AuthManager): Promise<AuthUser | null>;
|
|
221
|
+
|
|
222
|
+
declare function evaluatePermissionValue<TRoles extends string>(value: PermissionValue<TRoles> | undefined, ctx: RequestContext): Promise<boolean>;
|
|
223
|
+
declare function checkSchemaPermission<TRoles extends string>(schema: SchemaDefinition<TRoles>, ctx: RequestContext, defaultPermission?: DefaultPermission<TRoles>): Promise<PermissionCheckResult>;
|
|
224
|
+
declare function filterFieldsForRead<TRoles extends string, TRecord extends DatrixEntry>(schema: SchemaDefinition<TRoles>, record: TRecord, ctx: RequestContext): Promise<{
|
|
225
|
+
data: Partial<TRecord>;
|
|
226
|
+
deniedFields: string[];
|
|
227
|
+
}>;
|
|
228
|
+
declare function checkFieldsForWrite<TRoles extends string>(schema: SchemaDefinition<TRoles>, ctx: RequestContext): Promise<FieldPermissionCheckResult>;
|
|
229
|
+
declare function methodToAction(method: string): PermissionAction;
|
|
230
|
+
declare function filterRecordsForRead<TRoles extends string, TRecord extends DatrixEntry>(schema: SchemaDefinition<TRoles>, records: readonly TRecord[], ctx: RequestContext): Promise<Partial<TRecord>[]>;
|
|
231
|
+
|
|
232
|
+
declare function parseQuery(params: RawQueryParams, options?: Partial<ParserOptions>): ParsedQuery<DatrixEntry>;
|
|
233
|
+
|
|
234
|
+
declare function parseWhere(params: RawQueryParams): FallbackWhereClause | undefined;
|
|
235
|
+
|
|
236
|
+
declare function parsePopulate(params: RawQueryParams, maxDepth?: number): PopulateClause<DatrixRecord> | undefined;
|
|
237
|
+
|
|
238
|
+
declare function parseFields(params: RawQueryParams): string[] | "*" | undefined;
|
|
239
|
+
|
|
240
|
+
declare function queryToParams<T extends DatrixEntry = DatrixRecord>(query: ParsedQuery<T> | undefined): string;
|
|
241
|
+
declare function serializeQuery<T extends DatrixEntry = DatrixEntry>(query: ParsedQuery<T>): RawQueryParams;
|
|
242
|
+
|
|
243
|
+
declare function handleCrudRequest<TRole extends string = string>(request: Request, datrix: Datrix, api: IApiPlugin<TRole>, options?: ContextBuilderOptions): Promise<Response>;
|
|
244
|
+
|
|
245
|
+
interface AuthHandlerConfig {
|
|
246
|
+
readonly datrix: Datrix;
|
|
247
|
+
readonly authManager: AuthManager;
|
|
248
|
+
readonly authConfig: AuthConfig;
|
|
249
|
+
}
|
|
250
|
+
declare function createAuthHandlers(config: AuthHandlerConfig): {
|
|
251
|
+
register: (request: Request) => Promise<Response>;
|
|
252
|
+
login: (request: Request) => Promise<Response>;
|
|
253
|
+
logout: (request: Request) => Promise<Response>;
|
|
254
|
+
me: (request: Request) => Promise<Response>;
|
|
255
|
+
};
|
|
256
|
+
declare function createUnifiedAuthHandler(config: AuthHandlerConfig, apiPrefix?: string): (request: Request) => Promise<Response>;
|
|
257
|
+
|
|
258
|
+
declare class DatrixApiError extends DatrixError {
|
|
259
|
+
status: number;
|
|
260
|
+
constructor(message: string, options: ApiErrorOptions);
|
|
261
|
+
toJSON(): {
|
|
262
|
+
status: number;
|
|
263
|
+
type: string;
|
|
264
|
+
message: string;
|
|
265
|
+
code: string;
|
|
266
|
+
timestamp: string;
|
|
267
|
+
operation?: string;
|
|
268
|
+
context?: Record<string, unknown>;
|
|
269
|
+
suggestion?: string;
|
|
270
|
+
expected?: string;
|
|
271
|
+
received?: unknown;
|
|
272
|
+
documentation?: string;
|
|
273
|
+
cause?: {
|
|
274
|
+
readonly message: string;
|
|
275
|
+
readonly name: string;
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
interface ApiErrorOptions {
|
|
280
|
+
code: string;
|
|
281
|
+
status: number;
|
|
282
|
+
operation?: string;
|
|
283
|
+
context?: Record<string, unknown>;
|
|
284
|
+
suggestion?: string;
|
|
285
|
+
expected?: string;
|
|
286
|
+
received?: unknown;
|
|
287
|
+
cause?: Error;
|
|
288
|
+
}
|
|
289
|
+
declare const handlerError: {
|
|
290
|
+
schemaNotFound(tableName: string, availableModels?: string[]): DatrixApiError;
|
|
291
|
+
modelNotSpecified(): DatrixApiError;
|
|
292
|
+
recordNotFound(modelName: string, id: number | string): DatrixApiError;
|
|
293
|
+
invalidBody(reason?: string): DatrixApiError;
|
|
294
|
+
missingId(operation: string): DatrixApiError;
|
|
295
|
+
methodNotAllowed(method: string): DatrixApiError;
|
|
296
|
+
permissionDenied(reason: string, context?: Record<string, unknown>): DatrixApiError;
|
|
297
|
+
unauthorized(reason?: string): DatrixApiError;
|
|
298
|
+
internalError(message: string, cause?: Error): DatrixApiError;
|
|
299
|
+
conflict(reason: string, context?: Record<string, unknown>): DatrixApiError;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
declare function jsonResponse(data: unknown, status?: number): Response;
|
|
303
|
+
declare function datrixErrorResponse(error: DatrixApiError | DatrixError): Response;
|
|
304
|
+
|
|
305
|
+
export { ApiPlugin, type AuthHandlerConfig, ContextBuildError, type ContextBuilderOptions, DatrixApiError, type HttpMethod, MemorySessionStore, type RequestContext, authenticate, buildRequestContext, checkFieldsForWrite, checkSchemaPermission, createAuthHandlers, createUnifiedAuthHandler, datrixErrorResponse, evaluatePermissionValue, filterFieldsForRead, filterRecordsForRead, handleCrudRequest, handleRequest, handlerError, jsonResponse, methodToAction, parseFields, parsePopulate, parseQuery, parseWhere, queryToParams, serializeQuery };
|