@nuxt-customer-portal/planning 0.4.6 → 0.4.7
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Keep Google Calendar settings recoverable when stored authorization expires, and offer reconnection after a rejected token refresh.
|
|
8
|
+
- @nuxt-customer-portal/core@0.4.7
|
|
9
|
+
- @nuxt-customer-portal/products@0.4.7
|
|
10
|
+
- @nuxt-customer-portal/ui@0.4.7
|
|
11
|
+
|
|
3
12
|
## 0.4.6
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
|
@@ -46,7 +46,23 @@ async function load() {
|
|
|
46
46
|
busyCalendarIds: settings.busyCalendarIds,
|
|
47
47
|
writeCalendarId: settings.writeCalendarId || ''
|
|
48
48
|
})
|
|
49
|
-
|
|
49
|
+
if (googleConnected.value) {
|
|
50
|
+
try {
|
|
51
|
+
calendars.value = await api.calendars()
|
|
52
|
+
} catch (calendarError) {
|
|
53
|
+
const refreshedSettings = await api.provider().catch(() => null)
|
|
54
|
+
const connectionStillHealthy = refreshedSettings?.connections.some(
|
|
55
|
+
(connection) => connection.provider === 'google' && connection.healthy
|
|
56
|
+
)
|
|
57
|
+
if (connectionStillHealthy !== false) {
|
|
58
|
+
throw calendarError
|
|
59
|
+
}
|
|
60
|
+
googleConnected.value = false
|
|
61
|
+
calendars.value = []
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
calendars.value = []
|
|
65
|
+
}
|
|
50
66
|
ready.value = true
|
|
51
67
|
} catch {
|
|
52
68
|
error.value = t('planning.loadError')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuxt-customer-portal/planning",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"homepage": "https://nuxt-customer-portal.com",
|
|
6
6
|
"repository": {
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
"drizzle-orm": "^0.45.2",
|
|
41
41
|
"pg": "^8.22.0",
|
|
42
42
|
"zod": "^4.4.3",
|
|
43
|
-
"@nuxt-customer-portal/core": "^0.4.
|
|
44
|
-
"@nuxt-customer-portal/
|
|
45
|
-
"@nuxt-customer-portal/
|
|
43
|
+
"@nuxt-customer-portal/core": "^0.4.7",
|
|
44
|
+
"@nuxt-customer-portal/ui": "^0.4.7",
|
|
45
|
+
"@nuxt-customer-portal/products": "^0.4.7"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"nuxt": "^4.5.1",
|
package/portal.manifest.mjs
CHANGED
package/server/utils/adapters.ts
CHANGED
|
@@ -54,6 +54,14 @@ export interface MeetingAdapter {
|
|
|
54
54
|
): Promise<{ id: string; url: string }>
|
|
55
55
|
remove(storeId: string, userId: string, id: string): Promise<void>
|
|
56
56
|
}
|
|
57
|
+
class AuthorizationExchangeError extends Error {
|
|
58
|
+
constructor(
|
|
59
|
+
public readonly status: number,
|
|
60
|
+
message = `Calendar/meeting authorization failed (${status})`
|
|
61
|
+
) {
|
|
62
|
+
super(message)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
57
65
|
export function oauthConfig(provider: 'google' | 'zoom') {
|
|
58
66
|
const prefix = `PLANNING_${provider.toUpperCase()}`
|
|
59
67
|
const clientId = process.env[`${prefix}_CLIENT_ID`],
|
|
@@ -84,7 +92,7 @@ export async function exchange(provider: 'google' | 'zoom', params: Record<strin
|
|
|
84
92
|
}
|
|
85
93
|
)
|
|
86
94
|
if (!response.ok) {
|
|
87
|
-
throw new
|
|
95
|
+
throw new AuthorizationExchangeError(response.status)
|
|
88
96
|
}
|
|
89
97
|
const value = (await response.json()) as { access_token: string; refresh_token?: string; expires_in: number }
|
|
90
98
|
if (!value.access_token || !value.expires_in) {
|
|
@@ -111,7 +119,21 @@ async function accessToken(storeId: string, userId: string, provider: 'google' |
|
|
|
111
119
|
}
|
|
112
120
|
let credentials = decrypt<Credentials>(connection.credentials)
|
|
113
121
|
if (credentials.expiresAt < Date.now() + 60000) {
|
|
114
|
-
|
|
122
|
+
try {
|
|
123
|
+
credentials = await exchange(provider, {
|
|
124
|
+
grant_type: 'refresh_token',
|
|
125
|
+
refresh_token: credentials.refresh_token
|
|
126
|
+
})
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if (error instanceof AuthorizationExchangeError && [400, 401].includes(error.status)) {
|
|
129
|
+
await rows(
|
|
130
|
+
'UPDATE planning.connection SET healthy=false,error=$4 WHERE store_id=$1 AND user_id=$2 AND provider=$3',
|
|
131
|
+
[storeId, userId, provider, 'Authorization expired; reconnect'],
|
|
132
|
+
client
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
throw error
|
|
136
|
+
}
|
|
115
137
|
await rows(
|
|
116
138
|
'UPDATE planning.connection SET credentials=$4,error=NULL WHERE store_id=$1 AND user_id=$2 AND provider=$3',
|
|
117
139
|
[storeId, userId, provider, encrypt(credentials)],
|