@dragonmastery/dragoncore-vue 0.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/LICENSE +75 -0
- package/dist/AppLink-CHMMrSFI.js +54 -0
- package/dist/AppLink-CHMMrSFI.js.map +1 -0
- package/dist/Appearance-BfPdKMXw.js +70 -0
- package/dist/Appearance-BfPdKMXw.js.map +1 -0
- package/dist/Appearance-C3WguxT-.js +3 -0
- package/dist/ChangePasswordPage-Btu5lf-r.js +86 -0
- package/dist/ChangePasswordPage-Btu5lf-r.js.map +1 -0
- package/dist/ChangePasswordPage-mBBuQMkT.js +6 -0
- package/dist/CreateTeamForm-n2ut93vM.js +43 -0
- package/dist/CreateTeamMemberForm-CcH3AxNL.js +43 -0
- package/dist/CreateUserPage-CDrGuW9B.js +6 -0
- package/dist/CreateUserPage-Cmx8xjjv.js +76 -0
- package/dist/CreateUserPage-Cmx8xjjv.js.map +1 -0
- package/dist/CreditBalanceDashboard-DLz0ioP3.js +43 -0
- package/dist/CreditManagement-D3q5S-qc.js +43 -0
- package/dist/CustomerCreateSupportTicketForm-Ci7QYkG-.js +43 -0
- package/dist/CustomerEditSupportTicketForm-Dd5ZB74k.js +159 -0
- package/dist/CustomerEditSupportTicketForm-Dd5ZB74k.js.map +1 -0
- package/dist/CustomerEditSupportTicketForm-lLchVjnw.js +9 -0
- package/dist/CustomerSupportTicketAttachmentsTab-gBrVO97t.js +43 -0
- package/dist/CustomerSupportTicketCustomerNotesTab-D0jhzbOY.js +8 -0
- package/dist/CustomerSupportTicketCustomerNotesTab-D1aa9It7.js +23 -0
- package/dist/CustomerSupportTicketCustomerNotesTab-D1aa9It7.js.map +1 -0
- package/dist/CustomerSupportTicketHistoryTab-BNTf8EZq.js +6 -0
- package/dist/CustomerSupportTicketHistoryTab-CFYN_Sa4.js +17 -0
- package/dist/CustomerSupportTicketHistoryTab-CFYN_Sa4.js.map +1 -0
- package/dist/CustomerSupportTicketList-BkOzFxMP.js +6 -0
- package/dist/CustomerSupportTicketList-C2nUPawb.js +166 -0
- package/dist/CustomerSupportTicketList-C2nUPawb.js.map +1 -0
- package/dist/CustomerSupportTicketParent-2mONd9kL.js +66 -0
- package/dist/CustomerSupportTicketParent-2mONd9kL.js.map +1 -0
- package/dist/CustomerSupportTicketParent-N8ko1yFE.js +7 -0
- package/dist/CustomerSupportTicketSuccess-w_-9NXT4.js +43 -0
- package/dist/CustomerViewSupportTicket-CVwNH0lS.js +11 -0
- package/dist/CustomerViewSupportTicket-tZkxragu.js +363 -0
- package/dist/CustomerViewSupportTicket-tZkxragu.js.map +1 -0
- package/dist/EditTeamForm-BioqiTWE.js +43 -0
- package/dist/EditTeamMemberForm-DCq0Gsn_.js +7 -0
- package/dist/EditTeamMemberForm-ru4WgLz-.js +169 -0
- package/dist/EditTeamMemberForm-ru4WgLz-.js.map +1 -0
- package/dist/EditUserPage-BxJ5QvIM.js +112 -0
- package/dist/EditUserPage-BxJ5QvIM.js.map +1 -0
- package/dist/EditUserPage-XOBuxUxd.js +7 -0
- package/dist/FieldsetSection-CsHN38_o.js +27 -0
- package/dist/FieldsetSection-CsHN38_o.js.map +1 -0
- package/dist/ForgotPassword-CpqvcSFg.js +7 -0
- package/dist/ForgotPassword-CqhenzUG.js +73 -0
- package/dist/ForgotPassword-CqhenzUG.js.map +1 -0
- package/dist/InlineAttachments-I39rOvip.js +1351 -0
- package/dist/InlineAttachments-I39rOvip.js.map +1 -0
- package/dist/LoginForm-AM0qkfbU.js +7 -0
- package/dist/LoginForm-_PZ51Uwe.js +116 -0
- package/dist/LoginForm-_PZ51Uwe.js.map +1 -0
- package/dist/Logout-BMjiqHnS.js +38 -0
- package/dist/Logout-BMjiqHnS.js.map +1 -0
- package/dist/Logout-BfiBjlaH.js +6 -0
- package/dist/NoteList-C0hRPNMO.js +497 -0
- package/dist/NoteList-C0hRPNMO.js.map +1 -0
- package/dist/NotificationEmailsPage-BjRqtW95.js +141 -0
- package/dist/NotificationEmailsPage-BjRqtW95.js.map +1 -0
- package/dist/NotificationEmailsPage-bx-9rg3x.js +7 -0
- package/dist/ResetPassword-BQLkR9TZ.js +43 -0
- package/dist/Signup-CnCcQlB8.js +7 -0
- package/dist/Signup-c2-_yMOM.js +106 -0
- package/dist/Signup-c2-_yMOM.js.map +1 -0
- package/dist/StaffCreateSupportTicketForm-ChVFDJdA.js +43 -0
- package/dist/StaffEditSupportTicketForm-DY1Zkf5k.js +9 -0
- package/dist/StaffEditSupportTicketForm-DuUKuIGg.js +263 -0
- package/dist/StaffEditSupportTicketForm-DuUKuIGg.js.map +1 -0
- package/dist/StaffSupportTicketAttachmentsTab-DpDXsHXP.js +43 -0
- package/dist/StaffSupportTicketCustomerNotesTab-CusqQV2-.js +23 -0
- package/dist/StaffSupportTicketCustomerNotesTab-CusqQV2-.js.map +1 -0
- package/dist/StaffSupportTicketCustomerNotesTab-rbJHJ0_V.js +8 -0
- package/dist/StaffSupportTicketHistoryTab-D24myEm3.js +17 -0
- package/dist/StaffSupportTicketHistoryTab-D24myEm3.js.map +1 -0
- package/dist/StaffSupportTicketHistoryTab-nmVma5vp.js +6 -0
- package/dist/StaffSupportTicketInternalNotesTab-D8HM--dp.js +23 -0
- package/dist/StaffSupportTicketInternalNotesTab-D8HM--dp.js.map +1 -0
- package/dist/StaffSupportTicketInternalNotesTab-DihYd5XI.js +8 -0
- package/dist/StaffSupportTicketList-DelptSmK.js +43 -0
- package/dist/StaffSupportTicketParent-BCrj3ckV.js +7 -0
- package/dist/StaffSupportTicketParent-Cx1buQZw.js +66 -0
- package/dist/StaffSupportTicketParent-Cx1buQZw.js.map +1 -0
- package/dist/StaffSupportTicketSuccess-BYxtY5wZ.js +43 -0
- package/dist/StaffSupportTicketWorkflowTab-BrDDBeK9.js +9 -0
- package/dist/StaffSupportTicketWorkflowTab-DmVTPzxS.js +1234 -0
- package/dist/StaffSupportTicketWorkflowTab-DmVTPzxS.js.map +1 -0
- package/dist/SupportTicketHistoryTab-CLMopA7a.js +220 -0
- package/dist/SupportTicketHistoryTab-CLMopA7a.js.map +1 -0
- package/dist/SupportTicketStatusBadge-YdZzjvkh.js +163 -0
- package/dist/SupportTicketStatusBadge-YdZzjvkh.js.map +1 -0
- package/dist/TeamAttachmentsTab-BxUpTWYh.js +43 -0
- package/dist/TeamHistoryTab-CUCT9MRG.js +5 -0
- package/dist/TeamHistoryTab-gB3H2KZv.js +219 -0
- package/dist/TeamHistoryTab-gB3H2KZv.js.map +1 -0
- package/dist/TeamList-By6pzWm5.js +43 -0
- package/dist/TeamMemberList-CYV9fWEb.js +43 -0
- package/dist/TeamMemberParent-CVvGqpxD.js +43 -0
- package/dist/TeamMembersTab-4gmnP9sD.js +21 -0
- package/dist/TeamMembersTab-4gmnP9sD.js.map +1 -0
- package/dist/TeamMembersTab-CpE9BaCi.js +3 -0
- package/dist/TeamNotesTab-pfXTDhg6.js +23 -0
- package/dist/TeamNotesTab-pfXTDhg6.js.map +1 -0
- package/dist/TeamNotesTab-u4cDC67X.js +8 -0
- package/dist/TeamParent-BxT1KubK.js +43 -0
- package/dist/UserListPage-DsQdH2Sm.js +4 -0
- package/dist/UserListPage-WU56KiWj.js +153 -0
- package/dist/UserListPage-WU56KiWj.js.map +1 -0
- package/dist/UserProfilePage-B73JhjUu.js +7 -0
- package/dist/UserProfilePage-BtLUY1kt.js +125 -0
- package/dist/UserProfilePage-BtLUY1kt.js.map +1 -0
- package/dist/ViewTeam-DzX-obEl.js +43 -0
- package/dist/ViewTeamMember-PF6S_4Pb.js +43 -0
- package/dist/ZiniaContainer-C7c7Vwkh.js +18 -0
- package/dist/ZiniaContainer-C7c7Vwkh.js.map +1 -0
- package/dist/convertToLocalDateTime-D4IoNvRj.js +111 -0
- package/dist/convertToLocalDateTime-D4IoNvRj.js.map +1 -0
- package/dist/creditValueFormatter-DftEzu8d.js +128 -0
- package/dist/creditValueFormatter-DftEzu8d.js.map +1 -0
- package/dist/displayIdFormatter-Dz900Awr.js +13 -0
- package/dist/displayIdFormatter-Dz900Awr.js.map +1 -0
- package/dist/index.d.ts +6068 -0
- package/dist/index.js +45 -0
- package/dist/src-o5fMIo5_.js +6649 -0
- package/dist/src-o5fMIo5_.js.map +1 -0
- package/dist/useBreadcrumbs-DmgSucoe.js +41 -0
- package/dist/useBreadcrumbs-DmgSucoe.js.map +1 -0
- package/dist/useMutation-CFwe7H9j.js +50 -0
- package/dist/useMutation-CFwe7H9j.js.map +1 -0
- package/dist/useQuery-p7oJO7OD.js +107 -0
- package/dist/useQuery-p7oJO7OD.js.map +1 -0
- package/dist/useQueryCache-ByayvZgZ.js +254 -0
- package/dist/useQueryCache-ByayvZgZ.js.map +1 -0
- package/dist/useRpcAuth-BLlRSHy8.js +722 -0
- package/dist/useRpcAuth-BLlRSHy8.js.map +1 -0
- package/package.json +62 -0
- package/src/daisyui.css +63 -0
|
@@ -0,0 +1,722 @@
|
|
|
1
|
+
import { computed, readonly, ref, watchEffect } from "vue";
|
|
2
|
+
import { newHttpBatchRpcSession } from "capnweb";
|
|
3
|
+
import { jwtDecode } from "jwt-decode";
|
|
4
|
+
import { defineStore } from "pinia";
|
|
5
|
+
|
|
6
|
+
//#region src/composables/useEnv.ts
|
|
7
|
+
function useEnv() {
|
|
8
|
+
return readonly({
|
|
9
|
+
restApiClient: {
|
|
10
|
+
apiUrl: import.meta.env.VITE_API_CLIENT_URL,
|
|
11
|
+
authOptions: {
|
|
12
|
+
userDetails: import.meta.env.VITE_CF_APP_USER_DETAILS,
|
|
13
|
+
accessToken: import.meta.env.VITE_CF_APP_ACCESS_TOKEN,
|
|
14
|
+
decodedToken: import.meta.env.VITE_CF_APP_DECODED_TOKEN
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
allowUserSignup: import.meta.env.VITE_ALLOW_USER_SIGNUP === "true",
|
|
18
|
+
maxAttachmentFileSize: import.meta.env.VITE_MAX_ATTACHMENT_FILE_SIZE_MB ? parseInt(import.meta.env.VITE_MAX_ATTACHMENT_FILE_SIZE_MB, 10) * 1024 * 1024 : 50 * 1024 * 1024
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/utils/logger.ts
|
|
24
|
+
/**
|
|
25
|
+
* Log levels as string literals
|
|
26
|
+
*/
|
|
27
|
+
const LOG_LEVEL = {
|
|
28
|
+
NONE: "NONE",
|
|
29
|
+
ERROR: "ERROR",
|
|
30
|
+
WARN: "WARN",
|
|
31
|
+
INFO: "INFO",
|
|
32
|
+
DEBUG: "DEBUG",
|
|
33
|
+
TRACE: "TRACE"
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Logger utility that conditionally logs based on configured log level
|
|
37
|
+
*/
|
|
38
|
+
var Logger = class Logger {
|
|
39
|
+
static instance;
|
|
40
|
+
logLevel = LOG_LEVEL.INFO;
|
|
41
|
+
constructor() {
|
|
42
|
+
this.setLogLevelFromEnv();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get the singleton logger instance
|
|
46
|
+
*/
|
|
47
|
+
static getInstance() {
|
|
48
|
+
if (!Logger.instance) Logger.instance = new Logger();
|
|
49
|
+
return Logger.instance;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Set the log level from environment variables
|
|
53
|
+
*/
|
|
54
|
+
setLogLevelFromEnv() {
|
|
55
|
+
const logLevelStr = import.meta.env.VITE_LOG_LEVEL || "";
|
|
56
|
+
if (logLevelStr) switch (logLevelStr.toUpperCase()) {
|
|
57
|
+
case "NONE":
|
|
58
|
+
this.logLevel = LOG_LEVEL.NONE;
|
|
59
|
+
break;
|
|
60
|
+
case "ERROR":
|
|
61
|
+
this.logLevel = LOG_LEVEL.ERROR;
|
|
62
|
+
break;
|
|
63
|
+
case "WARN":
|
|
64
|
+
this.logLevel = LOG_LEVEL.WARN;
|
|
65
|
+
break;
|
|
66
|
+
case "INFO":
|
|
67
|
+
this.logLevel = LOG_LEVEL.INFO;
|
|
68
|
+
break;
|
|
69
|
+
case "DEBUG":
|
|
70
|
+
this.logLevel = LOG_LEVEL.DEBUG;
|
|
71
|
+
break;
|
|
72
|
+
case "TRACE":
|
|
73
|
+
this.logLevel = LOG_LEVEL.TRACE;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
else this.logLevel = import.meta.env.DEV === true || import.meta.env.MODE === "development" || import.meta.env.VITE_APP_ENV === "local" || import.meta.env.VITE_APP_ENV === "dev" ? LOG_LEVEL.DEBUG : LOG_LEVEL.INFO;
|
|
77
|
+
console.log(`[Logger] Log level set to: ${this.logLevel.toUpperCase()}`);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Set the log level programmatically
|
|
81
|
+
*/
|
|
82
|
+
setLogLevel(level) {
|
|
83
|
+
this.logLevel = level;
|
|
84
|
+
console.log(`[Logger] Log level changed to: ${this.logLevel.toUpperCase()}`);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get the current log level
|
|
88
|
+
*/
|
|
89
|
+
getLogLevel() {
|
|
90
|
+
return this.logLevel;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Determine if a message at the given level should be logged
|
|
94
|
+
* based on the current log level setting
|
|
95
|
+
*/
|
|
96
|
+
shouldLog(level) {
|
|
97
|
+
const levelPriority = {
|
|
98
|
+
[LOG_LEVEL.NONE]: 0,
|
|
99
|
+
[LOG_LEVEL.ERROR]: 1,
|
|
100
|
+
[LOG_LEVEL.WARN]: 2,
|
|
101
|
+
[LOG_LEVEL.INFO]: 3,
|
|
102
|
+
[LOG_LEVEL.DEBUG]: 4,
|
|
103
|
+
[LOG_LEVEL.TRACE]: 5
|
|
104
|
+
};
|
|
105
|
+
return levelPriority[this.logLevel] >= levelPriority[level];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Log a trace message (most verbose)
|
|
109
|
+
*/
|
|
110
|
+
trace(message, ...args) {
|
|
111
|
+
if (this.shouldLog(LOG_LEVEL.TRACE)) console.log(`[TRACE] ${message}`, ...args);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Log a debug message
|
|
115
|
+
*/
|
|
116
|
+
debug(message, ...args) {
|
|
117
|
+
if (this.shouldLog(LOG_LEVEL.DEBUG)) console.debug(`[DEBUG] ${message}`, ...args);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Log an info message
|
|
121
|
+
*/
|
|
122
|
+
info(message, ...args) {
|
|
123
|
+
if (this.shouldLog(LOG_LEVEL.INFO)) console.log(`[INFO] ${message}`, ...args);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Log a warning message
|
|
127
|
+
*/
|
|
128
|
+
warn(message, ...args) {
|
|
129
|
+
if (this.shouldLog(LOG_LEVEL.WARN)) console.warn(`[WARN] ${message}`, ...args);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Log an error message
|
|
133
|
+
*/
|
|
134
|
+
error(message, ...args) {
|
|
135
|
+
if (this.shouldLog(LOG_LEVEL.ERROR)) console.error(`[ERROR] ${message}`, ...args);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
const logger = Logger.getInstance();
|
|
139
|
+
/**
|
|
140
|
+
* Conditionally log based on per-instance debug flag and global log level
|
|
141
|
+
* - If debug === false: never log (explicitly disabled)
|
|
142
|
+
* - If debug === true: always log (bypasses global log level)
|
|
143
|
+
* - If debug === undefined: use global log level (default behavior)
|
|
144
|
+
*/
|
|
145
|
+
function logIfEnabled(level, message, debug, ...args) {
|
|
146
|
+
if (debug === false) return;
|
|
147
|
+
if (debug === true) {
|
|
148
|
+
const prefix = `[${level.toUpperCase()}]`;
|
|
149
|
+
if (level === "error") console.error(prefix, message, ...args);
|
|
150
|
+
else if (level === "warn") console.warn(prefix, message, ...args);
|
|
151
|
+
else console.log(prefix, message, ...args);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (level === "debug") logger.debug(message, ...args);
|
|
155
|
+
else if (level === "error") logger.error(message, ...args);
|
|
156
|
+
else if (level === "warn") logger.warn(message, ...args);
|
|
157
|
+
else logger.info(message, ...args);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/composables/useUserSessionStore.ts
|
|
162
|
+
/**
|
|
163
|
+
* Generic user session store for Dragoncore applications.
|
|
164
|
+
*
|
|
165
|
+
* Responsibilities:
|
|
166
|
+
* - Store and manage user session state (accessToken, userSession)
|
|
167
|
+
* - Sync with localStorage
|
|
168
|
+
* - Provide token refresh capability
|
|
169
|
+
* - Provide token expiration checking
|
|
170
|
+
*
|
|
171
|
+
* Note: This store is intentionally NOT generic on TApi.
|
|
172
|
+
* The refresh method uses createAppBatch which can be typed on a per-call basis.
|
|
173
|
+
*/
|
|
174
|
+
const useUserSessionStore = defineStore("userSession", () => {
|
|
175
|
+
const env = useEnv().restApiClient;
|
|
176
|
+
const userSession = ref(null);
|
|
177
|
+
const accessToken = ref(null);
|
|
178
|
+
const decodedToken = ref(null);
|
|
179
|
+
const isRefreshing = ref(false);
|
|
180
|
+
const debug = ref(false);
|
|
181
|
+
watchEffect(() => {
|
|
182
|
+
if (!env.authOptions.accessToken || !env.authOptions.userDetails) {
|
|
183
|
+
logIfEnabled("error", "[UserSessionStore] localStorage keys are undefined - check environment variables", debug.value);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const storedAccessToken = localStorage.getItem(env.authOptions.accessToken);
|
|
187
|
+
if (storedAccessToken !== accessToken.value) {
|
|
188
|
+
accessToken.value = storedAccessToken;
|
|
189
|
+
try {
|
|
190
|
+
decodedToken.value = storedAccessToken ? jwtDecode(storedAccessToken) : null;
|
|
191
|
+
} catch (error) {
|
|
192
|
+
logIfEnabled("error", "[UserSessionStore] Failed to decode access token:", debug.value, error);
|
|
193
|
+
decodedToken.value = null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const storedUserSession = localStorage.getItem(env.authOptions.userDetails);
|
|
197
|
+
if (storedUserSession) try {
|
|
198
|
+
const sessionData = jwtDecode(storedUserSession).details;
|
|
199
|
+
if (JSON.stringify(sessionData) !== JSON.stringify(userSession.value)) userSession.value = sessionData;
|
|
200
|
+
} catch (error) {
|
|
201
|
+
logIfEnabled("error", "[UserSessionStore] Failed to decode user session:", debug.value, error);
|
|
202
|
+
if (userSession.value !== null) userSession.value = null;
|
|
203
|
+
}
|
|
204
|
+
else if (userSession.value !== null) userSession.value = null;
|
|
205
|
+
});
|
|
206
|
+
const currentSession = computed(() => {
|
|
207
|
+
if (!userSession.value) return null;
|
|
208
|
+
return userSession.value;
|
|
209
|
+
});
|
|
210
|
+
const clientHeaders = computed(() => ({
|
|
211
|
+
"Content-Type": "application/json",
|
|
212
|
+
...accessToken.value ? { Authorization: `Bearer ${accessToken.value}` } : {}
|
|
213
|
+
}));
|
|
214
|
+
/**
|
|
215
|
+
* Refresh the access token using the refresh token (from HTTP-only cookie).
|
|
216
|
+
* This method uses createAppBatch directly to avoid circular dependencies.
|
|
217
|
+
*
|
|
218
|
+
* @returns true if refresh was successful, false otherwise
|
|
219
|
+
*/
|
|
220
|
+
async function refreshToken() {
|
|
221
|
+
try {
|
|
222
|
+
if (isRefreshing.value) {
|
|
223
|
+
logIfEnabled("debug", "[RefreshToken] Token refresh already in progress", debug.value);
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
isRefreshing.value = true;
|
|
227
|
+
logIfEnabled("debug", "[RefreshToken] Starting token refresh", debug.value);
|
|
228
|
+
if (userSession.value) {
|
|
229
|
+
if (isTokenExpired().refreshTokenExpired) {
|
|
230
|
+
logIfEnabled("warn", "[RefreshToken] Refresh token is already expired, cannot refresh", debug.value);
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const tokens = await createAppBatch$1({ credentials: "include" }).userSessions.refreshToken();
|
|
235
|
+
if (!tokens || !tokens.access_token || !tokens.user_details_token) {
|
|
236
|
+
logIfEnabled("error", "[RefreshToken] Invalid response from refresh endpoint", debug.value);
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
setSession(tokens.user_details_token);
|
|
240
|
+
setAccessToken(tokens.access_token);
|
|
241
|
+
if (!(!!userSession.value && !!accessToken.value)) {
|
|
242
|
+
logIfEnabled("error", "[RefreshToken] Failed to update all session components", debug.value);
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
if (isTokenExpired().accessTokenExpired) {
|
|
246
|
+
logIfEnabled("error", "[RefreshToken] New access token is already expired!", debug.value);
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
logIfEnabled("debug", "[RefreshToken] Token refresh successful", debug.value);
|
|
250
|
+
return true;
|
|
251
|
+
} catch (refreshError) {
|
|
252
|
+
logIfEnabled("error", "[RefreshToken] Token refresh failed:", debug.value, refreshError);
|
|
253
|
+
return false;
|
|
254
|
+
} finally {
|
|
255
|
+
isRefreshing.value = false;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Check if tokens are expired
|
|
260
|
+
*/
|
|
261
|
+
function isTokenExpired() {
|
|
262
|
+
const now = Date.now() / 1e3;
|
|
263
|
+
return {
|
|
264
|
+
accessTokenExpired: decodedToken.value?.exp ? decodedToken.value.exp < now : true,
|
|
265
|
+
refreshTokenExpired: userSession.value?.expires_at ? new Date(userSession.value.expires_at).getTime() / 1e3 < now : true
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Set the user session (stored in localStorage as JWT)
|
|
270
|
+
*/
|
|
271
|
+
function setSession(token) {
|
|
272
|
+
if (token) try {
|
|
273
|
+
userSession.value = jwtDecode(token).details;
|
|
274
|
+
localStorage.setItem(env.authOptions.userDetails, token);
|
|
275
|
+
logIfEnabled("debug", "[SetSession] User session set successfully", debug.value);
|
|
276
|
+
} catch (error) {
|
|
277
|
+
logger.error("[SetSession] Failed to decode user session token:", error);
|
|
278
|
+
userSession.value = null;
|
|
279
|
+
localStorage.removeItem(env.authOptions.userDetails);
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
userSession.value = null;
|
|
283
|
+
localStorage.removeItem(env.authOptions.userDetails);
|
|
284
|
+
logIfEnabled("debug", "[SetSession] User session cleared", debug.value);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Set the access token (stored in localStorage as JWT string)
|
|
289
|
+
*/
|
|
290
|
+
function setAccessToken(token) {
|
|
291
|
+
if (token) try {
|
|
292
|
+
const decoded = jwtDecode(token);
|
|
293
|
+
accessToken.value = token;
|
|
294
|
+
decodedToken.value = decoded;
|
|
295
|
+
localStorage.setItem(env.authOptions.accessToken, token);
|
|
296
|
+
logIfEnabled("debug", "[SetAccessToken] Access token set successfully", debug.value);
|
|
297
|
+
} catch (error) {
|
|
298
|
+
logger.error("[SetAccessToken] Failed to decode access token:", error);
|
|
299
|
+
accessToken.value = null;
|
|
300
|
+
decodedToken.value = null;
|
|
301
|
+
localStorage.removeItem(env.authOptions.accessToken);
|
|
302
|
+
if (userSession.value) {
|
|
303
|
+
userSession.value = null;
|
|
304
|
+
localStorage.removeItem(env.authOptions.userDetails);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
accessToken.value = null;
|
|
309
|
+
decodedToken.value = null;
|
|
310
|
+
localStorage.removeItem(env.authOptions.accessToken);
|
|
311
|
+
logIfEnabled("debug", "[SetAccessToken] Access token cleared", debug.value);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Clear all session data
|
|
316
|
+
*/
|
|
317
|
+
function clearSession() {
|
|
318
|
+
setSession(null);
|
|
319
|
+
setAccessToken(null);
|
|
320
|
+
}
|
|
321
|
+
return {
|
|
322
|
+
userSession,
|
|
323
|
+
currentSession,
|
|
324
|
+
accessToken,
|
|
325
|
+
clientHeaders,
|
|
326
|
+
debug,
|
|
327
|
+
setSession,
|
|
328
|
+
setAccessToken,
|
|
329
|
+
clearSession,
|
|
330
|
+
refreshToken,
|
|
331
|
+
isTokenExpired
|
|
332
|
+
};
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/composables/useRpc.ts
|
|
337
|
+
/**
|
|
338
|
+
* Creates a fresh RPC batch session with authentication headers.
|
|
339
|
+
* Each call creates a new session, which is the correct pattern for Cap'n Web RPC.
|
|
340
|
+
*
|
|
341
|
+
* This is the simple version - just creates a batch with auth headers.
|
|
342
|
+
* For advanced features (token refresh, retry, tracked sessions), use executeWithAuth from useRpcAuth.
|
|
343
|
+
*
|
|
344
|
+
* @param options - Optional configuration for the RPC batch
|
|
345
|
+
* @param options.credentials - Credentials mode for the request. Use 'include' for login/logout/refreshToken to allow cookies.
|
|
346
|
+
*/
|
|
347
|
+
function createAppBatch$1(options) {
|
|
348
|
+
const userStore = useUserSessionStore();
|
|
349
|
+
const env = useEnv();
|
|
350
|
+
const headers = {};
|
|
351
|
+
const accessToken = userStore.accessToken;
|
|
352
|
+
if (accessToken) headers["Authorization"] = `Bearer ${accessToken}`;
|
|
353
|
+
return newHttpBatchRpcSession(new Request(env.restApiClient.apiUrl + "/rpc", {
|
|
354
|
+
method: "POST",
|
|
355
|
+
headers,
|
|
356
|
+
credentials: options?.credentials ?? "omit"
|
|
357
|
+
}));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
//#endregion
|
|
361
|
+
//#region src/utils/EnhancedRefreshTokenHandler.ts
|
|
362
|
+
var EnhancedRefreshTokenHandler = class {
|
|
363
|
+
refreshState = {
|
|
364
|
+
isRefreshing: false,
|
|
365
|
+
pendingPromise: null,
|
|
366
|
+
lastAttempt: 0,
|
|
367
|
+
failureCount: 0
|
|
368
|
+
};
|
|
369
|
+
async performRefreshWithStrategies() {
|
|
370
|
+
if (this.refreshState.isRefreshing && this.refreshState.pendingPromise) {
|
|
371
|
+
logger.debug("[EnhancedRefresh] Refresh already in progress, returning existing promise");
|
|
372
|
+
return this.refreshState.pendingPromise;
|
|
373
|
+
}
|
|
374
|
+
this.refreshState.isRefreshing = true;
|
|
375
|
+
this.refreshState.pendingPromise = this.executeRefreshStrategies();
|
|
376
|
+
try {
|
|
377
|
+
const tokens = await this.refreshState.pendingPromise;
|
|
378
|
+
this.refreshState.failureCount = 0;
|
|
379
|
+
logger.debug("[EnhancedRefresh] Refresh completed successfully");
|
|
380
|
+
return tokens;
|
|
381
|
+
} catch (error) {
|
|
382
|
+
this.refreshState.failureCount++;
|
|
383
|
+
logger.error("[EnhancedRefresh] All refresh strategies failed:", error);
|
|
384
|
+
throw error;
|
|
385
|
+
} finally {
|
|
386
|
+
this.refreshState.isRefreshing = false;
|
|
387
|
+
this.refreshState.pendingPromise = null;
|
|
388
|
+
this.refreshState.lastAttempt = Date.now();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
async executeRefreshStrategies() {
|
|
392
|
+
const strategies = [
|
|
393
|
+
{
|
|
394
|
+
name: "Direct RefreshToken",
|
|
395
|
+
execute: () => this.directRefreshToken()
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
name: "Warmup + Retry RefreshToken",
|
|
399
|
+
execute: () => this.warmupAndRetryRefresh()
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
name: "Alternative Domain RefreshToken",
|
|
403
|
+
execute: () => this.alternativeDomainRefresh()
|
|
404
|
+
}
|
|
405
|
+
];
|
|
406
|
+
let lastError;
|
|
407
|
+
let partialSuccess = false;
|
|
408
|
+
for (const [index, strategy] of strategies.entries()) try {
|
|
409
|
+
logger.debug(`[EnhancedRefresh] Attempting strategy ${index + 1}: ${strategy.name}`);
|
|
410
|
+
const tokens = await strategy.execute();
|
|
411
|
+
logger.debug(`[EnhancedRefresh] Strategy ${index + 1} (${strategy.name}) succeeded`);
|
|
412
|
+
return tokens;
|
|
413
|
+
} catch (error) {
|
|
414
|
+
lastError = error;
|
|
415
|
+
logger.debug(`[EnhancedRefresh] Strategy ${index + 1} (${strategy.name}) failed:`, error);
|
|
416
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
417
|
+
if (!errorMessage.includes("No refresh_token") && !errorMessage.includes("REFRESH_TOKEN_EXPIRED")) partialSuccess = true;
|
|
418
|
+
if (index < strategies.length - 1) {
|
|
419
|
+
const backoffDelay = 100 * (index + 1);
|
|
420
|
+
await this.delay(backoffDelay);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (lastError && partialSuccess) {
|
|
424
|
+
const enhancedError = lastError;
|
|
425
|
+
enhancedError.partialSuccess = true;
|
|
426
|
+
enhancedError.isTemporaryFailure = true;
|
|
427
|
+
}
|
|
428
|
+
throw lastError;
|
|
429
|
+
}
|
|
430
|
+
async directRefreshToken() {
|
|
431
|
+
try {
|
|
432
|
+
const tokens = await createAppBatch$1({ credentials: "include" }).userSessions.refreshToken();
|
|
433
|
+
if (!tokens || !tokens.access_token || !tokens.user_details_token) throw new Error("No refresh_token in response");
|
|
434
|
+
return {
|
|
435
|
+
access_token: tokens.access_token,
|
|
436
|
+
user_details_token: tokens.user_details_token
|
|
437
|
+
};
|
|
438
|
+
} catch (error) {
|
|
439
|
+
logger.error("[EnhancedRefresh] Refresh token call failed:", {
|
|
440
|
+
error,
|
|
441
|
+
name: error?.name,
|
|
442
|
+
message: error?.message,
|
|
443
|
+
code: error?.code,
|
|
444
|
+
originalError: error?.originalError,
|
|
445
|
+
stack: error?.stack
|
|
446
|
+
});
|
|
447
|
+
if (error?.name === "InternalServerError" && error?.originalError) throw error.originalError;
|
|
448
|
+
throw error;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
async warmupAndRetryRefresh() {
|
|
452
|
+
logger.debug("[EnhancedRefresh] Warming up cookie context");
|
|
453
|
+
await this.warmupCookieContext();
|
|
454
|
+
await this.delay(150);
|
|
455
|
+
return this.directRefreshToken();
|
|
456
|
+
}
|
|
457
|
+
async alternativeDomainRefresh() {
|
|
458
|
+
await this.warmupCookieContext();
|
|
459
|
+
await this.delay(300);
|
|
460
|
+
return this.directRefreshToken();
|
|
461
|
+
}
|
|
462
|
+
async warmupCookieContext() {
|
|
463
|
+
const warmupStrategies = [
|
|
464
|
+
() => fetch(window.location.origin + "/favicon.ico", {
|
|
465
|
+
method: "HEAD",
|
|
466
|
+
credentials: "include",
|
|
467
|
+
cache: "no-cache",
|
|
468
|
+
mode: "same-origin"
|
|
469
|
+
}),
|
|
470
|
+
() => {
|
|
471
|
+
const rpcEndpoint = useEnv().restApiClient.apiUrl + "/rpc";
|
|
472
|
+
return fetch(rpcEndpoint, {
|
|
473
|
+
method: "OPTIONS",
|
|
474
|
+
credentials: "include",
|
|
475
|
+
cache: "no-cache"
|
|
476
|
+
});
|
|
477
|
+
},
|
|
478
|
+
() => fetch(window.location.origin + "/", {
|
|
479
|
+
method: "HEAD",
|
|
480
|
+
credentials: "include",
|
|
481
|
+
cache: "no-cache",
|
|
482
|
+
mode: "same-origin"
|
|
483
|
+
})
|
|
484
|
+
];
|
|
485
|
+
try {
|
|
486
|
+
await Promise.race(warmupStrategies.map((strategy) => strategy().catch(() => {})));
|
|
487
|
+
logger.debug("[EnhancedRefresh] Cookie context warmed up");
|
|
488
|
+
} catch (error) {
|
|
489
|
+
logger.debug("[EnhancedRefresh] Cookie warmup failed, continuing:", error);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
delay(ms) {
|
|
493
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
494
|
+
}
|
|
495
|
+
shouldAttemptRefresh() {
|
|
496
|
+
return Date.now() - this.refreshState.lastAttempt > Math.min(5e3, 1e3 * Math.pow(2, this.refreshState.failureCount));
|
|
497
|
+
}
|
|
498
|
+
getRefreshState() {
|
|
499
|
+
return { ...this.refreshState };
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
/**
|
|
503
|
+
* Create an instance of the refresh token handler for your API type.
|
|
504
|
+
* Your API must extend AuthenticatableApi (must have userSessions.refreshToken method).
|
|
505
|
+
*
|
|
506
|
+
* @example
|
|
507
|
+
* ```typescript
|
|
508
|
+
* // In your app's setup/config
|
|
509
|
+
* import type { AppApi } from '@follow-zap/shared';
|
|
510
|
+
* import { createRefreshTokenHandler, setRefreshTokenHandler } from '@dragonmastery/dragoncore-vue';
|
|
511
|
+
*
|
|
512
|
+
* // Initialize during app setup
|
|
513
|
+
* setRefreshTokenHandler(createRefreshTokenHandler<AppApi>());
|
|
514
|
+
* ```
|
|
515
|
+
*/
|
|
516
|
+
function createRefreshTokenHandler() {
|
|
517
|
+
return new EnhancedRefreshTokenHandler();
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Global refresh token handler instance.
|
|
521
|
+
* Set this once in your app's initialization.
|
|
522
|
+
*/
|
|
523
|
+
let globalRefreshTokenHandler = null;
|
|
524
|
+
/**
|
|
525
|
+
* Set the global refresh token handler for your application.
|
|
526
|
+
* Call this once during app initialization.
|
|
527
|
+
*/
|
|
528
|
+
function setRefreshTokenHandler(handler) {
|
|
529
|
+
globalRefreshTokenHandler = handler;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Get the global refresh token handler.
|
|
533
|
+
* This is used internally by useQuery and useMutation.
|
|
534
|
+
*
|
|
535
|
+
* @throws Error if handler is not set
|
|
536
|
+
*/
|
|
537
|
+
function getRefreshTokenHandler() {
|
|
538
|
+
if (!globalRefreshTokenHandler) throw new Error("Refresh token handler not initialized. Call setRefreshTokenHandler() in your app initialization.");
|
|
539
|
+
return globalRefreshTokenHandler;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
//#endregion
|
|
543
|
+
//#region src/composables/useRpcAuth.ts
|
|
544
|
+
/**
|
|
545
|
+
* Auth error codes that should trigger token refresh
|
|
546
|
+
*/
|
|
547
|
+
const AUTH_ERROR_CODES = [
|
|
548
|
+
"ACCESS_TOKEN_EXPIRED",
|
|
549
|
+
"ACCESS_TOKEN_REVOKED",
|
|
550
|
+
"NO_ACCESS_TOKEN",
|
|
551
|
+
"NO_REFRESH_TOKEN"
|
|
552
|
+
];
|
|
553
|
+
const BATCH_MODE = {
|
|
554
|
+
batch: "batch",
|
|
555
|
+
tracked: "tracked"
|
|
556
|
+
};
|
|
557
|
+
/**
|
|
558
|
+
* Check if an error is an auth error that should trigger refresh
|
|
559
|
+
*/
|
|
560
|
+
function isAuthError(error) {
|
|
561
|
+
if (!error) return false;
|
|
562
|
+
let actualError = error;
|
|
563
|
+
if (error.name === "InternalServerError" && error.originalError) actualError = error.originalError;
|
|
564
|
+
if (actualError.name === "AuthenticationError" || error.name === "AuthenticationError") return true;
|
|
565
|
+
if (actualError.code && AUTH_ERROR_CODES.includes(actualError.code)) return true;
|
|
566
|
+
if (error.code && AUTH_ERROR_CODES.includes(error.code)) return true;
|
|
567
|
+
const errorMessage = actualError.message || error.message || String(error);
|
|
568
|
+
if (AUTH_ERROR_CODES.some((code) => errorMessage.includes(code))) return true;
|
|
569
|
+
if (errorMessage.includes("Unauthorized") || errorMessage.includes("authentication") || errorMessage.includes("token")) return errorMessage.toLowerCase().includes("expired") || errorMessage.toLowerCase().includes("revoked") || errorMessage.toLowerCase().includes("invalid token") || errorMessage.toLowerCase().includes("no refresh token");
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Global router instance.
|
|
574
|
+
* Set this once in your app's initialization.
|
|
575
|
+
*/
|
|
576
|
+
let globalRouter = null;
|
|
577
|
+
/**
|
|
578
|
+
* Set the global router instance for your application.
|
|
579
|
+
* Call this once during app initialization.
|
|
580
|
+
*/
|
|
581
|
+
function setRouter(router) {
|
|
582
|
+
globalRouter = router;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Get the global router instance.
|
|
586
|
+
* This is used internally by executeWithAuth for navigation.
|
|
587
|
+
*
|
|
588
|
+
* @throws Error if router is not set
|
|
589
|
+
*/
|
|
590
|
+
function getRouter() {
|
|
591
|
+
if (!globalRouter) throw new Error("Router not initialized. Call setRouter() in your app initialization.");
|
|
592
|
+
return globalRouter;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Check if token needs refresh before making a request
|
|
596
|
+
*/
|
|
597
|
+
function needsRefresh(userStore) {
|
|
598
|
+
if (!userStore.accessToken) return true;
|
|
599
|
+
const { accessTokenExpired, refreshTokenExpired } = userStore.isTokenExpired();
|
|
600
|
+
return accessTokenExpired || refreshTokenExpired;
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Attempt to refresh the token using the provided handler
|
|
604
|
+
*/
|
|
605
|
+
async function attemptRefresh(userStore, refreshTokenHandler) {
|
|
606
|
+
const router = getRouter();
|
|
607
|
+
try {
|
|
608
|
+
if (!userStore.currentSession && !userStore.accessToken) return false;
|
|
609
|
+
if (userStore.currentSession) {
|
|
610
|
+
const { refreshTokenExpired } = userStore.isTokenExpired();
|
|
611
|
+
if (refreshTokenExpired) {
|
|
612
|
+
await router.push("/auth/logout");
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
if (!refreshTokenHandler.shouldAttemptRefresh()) return false;
|
|
617
|
+
const tokens = await refreshTokenHandler.performRefreshWithStrategies();
|
|
618
|
+
userStore.setSession(tokens.user_details_token);
|
|
619
|
+
userStore.setAccessToken(tokens.access_token);
|
|
620
|
+
return true;
|
|
621
|
+
} catch (error) {
|
|
622
|
+
console.error("[RPC Auth] Refresh attempt failed:", {
|
|
623
|
+
error,
|
|
624
|
+
name: error?.name,
|
|
625
|
+
message: error?.message,
|
|
626
|
+
code: error?.code,
|
|
627
|
+
originalError: error?.originalError
|
|
628
|
+
});
|
|
629
|
+
const combinedMessage = `${((error?.originalError || error)?.message || error?.message || "").toUpperCase()} ${(error?.originalError?.message || "").toUpperCase()}`;
|
|
630
|
+
if (combinedMessage.includes("NO_REFRESH_TOKEN") || combinedMessage.includes("REFRESH_TOKEN_EXPIRED") || combinedMessage.includes("REFRESH_TOKEN_REVOKED") || combinedMessage.includes("INVALID REFRESH TOKEN") || combinedMessage.includes("NO REFRESH TOKEN FOUND")) await router.push("/auth/logout");
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Create a base RPC batch with current auth token
|
|
636
|
+
*/
|
|
637
|
+
function createAppBatch(options) {
|
|
638
|
+
const userStore = useUserSessionStore();
|
|
639
|
+
const env = useEnv();
|
|
640
|
+
const headers = {};
|
|
641
|
+
const accessToken = userStore.accessToken;
|
|
642
|
+
if (accessToken) headers["Authorization"] = `Bearer ${accessToken}`;
|
|
643
|
+
const request = new Request(env.restApiClient.apiUrl + "/rpc", {
|
|
644
|
+
method: "POST",
|
|
645
|
+
headers,
|
|
646
|
+
credentials: options?.credentials ?? "omit"
|
|
647
|
+
});
|
|
648
|
+
if (options?.mode === BATCH_MODE.batch && !options?.trackedSegment) return newHttpBatchRpcSession(request);
|
|
649
|
+
return createTrackedSession(request, options?.trackedSegment);
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Execute an RPC call with automatic auth handling
|
|
653
|
+
* - Checks token expiration before call
|
|
654
|
+
* - Handles auth errors and retries after refresh
|
|
655
|
+
*
|
|
656
|
+
* @param fn - Function that receives an RPC batch and returns a promise
|
|
657
|
+
* @param options - Configuration options
|
|
658
|
+
* @param options.refreshTokenHandler - Instance of refresh token handler for your API type
|
|
659
|
+
*/
|
|
660
|
+
async function executeWithAuth(fn, options) {
|
|
661
|
+
const userStore = useUserSessionStore();
|
|
662
|
+
const router = getRouter();
|
|
663
|
+
if (options?.skipAuthCheck) return await fn(createAppBatch(options));
|
|
664
|
+
if (needsRefresh(userStore)) {
|
|
665
|
+
if (!await attemptRefresh(userStore, options.refreshTokenHandler)) {
|
|
666
|
+
if (!userStore.accessToken) {
|
|
667
|
+
await router.push("/auth/logout");
|
|
668
|
+
throw new Error("Authentication required");
|
|
669
|
+
}
|
|
670
|
+
} else if (!userStore.accessToken) {
|
|
671
|
+
await router.push("/auth/logout");
|
|
672
|
+
throw new Error("Token refresh failed to set access token");
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
try {
|
|
676
|
+
if (!userStore.accessToken) {
|
|
677
|
+
await router.push("/auth/logout");
|
|
678
|
+
throw new Error("Authentication required");
|
|
679
|
+
}
|
|
680
|
+
return await fn(createAppBatch(options));
|
|
681
|
+
} catch (error) {
|
|
682
|
+
if (isAuthError(error)) {
|
|
683
|
+
if (await attemptRefresh(userStore, options.refreshTokenHandler)) return await fn(createAppBatch(options));
|
|
684
|
+
await router.push("/auth/logout");
|
|
685
|
+
throw error;
|
|
686
|
+
}
|
|
687
|
+
throw error;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Create a tracked RPC session that appends method names to URL
|
|
692
|
+
*/
|
|
693
|
+
function createTrackedSession(request, trackedSegment) {
|
|
694
|
+
const baseUrl = request.url;
|
|
695
|
+
if (trackedSegment) return newHttpBatchRpcSession(new Request(`${baseUrl}/${trackedSegment}`, {
|
|
696
|
+
method: request.method,
|
|
697
|
+
headers: request.headers,
|
|
698
|
+
credentials: request.credentials
|
|
699
|
+
}));
|
|
700
|
+
function createProxy(path = []) {
|
|
701
|
+
return new Proxy(() => {}, {
|
|
702
|
+
get(_target, prop) {
|
|
703
|
+
if (typeof prop === "string") return createProxy([...path, prop]);
|
|
704
|
+
},
|
|
705
|
+
apply(_target, _thisArg, args) {
|
|
706
|
+
const fullPath = path.join(".");
|
|
707
|
+
let method = newHttpBatchRpcSession(new Request(`${baseUrl}/${fullPath}`, {
|
|
708
|
+
method: request.method,
|
|
709
|
+
headers: request.headers,
|
|
710
|
+
credentials: request.credentials
|
|
711
|
+
}));
|
|
712
|
+
for (const segment of path) method = method[segment];
|
|
713
|
+
return method(...args);
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
return createProxy();
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
//#endregion
|
|
721
|
+
export { EnhancedRefreshTokenHandler as a, setRefreshTokenHandler as c, Logger as d, logIfEnabled as f, setRouter as i, useUserSessionStore as l, useEnv as m, createAppBatch as n, createRefreshTokenHandler as o, logger as p, executeWithAuth as r, getRefreshTokenHandler as s, BATCH_MODE as t, LOG_LEVEL as u };
|
|
722
|
+
//# sourceMappingURL=useRpcAuth-BLlRSHy8.js.map
|