diff --git a/app/package.json b/app/package.json index e0e3338..bd3e221 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "type": "module", "sideEffects": false, "bin": "./dist/cli/index.js", - "version": "0.5.0-rc14", + "version": "0.5.0-rc15", "scripts": { "build:all": "NODE_ENV=production bun run build.ts --minify --types --clean && bun run build:cli", "dev": "vite", diff --git a/app/src/auth/authenticate/Authenticator.ts b/app/src/auth/authenticate/Authenticator.ts index 770b199..0dc479d 100644 --- a/app/src/auth/authenticate/Authenticator.ts +++ b/app/src/auth/authenticate/Authenticator.ts @@ -1,14 +1,6 @@ import { Exception } from "core"; import { addFlashMessage } from "core/server/flash"; -import { - type Static, - StringEnum, - type TSchema, - Type, - parse, - randomString, - transformObject -} from "core/utils"; +import { type Static, StringEnum, Type, parse, runtimeSupports, transformObject } from "core/utils"; import type { Context, Hono } from "hono"; import { deleteCookie, getSignedCookie, setSignedCookie } from "hono/cookie"; import { sign, verify } from "hono/jwt"; @@ -282,20 +274,31 @@ export class Authenticator = Record< return c.req.header("Content-Type") === "application/json"; } + private getSuccessPath(c: Context) { + const p = (this.config.cookie.pathSuccess ?? "/").replace(/\/+$/, "/"); + + // nextjs doesn't support non-fq urls + // but env could be proxied (stackblitz), so we shouldn't fq every url + if (!runtimeSupports("redirects_non_fq")) { + return new URL(c.req.url).origin + p; + } + + return p; + } + async respond(c: Context, data: AuthResponse | Error | any, redirect?: string) { if (this.isJsonRequest(c)) { return c.json(data); } - const successPath = this.config.cookie.pathSuccess ?? "/"; - const successUrl = new URL(c.req.url).origin + successPath.replace(/\/+$/, "/"); - const referer = new URL(redirect ?? c.req.header("Referer") ?? successUrl); - console.log("auth respond", { redirect, successUrl, successPath }); + const successUrl = this.getSuccessPath(c); + const referer = redirect ?? c.req.header("Referer") ?? successUrl; + //console.log("auth respond", { redirect, successUrl, successPath }); if ("token" in data) { await this.setAuthCookie(c, data.token); // can't navigate to "/" – doesn't work on nextjs - console.log("auth success, redirecting to", successUrl); + //console.log("auth success, redirecting to", successUrl); return c.redirect(successUrl); } @@ -305,7 +308,7 @@ export class Authenticator = Record< } await addFlashMessage(c, message, "error"); - console.log("auth failed, redirecting to", referer); + //console.log("auth failed, redirecting to", referer); return c.redirect(referer); } diff --git a/app/src/core/utils/index.ts b/app/src/core/utils/index.ts index dcd5aff..85809e2 100644 --- a/app/src/core/utils/index.ts +++ b/app/src/core/utils/index.ts @@ -11,3 +11,4 @@ export * from "./crypto"; export * from "./uuid"; export { FromSchema } from "./typebox/from-schema"; export * from "./test"; +export * from "./runtime"; diff --git a/app/src/core/utils/runtime.ts b/app/src/core/utils/runtime.ts new file mode 100644 index 0000000..c7007fa --- /dev/null +++ b/app/src/core/utils/runtime.ts @@ -0,0 +1,41 @@ +import { getRuntimeKey as honoGetRuntimeKey } from "hono/adapter"; + +/** + * Adds additional checks for nextjs + */ +export function getRuntimeKey(): string { + const global = globalThis as any; + + // Detect Next.js server-side runtime + if (global?.process?.env?.NEXT_RUNTIME === "nodejs") { + return "nextjs"; + } + + // Detect Next.js edge runtime + if (global?.process?.env?.NEXT_RUNTIME === "edge") { + return "nextjs-edge"; + } + + // Detect Next.js client-side runtime + // @ts-ignore + if (typeof window !== "undefined" && window.__NEXT_DATA__) { + return "nextjs-client"; + } + + return honoGetRuntimeKey(); +} + +const features = { + // supports the redirect of not full qualified addresses + // not supported in nextjs + redirects_non_fq: true +}; + +export function runtimeSupports(feature: keyof typeof features) { + const runtime = getRuntimeKey(); + if (runtime.startsWith("nextjs")) { + features.redirects_non_fq = false; + } + + return features[feature]; +} diff --git a/app/src/modules/server/SystemController.ts b/app/src/modules/server/SystemController.ts index 47ba55f..8fd50cc 100644 --- a/app/src/modules/server/SystemController.ts +++ b/app/src/modules/server/SystemController.ts @@ -3,6 +3,7 @@ import type { App } from "App"; import { tbValidator as tb } from "core"; import { StringEnum, Type, TypeInvalidError } from "core/utils"; +import { getRuntimeKey } from "core/utils"; import type { Context, Hono } from "hono"; import { Controller } from "modules/Controller"; @@ -292,7 +293,8 @@ export class SystemController extends Controller { return c.json({ version: this.app.version(), test: 2, - app: c.get("app")?.version() + app: c.get("app")?.version(), + runtime: getRuntimeKey() }); }); diff --git a/examples/remix/app/routes/_index.tsx b/examples/remix/app/routes/_index.tsx index c538d0f..5b419f1 100644 --- a/examples/remix/app/routes/_index.tsx +++ b/examples/remix/app/routes/_index.tsx @@ -7,7 +7,7 @@ export const meta: MetaFunction = () => { export const loader = async (args: LoaderFunctionArgs) => { const api = args.context.api; - const user = (await api.getVerifiedAuthState()).user; + const user = (await api.getVerifiedAuthState(true)).user; const { data } = await api.data.readMany("todos"); return { data, user }; };