@lukoweb/apitogo-module-user-panel 0.2.22 → 0.2.24
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/package.json +2 -2
- package/src/account-paths.ts +42 -42
- package/src/components/AccountLayout.tsx +119 -119
- package/src/hooks/useAccountSettings.ts +93 -93
- package/src/index.tsx +14 -14
- package/src/pages/DefaultDashboardPage.tsx +126 -126
- package/src/pages/DefaultPlansPage.tsx +62 -62
- package/src/pages/DefaultProfilePage.tsx +79 -79
- package/src/pages/SettingsPage.tsx +279 -279
- package/src/renderModuleElement.tsx +18 -18
- package/src/userPanelModule.tsx +152 -152
- package/src/utils/dev-portal-api.ts +125 -125
package/src/userPanelModule.tsx
CHANGED
|
@@ -1,152 +1,152 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ResolvedDashboardModule,
|
|
3
|
-
ResolvedPlansModule,
|
|
4
|
-
ResolvedUserManagementModule,
|
|
5
|
-
ResolvedUserPanelModule,
|
|
6
|
-
} from "@lukoweb/apitogo";
|
|
7
|
-
import {
|
|
8
|
-
CreditCardIcon,
|
|
9
|
-
LayoutDashboardIcon,
|
|
10
|
-
SettingsIcon,
|
|
11
|
-
} from "@lukoweb/apitogo/icons";
|
|
12
|
-
import type {
|
|
13
|
-
ProfileMenuPlugin,
|
|
14
|
-
ApitogoPlugin,
|
|
15
|
-
} from "@lukoweb/apitogo/plugins";
|
|
16
|
-
import type { ComponentType, ReactNode } from "react";
|
|
17
|
-
import type { RouteObject } from "react-router";
|
|
18
|
-
import { toAccountChildPath } from "./account-paths.js";
|
|
19
|
-
import { AccountLayout } from "./components/AccountLayout.js";
|
|
20
|
-
import { DefaultDashboardPage } from "./pages/DefaultDashboardPage.js";
|
|
21
|
-
import { DefaultPlansPage } from "./pages/DefaultPlansPage.js";
|
|
22
|
-
import { SettingsPage } from "./pages/SettingsPage.js";
|
|
23
|
-
import { renderModuleElement } from "./renderModuleElement.js";
|
|
24
|
-
|
|
25
|
-
type DashboardModuleOptions = ResolvedDashboardModule & {
|
|
26
|
-
component?: ComponentType | ReactNode;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
type UserManagementModuleOptions = ResolvedUserManagementModule & {
|
|
30
|
-
component?: ComponentType | ReactNode;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
type PlansModuleOptions = ResolvedPlansModule & {
|
|
34
|
-
component?: ComponentType | ReactNode;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export type UserPanelModuleOptions = ResolvedUserPanelModule & {
|
|
38
|
-
dashboard: DashboardModuleOptions;
|
|
39
|
-
userManagement: UserManagementModuleOptions;
|
|
40
|
-
plans: PlansModuleOptions;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const protectAccountRoute = (
|
|
44
|
-
protectedRoutes: string[],
|
|
45
|
-
enabled: boolean,
|
|
46
|
-
path: string,
|
|
47
|
-
) => {
|
|
48
|
-
if (enabled) {
|
|
49
|
-
protectedRoutes.push(path);
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export const userPanelModule = (
|
|
54
|
-
options: UserPanelModuleOptions,
|
|
55
|
-
): (ApitogoPlugin & ProfileMenuPlugin) | undefined => {
|
|
56
|
-
if (!options.enabled) {
|
|
57
|
-
return undefined;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const routes: RouteObject[] = [];
|
|
61
|
-
const protectedRoutes: string[] = [];
|
|
62
|
-
const profileMenuItems: ReturnType<ProfileMenuPlugin["getProfileMenuItems"]> =
|
|
63
|
-
[];
|
|
64
|
-
const protectAccountRoutes = options.userManagement.enabled;
|
|
65
|
-
const basePath = options.basePath ?? "/account";
|
|
66
|
-
const accountChildren: RouteObject[] = [];
|
|
67
|
-
|
|
68
|
-
if (options.dashboard.enabled) {
|
|
69
|
-
accountChildren.push({
|
|
70
|
-
path: toAccountChildPath(basePath, options.dashboard.path),
|
|
71
|
-
element: renderModuleElement(
|
|
72
|
-
options.dashboard.component,
|
|
73
|
-
<DefaultDashboardPage content={options.dashboard.content} />,
|
|
74
|
-
),
|
|
75
|
-
handle: { layout: "none" },
|
|
76
|
-
});
|
|
77
|
-
protectAccountRoute(
|
|
78
|
-
protectedRoutes,
|
|
79
|
-
protectAccountRoutes,
|
|
80
|
-
options.dashboard.path,
|
|
81
|
-
);
|
|
82
|
-
profileMenuItems.push({
|
|
83
|
-
label: "Dashboard",
|
|
84
|
-
path: options.dashboard.path,
|
|
85
|
-
category: "top",
|
|
86
|
-
icon: LayoutDashboardIcon,
|
|
87
|
-
weight: 10,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (options.userManagement.enabled) {
|
|
92
|
-
const profilePath = options.userManagement.routes.profile;
|
|
93
|
-
accountChildren.push({
|
|
94
|
-
path: toAccountChildPath(basePath, profilePath),
|
|
95
|
-
element: renderModuleElement(
|
|
96
|
-
options.userManagement.component,
|
|
97
|
-
<SettingsPage content={options.userManagement.content} />,
|
|
98
|
-
),
|
|
99
|
-
handle: { layout: "none" },
|
|
100
|
-
});
|
|
101
|
-
protectAccountRoute(protectedRoutes, protectAccountRoutes, profilePath);
|
|
102
|
-
profileMenuItems.push({
|
|
103
|
-
label: "Settings",
|
|
104
|
-
path: profilePath,
|
|
105
|
-
category: "middle",
|
|
106
|
-
icon: SettingsIcon,
|
|
107
|
-
weight: 20,
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (options.plans.enabled) {
|
|
112
|
-
accountChildren.push({
|
|
113
|
-
path: toAccountChildPath(basePath, options.plans.path),
|
|
114
|
-
element: renderModuleElement(
|
|
115
|
-
options.plans.component,
|
|
116
|
-
<DefaultPlansPage content={options.plans.content} />,
|
|
117
|
-
),
|
|
118
|
-
handle: { layout: "none" },
|
|
119
|
-
});
|
|
120
|
-
protectAccountRoute(protectedRoutes, true, options.plans.path);
|
|
121
|
-
profileMenuItems.push({
|
|
122
|
-
label: "Plans",
|
|
123
|
-
path: options.plans.path,
|
|
124
|
-
category: "middle",
|
|
125
|
-
icon: CreditCardIcon,
|
|
126
|
-
weight: 30,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (options.apiKeys.enabled) {
|
|
131
|
-
protectAccountRoute(
|
|
132
|
-
protectedRoutes,
|
|
133
|
-
protectAccountRoutes,
|
|
134
|
-
options.apiKeys.path,
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (accountChildren.length > 0) {
|
|
139
|
-
routes.push({
|
|
140
|
-
path: basePath,
|
|
141
|
-
element: <AccountLayout basePath={basePath} />,
|
|
142
|
-
handle: { layout: "default" },
|
|
143
|
-
children: accountChildren,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return {
|
|
148
|
-
getRoutes: () => routes,
|
|
149
|
-
getProtectedRoutes: () => protectedRoutes,
|
|
150
|
-
getProfileMenuItems: () => profileMenuItems,
|
|
151
|
-
};
|
|
152
|
-
};
|
|
1
|
+
import type {
|
|
2
|
+
ResolvedDashboardModule,
|
|
3
|
+
ResolvedPlansModule,
|
|
4
|
+
ResolvedUserManagementModule,
|
|
5
|
+
ResolvedUserPanelModule,
|
|
6
|
+
} from "@lukoweb/apitogo";
|
|
7
|
+
import {
|
|
8
|
+
CreditCardIcon,
|
|
9
|
+
LayoutDashboardIcon,
|
|
10
|
+
SettingsIcon,
|
|
11
|
+
} from "@lukoweb/apitogo/icons";
|
|
12
|
+
import type {
|
|
13
|
+
ProfileMenuPlugin,
|
|
14
|
+
ApitogoPlugin,
|
|
15
|
+
} from "@lukoweb/apitogo/plugins";
|
|
16
|
+
import type { ComponentType, ReactNode } from "react";
|
|
17
|
+
import type { RouteObject } from "react-router";
|
|
18
|
+
import { toAccountChildPath } from "./account-paths.js";
|
|
19
|
+
import { AccountLayout } from "./components/AccountLayout.js";
|
|
20
|
+
import { DefaultDashboardPage } from "./pages/DefaultDashboardPage.js";
|
|
21
|
+
import { DefaultPlansPage } from "./pages/DefaultPlansPage.js";
|
|
22
|
+
import { SettingsPage } from "./pages/SettingsPage.js";
|
|
23
|
+
import { renderModuleElement } from "./renderModuleElement.js";
|
|
24
|
+
|
|
25
|
+
type DashboardModuleOptions = ResolvedDashboardModule & {
|
|
26
|
+
component?: ComponentType | ReactNode;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
type UserManagementModuleOptions = ResolvedUserManagementModule & {
|
|
30
|
+
component?: ComponentType | ReactNode;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type PlansModuleOptions = ResolvedPlansModule & {
|
|
34
|
+
component?: ComponentType | ReactNode;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type UserPanelModuleOptions = ResolvedUserPanelModule & {
|
|
38
|
+
dashboard: DashboardModuleOptions;
|
|
39
|
+
userManagement: UserManagementModuleOptions;
|
|
40
|
+
plans: PlansModuleOptions;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const protectAccountRoute = (
|
|
44
|
+
protectedRoutes: string[],
|
|
45
|
+
enabled: boolean,
|
|
46
|
+
path: string,
|
|
47
|
+
) => {
|
|
48
|
+
if (enabled) {
|
|
49
|
+
protectedRoutes.push(path);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const userPanelModule = (
|
|
54
|
+
options: UserPanelModuleOptions,
|
|
55
|
+
): (ApitogoPlugin & ProfileMenuPlugin) | undefined => {
|
|
56
|
+
if (!options.enabled) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const routes: RouteObject[] = [];
|
|
61
|
+
const protectedRoutes: string[] = [];
|
|
62
|
+
const profileMenuItems: ReturnType<ProfileMenuPlugin["getProfileMenuItems"]> =
|
|
63
|
+
[];
|
|
64
|
+
const protectAccountRoutes = options.userManagement.enabled;
|
|
65
|
+
const basePath = options.basePath ?? "/account";
|
|
66
|
+
const accountChildren: RouteObject[] = [];
|
|
67
|
+
|
|
68
|
+
if (options.dashboard.enabled) {
|
|
69
|
+
accountChildren.push({
|
|
70
|
+
path: toAccountChildPath(basePath, options.dashboard.path),
|
|
71
|
+
element: renderModuleElement(
|
|
72
|
+
options.dashboard.component,
|
|
73
|
+
<DefaultDashboardPage content={options.dashboard.content} />,
|
|
74
|
+
),
|
|
75
|
+
handle: { layout: "none" },
|
|
76
|
+
});
|
|
77
|
+
protectAccountRoute(
|
|
78
|
+
protectedRoutes,
|
|
79
|
+
protectAccountRoutes,
|
|
80
|
+
options.dashboard.path,
|
|
81
|
+
);
|
|
82
|
+
profileMenuItems.push({
|
|
83
|
+
label: "Dashboard",
|
|
84
|
+
path: options.dashboard.path,
|
|
85
|
+
category: "top",
|
|
86
|
+
icon: LayoutDashboardIcon,
|
|
87
|
+
weight: 10,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (options.userManagement.enabled) {
|
|
92
|
+
const profilePath = options.userManagement.routes.profile;
|
|
93
|
+
accountChildren.push({
|
|
94
|
+
path: toAccountChildPath(basePath, profilePath),
|
|
95
|
+
element: renderModuleElement(
|
|
96
|
+
options.userManagement.component,
|
|
97
|
+
<SettingsPage content={options.userManagement.content} />,
|
|
98
|
+
),
|
|
99
|
+
handle: { layout: "none" },
|
|
100
|
+
});
|
|
101
|
+
protectAccountRoute(protectedRoutes, protectAccountRoutes, profilePath);
|
|
102
|
+
profileMenuItems.push({
|
|
103
|
+
label: "Settings",
|
|
104
|
+
path: profilePath,
|
|
105
|
+
category: "middle",
|
|
106
|
+
icon: SettingsIcon,
|
|
107
|
+
weight: 20,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (options.plans.enabled) {
|
|
112
|
+
accountChildren.push({
|
|
113
|
+
path: toAccountChildPath(basePath, options.plans.path),
|
|
114
|
+
element: renderModuleElement(
|
|
115
|
+
options.plans.component,
|
|
116
|
+
<DefaultPlansPage content={options.plans.content} />,
|
|
117
|
+
),
|
|
118
|
+
handle: { layout: "none" },
|
|
119
|
+
});
|
|
120
|
+
protectAccountRoute(protectedRoutes, true, options.plans.path);
|
|
121
|
+
profileMenuItems.push({
|
|
122
|
+
label: "Plans",
|
|
123
|
+
path: options.plans.path,
|
|
124
|
+
category: "middle",
|
|
125
|
+
icon: CreditCardIcon,
|
|
126
|
+
weight: 30,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (options.apiKeys.enabled) {
|
|
131
|
+
protectAccountRoute(
|
|
132
|
+
protectedRoutes,
|
|
133
|
+
protectAccountRoutes,
|
|
134
|
+
options.apiKeys.path,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (accountChildren.length > 0) {
|
|
139
|
+
routes.push({
|
|
140
|
+
path: basePath,
|
|
141
|
+
element: <AccountLayout basePath={basePath} />,
|
|
142
|
+
handle: { layout: "default" },
|
|
143
|
+
children: accountChildren,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
getRoutes: () => routes,
|
|
149
|
+
getProtectedRoutes: () => protectedRoutes,
|
|
150
|
+
getProfileMenuItems: () => profileMenuItems,
|
|
151
|
+
};
|
|
152
|
+
};
|
|
@@ -1,125 +1,125 @@
|
|
|
1
|
-
type DevPortalProviderData = {
|
|
2
|
-
type: "dev-portal";
|
|
3
|
-
apiBaseUrl: string;
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
export function resolveDevPortalApiBaseUrl(
|
|
7
|
-
providerData: DevPortalProviderData | null | undefined,
|
|
8
|
-
): string | undefined {
|
|
9
|
-
if (providerData?.type === "dev-portal" && providerData.apiBaseUrl) {
|
|
10
|
-
return providerData.apiBaseUrl.replace(/\/+$/, "");
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const envUrl =
|
|
14
|
-
(typeof import.meta.env.VITE_APITOGO_AUTHENTICATION_API_BASE_URL ===
|
|
15
|
-
"string"
|
|
16
|
-
? import.meta.env.VITE_APITOGO_AUTHENTICATION_API_BASE_URL.trim()
|
|
17
|
-
: "") ||
|
|
18
|
-
(typeof import.meta.env.VITE_APITOGO_DEV_PORTAL_API_URL === "string"
|
|
19
|
-
? import.meta.env.VITE_APITOGO_DEV_PORTAL_API_URL.trim()
|
|
20
|
-
: "");
|
|
21
|
-
|
|
22
|
-
return envUrl ? envUrl.replace(/\/+$/, "") : undefined;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export type AccountMeResponse = {
|
|
26
|
-
userId: string;
|
|
27
|
-
displayName: string | null;
|
|
28
|
-
email?: string | null;
|
|
29
|
-
emailVerified?: boolean;
|
|
30
|
-
hasNativePassword?: boolean;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
async function readErrorMessage(response: Response, fallback: string) {
|
|
34
|
-
try {
|
|
35
|
-
const body = (await response.json()) as {
|
|
36
|
-
detail?: string;
|
|
37
|
-
message?: string;
|
|
38
|
-
};
|
|
39
|
-
return body.detail ?? body.message ?? fallback;
|
|
40
|
-
} catch {
|
|
41
|
-
return fallback;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export async function fetchAccountMe(
|
|
46
|
-
apiBaseUrl: string,
|
|
47
|
-
): Promise<AccountMeResponse> {
|
|
48
|
-
const response = await fetch(`${apiBaseUrl}/api/v1/auth/me`, {
|
|
49
|
-
method: "GET",
|
|
50
|
-
headers: { Accept: "application/json" },
|
|
51
|
-
credentials: "include",
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
if (!response.ok) {
|
|
55
|
-
throw new Error("Failed to load account details.");
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return (await response.json()) as AccountMeResponse;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export async function updateAccountProfile(
|
|
62
|
-
apiBaseUrl: string,
|
|
63
|
-
displayName: string,
|
|
64
|
-
): Promise<AccountMeResponse> {
|
|
65
|
-
const response = await fetch(`${apiBaseUrl}/api/v1/account`, {
|
|
66
|
-
method: "PATCH",
|
|
67
|
-
headers: {
|
|
68
|
-
Accept: "application/json",
|
|
69
|
-
"Content-Type": "application/json",
|
|
70
|
-
},
|
|
71
|
-
credentials: "include",
|
|
72
|
-
body: JSON.stringify({ displayName }),
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
if (!response.ok) {
|
|
76
|
-
throw new Error(
|
|
77
|
-
await readErrorMessage(response, "Unable to save profile."),
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return (await response.json()) as AccountMeResponse;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export async function changeAccountPassword(
|
|
85
|
-
apiBaseUrl: string,
|
|
86
|
-
currentPassword: string,
|
|
87
|
-
newPassword: string,
|
|
88
|
-
): Promise<void> {
|
|
89
|
-
const response = await fetch(`${apiBaseUrl}/api/v1/account/change-password`, {
|
|
90
|
-
method: "POST",
|
|
91
|
-
headers: {
|
|
92
|
-
Accept: "application/json",
|
|
93
|
-
"Content-Type": "application/json",
|
|
94
|
-
},
|
|
95
|
-
credentials: "include",
|
|
96
|
-
body: JSON.stringify({ currentPassword, newPassword }),
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
if (!response.ok) {
|
|
100
|
-
throw new Error(
|
|
101
|
-
await readErrorMessage(response, "Unable to update password."),
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export async function deleteAccount(
|
|
107
|
-
apiBaseUrl: string,
|
|
108
|
-
password?: string,
|
|
109
|
-
): Promise<void> {
|
|
110
|
-
const response = await fetch(`${apiBaseUrl}/api/v1/account`, {
|
|
111
|
-
method: "DELETE",
|
|
112
|
-
headers: {
|
|
113
|
-
Accept: "application/json",
|
|
114
|
-
"Content-Type": "application/json",
|
|
115
|
-
},
|
|
116
|
-
credentials: "include",
|
|
117
|
-
body: JSON.stringify({ password: password ?? null }),
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
if (!response.ok) {
|
|
121
|
-
throw new Error(
|
|
122
|
-
await readErrorMessage(response, "Unable to delete account."),
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
1
|
+
type DevPortalProviderData = {
|
|
2
|
+
type: "dev-portal";
|
|
3
|
+
apiBaseUrl: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export function resolveDevPortalApiBaseUrl(
|
|
7
|
+
providerData: DevPortalProviderData | null | undefined,
|
|
8
|
+
): string | undefined {
|
|
9
|
+
if (providerData?.type === "dev-portal" && providerData.apiBaseUrl) {
|
|
10
|
+
return providerData.apiBaseUrl.replace(/\/+$/, "");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const envUrl =
|
|
14
|
+
(typeof import.meta.env.VITE_APITOGO_AUTHENTICATION_API_BASE_URL ===
|
|
15
|
+
"string"
|
|
16
|
+
? import.meta.env.VITE_APITOGO_AUTHENTICATION_API_BASE_URL.trim()
|
|
17
|
+
: "") ||
|
|
18
|
+
(typeof import.meta.env.VITE_APITOGO_DEV_PORTAL_API_URL === "string"
|
|
19
|
+
? import.meta.env.VITE_APITOGO_DEV_PORTAL_API_URL.trim()
|
|
20
|
+
: "");
|
|
21
|
+
|
|
22
|
+
return envUrl ? envUrl.replace(/\/+$/, "") : undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type AccountMeResponse = {
|
|
26
|
+
userId: string;
|
|
27
|
+
displayName: string | null;
|
|
28
|
+
email?: string | null;
|
|
29
|
+
emailVerified?: boolean;
|
|
30
|
+
hasNativePassword?: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
async function readErrorMessage(response: Response, fallback: string) {
|
|
34
|
+
try {
|
|
35
|
+
const body = (await response.json()) as {
|
|
36
|
+
detail?: string;
|
|
37
|
+
message?: string;
|
|
38
|
+
};
|
|
39
|
+
return body.detail ?? body.message ?? fallback;
|
|
40
|
+
} catch {
|
|
41
|
+
return fallback;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function fetchAccountMe(
|
|
46
|
+
apiBaseUrl: string,
|
|
47
|
+
): Promise<AccountMeResponse> {
|
|
48
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/auth/me`, {
|
|
49
|
+
method: "GET",
|
|
50
|
+
headers: { Accept: "application/json" },
|
|
51
|
+
credentials: "include",
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
throw new Error("Failed to load account details.");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return (await response.json()) as AccountMeResponse;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function updateAccountProfile(
|
|
62
|
+
apiBaseUrl: string,
|
|
63
|
+
displayName: string,
|
|
64
|
+
): Promise<AccountMeResponse> {
|
|
65
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/account`, {
|
|
66
|
+
method: "PATCH",
|
|
67
|
+
headers: {
|
|
68
|
+
Accept: "application/json",
|
|
69
|
+
"Content-Type": "application/json",
|
|
70
|
+
},
|
|
71
|
+
credentials: "include",
|
|
72
|
+
body: JSON.stringify({ displayName }),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
await readErrorMessage(response, "Unable to save profile."),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return (await response.json()) as AccountMeResponse;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function changeAccountPassword(
|
|
85
|
+
apiBaseUrl: string,
|
|
86
|
+
currentPassword: string,
|
|
87
|
+
newPassword: string,
|
|
88
|
+
): Promise<void> {
|
|
89
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/account/change-password`, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
headers: {
|
|
92
|
+
Accept: "application/json",
|
|
93
|
+
"Content-Type": "application/json",
|
|
94
|
+
},
|
|
95
|
+
credentials: "include",
|
|
96
|
+
body: JSON.stringify({ currentPassword, newPassword }),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (!response.ok) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
await readErrorMessage(response, "Unable to update password."),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function deleteAccount(
|
|
107
|
+
apiBaseUrl: string,
|
|
108
|
+
password?: string,
|
|
109
|
+
): Promise<void> {
|
|
110
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/account`, {
|
|
111
|
+
method: "DELETE",
|
|
112
|
+
headers: {
|
|
113
|
+
Accept: "application/json",
|
|
114
|
+
"Content-Type": "application/json",
|
|
115
|
+
},
|
|
116
|
+
credentials: "include",
|
|
117
|
+
body: JSON.stringify({ password: password ?? null }),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
await readErrorMessage(response, "Unable to delete account."),
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|