@blazeo.com/calendar-client 1.0.24 → 1.0.27
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 +36 -1
- package/dist/index.d.ts +55 -2
- package/dist/index.js +265 -13
- package/dist/index.mjs +250 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,12 +22,26 @@ cd your-app && npm link @blazeo.com/calendar-client
|
|
|
22
22
|
Configure once at app startup, then use models and their methods:
|
|
23
23
|
|
|
24
24
|
```js
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
configure,
|
|
27
|
+
setBaseUrl,
|
|
28
|
+
setConsumer,
|
|
29
|
+
setApiCredentials,
|
|
30
|
+
fetchAccessToken,
|
|
31
|
+
CalendarModel,
|
|
32
|
+
createRootStore,
|
|
33
|
+
} from '@blazeo.com/calendar-client';
|
|
26
34
|
|
|
27
35
|
configure({ baseUrl: 'https://your-appointment-api.example.com' });
|
|
28
36
|
// or: setBaseUrl('https://localhost:7051');
|
|
29
37
|
setConsumer('my-app'); // optional: sent as Consumer header (e.g. for lead source)
|
|
30
38
|
|
|
39
|
+
// API JWT (for Authorized routes such as lead export)
|
|
40
|
+
setApiCredentials('your-api-key', 'your-api-secret');
|
|
41
|
+
await fetchAccessToken();
|
|
42
|
+
// Or set a token you obtained elsewhere:
|
|
43
|
+
// setAccessToken('eyJ...', '2026-05-19T12:00:00Z');
|
|
44
|
+
|
|
31
45
|
// Calendar static methods (no store needed)
|
|
32
46
|
const timezones = await CalendarModel.getTimeZones();
|
|
33
47
|
const calendar = await CalendarModel.get('calendar-guid');
|
|
@@ -38,12 +52,33 @@ const cal = store.addCalendar({ calendarId: 'my-cal', name: 'My Calendar' });
|
|
|
38
52
|
await cal.create(); // POST to backend
|
|
39
53
|
```
|
|
40
54
|
|
|
55
|
+
### API JWT authentication
|
|
56
|
+
|
|
57
|
+
The backend issues JWTs via `POST Api/Auth/Token` (`api_key` + `api_secret`). There is no refresh endpoint; when the token expires (~1 hour), call `fetchAccessToken()` again.
|
|
58
|
+
|
|
59
|
+
| Function | Purpose |
|
|
60
|
+
|----------|---------|
|
|
61
|
+
| `setApiCredentials(apiKey, apiSecret)` | Store credentials for token exchange |
|
|
62
|
+
| `fetchAccessToken()` | Get JWT from API and store on config |
|
|
63
|
+
| `setAccessToken(token, expiresAtUtc?)` | Use a token you already have |
|
|
64
|
+
| `ensureValidAccessToken()` | Fetch only if missing or near expiry |
|
|
65
|
+
| `clearAuth()` | Clear token and credentials |
|
|
66
|
+
|
|
67
|
+
All `reqGet` / `reqPost` calls automatically attach `Authorization: Bearer …` and re-issue the token before requests when credentials are configured.
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
configure({ baseUrl: 'https://your-api', apiKey: 'key', apiSecret: 'secret' });
|
|
71
|
+
await fetchAccessToken();
|
|
72
|
+
await LeadModel.requestExport('company-key');
|
|
73
|
+
```
|
|
74
|
+
|
|
41
75
|
## API overview
|
|
42
76
|
|
|
43
77
|
- **CalendarModel (static):** `get`, `getByCompany`, `getTimeZones`, `getTimeZone`, `getParticipants`, `getMonth`, `getEvents`, etc.
|
|
44
78
|
- **EventModel (instance):** `get`, `create`, `cancel`, `getCancellable`, `getAvailability`, `setReminder`
|
|
45
79
|
- **FlowModel:** Same pattern as Calendar — `FlowModel.create({}, { env })` with no fields; static `get`, `getRaw`, `list`, `createFlow`, `updateFlow`, `delete`, `duplicate`, appearance/embed/public/preview helpers; instance methods mirror those using `flowId` on the snapshot
|
|
46
80
|
- **LeadModel:** `LeadModel.create({}, { env })`; static `get`, `getRaw`, `getByEmail`, `getByCompany`; instance `get`, `getByEmail`, `getByCompany` (uses `leadId` / `email` / `companyKey` on the snapshot)
|
|
81
|
+
- **ParticipantModel:** static `get`, `getByEmail`, `getByIds`, `getAll`, …; instance `getByEmail` uses `email` + `companyKey` on the snapshot (see `GET /participant/getbyemail`)
|
|
47
82
|
- **AuthModel (calendar OAuth / Connect Calendar):** `getCalendarProviders`, `getAuthorizationUrl`, `getAuthorizationStatus`, `openOAuthPopup`, `onCalendarAuthMessage` — see [Calendar authorization flow](#calendar-authorization-direct-ui)
|
|
48
83
|
- **RootStore:** `addCalendar`, `addEvent`
|
|
49
84
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,61 @@
|
|
|
1
1
|
/** @blazeo.com/calendar-client - type declarations */
|
|
2
2
|
|
|
3
|
-
export
|
|
4
|
-
|
|
3
|
+
export type AccessTokenResult = {
|
|
4
|
+
status: string;
|
|
5
|
+
message?: string;
|
|
6
|
+
accessToken?: string;
|
|
7
|
+
expiresAtUtc?: string | null;
|
|
8
|
+
tokenType?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type AuthState = {
|
|
12
|
+
accessToken?: string;
|
|
13
|
+
tokenExpiresAt?: string;
|
|
14
|
+
apiKey?: string;
|
|
15
|
+
apiSecret?: string;
|
|
16
|
+
hasApiCredentials: boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function configure(env: {
|
|
20
|
+
baseUrl?: string;
|
|
21
|
+
consumer?: string;
|
|
22
|
+
fetch?: typeof fetch;
|
|
23
|
+
getDefaultOffset?: () => number;
|
|
24
|
+
accessToken?: string;
|
|
25
|
+
expiresAtUtc?: string;
|
|
26
|
+
tokenExpiresAt?: string;
|
|
27
|
+
expires_at_utc?: string;
|
|
28
|
+
apiKey?: string;
|
|
29
|
+
api_key?: string;
|
|
30
|
+
apiSecret?: string;
|
|
31
|
+
api_secret?: string;
|
|
32
|
+
}): void;
|
|
33
|
+
|
|
34
|
+
export function getConfig(): {
|
|
35
|
+
baseUrl?: string;
|
|
36
|
+
consumer?: string;
|
|
37
|
+
fetch?: typeof fetch;
|
|
38
|
+
getDefaultOffset?: () => number;
|
|
39
|
+
accessToken?: string;
|
|
40
|
+
tokenExpiresAt?: string;
|
|
41
|
+
hasApiCredentials?: boolean;
|
|
42
|
+
} | null;
|
|
43
|
+
|
|
5
44
|
export function setBaseUrl(baseUrl: string): void;
|
|
6
45
|
export function setConsumer(consumer: string): void;
|
|
46
|
+
export function setAccessToken(accessToken: string, expiresAtUtc?: string): void;
|
|
47
|
+
export function clearAccessToken(): void;
|
|
48
|
+
export function setApiCredentials(apiKey: string, apiSecret: string): void;
|
|
49
|
+
export function clearApiCredentials(): void;
|
|
50
|
+
export function clearAuth(): void;
|
|
51
|
+
export function getAuth(): AuthState;
|
|
52
|
+
export function fetchAccessToken(apiKey?: string, apiSecret?: string): Promise<AccessTokenResult>;
|
|
53
|
+
export function ensureValidAccessToken(): Promise<string | undefined>;
|
|
54
|
+
export function requestAccessToken(apiKey: string, apiSecret: string, opts?: { baseUrl?: string; fetch?: typeof fetch }): Promise<AccessTokenResult>;
|
|
55
|
+
export function isAccessTokenExpired(expiresAtUtc?: string | number | Date | null, skewMs?: number): boolean;
|
|
56
|
+
export function buildAuthHeaders(extra?: Record<string, string>): Record<string, string>;
|
|
57
|
+
export const TOKEN_PATH: '/Api/Auth/Token';
|
|
58
|
+
export const DEFAULT_TOKEN_REFRESH_SKEW_MS: number;
|
|
7
59
|
export function getConfigStore(): unknown;
|
|
8
60
|
|
|
9
61
|
export const ConfigModel: unknown;
|
|
@@ -79,6 +131,7 @@ export const CalendarParticipantModel: {
|
|
|
79
131
|
};
|
|
80
132
|
export const ParticipantModel: {
|
|
81
133
|
get(participantId: string): Promise<unknown>;
|
|
134
|
+
getByEmail(email: string, companyKey: string): Promise<unknown>;
|
|
82
135
|
getByIds(participantIds: string[] | string): Promise<unknown[] | null>;
|
|
83
136
|
getAll(companyKey: string): Promise<unknown[] | null>;
|
|
84
137
|
add(payload: object, calendarId?: string): Promise<unknown>;
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
CompanyModel: () => Company_default,
|
|
35
35
|
ConfigModel: () => ConfigModel_default,
|
|
36
36
|
CustomFieldModel: () => CustomField_default,
|
|
37
|
+
DEFAULT_TOKEN_REFRESH_SKEW_MS: () => DEFAULT_TOKEN_REFRESH_SKEW_MS,
|
|
37
38
|
DayOfWeek: () => DayOfWeek,
|
|
38
39
|
EmailProvider: () => EmailProvider,
|
|
39
40
|
EventModel: () => Event_default,
|
|
@@ -48,14 +49,27 @@ __export(index_exports, {
|
|
|
48
49
|
RecurringFrequency: () => RecurringFrequency,
|
|
49
50
|
RootStore: () => RootStore,
|
|
50
51
|
SettingModel: () => Setting_default,
|
|
52
|
+
TOKEN_PATH: () => TOKEN_PATH,
|
|
51
53
|
TimeFrameModel: () => TimeFrame_default,
|
|
52
54
|
TimeSlotModel: () => TimeSlot_default,
|
|
53
55
|
Unit: () => Unit,
|
|
56
|
+
buildAuthHeaders: () => buildAuthHeaders,
|
|
57
|
+
clearAccessToken: () => clearAccessToken,
|
|
58
|
+
clearApiCredentials: () => clearApiCredentials,
|
|
59
|
+
clearAuth: () => clearAuth,
|
|
54
60
|
configure: () => configure,
|
|
55
61
|
createRootStore: () => createRootStore,
|
|
62
|
+
ensureValidAccessToken: () => ensureValidAccessToken,
|
|
63
|
+
fetchAccessToken: () => fetchAccessToken2,
|
|
64
|
+
getAuth: () => getAuth,
|
|
56
65
|
getConfig: () => getConfig,
|
|
57
66
|
getConfigStore: () => getConfigStore,
|
|
58
|
-
|
|
67
|
+
isAccessTokenExpired: () => isAccessTokenExpired,
|
|
68
|
+
requestAccessToken: () => requestAccessToken,
|
|
69
|
+
setAccessToken: () => setAccessToken,
|
|
70
|
+
setApiCredentials: () => setApiCredentials,
|
|
71
|
+
setBaseUrl: () => setBaseUrl,
|
|
72
|
+
setConsumer: () => setConsumer
|
|
59
73
|
});
|
|
60
74
|
module.exports = __toCommonJS(index_exports);
|
|
61
75
|
|
|
@@ -69,6 +83,10 @@ var ConfigModel = import_mobx_state_tree.types.model("Config", {
|
|
|
69
83
|
consumer: import_mobx_state_tree.types.optional(import_mobx_state_tree.types.string, "")
|
|
70
84
|
}).volatile(() => ({
|
|
71
85
|
fetch: void 0,
|
|
86
|
+
accessToken: void 0,
|
|
87
|
+
tokenExpiresAt: void 0,
|
|
88
|
+
apiKey: void 0,
|
|
89
|
+
apiSecret: void 0,
|
|
72
90
|
getDefaultOffset: () => -(/* @__PURE__ */ new Date()).getTimezoneOffset()
|
|
73
91
|
})).actions((self) => ({
|
|
74
92
|
setBaseUrl(url) {
|
|
@@ -83,19 +101,57 @@ var ConfigModel = import_mobx_state_tree.types.model("Config", {
|
|
|
83
101
|
setGetDefaultOffset(fn) {
|
|
84
102
|
self.getDefaultOffset = fn;
|
|
85
103
|
},
|
|
104
|
+
setAccessToken(token, expiresAtUtc = void 0) {
|
|
105
|
+
self.accessToken = token ? String(token) : void 0;
|
|
106
|
+
self.tokenExpiresAt = expiresAtUtc != null && expiresAtUtc !== "" ? String(expiresAtUtc) : void 0;
|
|
107
|
+
},
|
|
108
|
+
clearAccessToken() {
|
|
109
|
+
self.accessToken = void 0;
|
|
110
|
+
self.tokenExpiresAt = void 0;
|
|
111
|
+
},
|
|
112
|
+
setApiCredentials(apiKey, apiSecret) {
|
|
113
|
+
self.apiKey = apiKey != null ? String(apiKey) : void 0;
|
|
114
|
+
self.apiSecret = apiSecret != null ? String(apiSecret) : void 0;
|
|
115
|
+
},
|
|
116
|
+
clearApiCredentials() {
|
|
117
|
+
self.apiKey = void 0;
|
|
118
|
+
self.apiSecret = void 0;
|
|
119
|
+
},
|
|
120
|
+
clearAuth() {
|
|
121
|
+
self.clearAccessToken();
|
|
122
|
+
self.clearApiCredentials();
|
|
123
|
+
},
|
|
86
124
|
configure(env) {
|
|
87
125
|
if (env.baseUrl != null) self.baseUrl = env.baseUrl;
|
|
88
126
|
if (env.consumer != null) self.consumer = env.consumer;
|
|
89
127
|
if (env.fetch != null) self.fetch = env.fetch;
|
|
90
128
|
if (env.getDefaultOffset != null) self.getDefaultOffset = env.getDefaultOffset;
|
|
129
|
+
if (env.accessToken != null) {
|
|
130
|
+
self.setAccessToken(
|
|
131
|
+
env.accessToken,
|
|
132
|
+
env.expiresAtUtc ?? env.tokenExpiresAt ?? env.expires_at_utc
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
if (env.apiKey != null || env.api_key != null) {
|
|
136
|
+
self.apiKey = String(env.apiKey ?? env.api_key);
|
|
137
|
+
}
|
|
138
|
+
if (env.apiSecret != null || env.api_secret != null) {
|
|
139
|
+
self.apiSecret = String(env.apiSecret ?? env.api_secret);
|
|
140
|
+
}
|
|
91
141
|
}
|
|
92
142
|
})).views((self) => ({
|
|
143
|
+
get hasApiCredentials() {
|
|
144
|
+
return Boolean(self.apiKey && self.apiSecret);
|
|
145
|
+
},
|
|
93
146
|
getEnv() {
|
|
94
147
|
return {
|
|
95
148
|
baseUrl: self.baseUrl || void 0,
|
|
96
149
|
consumer: self.consumer || void 0,
|
|
97
150
|
fetch: self.fetch,
|
|
98
|
-
getDefaultOffset: self.getDefaultOffset
|
|
151
|
+
getDefaultOffset: self.getDefaultOffset,
|
|
152
|
+
accessToken: self.accessToken,
|
|
153
|
+
tokenExpiresAt: self.tokenExpiresAt,
|
|
154
|
+
hasApiCredentials: self.hasApiCredentials
|
|
99
155
|
};
|
|
100
156
|
}
|
|
101
157
|
}));
|
|
@@ -106,6 +162,122 @@ function getConfigStore() {
|
|
|
106
162
|
}
|
|
107
163
|
var ConfigModel_default = ConfigModel;
|
|
108
164
|
|
|
165
|
+
// src/apiAuth.js
|
|
166
|
+
var TOKEN_PATH = "/Api/Auth/Token";
|
|
167
|
+
var DEFAULT_TOKEN_REFRESH_SKEW_MS = 6e4;
|
|
168
|
+
function defaultFetch() {
|
|
169
|
+
if (typeof fetch === "undefined") {
|
|
170
|
+
throw new Error("fetch not available");
|
|
171
|
+
}
|
|
172
|
+
return fetch;
|
|
173
|
+
}
|
|
174
|
+
function pickTokenPayload(data) {
|
|
175
|
+
if (!data || typeof data !== "object") return null;
|
|
176
|
+
const nested = data.data && typeof data.data === "object" ? data.data : data;
|
|
177
|
+
const accessToken = nested.access_token ?? nested.accessToken ?? nested.AccessToken ?? null;
|
|
178
|
+
if (!accessToken) return null;
|
|
179
|
+
const expiresAtUtc = nested.expires_at_utc ?? nested.expiresAtUtc ?? nested.ExpiresAtUtc ?? null;
|
|
180
|
+
return {
|
|
181
|
+
accessToken: String(accessToken),
|
|
182
|
+
expiresAtUtc: expiresAtUtc != null ? String(expiresAtUtc) : null,
|
|
183
|
+
tokenType: nested.token_type ?? nested.tokenType ?? "Bearer"
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function isAccessTokenExpired(expiresAtUtc, skewMs = DEFAULT_TOKEN_REFRESH_SKEW_MS) {
|
|
187
|
+
if (expiresAtUtc == null || expiresAtUtc === "") return false;
|
|
188
|
+
const expMs = new Date(expiresAtUtc).getTime();
|
|
189
|
+
if (Number.isNaN(expMs)) return false;
|
|
190
|
+
return Date.now() >= expMs - skewMs;
|
|
191
|
+
}
|
|
192
|
+
function getAuthState() {
|
|
193
|
+
const store = getConfigStore();
|
|
194
|
+
return {
|
|
195
|
+
accessToken: store.accessToken ?? void 0,
|
|
196
|
+
tokenExpiresAt: store.tokenExpiresAt ?? void 0,
|
|
197
|
+
apiKey: store.apiKey ?? void 0,
|
|
198
|
+
apiSecret: store.apiSecret ?? void 0,
|
|
199
|
+
hasApiCredentials: Boolean(store.apiKey && store.apiSecret)
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
async function requestAccessToken(apiKey, apiSecret, opts = {}) {
|
|
203
|
+
const store = getConfigStore();
|
|
204
|
+
const baseUrl = opts.baseUrl ?? store.baseUrl;
|
|
205
|
+
if (!baseUrl) {
|
|
206
|
+
return { status: "failure", message: "baseUrl required. Call configure({ baseUrl }) first." };
|
|
207
|
+
}
|
|
208
|
+
const key = apiKey != null ? String(apiKey).trim() : "";
|
|
209
|
+
const secret = apiSecret != null ? String(apiSecret).trim() : "";
|
|
210
|
+
if (!key || !secret) {
|
|
211
|
+
return { status: "failure", message: "api_key and api_secret are required" };
|
|
212
|
+
}
|
|
213
|
+
const fetchFn = opts.fetch ?? store.fetch ?? defaultFetch();
|
|
214
|
+
const url = `${String(baseUrl).replace(/\/+$/, "")}${TOKEN_PATH}`;
|
|
215
|
+
const httpRes = await fetchFn(url, {
|
|
216
|
+
method: "POST",
|
|
217
|
+
headers: { "Content-Type": "application/json" },
|
|
218
|
+
body: JSON.stringify({ api_key: key, api_secret: secret })
|
|
219
|
+
});
|
|
220
|
+
const text = await httpRes.text();
|
|
221
|
+
let body;
|
|
222
|
+
try {
|
|
223
|
+
body = JSON.parse(text);
|
|
224
|
+
} catch {
|
|
225
|
+
body = { status: "failure", message: text || httpRes.statusText };
|
|
226
|
+
}
|
|
227
|
+
if (!httpRes.ok && body.status !== "failure") {
|
|
228
|
+
body.status = "failure";
|
|
229
|
+
body.message = body.message ?? `HTTP ${httpRes.status}`;
|
|
230
|
+
}
|
|
231
|
+
if (body.status !== "success") {
|
|
232
|
+
return {
|
|
233
|
+
status: "failure",
|
|
234
|
+
message: body.message ?? "Failed to obtain access token"
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
const parsed = pickTokenPayload(body);
|
|
238
|
+
if (!parsed) {
|
|
239
|
+
return { status: "failure", message: "Token response missing access_token" };
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
status: "success",
|
|
243
|
+
accessToken: parsed.accessToken,
|
|
244
|
+
expiresAtUtc: parsed.expiresAtUtc,
|
|
245
|
+
tokenType: parsed.tokenType
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
async function fetchAccessToken(apiKey, apiSecret) {
|
|
249
|
+
const store = getConfigStore();
|
|
250
|
+
const key = apiKey ?? store.apiKey;
|
|
251
|
+
const secret = apiSecret ?? store.apiSecret;
|
|
252
|
+
const result = await requestAccessToken(key, secret);
|
|
253
|
+
if (result.status === "success") {
|
|
254
|
+
store.setAccessToken(result.accessToken, result.expiresAtUtc);
|
|
255
|
+
}
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
async function ensureAccessToken() {
|
|
259
|
+
const store = getConfigStore();
|
|
260
|
+
const { accessToken, tokenExpiresAt, apiKey, apiSecret } = getAuthState();
|
|
261
|
+
if (accessToken && !isAccessTokenExpired(tokenExpiresAt)) {
|
|
262
|
+
return accessToken;
|
|
263
|
+
}
|
|
264
|
+
if (apiKey && apiSecret) {
|
|
265
|
+
const result = await fetchAccessToken(apiKey, apiSecret);
|
|
266
|
+
if (result.status === "success") return result.accessToken;
|
|
267
|
+
}
|
|
268
|
+
return accessToken;
|
|
269
|
+
}
|
|
270
|
+
function buildAuthHeaders(extra = {}) {
|
|
271
|
+
const headers = { ...extra };
|
|
272
|
+
const { consumer } = getConfigStore();
|
|
273
|
+
const { accessToken } = getAuthState();
|
|
274
|
+
if (!headers.Consumer && consumer) headers.Consumer = consumer;
|
|
275
|
+
if (!headers.Authorization && accessToken) {
|
|
276
|
+
headers.Authorization = `Bearer ${accessToken}`;
|
|
277
|
+
}
|
|
278
|
+
return headers;
|
|
279
|
+
}
|
|
280
|
+
|
|
109
281
|
// src/config.js
|
|
110
282
|
function configure(env) {
|
|
111
283
|
const store = getConfigStore();
|
|
@@ -119,6 +291,33 @@ function getConfig() {
|
|
|
119
291
|
function setBaseUrl(baseUrl) {
|
|
120
292
|
getConfigStore().setBaseUrl(baseUrl);
|
|
121
293
|
}
|
|
294
|
+
function setConsumer(consumer) {
|
|
295
|
+
getConfigStore().setConsumer(consumer);
|
|
296
|
+
}
|
|
297
|
+
function setAccessToken(accessToken, expiresAtUtc) {
|
|
298
|
+
getConfigStore().setAccessToken(accessToken, expiresAtUtc);
|
|
299
|
+
}
|
|
300
|
+
function clearAccessToken() {
|
|
301
|
+
getConfigStore().clearAccessToken();
|
|
302
|
+
}
|
|
303
|
+
function setApiCredentials(apiKey, apiSecret) {
|
|
304
|
+
getConfigStore().setApiCredentials(apiKey, apiSecret);
|
|
305
|
+
}
|
|
306
|
+
function clearApiCredentials() {
|
|
307
|
+
getConfigStore().clearApiCredentials();
|
|
308
|
+
}
|
|
309
|
+
function clearAuth() {
|
|
310
|
+
getConfigStore().clearAuth();
|
|
311
|
+
}
|
|
312
|
+
function getAuth() {
|
|
313
|
+
return getAuthState();
|
|
314
|
+
}
|
|
315
|
+
function fetchAccessToken2(apiKey, apiSecret) {
|
|
316
|
+
return fetchAccessToken(apiKey, apiSecret);
|
|
317
|
+
}
|
|
318
|
+
function ensureValidAccessToken() {
|
|
319
|
+
return ensureAccessToken();
|
|
320
|
+
}
|
|
122
321
|
|
|
123
322
|
// src/apiRequest.js
|
|
124
323
|
function buildQuery(params) {
|
|
@@ -135,10 +334,18 @@ function buildQuery(params) {
|
|
|
135
334
|
return q ? `?${q}` : "";
|
|
136
335
|
}
|
|
137
336
|
async function request(baseUrl, fetchFn, path, options = {}) {
|
|
138
|
-
const { method = "GET", headers = {}, body, query, skipContentType } = options;
|
|
337
|
+
const { method = "GET", headers = {}, body, query, skipContentType, skipAuth } = options;
|
|
139
338
|
const url = `${String(baseUrl).replace(/\/+$/, "")}${path}${buildQuery(query)}`;
|
|
140
339
|
const reqHeaders = { ...headers };
|
|
141
340
|
if (!skipContentType && typeof body === "string") reqHeaders["Content-Type"] = "application/json";
|
|
341
|
+
if (!skipAuth) {
|
|
342
|
+
const store = getConfigStore();
|
|
343
|
+
if (!reqHeaders["Consumer"] && store.consumer) reqHeaders["Consumer"] = store.consumer;
|
|
344
|
+
const { accessToken } = getAuthState();
|
|
345
|
+
if (!reqHeaders["Authorization"] && accessToken) {
|
|
346
|
+
reqHeaders["Authorization"] = `Bearer ${accessToken}`;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
142
349
|
const res = await fetchFn(url, { method, headers: reqHeaders, body });
|
|
143
350
|
const text = await res.text();
|
|
144
351
|
let data;
|
|
@@ -151,23 +358,35 @@ async function request(baseUrl, fetchFn, path, options = {}) {
|
|
|
151
358
|
data.status = "failure";
|
|
152
359
|
data.message = data.message ?? `HTTP ${res.status}`;
|
|
153
360
|
}
|
|
361
|
+
data._httpStatus = res.status;
|
|
154
362
|
return data;
|
|
155
363
|
}
|
|
156
|
-
function
|
|
364
|
+
function mergeRequestHeaders(env, opts) {
|
|
157
365
|
const headers = { ...opts.headers || {} };
|
|
158
|
-
|
|
366
|
+
const store = getConfigStore();
|
|
367
|
+
if (!headers["Consumer"] && ((env == null ? void 0 : env.consumer) || store.consumer)) {
|
|
368
|
+
headers["Consumer"] = (env == null ? void 0 : env.consumer) || store.consumer;
|
|
369
|
+
}
|
|
370
|
+
const { accessToken } = getAuthState();
|
|
371
|
+
if (!headers["Authorization"] && accessToken) {
|
|
372
|
+
headers["Authorization"] = `Bearer ${accessToken}`;
|
|
373
|
+
}
|
|
159
374
|
return { ...opts, headers };
|
|
160
375
|
}
|
|
376
|
+
async function executeRequest(baseUrl, fetchFn, path, opts) {
|
|
377
|
+
if (!opts.skipAuth) await ensureAccessToken();
|
|
378
|
+
return request(baseUrl, fetchFn, path, mergeRequestHeaders(null, opts));
|
|
379
|
+
}
|
|
161
380
|
function createRequestHelpers(self, getEnv12) {
|
|
162
381
|
const env = () => getEnv12(self);
|
|
163
382
|
const baseUrl = () => env().baseUrl;
|
|
164
|
-
const fetchFn = () => env().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
383
|
+
const fetchFn = () => env().fetch ?? getConfigStore().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
165
384
|
throw new Error("fetch not available");
|
|
166
385
|
});
|
|
167
386
|
const req = (path, opts = {}) => {
|
|
168
387
|
const url = baseUrl();
|
|
169
388
|
if (!url) throw new Error("Model env requires baseUrl. Call configure({ baseUrl }) at app startup.");
|
|
170
|
-
return
|
|
389
|
+
return executeRequest(url, fetchFn(), path, opts);
|
|
171
390
|
};
|
|
172
391
|
return {
|
|
173
392
|
req,
|
|
@@ -178,14 +397,14 @@ function createRequestHelpers(self, getEnv12) {
|
|
|
178
397
|
function createRequestHelpersFromEnv(env) {
|
|
179
398
|
const e = env ?? getConfig();
|
|
180
399
|
if (!e) throw new Error("Env required. Pass env to the method or call configure({ baseUrl }) at app startup.");
|
|
181
|
-
const baseUrl = () => e == null ? void 0 : e.baseUrl;
|
|
182
|
-
const fetchFn = () => (e == null ? void 0 : e.fetch) ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
400
|
+
const baseUrl = () => (e == null ? void 0 : e.baseUrl) ?? getConfigStore().baseUrl;
|
|
401
|
+
const fetchFn = () => (e == null ? void 0 : e.fetch) ?? getConfigStore().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
183
402
|
throw new Error("fetch not available");
|
|
184
403
|
});
|
|
185
404
|
const req = (path, opts = {}) => {
|
|
186
405
|
const url = baseUrl();
|
|
187
406
|
if (!url) throw new Error("Env requires baseUrl. Call configure({ baseUrl }) at app startup.");
|
|
188
|
-
return
|
|
407
|
+
return executeRequest(url, fetchFn(), path, opts);
|
|
189
408
|
};
|
|
190
409
|
return {
|
|
191
410
|
env: e,
|
|
@@ -1300,6 +1519,15 @@ var ParticipantModel = import_mobx_state_tree11.types.model("Participant", {
|
|
|
1300
1519
|
if (res.status === "success" && res.data) (0, import_mobx_state_tree11.applySnapshot)(self, mapFromApi2(res.data));
|
|
1301
1520
|
return res;
|
|
1302
1521
|
},
|
|
1522
|
+
/** GET participant/getbyemail – fetch by companyKey + email on this snapshot */
|
|
1523
|
+
async getByEmail() {
|
|
1524
|
+
const res = await reqGet("/participant/getbyemail", {
|
|
1525
|
+
email: self.email,
|
|
1526
|
+
company_key: self.companyKey
|
|
1527
|
+
});
|
|
1528
|
+
if (res.status === "success" && res.data) (0, import_mobx_state_tree11.applySnapshot)(self, mapFromApi2(res.data));
|
|
1529
|
+
return res;
|
|
1530
|
+
},
|
|
1303
1531
|
/** POST participant/save – save participant (add or update) */
|
|
1304
1532
|
async save() {
|
|
1305
1533
|
const payload = toPayload(self);
|
|
@@ -1415,6 +1643,14 @@ function mapCalendarFromApi2(d) {
|
|
|
1415
1643
|
modifiedOn: pick2("modifiedOn", "ModifiedOn", "modified_on") ?? null
|
|
1416
1644
|
};
|
|
1417
1645
|
}
|
|
1646
|
+
ParticipantModel.getByEmail = async (email, companyKey) => {
|
|
1647
|
+
const { reqGet } = createRequestHelpersFromEnv(getConfig());
|
|
1648
|
+
const res = await reqGet("/participant/getbyemail", { email, company_key: companyKey });
|
|
1649
|
+
if (res.status === "success" && res.data) {
|
|
1650
|
+
return ParticipantModel.create(mapFromApi2(res.data), { env: getConfig() });
|
|
1651
|
+
}
|
|
1652
|
+
return null;
|
|
1653
|
+
};
|
|
1418
1654
|
ParticipantModel.get = async (participantId) => {
|
|
1419
1655
|
const { reqGet } = createRequestHelpersFromEnv(getConfig());
|
|
1420
1656
|
const res = await reqGet("/participant/get", { participant_id: participantId });
|
|
@@ -1593,6 +1829,7 @@ SettingModel.save = async (payload) => {
|
|
|
1593
1829
|
SettingModel.uploadLogo = async (calendarId, file) => {
|
|
1594
1830
|
const cfg = getConfig();
|
|
1595
1831
|
if (!(cfg == null ? void 0 : cfg.baseUrl)) throw new Error("Configure baseUrl before uploadLogo");
|
|
1832
|
+
await ensureAccessToken();
|
|
1596
1833
|
const fetchFn = cfg.fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
1597
1834
|
throw new Error("fetch not available");
|
|
1598
1835
|
});
|
|
@@ -1602,6 +1839,7 @@ SettingModel.uploadLogo = async (calendarId, file) => {
|
|
|
1602
1839
|
formData.append("file", file);
|
|
1603
1840
|
const res = await fetchFn(url, {
|
|
1604
1841
|
method: "POST",
|
|
1842
|
+
headers: buildAuthHeaders(),
|
|
1605
1843
|
body: formData
|
|
1606
1844
|
});
|
|
1607
1845
|
const text = await res.text();
|
|
@@ -1724,6 +1962,7 @@ AssetModel.upload = async (file, opts = {}) => {
|
|
|
1724
1962
|
if (!file) return { status: "failure", message: "file is required" };
|
|
1725
1963
|
const category = opts.category != null ? String(opts.category).trim() : "";
|
|
1726
1964
|
if (!category) return { status: "failure", message: "category is required" };
|
|
1965
|
+
await ensureAccessToken();
|
|
1727
1966
|
const fetchFn = cfg.fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
1728
1967
|
throw new Error("fetch not available");
|
|
1729
1968
|
});
|
|
@@ -1738,8 +1977,7 @@ AssetModel.upload = async (file, opts = {}) => {
|
|
|
1738
1977
|
if (opts.calendarId != null && String(opts.calendarId).trim() !== "") {
|
|
1739
1978
|
formData.append("calendar_id", String(opts.calendarId).trim());
|
|
1740
1979
|
}
|
|
1741
|
-
const headers =
|
|
1742
|
-
if (cfg.consumer) headers.Consumer = cfg.consumer;
|
|
1980
|
+
const headers = buildAuthHeaders();
|
|
1743
1981
|
if (opts.consumer != null && String(opts.consumer).trim() !== "") {
|
|
1744
1982
|
headers.Consumer = String(opts.consumer).trim();
|
|
1745
1983
|
}
|
|
@@ -2782,6 +3020,7 @@ function createRootStore(initialState = {}) {
|
|
|
2782
3020
|
CompanyModel,
|
|
2783
3021
|
ConfigModel,
|
|
2784
3022
|
CustomFieldModel,
|
|
3023
|
+
DEFAULT_TOKEN_REFRESH_SKEW_MS,
|
|
2785
3024
|
DayOfWeek,
|
|
2786
3025
|
EmailProvider,
|
|
2787
3026
|
EventModel,
|
|
@@ -2796,12 +3035,25 @@ function createRootStore(initialState = {}) {
|
|
|
2796
3035
|
RecurringFrequency,
|
|
2797
3036
|
RootStore,
|
|
2798
3037
|
SettingModel,
|
|
3038
|
+
TOKEN_PATH,
|
|
2799
3039
|
TimeFrameModel,
|
|
2800
3040
|
TimeSlotModel,
|
|
2801
3041
|
Unit,
|
|
3042
|
+
buildAuthHeaders,
|
|
3043
|
+
clearAccessToken,
|
|
3044
|
+
clearApiCredentials,
|
|
3045
|
+
clearAuth,
|
|
2802
3046
|
configure,
|
|
2803
3047
|
createRootStore,
|
|
3048
|
+
ensureValidAccessToken,
|
|
3049
|
+
fetchAccessToken,
|
|
3050
|
+
getAuth,
|
|
2804
3051
|
getConfig,
|
|
2805
3052
|
getConfigStore,
|
|
2806
|
-
|
|
3053
|
+
isAccessTokenExpired,
|
|
3054
|
+
requestAccessToken,
|
|
3055
|
+
setAccessToken,
|
|
3056
|
+
setApiCredentials,
|
|
3057
|
+
setBaseUrl,
|
|
3058
|
+
setConsumer
|
|
2807
3059
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -8,6 +8,10 @@ var ConfigModel = types.model("Config", {
|
|
|
8
8
|
consumer: types.optional(types.string, "")
|
|
9
9
|
}).volatile(() => ({
|
|
10
10
|
fetch: void 0,
|
|
11
|
+
accessToken: void 0,
|
|
12
|
+
tokenExpiresAt: void 0,
|
|
13
|
+
apiKey: void 0,
|
|
14
|
+
apiSecret: void 0,
|
|
11
15
|
getDefaultOffset: () => -(/* @__PURE__ */ new Date()).getTimezoneOffset()
|
|
12
16
|
})).actions((self) => ({
|
|
13
17
|
setBaseUrl(url) {
|
|
@@ -22,19 +26,57 @@ var ConfigModel = types.model("Config", {
|
|
|
22
26
|
setGetDefaultOffset(fn) {
|
|
23
27
|
self.getDefaultOffset = fn;
|
|
24
28
|
},
|
|
29
|
+
setAccessToken(token, expiresAtUtc = void 0) {
|
|
30
|
+
self.accessToken = token ? String(token) : void 0;
|
|
31
|
+
self.tokenExpiresAt = expiresAtUtc != null && expiresAtUtc !== "" ? String(expiresAtUtc) : void 0;
|
|
32
|
+
},
|
|
33
|
+
clearAccessToken() {
|
|
34
|
+
self.accessToken = void 0;
|
|
35
|
+
self.tokenExpiresAt = void 0;
|
|
36
|
+
},
|
|
37
|
+
setApiCredentials(apiKey, apiSecret) {
|
|
38
|
+
self.apiKey = apiKey != null ? String(apiKey) : void 0;
|
|
39
|
+
self.apiSecret = apiSecret != null ? String(apiSecret) : void 0;
|
|
40
|
+
},
|
|
41
|
+
clearApiCredentials() {
|
|
42
|
+
self.apiKey = void 0;
|
|
43
|
+
self.apiSecret = void 0;
|
|
44
|
+
},
|
|
45
|
+
clearAuth() {
|
|
46
|
+
self.clearAccessToken();
|
|
47
|
+
self.clearApiCredentials();
|
|
48
|
+
},
|
|
25
49
|
configure(env) {
|
|
26
50
|
if (env.baseUrl != null) self.baseUrl = env.baseUrl;
|
|
27
51
|
if (env.consumer != null) self.consumer = env.consumer;
|
|
28
52
|
if (env.fetch != null) self.fetch = env.fetch;
|
|
29
53
|
if (env.getDefaultOffset != null) self.getDefaultOffset = env.getDefaultOffset;
|
|
54
|
+
if (env.accessToken != null) {
|
|
55
|
+
self.setAccessToken(
|
|
56
|
+
env.accessToken,
|
|
57
|
+
env.expiresAtUtc ?? env.tokenExpiresAt ?? env.expires_at_utc
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
if (env.apiKey != null || env.api_key != null) {
|
|
61
|
+
self.apiKey = String(env.apiKey ?? env.api_key);
|
|
62
|
+
}
|
|
63
|
+
if (env.apiSecret != null || env.api_secret != null) {
|
|
64
|
+
self.apiSecret = String(env.apiSecret ?? env.api_secret);
|
|
65
|
+
}
|
|
30
66
|
}
|
|
31
67
|
})).views((self) => ({
|
|
68
|
+
get hasApiCredentials() {
|
|
69
|
+
return Boolean(self.apiKey && self.apiSecret);
|
|
70
|
+
},
|
|
32
71
|
getEnv() {
|
|
33
72
|
return {
|
|
34
73
|
baseUrl: self.baseUrl || void 0,
|
|
35
74
|
consumer: self.consumer || void 0,
|
|
36
75
|
fetch: self.fetch,
|
|
37
|
-
getDefaultOffset: self.getDefaultOffset
|
|
76
|
+
getDefaultOffset: self.getDefaultOffset,
|
|
77
|
+
accessToken: self.accessToken,
|
|
78
|
+
tokenExpiresAt: self.tokenExpiresAt,
|
|
79
|
+
hasApiCredentials: self.hasApiCredentials
|
|
38
80
|
};
|
|
39
81
|
}
|
|
40
82
|
}));
|
|
@@ -45,6 +87,122 @@ function getConfigStore() {
|
|
|
45
87
|
}
|
|
46
88
|
var ConfigModel_default = ConfigModel;
|
|
47
89
|
|
|
90
|
+
// src/apiAuth.js
|
|
91
|
+
var TOKEN_PATH = "/Api/Auth/Token";
|
|
92
|
+
var DEFAULT_TOKEN_REFRESH_SKEW_MS = 6e4;
|
|
93
|
+
function defaultFetch() {
|
|
94
|
+
if (typeof fetch === "undefined") {
|
|
95
|
+
throw new Error("fetch not available");
|
|
96
|
+
}
|
|
97
|
+
return fetch;
|
|
98
|
+
}
|
|
99
|
+
function pickTokenPayload(data) {
|
|
100
|
+
if (!data || typeof data !== "object") return null;
|
|
101
|
+
const nested = data.data && typeof data.data === "object" ? data.data : data;
|
|
102
|
+
const accessToken = nested.access_token ?? nested.accessToken ?? nested.AccessToken ?? null;
|
|
103
|
+
if (!accessToken) return null;
|
|
104
|
+
const expiresAtUtc = nested.expires_at_utc ?? nested.expiresAtUtc ?? nested.ExpiresAtUtc ?? null;
|
|
105
|
+
return {
|
|
106
|
+
accessToken: String(accessToken),
|
|
107
|
+
expiresAtUtc: expiresAtUtc != null ? String(expiresAtUtc) : null,
|
|
108
|
+
tokenType: nested.token_type ?? nested.tokenType ?? "Bearer"
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function isAccessTokenExpired(expiresAtUtc, skewMs = DEFAULT_TOKEN_REFRESH_SKEW_MS) {
|
|
112
|
+
if (expiresAtUtc == null || expiresAtUtc === "") return false;
|
|
113
|
+
const expMs = new Date(expiresAtUtc).getTime();
|
|
114
|
+
if (Number.isNaN(expMs)) return false;
|
|
115
|
+
return Date.now() >= expMs - skewMs;
|
|
116
|
+
}
|
|
117
|
+
function getAuthState() {
|
|
118
|
+
const store = getConfigStore();
|
|
119
|
+
return {
|
|
120
|
+
accessToken: store.accessToken ?? void 0,
|
|
121
|
+
tokenExpiresAt: store.tokenExpiresAt ?? void 0,
|
|
122
|
+
apiKey: store.apiKey ?? void 0,
|
|
123
|
+
apiSecret: store.apiSecret ?? void 0,
|
|
124
|
+
hasApiCredentials: Boolean(store.apiKey && store.apiSecret)
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async function requestAccessToken(apiKey, apiSecret, opts = {}) {
|
|
128
|
+
const store = getConfigStore();
|
|
129
|
+
const baseUrl = opts.baseUrl ?? store.baseUrl;
|
|
130
|
+
if (!baseUrl) {
|
|
131
|
+
return { status: "failure", message: "baseUrl required. Call configure({ baseUrl }) first." };
|
|
132
|
+
}
|
|
133
|
+
const key = apiKey != null ? String(apiKey).trim() : "";
|
|
134
|
+
const secret = apiSecret != null ? String(apiSecret).trim() : "";
|
|
135
|
+
if (!key || !secret) {
|
|
136
|
+
return { status: "failure", message: "api_key and api_secret are required" };
|
|
137
|
+
}
|
|
138
|
+
const fetchFn = opts.fetch ?? store.fetch ?? defaultFetch();
|
|
139
|
+
const url = `${String(baseUrl).replace(/\/+$/, "")}${TOKEN_PATH}`;
|
|
140
|
+
const httpRes = await fetchFn(url, {
|
|
141
|
+
method: "POST",
|
|
142
|
+
headers: { "Content-Type": "application/json" },
|
|
143
|
+
body: JSON.stringify({ api_key: key, api_secret: secret })
|
|
144
|
+
});
|
|
145
|
+
const text = await httpRes.text();
|
|
146
|
+
let body;
|
|
147
|
+
try {
|
|
148
|
+
body = JSON.parse(text);
|
|
149
|
+
} catch {
|
|
150
|
+
body = { status: "failure", message: text || httpRes.statusText };
|
|
151
|
+
}
|
|
152
|
+
if (!httpRes.ok && body.status !== "failure") {
|
|
153
|
+
body.status = "failure";
|
|
154
|
+
body.message = body.message ?? `HTTP ${httpRes.status}`;
|
|
155
|
+
}
|
|
156
|
+
if (body.status !== "success") {
|
|
157
|
+
return {
|
|
158
|
+
status: "failure",
|
|
159
|
+
message: body.message ?? "Failed to obtain access token"
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const parsed = pickTokenPayload(body);
|
|
163
|
+
if (!parsed) {
|
|
164
|
+
return { status: "failure", message: "Token response missing access_token" };
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
status: "success",
|
|
168
|
+
accessToken: parsed.accessToken,
|
|
169
|
+
expiresAtUtc: parsed.expiresAtUtc,
|
|
170
|
+
tokenType: parsed.tokenType
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
async function fetchAccessToken(apiKey, apiSecret) {
|
|
174
|
+
const store = getConfigStore();
|
|
175
|
+
const key = apiKey ?? store.apiKey;
|
|
176
|
+
const secret = apiSecret ?? store.apiSecret;
|
|
177
|
+
const result = await requestAccessToken(key, secret);
|
|
178
|
+
if (result.status === "success") {
|
|
179
|
+
store.setAccessToken(result.accessToken, result.expiresAtUtc);
|
|
180
|
+
}
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
async function ensureAccessToken() {
|
|
184
|
+
const store = getConfigStore();
|
|
185
|
+
const { accessToken, tokenExpiresAt, apiKey, apiSecret } = getAuthState();
|
|
186
|
+
if (accessToken && !isAccessTokenExpired(tokenExpiresAt)) {
|
|
187
|
+
return accessToken;
|
|
188
|
+
}
|
|
189
|
+
if (apiKey && apiSecret) {
|
|
190
|
+
const result = await fetchAccessToken(apiKey, apiSecret);
|
|
191
|
+
if (result.status === "success") return result.accessToken;
|
|
192
|
+
}
|
|
193
|
+
return accessToken;
|
|
194
|
+
}
|
|
195
|
+
function buildAuthHeaders(extra = {}) {
|
|
196
|
+
const headers = { ...extra };
|
|
197
|
+
const { consumer } = getConfigStore();
|
|
198
|
+
const { accessToken } = getAuthState();
|
|
199
|
+
if (!headers.Consumer && consumer) headers.Consumer = consumer;
|
|
200
|
+
if (!headers.Authorization && accessToken) {
|
|
201
|
+
headers.Authorization = `Bearer ${accessToken}`;
|
|
202
|
+
}
|
|
203
|
+
return headers;
|
|
204
|
+
}
|
|
205
|
+
|
|
48
206
|
// src/config.js
|
|
49
207
|
function configure(env) {
|
|
50
208
|
const store = getConfigStore();
|
|
@@ -58,6 +216,33 @@ function getConfig() {
|
|
|
58
216
|
function setBaseUrl(baseUrl) {
|
|
59
217
|
getConfigStore().setBaseUrl(baseUrl);
|
|
60
218
|
}
|
|
219
|
+
function setConsumer(consumer) {
|
|
220
|
+
getConfigStore().setConsumer(consumer);
|
|
221
|
+
}
|
|
222
|
+
function setAccessToken(accessToken, expiresAtUtc) {
|
|
223
|
+
getConfigStore().setAccessToken(accessToken, expiresAtUtc);
|
|
224
|
+
}
|
|
225
|
+
function clearAccessToken() {
|
|
226
|
+
getConfigStore().clearAccessToken();
|
|
227
|
+
}
|
|
228
|
+
function setApiCredentials(apiKey, apiSecret) {
|
|
229
|
+
getConfigStore().setApiCredentials(apiKey, apiSecret);
|
|
230
|
+
}
|
|
231
|
+
function clearApiCredentials() {
|
|
232
|
+
getConfigStore().clearApiCredentials();
|
|
233
|
+
}
|
|
234
|
+
function clearAuth() {
|
|
235
|
+
getConfigStore().clearAuth();
|
|
236
|
+
}
|
|
237
|
+
function getAuth() {
|
|
238
|
+
return getAuthState();
|
|
239
|
+
}
|
|
240
|
+
function fetchAccessToken2(apiKey, apiSecret) {
|
|
241
|
+
return fetchAccessToken(apiKey, apiSecret);
|
|
242
|
+
}
|
|
243
|
+
function ensureValidAccessToken() {
|
|
244
|
+
return ensureAccessToken();
|
|
245
|
+
}
|
|
61
246
|
|
|
62
247
|
// src/apiRequest.js
|
|
63
248
|
function buildQuery(params) {
|
|
@@ -74,10 +259,18 @@ function buildQuery(params) {
|
|
|
74
259
|
return q ? `?${q}` : "";
|
|
75
260
|
}
|
|
76
261
|
async function request(baseUrl, fetchFn, path, options = {}) {
|
|
77
|
-
const { method = "GET", headers = {}, body, query, skipContentType } = options;
|
|
262
|
+
const { method = "GET", headers = {}, body, query, skipContentType, skipAuth } = options;
|
|
78
263
|
const url = `${String(baseUrl).replace(/\/+$/, "")}${path}${buildQuery(query)}`;
|
|
79
264
|
const reqHeaders = { ...headers };
|
|
80
265
|
if (!skipContentType && typeof body === "string") reqHeaders["Content-Type"] = "application/json";
|
|
266
|
+
if (!skipAuth) {
|
|
267
|
+
const store = getConfigStore();
|
|
268
|
+
if (!reqHeaders["Consumer"] && store.consumer) reqHeaders["Consumer"] = store.consumer;
|
|
269
|
+
const { accessToken } = getAuthState();
|
|
270
|
+
if (!reqHeaders["Authorization"] && accessToken) {
|
|
271
|
+
reqHeaders["Authorization"] = `Bearer ${accessToken}`;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
81
274
|
const res = await fetchFn(url, { method, headers: reqHeaders, body });
|
|
82
275
|
const text = await res.text();
|
|
83
276
|
let data;
|
|
@@ -90,23 +283,35 @@ async function request(baseUrl, fetchFn, path, options = {}) {
|
|
|
90
283
|
data.status = "failure";
|
|
91
284
|
data.message = data.message ?? `HTTP ${res.status}`;
|
|
92
285
|
}
|
|
286
|
+
data._httpStatus = res.status;
|
|
93
287
|
return data;
|
|
94
288
|
}
|
|
95
|
-
function
|
|
289
|
+
function mergeRequestHeaders(env, opts) {
|
|
96
290
|
const headers = { ...opts.headers || {} };
|
|
97
|
-
|
|
291
|
+
const store = getConfigStore();
|
|
292
|
+
if (!headers["Consumer"] && ((env == null ? void 0 : env.consumer) || store.consumer)) {
|
|
293
|
+
headers["Consumer"] = (env == null ? void 0 : env.consumer) || store.consumer;
|
|
294
|
+
}
|
|
295
|
+
const { accessToken } = getAuthState();
|
|
296
|
+
if (!headers["Authorization"] && accessToken) {
|
|
297
|
+
headers["Authorization"] = `Bearer ${accessToken}`;
|
|
298
|
+
}
|
|
98
299
|
return { ...opts, headers };
|
|
99
300
|
}
|
|
301
|
+
async function executeRequest(baseUrl, fetchFn, path, opts) {
|
|
302
|
+
if (!opts.skipAuth) await ensureAccessToken();
|
|
303
|
+
return request(baseUrl, fetchFn, path, mergeRequestHeaders(null, opts));
|
|
304
|
+
}
|
|
100
305
|
function createRequestHelpers(self, getEnv12) {
|
|
101
306
|
const env = () => getEnv12(self);
|
|
102
307
|
const baseUrl = () => env().baseUrl;
|
|
103
|
-
const fetchFn = () => env().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
308
|
+
const fetchFn = () => env().fetch ?? getConfigStore().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
104
309
|
throw new Error("fetch not available");
|
|
105
310
|
});
|
|
106
311
|
const req = (path, opts = {}) => {
|
|
107
312
|
const url = baseUrl();
|
|
108
313
|
if (!url) throw new Error("Model env requires baseUrl. Call configure({ baseUrl }) at app startup.");
|
|
109
|
-
return
|
|
314
|
+
return executeRequest(url, fetchFn(), path, opts);
|
|
110
315
|
};
|
|
111
316
|
return {
|
|
112
317
|
req,
|
|
@@ -117,14 +322,14 @@ function createRequestHelpers(self, getEnv12) {
|
|
|
117
322
|
function createRequestHelpersFromEnv(env) {
|
|
118
323
|
const e = env ?? getConfig();
|
|
119
324
|
if (!e) throw new Error("Env required. Pass env to the method or call configure({ baseUrl }) at app startup.");
|
|
120
|
-
const baseUrl = () => e == null ? void 0 : e.baseUrl;
|
|
121
|
-
const fetchFn = () => (e == null ? void 0 : e.fetch) ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
325
|
+
const baseUrl = () => (e == null ? void 0 : e.baseUrl) ?? getConfigStore().baseUrl;
|
|
326
|
+
const fetchFn = () => (e == null ? void 0 : e.fetch) ?? getConfigStore().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
122
327
|
throw new Error("fetch not available");
|
|
123
328
|
});
|
|
124
329
|
const req = (path, opts = {}) => {
|
|
125
330
|
const url = baseUrl();
|
|
126
331
|
if (!url) throw new Error("Env requires baseUrl. Call configure({ baseUrl }) at app startup.");
|
|
127
|
-
return
|
|
332
|
+
return executeRequest(url, fetchFn(), path, opts);
|
|
128
333
|
};
|
|
129
334
|
return {
|
|
130
335
|
env: e,
|
|
@@ -1239,6 +1444,15 @@ var ParticipantModel = types11.model("Participant", {
|
|
|
1239
1444
|
if (res.status === "success" && res.data) applySnapshot3(self, mapFromApi2(res.data));
|
|
1240
1445
|
return res;
|
|
1241
1446
|
},
|
|
1447
|
+
/** GET participant/getbyemail – fetch by companyKey + email on this snapshot */
|
|
1448
|
+
async getByEmail() {
|
|
1449
|
+
const res = await reqGet("/participant/getbyemail", {
|
|
1450
|
+
email: self.email,
|
|
1451
|
+
company_key: self.companyKey
|
|
1452
|
+
});
|
|
1453
|
+
if (res.status === "success" && res.data) applySnapshot3(self, mapFromApi2(res.data));
|
|
1454
|
+
return res;
|
|
1455
|
+
},
|
|
1242
1456
|
/** POST participant/save – save participant (add or update) */
|
|
1243
1457
|
async save() {
|
|
1244
1458
|
const payload = toPayload(self);
|
|
@@ -1354,6 +1568,14 @@ function mapCalendarFromApi2(d) {
|
|
|
1354
1568
|
modifiedOn: pick2("modifiedOn", "ModifiedOn", "modified_on") ?? null
|
|
1355
1569
|
};
|
|
1356
1570
|
}
|
|
1571
|
+
ParticipantModel.getByEmail = async (email, companyKey) => {
|
|
1572
|
+
const { reqGet } = createRequestHelpersFromEnv(getConfig());
|
|
1573
|
+
const res = await reqGet("/participant/getbyemail", { email, company_key: companyKey });
|
|
1574
|
+
if (res.status === "success" && res.data) {
|
|
1575
|
+
return ParticipantModel.create(mapFromApi2(res.data), { env: getConfig() });
|
|
1576
|
+
}
|
|
1577
|
+
return null;
|
|
1578
|
+
};
|
|
1357
1579
|
ParticipantModel.get = async (participantId) => {
|
|
1358
1580
|
const { reqGet } = createRequestHelpersFromEnv(getConfig());
|
|
1359
1581
|
const res = await reqGet("/participant/get", { participant_id: participantId });
|
|
@@ -1532,6 +1754,7 @@ SettingModel.save = async (payload) => {
|
|
|
1532
1754
|
SettingModel.uploadLogo = async (calendarId, file) => {
|
|
1533
1755
|
const cfg = getConfig();
|
|
1534
1756
|
if (!(cfg == null ? void 0 : cfg.baseUrl)) throw new Error("Configure baseUrl before uploadLogo");
|
|
1757
|
+
await ensureAccessToken();
|
|
1535
1758
|
const fetchFn = cfg.fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
1536
1759
|
throw new Error("fetch not available");
|
|
1537
1760
|
});
|
|
@@ -1541,6 +1764,7 @@ SettingModel.uploadLogo = async (calendarId, file) => {
|
|
|
1541
1764
|
formData.append("file", file);
|
|
1542
1765
|
const res = await fetchFn(url, {
|
|
1543
1766
|
method: "POST",
|
|
1767
|
+
headers: buildAuthHeaders(),
|
|
1544
1768
|
body: formData
|
|
1545
1769
|
});
|
|
1546
1770
|
const text = await res.text();
|
|
@@ -1663,6 +1887,7 @@ AssetModel.upload = async (file, opts = {}) => {
|
|
|
1663
1887
|
if (!file) return { status: "failure", message: "file is required" };
|
|
1664
1888
|
const category = opts.category != null ? String(opts.category).trim() : "";
|
|
1665
1889
|
if (!category) return { status: "failure", message: "category is required" };
|
|
1890
|
+
await ensureAccessToken();
|
|
1666
1891
|
const fetchFn = cfg.fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
|
|
1667
1892
|
throw new Error("fetch not available");
|
|
1668
1893
|
});
|
|
@@ -1677,8 +1902,7 @@ AssetModel.upload = async (file, opts = {}) => {
|
|
|
1677
1902
|
if (opts.calendarId != null && String(opts.calendarId).trim() !== "") {
|
|
1678
1903
|
formData.append("calendar_id", String(opts.calendarId).trim());
|
|
1679
1904
|
}
|
|
1680
|
-
const headers =
|
|
1681
|
-
if (cfg.consumer) headers.Consumer = cfg.consumer;
|
|
1905
|
+
const headers = buildAuthHeaders();
|
|
1682
1906
|
if (opts.consumer != null && String(opts.consumer).trim() !== "") {
|
|
1683
1907
|
headers.Consumer = String(opts.consumer).trim();
|
|
1684
1908
|
}
|
|
@@ -2720,6 +2944,7 @@ export {
|
|
|
2720
2944
|
Company_default as CompanyModel,
|
|
2721
2945
|
ConfigModel_default as ConfigModel,
|
|
2722
2946
|
CustomField_default as CustomFieldModel,
|
|
2947
|
+
DEFAULT_TOKEN_REFRESH_SKEW_MS,
|
|
2723
2948
|
DayOfWeek,
|
|
2724
2949
|
EmailProvider,
|
|
2725
2950
|
Event_default as EventModel,
|
|
@@ -2734,12 +2959,25 @@ export {
|
|
|
2734
2959
|
RecurringFrequency,
|
|
2735
2960
|
RootStore,
|
|
2736
2961
|
Setting_default as SettingModel,
|
|
2962
|
+
TOKEN_PATH,
|
|
2737
2963
|
TimeFrame_default as TimeFrameModel,
|
|
2738
2964
|
TimeSlot_default as TimeSlotModel,
|
|
2739
2965
|
Unit,
|
|
2966
|
+
buildAuthHeaders,
|
|
2967
|
+
clearAccessToken,
|
|
2968
|
+
clearApiCredentials,
|
|
2969
|
+
clearAuth,
|
|
2740
2970
|
configure,
|
|
2741
2971
|
createRootStore,
|
|
2972
|
+
ensureValidAccessToken,
|
|
2973
|
+
fetchAccessToken2 as fetchAccessToken,
|
|
2974
|
+
getAuth,
|
|
2742
2975
|
getConfig,
|
|
2743
2976
|
getConfigStore,
|
|
2744
|
-
|
|
2977
|
+
isAccessTokenExpired,
|
|
2978
|
+
requestAccessToken,
|
|
2979
|
+
setAccessToken,
|
|
2980
|
+
setApiCredentials,
|
|
2981
|
+
setBaseUrl,
|
|
2982
|
+
setConsumer
|
|
2745
2983
|
};
|