@econneq/gql-auth 1.2.11 → 2.0.1
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 +304 -22
- package/dist/api-factory.d.ts +11 -27
- package/dist/api-factory.d.ts.map +1 -1
- package/dist/api-factory.js +96 -43
- package/dist/api-factory.js.map +1 -0
- package/dist/authServer.d.ts +28 -1
- package/dist/authServer.d.ts.map +1 -1
- package/dist/authServer.js +66 -13
- package/dist/authServer.js.map +1 -0
- package/dist/client.d.ts +28 -11
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +75 -55
- package/dist/client.js.map +1 -0
- package/dist/env.d.ts +35 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +65 -0
- package/dist/env.js.map +1 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +24 -0
- package/dist/errors.js.map +1 -0
- package/dist/functions.d.ts +21 -3
- package/dist/functions.d.ts.map +1 -1
- package/dist/functions.js +74 -26
- package/dist/functions.js.map +1 -0
- package/dist/index.d.ts +11 -50
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -6
- package/dist/index.js.map +1 -0
- package/dist/query-server.d.ts +31 -9
- package/dist/query-server.d.ts.map +1 -1
- package/dist/query-server.js +63 -11
- package/dist/query-server.js.map +1 -0
- package/dist/tenant.d.ts +21 -0
- package/dist/tenant.d.ts.map +1 -0
- package/dist/tenant.js +50 -0
- package/dist/tenant.js.map +1 -0
- package/dist/types.d.ts +124 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/upload-gql.d.ts +2 -17
- package/dist/upload-gql.d.ts.map +1 -1
- package/dist/upload-gql.js +63 -44
- package/dist/upload-gql.js.map +1 -0
- package/dist/utils.d.ts +24 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +257 -0
- package/dist/utilsGeneral.d.ts +1 -1
- package/dist/utilsGeneral.d.ts.map +1 -1
- package/package.json +49 -22
package/README.md
CHANGED
|
@@ -1,22 +1,304 @@
|
|
|
1
|
-
# @econneq/gql-auth
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
# @econneq/gql-auth
|
|
2
|
+
|
|
3
|
+
GraphQL + Auth utilities for **multi-tenant** and **single-tenant** Next.js apps.
|
|
4
|
+
|
|
5
|
+
Handles:
|
|
6
|
+
- Server-side (internal Docker network) and client-side (browser proxy) GraphQL
|
|
7
|
+
- Multi-tenant header injection (`X-Tenant-ID`, `X-Process-ID`, `X-Widget-ID`)
|
|
8
|
+
- JWT auth on both server and client
|
|
9
|
+
- File uploads (GraphQL multipart spec)
|
|
10
|
+
- Batch mutations with per-item error reporting
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @econneq/gql-auth
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Peer dependencies
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @apollo/client graphql next react
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Environment Variables
|
|
29
|
+
|
|
30
|
+
| Variable | Side | Description |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `DOMAIN_URL` | server | Base domain, e.g. `e-conneq.com` |
|
|
33
|
+
| `PROTOCOL` | server | e.g. `https://` |
|
|
34
|
+
| `BACKEND_URL_INT` | server | Internal Docker URL, e.g. `api:8000` |
|
|
35
|
+
| `BACKEND_URL_EXT` | server | External backend host, e.g. `admin.e-conneq.com` |
|
|
36
|
+
| `NEXT_PUBLIC_DOMAIN_URL` | both | Same as DOMAIN_URL (client-accessible) |
|
|
37
|
+
| `NEXT_PUBLIC_PROTOCOL` | both | Same as PROTOCOL (client-accessible) |
|
|
38
|
+
| `NEXT_PUBLIC_BACKEND_URL_EXT` | both | Same as BACKEND_URL_EXT (client-accessible) |
|
|
39
|
+
| `API_KEY` | server | Optional service-to-service key |
|
|
40
|
+
|
|
41
|
+
> The internal URL (`BACKEND_URL_INT`) is **never** exposed to the browser.
|
|
42
|
+
> Client-side mutations route through Next.js `/api/graphql` proxy.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Config Object
|
|
47
|
+
|
|
48
|
+
Every function accepts a `config` object describing your tenants.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
// config.ts (shared across your project)
|
|
52
|
+
import type { ConfigType } from "@econneq/gql-auth";
|
|
53
|
+
|
|
54
|
+
export const config: ConfigType = {
|
|
55
|
+
Subdomains: [
|
|
56
|
+
{ id: 1, subdomain: "acme", processId: "proc-1", widgetID: "wgt-1" },
|
|
57
|
+
{ id: 2, subdomain: "globex", processId: "proc-2" },
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Single-tenant**: put one entry in `Subdomains`. The library will always use it
|
|
63
|
+
without requiring a `domain` parameter.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Quick start
|
|
68
|
+
|
|
69
|
+
### 1. Register the cookie helper (Next.js app)
|
|
70
|
+
|
|
71
|
+
In your root layout (`app/layout.tsx`) or instrumentation file:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { cookies } from "next/headers";
|
|
75
|
+
import { registerCookiesFn } from "@econneq/gql-auth";
|
|
76
|
+
|
|
77
|
+
registerCookiesFn(cookies);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This lets the Apollo client forward cookies server-side without a hard
|
|
81
|
+
`next/headers` import inside the package.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
### 2. Server-side query (Server Component / Route Handler)
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { queryServerGraphQL } from "@econneq/gql-auth";
|
|
89
|
+
import { config } from "@/config";
|
|
90
|
+
import { GET_USERS } from "@/graphql/queries";
|
|
91
|
+
|
|
92
|
+
// Multi-tenant
|
|
93
|
+
const data = await queryServerGraphQL({
|
|
94
|
+
query: GET_USERS,
|
|
95
|
+
variables: { active: true },
|
|
96
|
+
domain: "acme", // ← matches Subdomains[].subdomain
|
|
97
|
+
config,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Single-tenant (omit domain)
|
|
101
|
+
const data = await queryServerGraphQL({
|
|
102
|
+
query: GET_USERS,
|
|
103
|
+
config,
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 3. Server-side mutation (Server Action)
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
"use server";
|
|
111
|
+
import { mutateServerGraphQL } from "@econneq/gql-auth";
|
|
112
|
+
|
|
113
|
+
const result = await mutateServerGraphQL({
|
|
114
|
+
query: CREATE_USER_MUTATION,
|
|
115
|
+
variables: { input: formData },
|
|
116
|
+
domain: params.domain,
|
|
117
|
+
config,
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
### 4. Client-side mutation with ApiFactory
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
"use client";
|
|
127
|
+
import { ApiFactory, getToken } from "@econneq/gql-auth";
|
|
128
|
+
import { useRouter } from "next/navigation";
|
|
129
|
+
import { config } from "@/config";
|
|
130
|
+
|
|
131
|
+
export function MyForm({ params }: { params: { domain: string } }) {
|
|
132
|
+
const router = useRouter();
|
|
133
|
+
|
|
134
|
+
const handleSubmit = async (formData: any) => {
|
|
135
|
+
await ApiFactory({
|
|
136
|
+
newData: formData,
|
|
137
|
+
mutationName: "createUser",
|
|
138
|
+
modelName: "user",
|
|
139
|
+
successField: "username",
|
|
140
|
+
query: CREATE_USER_MUTATION,
|
|
141
|
+
params: { domain: params.domain }, // omit for single-tenant
|
|
142
|
+
config,
|
|
143
|
+
router,
|
|
144
|
+
token: getToken() ?? undefined,
|
|
145
|
+
onAlert: ({ title, status }) => {
|
|
146
|
+
// Connect to your toast/alert system
|
|
147
|
+
console.log(title, status);
|
|
148
|
+
},
|
|
149
|
+
redirect: true,
|
|
150
|
+
redirectPath: "/dashboard",
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
return <form onSubmit={...}>...</form>;
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
#### Batch mutations
|
|
159
|
+
|
|
160
|
+
Pass an array to `newData`:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
await ApiFactory({
|
|
164
|
+
newData: [item1, item2, item3],
|
|
165
|
+
mutationName: "createProduct",
|
|
166
|
+
modelName: "product",
|
|
167
|
+
// ...
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### File uploads
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
await ApiFactory({
|
|
175
|
+
newData: formData,
|
|
176
|
+
getFileMap: (item) => ({ avatar: item.avatarFile }),
|
|
177
|
+
// ...
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### Return data instead of alert
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
const user = await ApiFactory<{ id: string; username: string }>({
|
|
185
|
+
returnResponseObject: true,
|
|
186
|
+
// ...
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
### 5. Direct upload mutation
|
|
193
|
+
|
|
194
|
+
For full control over multipart uploads:
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
import { uploadGraphQLMutation } from "@econneq/gql-auth";
|
|
198
|
+
|
|
199
|
+
const response = await uploadGraphQLMutation({
|
|
200
|
+
query: UPLOAD_DOCUMENT_MUTATION.loc!.source.body,
|
|
201
|
+
variables: { title: "Report", file: null },
|
|
202
|
+
fileMap: { file: selectedFile },
|
|
203
|
+
params: { domain: "acme" },
|
|
204
|
+
config,
|
|
205
|
+
token: userToken,
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
### 6. Auth utilities
|
|
212
|
+
|
|
213
|
+
#### Client-side
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { getToken, getUser, logout } from "@econneq/gql-auth";
|
|
217
|
+
|
|
218
|
+
const token = getToken(); // cookie → localStorage
|
|
219
|
+
const user = getUser(); // decoded JwtPayload
|
|
220
|
+
logout(); // clears cookies + localStorage
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### Server-side
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
import {
|
|
227
|
+
getServerToken,
|
|
228
|
+
getServerUser,
|
|
229
|
+
getPermissions,
|
|
230
|
+
} from "@econneq/gql-auth";
|
|
231
|
+
|
|
232
|
+
const token = await getServerToken();
|
|
233
|
+
const user = await getServerUser();
|
|
234
|
+
|
|
235
|
+
// Does the user have any access to module 42?
|
|
236
|
+
const canRead = getPermissions(user?.permissions ?? [], 42);
|
|
237
|
+
|
|
238
|
+
// Does the user have CREATE access to module 42?
|
|
239
|
+
const canCreate = getPermissions(user?.permissions ?? [], 42, "C");
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
### 7. Apollo client (advanced)
|
|
245
|
+
|
|
246
|
+
Direct access when you need a custom query/mutation outside ApiFactory:
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
import { getApolloClient } from "@econneq/gql-auth";
|
|
250
|
+
|
|
251
|
+
const client = await getApolloClient({
|
|
252
|
+
domain: "acme", // omit for single-tenant
|
|
253
|
+
config,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const { data } = await client.query({ query: MY_QUERY });
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Architecture
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
Browser Next.js Server Backend (Docker)
|
|
265
|
+
───────── ────────────── ────────────────
|
|
266
|
+
uploadGraphQLMutation ──► /api/graphql (proxy) ──► api:8000/graphql/
|
|
267
|
+
ApiFactory ──► /api/graphql (proxy) ──► api:8000/graphql/
|
|
268
|
+
|
|
269
|
+
queryServerGraphQL ────────────────────────────► http://api:8000/graphql/
|
|
270
|
+
mutateServerGraphQL ────────────────────────────► http://api:8000/graphql/
|
|
271
|
+
getApolloClient ────────────────────────────► http://api:8000/graphql/
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Server-side calls skip the proxy entirely and hit the backend directly
|
|
275
|
+
through Docker's internal network — faster and no public exposure.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Permissions format
|
|
280
|
+
|
|
281
|
+
Permission strings encode resource + action:
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
"42" → access resource 42 (any action)
|
|
285
|
+
"42C" → create on resource 42
|
|
286
|
+
"42R" → read on resource 42
|
|
287
|
+
"42U" → update on resource 42
|
|
288
|
+
"42D" → delete on resource 42
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Changelog
|
|
294
|
+
|
|
295
|
+
### 2.0.0
|
|
296
|
+
- Centralised env resolution (`env.ts`)
|
|
297
|
+
- Tenant resolution separated into `tenant.ts` — single-tenant fallback
|
|
298
|
+
- `mutateServerGraphQL` added (server-side mutations)
|
|
299
|
+
- `ApiFactory` typed generics, batch partial-error reporting
|
|
300
|
+
- `registerCookiesFn` removes hard `next/headers` dependency
|
|
301
|
+
- `getServerCookieString` helper exported
|
|
302
|
+
- `parseGraphQLErrors` exported for custom use
|
|
303
|
+
- Subdomains[0] fallback for single-tenant setups
|
|
304
|
+
- `uploadGraphQLMutation` no longer throws on missing tenant in single-tenant mode
|
package/dist/api-factory.d.ts
CHANGED
|
@@ -1,28 +1,12 @@
|
|
|
1
|
-
type
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
Subdomains: any[];
|
|
13
|
-
};
|
|
14
|
-
onAlert: (options: {
|
|
15
|
-
title: string;
|
|
16
|
-
status: boolean;
|
|
17
|
-
duration: number;
|
|
18
|
-
}) => void;
|
|
19
|
-
redirect?: boolean;
|
|
20
|
-
redirectPath?: string;
|
|
21
|
-
returnResponseField?: boolean;
|
|
22
|
-
returnResponseObject?: boolean;
|
|
23
|
-
reload?: boolean;
|
|
24
|
-
getFileMap?: (item: any) => Record<string, File>;
|
|
25
|
-
};
|
|
26
|
-
export declare const ApiFactory: (options: SubmitOptions) => Promise<any>;
|
|
27
|
-
export {};
|
|
1
|
+
import type { ApiFactoryOptions } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Run one or more GraphQL mutations and handle the full
|
|
4
|
+
* response → alert / redirect / reload lifecycle.
|
|
5
|
+
*
|
|
6
|
+
* @returns
|
|
7
|
+
* - `returnResponseObject = true` → the raw mutation response object
|
|
8
|
+
* - `returnResponseField = true` → the value of `successField` from result
|
|
9
|
+
* - Otherwise → `void` (triggers alert + reload/redirect)
|
|
10
|
+
*/
|
|
11
|
+
export declare function ApiFactory<T = any>(options: ApiFactoryOptions): Promise<T | void>;
|
|
28
12
|
//# sourceMappingURL=api-factory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-factory.d.ts","sourceRoot":"","sources":["../src/api-factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api-factory.d.ts","sourceRoot":"","sources":["../src/api-factory.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAiBjD;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAAC,CAAC,GAAG,GAAG,EACtC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CA8GnB"}
|
package/dist/api-factory.js
CHANGED
|
@@ -1,66 +1,119 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
// ============================================================
|
|
2
|
+
// @econneq/gql-auth — ApiFactory
|
|
3
|
+
// ============================================================
|
|
4
|
+
//
|
|
5
|
+
// Client-side orchestrator for GraphQL mutations.
|
|
6
|
+
//
|
|
7
|
+
// Features
|
|
8
|
+
// ─────────
|
|
9
|
+
// • Single item or batch (array) mutations in one call
|
|
10
|
+
// • File uploads via multipart (getFileMap)
|
|
11
|
+
// • Multi-tenant and single-tenant support
|
|
12
|
+
// • Typed overloads for returnResponseField / returnResponseObject
|
|
13
|
+
// • Granular alert feedback for partial batch failures
|
|
14
|
+
// • Automatic token resolution (prop → cookie → localStorage)
|
|
15
|
+
// ─────────────────────────────────────────────────────────────
|
|
16
|
+
import { uploadGraphQLMutation } from "./upload-gql";
|
|
17
|
+
import { parseGraphQLErrors } from "./errors";
|
|
18
|
+
import { removeEmptyFields, getToken } from "./functions";
|
|
19
|
+
// ─── Internal helpers ────────────────────────────────────────
|
|
20
|
+
function resolveToken(token) {
|
|
21
|
+
if (token)
|
|
22
|
+
return token;
|
|
23
|
+
if (typeof window !== "undefined") {
|
|
24
|
+
return getToken() ?? undefined;
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
// ─── Main ────────────────────────────────────────────────────
|
|
29
|
+
/**
|
|
30
|
+
* Run one or more GraphQL mutations and handle the full
|
|
31
|
+
* response → alert / redirect / reload lifecycle.
|
|
32
|
+
*
|
|
33
|
+
* @returns
|
|
34
|
+
* - `returnResponseObject = true` → the raw mutation response object
|
|
35
|
+
* - `returnResponseField = true` → the value of `successField` from result
|
|
36
|
+
* - Otherwise → `void` (triggers alert + reload/redirect)
|
|
37
|
+
*/
|
|
38
|
+
export async function ApiFactory(options) {
|
|
39
|
+
const { newData, editData, mutationName, modelName, successField = "title", query, router, params, reload = true, redirect, redirectPath, returnResponseField = false, returnResponseObject = false, getFileMap, config, onAlert, } = options;
|
|
40
|
+
const token = resolveToken(options.token);
|
|
41
|
+
// Support single item or batch array
|
|
42
|
+
const source = newData ?? editData;
|
|
43
|
+
const items = Array.isArray(source) ? source : [source];
|
|
6
44
|
const successMessages = [];
|
|
7
45
|
const errorMessages = [];
|
|
8
46
|
let responseFieldData = null;
|
|
9
|
-
|
|
47
|
+
// ── Batch loop ─────────────────────────────────────────
|
|
48
|
+
for (const item of items) {
|
|
10
49
|
try {
|
|
50
|
+
const cleanVariables = removeEmptyFields(item);
|
|
11
51
|
const response = await uploadGraphQLMutation({
|
|
12
|
-
query: query
|
|
13
|
-
variables:
|
|
14
|
-
fileMap: getFileMap ? getFileMap(
|
|
52
|
+
query: query?.loc?.source?.body ?? query,
|
|
53
|
+
variables: cleanVariables,
|
|
54
|
+
fileMap: getFileMap ? getFileMap(item) : {},
|
|
15
55
|
params,
|
|
16
56
|
token,
|
|
17
57
|
config,
|
|
18
58
|
});
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
59
|
+
// ── Parse success ──────────────────────────────────
|
|
60
|
+
const mutationPayload = response?.data?.[mutationName];
|
|
61
|
+
if (mutationPayload !== undefined) {
|
|
62
|
+
const result = mutationPayload?.[modelName];
|
|
63
|
+
const label = result?.[successField] ?? "Operation successful ✅";
|
|
64
|
+
successMessages.push(label);
|
|
65
|
+
if (returnResponseObject) {
|
|
66
|
+
responseFieldData = mutationPayload;
|
|
67
|
+
}
|
|
68
|
+
else if (returnResponseField && result?.id) {
|
|
25
69
|
responseFieldData = result[successField];
|
|
26
|
-
if (result?.id || result?.token || result === null) {
|
|
27
70
|
}
|
|
28
71
|
}
|
|
29
|
-
//
|
|
30
|
-
if (response?.errors) {
|
|
31
|
-
|
|
72
|
+
// ── Parse GraphQL errors ───────────────────────────
|
|
73
|
+
if (response?.errors?.length) {
|
|
74
|
+
errorMessages.push(...parseGraphQLErrors(response.errors));
|
|
32
75
|
}
|
|
33
76
|
}
|
|
34
77
|
catch (err) {
|
|
35
|
-
errorMessages.push(`
|
|
78
|
+
errorMessages.push(`Network error: ${err?.message ?? "Unknown"}`);
|
|
36
79
|
}
|
|
37
80
|
}
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
81
|
+
// ── Response / UI logic ────────────────────────────────
|
|
82
|
+
const hasSuccess = successMessages.length > 0;
|
|
83
|
+
const hasErrors = errorMessages.length > 0;
|
|
84
|
+
// Partial batch: some succeeded, some failed
|
|
85
|
+
if (hasSuccess && hasErrors) {
|
|
86
|
+
onAlert({
|
|
87
|
+
title: `⚠️ Partial success:\n${successMessages.join(", ")}\n\nErrors:\n${errorMessages.join("\n")}`,
|
|
88
|
+
status: false,
|
|
89
|
+
duration: 6000,
|
|
90
|
+
});
|
|
91
|
+
return;
|
|
47
92
|
}
|
|
48
|
-
|
|
93
|
+
if (hasErrors) {
|
|
49
94
|
onAlert({
|
|
50
95
|
title: `❌ Errors:\n${errorMessages.join("\n")}`,
|
|
51
96
|
status: false,
|
|
52
|
-
duration: 5000
|
|
97
|
+
duration: 5000,
|
|
53
98
|
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (hasSuccess) {
|
|
102
|
+
// Return data when caller wants it
|
|
103
|
+
if (returnResponseObject || returnResponseField) {
|
|
104
|
+
return responseFieldData;
|
|
105
|
+
}
|
|
106
|
+
onAlert({
|
|
107
|
+
title: `Successfully submitted ✅`,
|
|
108
|
+
status: true,
|
|
109
|
+
duration: 3000,
|
|
110
|
+
});
|
|
111
|
+
if (redirect && redirectPath && router) {
|
|
112
|
+
router.push(redirectPath);
|
|
113
|
+
}
|
|
114
|
+
else if (reload && typeof window !== "undefined") {
|
|
115
|
+
window.location.reload();
|
|
116
|
+
}
|
|
54
117
|
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const handleGraphQLErrors = (errors, errorList) => {
|
|
58
|
-
errors.forEach(error => {
|
|
59
|
-
if (error.message.includes("duplicate"))
|
|
60
|
-
errorList.push("Record Already Exists ❌");
|
|
61
|
-
else if (error.message.includes("Authentication"))
|
|
62
|
-
errorList.push("Login Required ❌");
|
|
63
|
-
else
|
|
64
|
-
errorList.push(error.message);
|
|
65
|
-
});
|
|
66
|
-
};
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=api-factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-factory.js","sourceRoot":"","sources":["../src/api-factory.ts"],"names":[],"mappings":"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"}
|
package/dist/authServer.d.ts
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
|
-
import { JwtPayload } from "./
|
|
1
|
+
import type { JwtPayload } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Read the JWT from the incoming request cookies.
|
|
4
|
+
* Returns null when no token cookie is present.
|
|
5
|
+
*/
|
|
2
6
|
export declare const getServerToken: () => Promise<string | null>;
|
|
7
|
+
/**
|
|
8
|
+
* Decode the server-side JWT and return the payload.
|
|
9
|
+
* Returns null when no token is present or decoding fails.
|
|
10
|
+
*/
|
|
3
11
|
export declare const getServerUser: () => Promise<JwtPayload | null>;
|
|
12
|
+
/**
|
|
13
|
+
* Check whether a permission list grants access to a resource.
|
|
14
|
+
*
|
|
15
|
+
* @param list - Array of permission strings (from JwtPayload.permissions)
|
|
16
|
+
* @param access - Numeric resource/module identifier
|
|
17
|
+
* @param action - Optional action suffix (e.g. "C", "R", "U", "D")
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // Does the user have ANY access to module 42?
|
|
21
|
+
* getPermissions(user.permissions, 42);
|
|
22
|
+
*
|
|
23
|
+
* // Does the user have CREATE access to module 42?
|
|
24
|
+
* getPermissions(user.permissions, 42, "C");
|
|
25
|
+
*/
|
|
4
26
|
export declare const getPermissions: (list: string[], access: number, action?: string) => boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Return the full cookie header string for forwarding to the
|
|
29
|
+
* backend (Django session / CSRF pass-through).
|
|
30
|
+
*/
|
|
31
|
+
export declare const getServerCookieString: () => Promise<string>;
|
|
5
32
|
//# sourceMappingURL=authServer.d.ts.map
|
package/dist/authServer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authServer.d.ts","sourceRoot":"","sources":["../src/authServer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"authServer.d.ts","sourceRoot":"","sources":["../src/authServer.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAI1C;;;GAGG;AACH,eAAO,MAAM,cAAc,QAAa,OAAO,CAAC,MAAM,GAAG,IAAI,CAS5D,CAAC;AAIF;;;GAGG;AACH,eAAO,MAAM,aAAa,QAAa,OAAO,CAAC,UAAU,GAAG,IAAI,CAU/D,CAAC;AAIF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EAAE,EACd,QAAQ,MAAM,EACd,SAAS,MAAM,KACd,OAoBF,CAAC;AAIF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAAa,OAAO,CAAC,MAAM,CAQ5D,CAAC"}
|