@lukoweb/apitogo 0.1.65 → 0.1.66

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/dist/cli/cli.js CHANGED
@@ -433,6 +433,19 @@ function isUserPanelEnabled(normalized) {
433
433
  const plans = asRecord(userPanel?.plans);
434
434
  return dashboard?.enabled === true || userManagement?.enabled === true || apiKeys?.enabled === true || plans?.enabled === true;
435
435
  }
436
+ function resolveDashboardRedirectPath(normalized) {
437
+ const userPanel = asRecord(asRecord(normalized.modules)?.userPanel);
438
+ const dashboard = asRecord(userPanel?.dashboard);
439
+ if (dashboard?.enabled !== true) {
440
+ return void 0;
441
+ }
442
+ const basePath = typeof userPanel?.basePath === "string" && userPanel.basePath.length > 0 ? userPanel.basePath.replace(/\/+$/, "") : "/account";
443
+ const dashboardPath = typeof dashboard.path === "string" && dashboard.path.length > 0 ? dashboard.path : "dashboard";
444
+ if (dashboardPath.startsWith("/")) {
445
+ return dashboardPath.replace(/\/+$/, "") || "/";
446
+ }
447
+ return `${basePath}/${dashboardPath.replace(/^\/+/, "")}`;
448
+ }
436
449
  function syncAuthentication(normalized) {
437
450
  const userPanelEnabled = isUserPanelEnabled(normalized);
438
451
  const authentication = asRecord(normalized.authentication);
@@ -455,6 +468,15 @@ function syncAuthentication(normalized) {
455
468
  if (auth.demoMode === void 0) {
456
469
  auth.demoMode = topLevelDemoMode !== false;
457
470
  }
471
+ const dashboardRedirect = resolveDashboardRedirectPath(normalized);
472
+ if (dashboardRedirect) {
473
+ if (auth.redirectToAfterSignIn === void 0) {
474
+ auth.redirectToAfterSignIn = dashboardRedirect;
475
+ }
476
+ if (auth.redirectToAfterSignUp === void 0) {
477
+ auth.redirectToAfterSignUp = dashboardRedirect;
478
+ }
479
+ }
458
480
  }
459
481
  if (normalized.demoMode !== void 0) {
460
482
  auth.demoMode = normalized.demoMode !== false;
@@ -5335,7 +5357,7 @@ import {
5335
5357
  // package.json
5336
5358
  var package_default = {
5337
5359
  name: "@lukoweb/apitogo",
5338
- version: "0.1.65",
5360
+ version: "0.1.66",
5339
5361
  type: "module",
5340
5362
  sideEffects: [
5341
5363
  "**/*.css",
@@ -1,9 +1,11 @@
1
- export declare function DevPortalLoginPage({ apiBaseUrl, demoMode, }: {
1
+ export declare function DevPortalLoginPage({ apiBaseUrl, demoMode, defaultReturnUrl, }: {
2
2
  apiBaseUrl?: string;
3
3
  demoMode?: boolean;
4
+ defaultReturnUrl?: string;
4
5
  }): import("react/jsx-runtime").JSX.Element;
5
- export declare function DevPortalRegisterPage({ apiBaseUrl }: {
6
+ export declare function DevPortalRegisterPage({ apiBaseUrl, defaultReturnUrl, }: {
6
7
  apiBaseUrl?: string;
8
+ defaultReturnUrl?: string;
7
9
  }): import("react/jsx-runtime").JSX.Element;
8
10
  export declare function DevPortalVerifyEmailPage({ apiBaseUrl, }: {
9
11
  apiBaseUrl?: string;
@@ -1,6 +1,6 @@
1
1
  export declare const DEV_PORTAL_PLACEHOLDER_API_URL = "https://dp-example.azurecontainerapps.io";
2
2
  export type DevPortalAuthMode = "live" | "preview";
3
- export type EndUserMeResponse = {
3
+ export type UserMeResponse = {
4
4
  userId: string;
5
5
  displayName: string | null;
6
6
  email?: string | null;
@@ -1,6 +1,6 @@
1
1
  import type { DevPortalAuthenticationConfig } from "../../../config/config.js";
2
2
  import type { UserProfile } from "../state.js";
3
- import type { EndUserMeResponse } from "./dev-portal-constants.js";
3
+ import type { UserMeResponse } from "./dev-portal-constants.js";
4
4
  export declare const DEMO_EMAIL = "demo@apitogo.local";
5
5
  export declare const DEMO_PASSWORD = "password";
6
6
  export declare const DEMO_USER_ID = "00000000-0000-0000-0000-000000000001";
@@ -14,7 +14,7 @@ export declare function resolveDemoMode(config: Pick<DevPortalAuthenticationConf
14
14
  export declare function isDemoCredential(email: string, password: string): boolean;
15
15
  export declare function createDemoProfile(): UserProfile;
16
16
  export declare function resolveDemoPlanSku(plans: DemoPlanInput[] | undefined): string;
17
- export declare function createDemoMeResponse(plans?: DemoPlanInput[]): EndUserMeResponse;
17
+ export declare function createDemoMeResponse(plans?: DemoPlanInput[]): UserMeResponse;
18
18
  export type DemoCredentialsResponse = {
19
19
  idToken: string;
20
20
  accessToken: string | null;
@@ -1,5 +1,5 @@
1
1
  import type { DevPortalAuthenticationConfig } from "../../../config/config.js";
2
- import { type EndUserMeResponse } from "./dev-portal-constants.js";
2
+ import { type UserMeResponse } from "./dev-portal-constants.js";
3
3
  export declare function resolveDevPortalApiBaseUrl(config: Pick<DevPortalAuthenticationConfig, "apiBaseUrl">): string | undefined;
4
4
  export declare function isPlaceholderDevPortalApiUrl(apiBaseUrl: string | undefined): boolean;
5
5
  export declare function toAbsoluteReturnUrl(pathOrUrl: string): string;
@@ -11,4 +11,4 @@ export declare function isNativeAuthEnabled(config: import("./dev-portal-constan
11
11
  export declare function fetchDevPortalAuthConfig(apiBaseUrl: string, fetchImpl?: typeof fetch): Promise<import("./dev-portal-constants.js").DevPortalAuthConfigResponse>;
12
12
  export declare function postDevPortalAuth<T>(apiBaseUrl: string, path: string, body: unknown, fetchImpl?: typeof fetch): Promise<T>;
13
13
  export declare function probeDevPortalBackend(apiBaseUrl: string, fetchImpl?: typeof fetch): Promise<boolean>;
14
- export declare function mapEndUserMeToProfile(me: EndUserMeResponse): import("../state.js").UserProfile;
14
+ export declare function mapUserMeToProfile(me: UserMeResponse): import("../state.js").UserProfile;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo",
3
- "version": "0.1.65",
3
+ "version": "0.1.66",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -511,6 +511,32 @@ export function isNativeAuthEnabled(config: ApitogoJsonConfig): boolean {
511
511
  return native?.enabled !== false;
512
512
  }
513
513
 
514
+ /** Default account dashboard path when modules.userPanel.dashboard is enabled. */
515
+ function resolveDashboardRedirectPath(
516
+ normalized: ApitogoJsonConfig,
517
+ ): string | undefined {
518
+ const userPanel = asRecord(asRecord(normalized.modules)?.userPanel);
519
+ const dashboard = asRecord(userPanel?.dashboard);
520
+ if (dashboard?.enabled !== true) {
521
+ return undefined;
522
+ }
523
+
524
+ const basePath =
525
+ typeof userPanel?.basePath === "string" && userPanel.basePath.length > 0
526
+ ? userPanel.basePath.replace(/\/+$/, "")
527
+ : "/account";
528
+ const dashboardPath =
529
+ typeof dashboard.path === "string" && dashboard.path.length > 0
530
+ ? dashboard.path
531
+ : "dashboard";
532
+
533
+ if (dashboardPath.startsWith("/")) {
534
+ return dashboardPath.replace(/\/+$/, "") || "/";
535
+ }
536
+
537
+ return `${basePath}/${dashboardPath.replace(/^\/+/, "")}`;
538
+ }
539
+
514
540
  function syncAuthentication(normalized: ApitogoJsonConfig): void {
515
541
  const userPanelEnabled = isUserPanelEnabled(normalized);
516
542
  const authentication = asRecord(normalized.authentication);
@@ -538,6 +564,16 @@ function syncAuthentication(normalized: ApitogoJsonConfig): void {
538
564
  if (auth.demoMode === undefined) {
539
565
  auth.demoMode = topLevelDemoMode !== false;
540
566
  }
567
+
568
+ const dashboardRedirect = resolveDashboardRedirectPath(normalized);
569
+ if (dashboardRedirect) {
570
+ if (auth.redirectToAfterSignIn === undefined) {
571
+ auth.redirectToAfterSignIn = dashboardRedirect;
572
+ }
573
+ if (auth.redirectToAfterSignUp === undefined) {
574
+ auth.redirectToAfterSignUp = dashboardRedirect;
575
+ }
576
+ }
541
577
  }
542
578
 
543
579
  if (normalized.demoMode !== undefined) {
@@ -95,13 +95,8 @@ export const useAuth = () => {
95
95
  throw new Error("Authentication is not enabled.");
96
96
  }
97
97
  // TODO: Should handle errors/state
98
- await authentication.signIn(
99
- { navigate },
100
- {
101
- ...options,
102
- redirectTo: options?.redirectTo ?? window.location.href,
103
- },
104
- );
98
+ // Omit redirectTo when unset so providers can apply redirectToAfterSignIn.
99
+ await authentication.signIn({ navigate }, options);
105
100
  },
106
101
 
107
102
  logout: async () => {
@@ -119,13 +114,8 @@ export const useAuth = () => {
119
114
  if (!isAuthEnabled) {
120
115
  throw new Error("Authentication is not enabled.");
121
116
  }
122
- await authentication.signUp(
123
- { navigate },
124
- {
125
- ...options,
126
- redirectTo: options?.redirectTo ?? window.location.href,
127
- },
128
- );
117
+ // Omit redirectTo when unset so providers can apply redirectToAfterSignUp.
118
+ await authentication.signUp({ navigate }, options);
129
119
  },
130
120
 
131
121
  requestEmailVerification: async (options?: AuthActionOptions) => {
@@ -50,9 +50,9 @@ function AuthPageShell({ title, description, children }: AuthPageShellProps) {
50
50
  );
51
51
  }
52
52
 
53
- function useReturnUrl(): string {
53
+ function useReturnUrl(defaultReturnUrl = "/"): string {
54
54
  const [search] = useSearchParams();
55
- return search.get("redirect") ?? search.get("returnUrl") ?? "/";
55
+ return search.get("redirect") ?? search.get("returnUrl") ?? defaultReturnUrl;
56
56
  }
57
57
 
58
58
  function useApiBaseUrl(apiBaseUrl?: string): string | undefined {
@@ -124,12 +124,14 @@ function DemoLoginHint() {
124
124
  export function DevPortalLoginPage({
125
125
  apiBaseUrl,
126
126
  demoMode = false,
127
+ defaultReturnUrl = "/",
127
128
  }: {
128
129
  apiBaseUrl?: string;
129
130
  demoMode?: boolean;
131
+ defaultReturnUrl?: string;
130
132
  }) {
131
133
  const baseUrl = useApiBaseUrl(apiBaseUrl);
132
- const returnUrl = useReturnUrl();
134
+ const returnUrl = useReturnUrl(defaultReturnUrl);
133
135
  const [config, setConfig] = useState<DevPortalAuthConfigResponse | null>(
134
136
  null,
135
137
  );
@@ -305,9 +307,15 @@ export function DevPortalLoginPage({
305
307
  );
306
308
  }
307
309
 
308
- export function DevPortalRegisterPage({ apiBaseUrl }: { apiBaseUrl?: string }) {
310
+ export function DevPortalRegisterPage({
311
+ apiBaseUrl,
312
+ defaultReturnUrl = "/",
313
+ }: {
314
+ apiBaseUrl?: string;
315
+ defaultReturnUrl?: string;
316
+ }) {
309
317
  const baseUrl = useApiBaseUrl(apiBaseUrl);
310
- const returnUrl = useReturnUrl();
318
+ const returnUrl = useReturnUrl(defaultReturnUrl);
311
319
  const [email, setEmail] = useState("");
312
320
  const [password, setPassword] = useState("");
313
321
  const [displayName, setDisplayName] = useState("");
@@ -4,7 +4,7 @@ export const DEV_PORTAL_PLACEHOLDER_API_URL =
4
4
 
5
5
  export type DevPortalAuthMode = "live" | "preview";
6
6
 
7
- export type EndUserMeResponse = {
7
+ export type UserMeResponse = {
8
8
  userId: string;
9
9
  displayName: string | null;
10
10
  email?: string | null;
@@ -1,7 +1,7 @@
1
1
  import type { DevPortalAuthenticationConfig } from "../../../config/config.js";
2
2
  import type { UserProfile } from "../state.js";
3
3
  import { useAuthState } from "../state.js";
4
- import type { EndUserMeResponse } from "./dev-portal-constants.js";
4
+ import type { UserMeResponse } from "./dev-portal-constants.js";
5
5
 
6
6
  export const DEMO_EMAIL = "demo@apitogo.local";
7
7
  export const DEMO_PASSWORD = "password";
@@ -47,9 +47,7 @@ export function resolveDemoPlanSku(plans: DemoPlanInput[] | undefined): string {
47
47
  return first?.sku ?? "free";
48
48
  }
49
49
 
50
- export function createDemoMeResponse(
51
- plans?: DemoPlanInput[],
52
- ): EndUserMeResponse {
50
+ export function createDemoMeResponse(plans?: DemoPlanInput[]): UserMeResponse {
53
51
  const planSku = resolveDemoPlanSku(plans);
54
52
  const periodEnd = new Date();
55
53
  periodEnd.setMonth(periodEnd.getMonth() + 1);
@@ -2,7 +2,7 @@ import type { DevPortalAuthenticationConfig } from "../../../config/config.js";
2
2
  import {
3
3
  DEV_PORTAL_PLACEHOLDER_API_URL,
4
4
  type DevPortalAuthStatusResponse,
5
- type EndUserMeResponse,
5
+ type UserMeResponse,
6
6
  } from "./dev-portal-constants.js";
7
7
 
8
8
  declare const VITE_APITOGO_AUTHENTICATION_API_BASE_URL: string | undefined;
@@ -261,10 +261,10 @@ function readOptionalBoolean(...values: unknown[]): boolean | undefined {
261
261
  return undefined;
262
262
  }
263
263
 
264
- export function mapEndUserMeToProfile(
265
- me: EndUserMeResponse,
264
+ export function mapUserMeToProfile(
265
+ me: UserMeResponse,
266
266
  ): import("../state.js").UserProfile {
267
- const raw = me as EndUserMeResponse & Record<string, unknown>;
267
+ const raw = me as UserMeResponse & Record<string, unknown>;
268
268
  const email = readOptionalString(raw.email, raw.Email, raw.emailAddress);
269
269
  const emailVerified =
270
270
  readOptionalBoolean(
@@ -21,7 +21,7 @@ import {
21
21
  } from "./dev-portal-auth-pages.js";
22
22
  import type {
23
23
  DevPortalAuthMode,
24
- EndUserMeResponse,
24
+ UserMeResponse,
25
25
  } from "./dev-portal-constants.js";
26
26
  import {
27
27
  clearDemoSession,
@@ -33,7 +33,7 @@ import {
33
33
  buildDevPortalLogoutUrl,
34
34
  isNativeAuthEnabled,
35
35
  isPlaceholderDevPortalApiUrl,
36
- mapEndUserMeToProfile,
36
+ mapUserMeToProfile,
37
37
  probeDevPortalBackend,
38
38
  resolveDevPortalApiBaseUrl,
39
39
  toAbsoluteReturnUrl,
@@ -84,14 +84,34 @@ export class DevPortalAuthenticationProvider
84
84
  }
85
85
 
86
86
  getRoutes() {
87
+ const defaultReturnUrl =
88
+ this.redirectToAfterSignIn ?? this.redirectToAfterSignUp ?? "/";
87
89
  const pageProps = {
88
90
  apiBaseUrl: this.apiBaseUrl,
89
91
  demoMode: this.demoMode,
90
92
  };
91
93
  return [
92
94
  ...super.getRoutes(),
93
- { path: "/login", element: <DevPortalLoginPage {...pageProps} /> },
94
- { path: "/register", element: <DevPortalRegisterPage {...pageProps} /> },
95
+ {
96
+ path: "/login",
97
+ element: (
98
+ <DevPortalLoginPage
99
+ {...pageProps}
100
+ defaultReturnUrl={defaultReturnUrl}
101
+ />
102
+ ),
103
+ },
104
+ {
105
+ path: "/register",
106
+ element: (
107
+ <DevPortalRegisterPage
108
+ apiBaseUrl={this.apiBaseUrl}
109
+ defaultReturnUrl={
110
+ this.redirectToAfterSignUp ?? this.redirectToAfterSignIn ?? "/"
111
+ }
112
+ />
113
+ ),
114
+ },
95
115
  {
96
116
  path: "/verify-email",
97
117
  element: <DevPortalVerifyEmailPage {...pageProps} />,
@@ -297,12 +317,12 @@ export class DevPortalAuthenticationProvider
297
317
  return false;
298
318
  }
299
319
 
300
- const me = (await response.json()) as EndUserMeResponse;
320
+ const me = (await response.json()) as UserMeResponse;
301
321
  if (generation !== this.refreshGeneration) {
302
322
  return useAuthState.getState().isAuthenticated;
303
323
  }
304
324
 
305
- const profile: UserProfile = mapEndUserMeToProfile(me);
325
+ const profile: UserProfile = mapUserMeToProfile(me);
306
326
 
307
327
  useAuthState.setState({
308
328
  isAuthenticated: true,
@@ -1,12 +1,10 @@
1
1
  import { Button } from "@lukoweb/apitogo/ui/Button.js";
2
2
  import { ArrowRightIcon } from "lucide-react";
3
- import { useLocation } from "react-router";
4
3
  import { useAuth } from "../authentication/hook.js";
5
4
  import { cn } from "../util/cn.js";
6
5
 
7
6
  export const HeaderAuthActions = ({ className }: { className?: string }) => {
8
7
  const auth = useAuth();
9
- const location = useLocation();
10
8
  const { isAuthEnabled, isAuthenticated } = auth;
11
9
 
12
10
  if (!isAuthEnabled || isAuthenticated) {
@@ -19,14 +17,14 @@ export const HeaderAuthActions = ({ className }: { className?: string }) => {
19
17
  variant="ghost"
20
18
  size="lg"
21
19
  className="font-semibold text-foreground hover:text-primary px-3"
22
- onClick={() => void auth.login({ redirectTo: location.pathname })}
20
+ onClick={() => void auth.login()}
23
21
  >
24
22
  Sign in
25
23
  </Button>
26
24
  <Button
27
25
  size="lg"
28
26
  className="gap-1.5 rounded-[9px] px-4 font-semibold shadow-[0_4px_14px_-4px] shadow-primary/30"
29
- onClick={() => void auth.signup({ redirectTo: location.pathname })}
27
+ onClick={() => void auth.signup()}
30
28
  >
31
29
  Start free
32
30
  <ArrowRightIcon size={15} strokeWidth={2} />
@@ -10,7 +10,7 @@ import {
10
10
  type LucideIcon,
11
11
  } from "lucide-react";
12
12
  import { useState } from "react";
13
- import { Link, useLocation } from "react-router";
13
+ import { Link } from "react-router";
14
14
  import {
15
15
  isHeaderNavGroup,
16
16
  type HeaderNavItem,
@@ -122,7 +122,6 @@ const MobileHeaderNavItem = ({
122
122
  export const MobileTopNavigation = () => {
123
123
  const context = useZudoku();
124
124
  const authState = useAuth();
125
- const location = useLocation();
126
125
  const currentNav = useCurrentNavigation();
127
126
 
128
127
  const {
@@ -256,7 +255,7 @@ export const MobileTopNavigation = () => {
256
255
  className="font-semibold"
257
256
  onClick={() => {
258
257
  setDrawerOpen(false);
259
- void login({ redirectTo: location.pathname });
258
+ void login();
260
259
  }}
261
260
  >
262
261
  Sign in
@@ -265,7 +264,7 @@ export const MobileTopNavigation = () => {
265
264
  className="gap-1.5"
266
265
  onClick={() => {
267
266
  setDrawerOpen(false);
268
- void signup({ redirectTo: location.pathname });
267
+ void signup();
269
268
  }}
270
269
  >
271
270
  Start free