@thinhnguyencth1204/nextcli 0.9.0 → 1.0.0
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.js +3 -3
- package/package.json +1 -1
- package/templates/features/api/src/lib/api/axios.ts +1 -90
- package/templates/features/auth/messages/vi/auth.json +2 -1
- package/templates/features/auth/src/app/(auth)/change-password/page.tsx +5 -4
- package/templates/features/auth/src/app/(auth)/layout.tsx +2 -5
- package/templates/features/auth/src/app/(auth)/sign-in/page.tsx +5 -4
- package/templates/features/auth/src/app/api/v1/auth/login/route.ts +24 -29
- package/templates/features/auth/src/app/api/v1/auth/logout/route.ts +0 -5
- package/templates/features/auth/src/components/layout/auth/auth-shell.tsx +24 -0
- package/templates/features/auth/src/features/auth/components/account-panel.tsx +15 -3
- package/templates/features/auth/src/features/auth/components/change-password-form.tsx +27 -30
- package/templates/features/auth/src/features/auth/components/sign-in-form.tsx +33 -42
- package/templates/features/auth/src/lib/auth/client.ts +2 -2
- package/templates/features/auth/src/lib/auth/server.ts +2 -2
- package/templates/features/dashboard/src/app/(dashboard)/account/page.tsx +9 -7
- package/templates/features/dashboard/src/app/(dashboard)/dashboard/page.tsx +24 -10
- package/templates/features/dashboard/src/components/layout/private/app-sidebar.tsx +1 -13
- package/templates/features/dashboard/src/components/layout/private/dashboard-layout.tsx +31 -22
- package/templates/features/dashboard/src/components/layout/private/page-shell.tsx +40 -0
- package/templates/features/database/prisma/schema.prisma +1 -0
- package/templates/features/example/messages/vi/example.json +11 -1
- package/templates/features/example/src/app/(dashboard)/example/page.tsx +92 -3
- package/templates/features/example/src/example/components/example-table.tsx +15 -2
- package/templates/next-base/bun.lock +407 -0
- package/templates/next-base/messages/vi/auth.json +2 -1
- package/templates/next-base/messages/vi/common.json +19 -0
- package/templates/next-base/messages/vi/example.json +11 -1
- package/templates/next-base/next-env.d.ts +1 -1
- package/templates/next-base/prisma/schema.prisma +1 -0
- package/templates/next-base/src/app/(auth)/change-password/page.tsx +5 -4
- package/templates/next-base/src/app/(auth)/layout.tsx +2 -5
- package/templates/next-base/src/app/(auth)/sign-in/page.tsx +5 -4
- package/templates/next-base/src/app/(dashboard)/account/page.tsx +9 -7
- package/templates/next-base/src/app/(dashboard)/dashboard/page.tsx +24 -10
- package/templates/next-base/src/app/(dashboard)/example/page.tsx +92 -3
- package/templates/next-base/src/app/api/v1/auth/login/route.ts +24 -29
- package/templates/next-base/src/app/api/v1/auth/logout/route.ts +0 -5
- package/templates/next-base/src/components/branding/logo.tsx +27 -4
- package/templates/next-base/src/components/layout/auth/auth-shell.tsx +24 -0
- package/templates/next-base/src/components/layout/private/app-sidebar.tsx +1 -13
- package/templates/next-base/src/components/layout/private/dashboard-layout.tsx +31 -22
- package/templates/next-base/src/components/layout/private/page-shell.tsx +40 -0
- package/templates/next-base/src/example/components/example-table.tsx +15 -2
- package/templates/next-base/src/features/auth/components/account-panel.tsx +15 -3
- package/templates/next-base/src/features/auth/components/change-password-form.tsx +27 -30
- package/templates/next-base/src/features/auth/components/sign-in-form.tsx +33 -42
- package/templates/next-base/src/lib/api/axios.ts +1 -90
- package/templates/next-base/src/lib/auth/client.ts +2 -2
- package/templates/next-base/src/lib/auth/server.ts +2 -2
- package/templates/features/api/src/lib/api/token-store.ts +0 -13
- package/templates/features/auth/src/app/api/v1/auth/refresh/route.ts +0 -32
- package/templates/features/auth/src/lib/auth/cookies.ts +0 -15
- package/templates/next-base/src/app/api/v1/auth/refresh/route.ts +0 -32
- package/templates/next-base/src/lib/api/token-store.ts +0 -13
- package/templates/next-base/src/lib/auth/cookies.ts +0 -15
|
@@ -49,17 +49,29 @@ export function AccountPanel() {
|
|
|
49
49
|
}, []);
|
|
50
50
|
|
|
51
51
|
if (loading) {
|
|
52
|
-
return
|
|
52
|
+
return (
|
|
53
|
+
<Card className="max-w-xl">
|
|
54
|
+
<CardContent className="py-8 text-sm text-muted-foreground">
|
|
55
|
+
{t("loading")}
|
|
56
|
+
</CardContent>
|
|
57
|
+
</Card>
|
|
58
|
+
);
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
if (!session?.user) {
|
|
56
|
-
return
|
|
62
|
+
return (
|
|
63
|
+
<Card className="max-w-xl">
|
|
64
|
+
<CardContent className="py-8 text-sm text-muted-foreground">
|
|
65
|
+
{t("noSession")}
|
|
66
|
+
</CardContent>
|
|
67
|
+
</Card>
|
|
68
|
+
);
|
|
57
69
|
}
|
|
58
70
|
|
|
59
71
|
return (
|
|
60
72
|
<Card className="max-w-xl">
|
|
61
73
|
<CardHeader>
|
|
62
|
-
<CardTitle>{t("
|
|
74
|
+
<CardTitle>{t("panelTitle")}</CardTitle>
|
|
63
75
|
</CardHeader>
|
|
64
76
|
<CardContent className="space-y-2 text-sm">
|
|
65
77
|
<p>
|
|
@@ -8,7 +8,6 @@ import { toast } from "sonner";
|
|
|
8
8
|
import { protectedApi } from "@/lib/api/axios";
|
|
9
9
|
import { changePasswordSchema } from "@/features/auth/validations";
|
|
10
10
|
import { Button } from "@/components/ui/button";
|
|
11
|
-
import { Card, CardContent } from "@/components/ui/card";
|
|
12
11
|
import { Input } from "@/components/ui/input";
|
|
13
12
|
import { Label } from "@/components/ui/label";
|
|
14
13
|
|
|
@@ -49,34 +48,32 @@ export function ChangePasswordForm() {
|
|
|
49
48
|
};
|
|
50
49
|
|
|
51
50
|
return (
|
|
52
|
-
<
|
|
53
|
-
<
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
</CardContent>
|
|
80
|
-
</Card>
|
|
51
|
+
<form onSubmit={onSubmit} className="space-y-6">
|
|
52
|
+
<div className="space-y-2">
|
|
53
|
+
<Label htmlFor="currentPassword">{t("currentPassword")}</Label>
|
|
54
|
+
<Input
|
|
55
|
+
id="currentPassword"
|
|
56
|
+
type="password"
|
|
57
|
+
value={currentPassword}
|
|
58
|
+
onChange={(event) => setCurrentPassword(event.target.value)}
|
|
59
|
+
className="h-12"
|
|
60
|
+
required
|
|
61
|
+
/>
|
|
62
|
+
</div>
|
|
63
|
+
<div className="space-y-2">
|
|
64
|
+
<Label htmlFor="newPassword">{t("newPassword")}</Label>
|
|
65
|
+
<Input
|
|
66
|
+
id="newPassword"
|
|
67
|
+
type="password"
|
|
68
|
+
value={newPassword}
|
|
69
|
+
onChange={(event) => setNewPassword(event.target.value)}
|
|
70
|
+
className="h-12"
|
|
71
|
+
required
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
<Button type="submit" className="h-12 w-full" disabled={isSubmitting}>
|
|
75
|
+
{isSubmitting ? t("submitting") : t("submit")}
|
|
76
|
+
</Button>
|
|
77
|
+
</form>
|
|
81
78
|
);
|
|
82
79
|
}
|
|
@@ -6,16 +6,13 @@ import { useRouter } from "next/navigation";
|
|
|
6
6
|
import { useTranslations } from "next-intl";
|
|
7
7
|
import { toast } from "sonner";
|
|
8
8
|
import { publicApi } from "@/lib/api/axios";
|
|
9
|
-
import { setAccessToken } from "@/lib/api/token-store";
|
|
10
9
|
import { signInSchema } from "@/features/auth/validations";
|
|
11
10
|
import type { ApiSuccess } from "@/types";
|
|
12
11
|
import { Button } from "@/components/ui/button";
|
|
13
|
-
import { Card, CardContent } from "@/components/ui/card";
|
|
14
12
|
import { Input } from "@/components/ui/input";
|
|
15
13
|
import { Label } from "@/components/ui/label";
|
|
16
14
|
|
|
17
15
|
type LoginResponse = {
|
|
18
|
-
accessToken: string;
|
|
19
16
|
requirePasswordChange: boolean;
|
|
20
17
|
};
|
|
21
18
|
|
|
@@ -41,14 +38,10 @@ export function SignInForm() {
|
|
|
41
38
|
withCredentials: true,
|
|
42
39
|
});
|
|
43
40
|
const payload = (response.data as ApiSuccess<LoginResponse>).data;
|
|
44
|
-
if (!payload?.accessToken) {
|
|
45
|
-
toast.error(t("missingAccessToken"));
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
setAccessToken(payload.accessToken);
|
|
50
41
|
toast.success(t("success"));
|
|
51
|
-
router.push(
|
|
42
|
+
router.push(
|
|
43
|
+
payload.requirePasswordChange ? "/change-password" : "/dashboard",
|
|
44
|
+
);
|
|
52
45
|
router.refresh();
|
|
53
46
|
} catch (error) {
|
|
54
47
|
const message = error instanceof Error ? error.message : t("failed");
|
|
@@ -59,37 +52,35 @@ export function SignInForm() {
|
|
|
59
52
|
};
|
|
60
53
|
|
|
61
54
|
return (
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
</CardContent>
|
|
93
|
-
</Card>
|
|
55
|
+
<form onSubmit={onSubmit} className="space-y-6">
|
|
56
|
+
<div className="space-y-2">
|
|
57
|
+
<Label htmlFor="username">{t("username")}</Label>
|
|
58
|
+
<Input
|
|
59
|
+
id="username"
|
|
60
|
+
value={username}
|
|
61
|
+
onChange={(event) => setUsername(event.target.value)}
|
|
62
|
+
placeholder={t("usernamePlaceholder")}
|
|
63
|
+
autoComplete="username"
|
|
64
|
+
className="h-12"
|
|
65
|
+
required
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
<div className="space-y-2">
|
|
69
|
+
<Label htmlFor="password">{t("password")}</Label>
|
|
70
|
+
<Input
|
|
71
|
+
id="password"
|
|
72
|
+
type="password"
|
|
73
|
+
value={password}
|
|
74
|
+
onChange={(event) => setPassword(event.target.value)}
|
|
75
|
+
placeholder={t("passwordPlaceholder")}
|
|
76
|
+
autoComplete="current-password"
|
|
77
|
+
className="h-12"
|
|
78
|
+
required
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
<Button type="submit" className="h-12 w-full" disabled={isSubmitting}>
|
|
82
|
+
{isSubmitting ? t("submitting") : t("submit")}
|
|
83
|
+
</Button>
|
|
84
|
+
</form>
|
|
94
85
|
);
|
|
95
86
|
}
|
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
|
-
import {
|
|
3
|
-
clearAccessToken,
|
|
4
|
-
getAccessToken,
|
|
5
|
-
setAccessToken,
|
|
6
|
-
} from "@/lib/api/token-store";
|
|
7
|
-
import type { ApiErrorResponse, ApiSuccess } from "@/types";
|
|
2
|
+
import type { ApiErrorResponse } from "@/types";
|
|
8
3
|
|
|
9
4
|
const baseURL = process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000";
|
|
10
5
|
|
|
11
|
-
type RetryableRequestConfig = {
|
|
12
|
-
_retry?: boolean;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
6
|
export const publicApi = axios.create({
|
|
16
7
|
baseURL,
|
|
17
8
|
withCredentials: true,
|
|
@@ -28,86 +19,6 @@ export const protectedApi = axios.create({
|
|
|
28
19
|
},
|
|
29
20
|
});
|
|
30
21
|
|
|
31
|
-
protectedApi.interceptors.request.use((config) => {
|
|
32
|
-
const token = getAccessToken();
|
|
33
|
-
if (token) {
|
|
34
|
-
config.headers = config.headers ?? {};
|
|
35
|
-
config.headers.Authorization = `Bearer ${token}`;
|
|
36
|
-
}
|
|
37
|
-
return config;
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
let isRefreshing = false;
|
|
41
|
-
let waitingQueue: Array<(token: string | null) => void> = [];
|
|
42
|
-
|
|
43
|
-
function flushQueue(token: string | null): void {
|
|
44
|
-
for (const resolve of waitingQueue) {
|
|
45
|
-
resolve(token);
|
|
46
|
-
}
|
|
47
|
-
waitingQueue = [];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async function refreshAccessToken(): Promise<string | null> {
|
|
51
|
-
try {
|
|
52
|
-
const response = await publicApi.post("/api/v1/auth/refresh", null, {
|
|
53
|
-
withCredentials: true,
|
|
54
|
-
});
|
|
55
|
-
const token = (response.data as ApiSuccess<{ accessToken: string }>).data
|
|
56
|
-
?.accessToken;
|
|
57
|
-
if (!token) {
|
|
58
|
-
clearAccessToken();
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
setAccessToken(token);
|
|
62
|
-
return token;
|
|
63
|
-
} catch {
|
|
64
|
-
clearAccessToken();
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
protectedApi.interceptors.response.use(
|
|
70
|
-
(response) => response,
|
|
71
|
-
async (error) => {
|
|
72
|
-
const config = error.config as typeof error.config & RetryableRequestConfig;
|
|
73
|
-
if (!config || config._retry || error.response?.status !== 401) {
|
|
74
|
-
return Promise.reject(error);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (isRefreshing) {
|
|
78
|
-
return new Promise((resolve, reject) => {
|
|
79
|
-
waitingQueue.push((token) => {
|
|
80
|
-
if (!token) {
|
|
81
|
-
reject(error);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
config.headers = config.headers ?? {};
|
|
86
|
-
config.headers.Authorization = `Bearer ${token}`;
|
|
87
|
-
resolve(protectedApi(config));
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
config._retry = true;
|
|
93
|
-
isRefreshing = true;
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
const token = await refreshAccessToken();
|
|
97
|
-
flushQueue(token);
|
|
98
|
-
if (!token) {
|
|
99
|
-
return Promise.reject(error);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
config.headers = config.headers ?? {};
|
|
103
|
-
config.headers.Authorization = `Bearer ${token}`;
|
|
104
|
-
return protectedApi(config);
|
|
105
|
-
} finally {
|
|
106
|
-
isRefreshing = false;
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
);
|
|
110
|
-
|
|
111
22
|
function extractApiErrorMessage(error: unknown): string {
|
|
112
23
|
if (!axios.isAxiosError(error)) {
|
|
113
24
|
return "Unexpected error occurred.";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createAuthClient } from "better-auth/react";
|
|
2
|
-
import {
|
|
2
|
+
import { usernameClient } from "better-auth/client/plugins";
|
|
3
3
|
|
|
4
4
|
export const authClient = createAuthClient({
|
|
5
5
|
baseURL: process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000",
|
|
6
|
-
plugins: [
|
|
6
|
+
plugins: [usernameClient()],
|
|
7
7
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { betterAuth } from "better-auth/minimal";
|
|
2
2
|
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
3
|
-
import {
|
|
3
|
+
import { username } from "better-auth/plugins";
|
|
4
4
|
import prisma from "@/lib/db/prisma";
|
|
5
5
|
|
|
6
6
|
const socialProviders = {
|
|
@@ -12,7 +12,7 @@ export const auth = betterAuth({
|
|
|
12
12
|
database: prismaAdapter(prisma, {
|
|
13
13
|
provider: "postgresql",
|
|
14
14
|
}),
|
|
15
|
-
plugins: [
|
|
15
|
+
plugins: [username()],
|
|
16
16
|
emailAndPassword: {
|
|
17
17
|
enabled: true,
|
|
18
18
|
minPasswordLength: 8,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
let accessToken: string | null = null;
|
|
2
|
-
|
|
3
|
-
export function getAccessToken(): string | null {
|
|
4
|
-
return accessToken;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function setAccessToken(token: string | null): void {
|
|
8
|
-
accessToken = token;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function clearAccessToken(): void {
|
|
12
|
-
accessToken = null;
|
|
13
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { getRefreshCookieName } from "@/lib/auth/cookies";
|
|
2
|
-
import { fail, ok } from "@/lib/api/response";
|
|
3
|
-
|
|
4
|
-
const authBaseUrl =
|
|
5
|
-
process.env.BETTER_AUTH_URL ??
|
|
6
|
-
process.env.NEXT_PUBLIC_APP_URL ??
|
|
7
|
-
"http://localhost:3000";
|
|
8
|
-
|
|
9
|
-
export async function POST(request: Request) {
|
|
10
|
-
const incomingCookie = request.headers.get("cookie") ?? "";
|
|
11
|
-
if (!incomingCookie.includes(getRefreshCookieName())) {
|
|
12
|
-
return fail("UNAUTHORIZED", "Refresh cookie is missing.", { status: 401 });
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const tokenResponse = await fetch(`${authBaseUrl}/api/auth/token`, {
|
|
16
|
-
method: "GET",
|
|
17
|
-
headers: {
|
|
18
|
-
cookie: incomingCookie,
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
if (!tokenResponse.ok) {
|
|
23
|
-
return fail("UNAUTHORIZED", "Failed to refresh access token.", {
|
|
24
|
-
status: tokenResponse.status,
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const tokenPayload = await tokenResponse.json();
|
|
29
|
-
return ok({
|
|
30
|
-
accessToken: tokenPayload.token,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
const refreshCookieName = "nextcli_refresh_token";
|
|
2
|
-
|
|
3
|
-
export function refreshCookieOptions() {
|
|
4
|
-
return {
|
|
5
|
-
httpOnly: true,
|
|
6
|
-
secure: process.env.NODE_ENV === "production",
|
|
7
|
-
sameSite: "lax",
|
|
8
|
-
path: "/",
|
|
9
|
-
maxAge: 60 * 60 * 24 * 30,
|
|
10
|
-
} as const;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function getRefreshCookieName(): string {
|
|
14
|
-
return refreshCookieName;
|
|
15
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { getRefreshCookieName } from "@/lib/auth/cookies";
|
|
2
|
-
import { fail, ok } from "@/lib/api/response";
|
|
3
|
-
|
|
4
|
-
const authBaseUrl =
|
|
5
|
-
process.env.BETTER_AUTH_URL ??
|
|
6
|
-
process.env.NEXT_PUBLIC_APP_URL ??
|
|
7
|
-
"http://localhost:3000";
|
|
8
|
-
|
|
9
|
-
export async function POST(request: Request) {
|
|
10
|
-
const incomingCookie = request.headers.get("cookie") ?? "";
|
|
11
|
-
if (!incomingCookie.includes(getRefreshCookieName())) {
|
|
12
|
-
return fail("UNAUTHORIZED", "Refresh cookie is missing.", { status: 401 });
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const tokenResponse = await fetch(`${authBaseUrl}/api/auth/token`, {
|
|
16
|
-
method: "GET",
|
|
17
|
-
headers: {
|
|
18
|
-
cookie: incomingCookie,
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
if (!tokenResponse.ok) {
|
|
23
|
-
return fail("UNAUTHORIZED", "Failed to refresh access token.", {
|
|
24
|
-
status: tokenResponse.status,
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const tokenPayload = await tokenResponse.json();
|
|
29
|
-
return ok({
|
|
30
|
-
accessToken: tokenPayload.token,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
let accessToken: string | null = null;
|
|
2
|
-
|
|
3
|
-
export function getAccessToken(): string | null {
|
|
4
|
-
return accessToken;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function setAccessToken(token: string | null): void {
|
|
8
|
-
accessToken = token;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function clearAccessToken(): void {
|
|
12
|
-
accessToken = null;
|
|
13
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
const refreshCookieName = "nextcli_refresh_token";
|
|
2
|
-
|
|
3
|
-
export function refreshCookieOptions() {
|
|
4
|
-
return {
|
|
5
|
-
httpOnly: true,
|
|
6
|
-
secure: process.env.NODE_ENV === "production",
|
|
7
|
-
sameSite: "lax",
|
|
8
|
-
path: "/",
|
|
9
|
-
maxAge: 60 * 60 * 24 * 30,
|
|
10
|
-
} as const;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function getRefreshCookieName(): string {
|
|
14
|
-
return refreshCookieName;
|
|
15
|
-
}
|