nextjs-cms 0.5.55 → 0.5.56

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.
@@ -63,6 +63,14 @@ declare const createCaller: import("@trpc/server").TRPCRouterCaller<{
63
63
  password: string;
64
64
  };
65
65
  output: {
66
+ status: number;
67
+ session: {
68
+ user: {
69
+ id: string;
70
+ name: string;
71
+ locale: string | null;
72
+ };
73
+ };
66
74
  user: {
67
75
  id: string;
68
76
  username: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAuB,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAElE;;;;;;GAMG;AACH,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAAiC,CAAA;AAEnD;;;;;IAKI;AACJ,KAAK,YAAY,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAA;AAEhD;;;;;IAKI;AACJ,KAAK,aAAa,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAA;AAElD,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,YAAY,EAAE,CAAA;AACrD,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAuB,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAElE;;;;;;GAMG;AACH,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAAiC,CAAA;AAEnD;;;;;IAKI;AACJ,KAAK,YAAY,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAA;AAEhD;;;;;IAKI;AACJ,KAAK,aAAa,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAA;AAElD,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,YAAY,EAAE,CAAA;AACrD,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA"}
@@ -52,6 +52,14 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
52
52
  password: string;
53
53
  };
54
54
  output: {
55
+ status: number;
56
+ session: {
57
+ user: {
58
+ id: string;
59
+ name: string;
60
+ locale: string | null;
61
+ };
62
+ };
55
63
  user: {
56
64
  id: string;
57
65
  username: string;
@@ -1 +1 @@
1
- {"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../src/api/root.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgBpB,CAAA;AAIF,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAA"}
1
+ {"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../src/api/root.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgBpB,CAAA;AAIF,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAA"}
@@ -29,6 +29,14 @@ export declare const authRouter: import("@trpc/server").TRPCBuiltRouter<{
29
29
  password: string;
30
30
  };
31
31
  output: {
32
+ status: number;
33
+ session: {
34
+ user: {
35
+ id: string;
36
+ name: string;
37
+ locale: string | null;
38
+ };
39
+ };
32
40
  user: {
33
41
  id: string;
34
42
  username: string;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/api/routers/auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAIxB,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsBrB,CAAA"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/api/routers/auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAIxB,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsBrB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"useRefreshToken.d.ts","sourceRoot":"","sources":["../../../src/auth/hooks/useRefreshToken.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AACH,QAAA,MAAM,eAAe,8BA6EpB,CAAA;AAED,eAAe,eAAe,CAAA"}
1
+ {"version":3,"file":"useRefreshToken.d.ts","sourceRoot":"","sources":["../../../src/auth/hooks/useRefreshToken.tsx"],"names":[],"mappings":"AAGA;;;GAGG;AACH,QAAA,MAAM,eAAe,8BA8EpB,CAAA;AAED,eAAe,eAAe,CAAA"}
@@ -1,13 +1,15 @@
1
+ import * as React from 'react';
1
2
  import { logout, refreshSession } from '../react.js';
2
3
  /**
3
4
  * This hook is used to refresh the access token when it expires.
4
5
  * It is used in the useAxiosPrivate hook to refresh the access token when a request returns a 401 error.
5
6
  */
6
7
  const useRefreshToken = () => {
7
- let isRefreshing = false; // Is a refresh request being sent?
8
- let failedQueue = []; // An array of requests that failed because of 401
8
+ const isRefreshingRef = React.useRef(false); // Is a refresh request being sent?
9
+ // An array of requests that failed because of 401
10
+ const failedQueueRef = React.useRef([]);
9
11
  const processQueue = (error, token = null) => {
10
- failedQueue.forEach((prom) => {
12
+ failedQueueRef.current.forEach((prom) => {
11
13
  if (error) {
12
14
  prom.reject(error);
13
15
  }
@@ -15,7 +17,7 @@ const useRefreshToken = () => {
15
17
  prom.resolve(token);
16
18
  }
17
19
  });
18
- failedQueue = [];
20
+ failedQueueRef.current = [];
19
21
  };
20
22
  const refresh = async () => {
21
23
  try {
@@ -24,7 +26,7 @@ const useRefreshToken = () => {
24
26
  /**
25
27
  * The refresh request is done
26
28
  */
27
- isRefreshing = false;
29
+ isRefreshingRef.current = false;
28
30
  if (response.status !== 200) {
29
31
  /**
30
32
  * If the refresh token is invalid, we log out the user
@@ -41,7 +43,7 @@ const useRefreshToken = () => {
41
43
  /**
42
44
  * update the session
43
45
  */
44
- await refreshSession();
46
+ await refreshSession(data.session);
45
47
  }
46
48
  /**
47
49
  * Process the failed requests
@@ -65,14 +67,14 @@ const useRefreshToken = () => {
65
67
  // TODO: Apply this inside useAxiosPrivate.tsx to prevent even the 401 errors from happening
66
68
  // Let's use semaphores to prevent multiple refreshes at the same time
67
69
  return async () => {
68
- if (isRefreshing) {
70
+ if (isRefreshingRef.current) {
69
71
  // If a refresh request is being sent, we return a promise
70
72
  // that will be resolved when the refresh request is done
71
73
  return new Promise((resolve, reject) => {
72
- failedQueue.push({ resolve, reject });
74
+ failedQueueRef.current.push({ resolve, reject });
73
75
  });
74
76
  }
75
- isRefreshing = true; // A refresh request is being sent
77
+ isRefreshingRef.current = true; // A refresh request is being sent
76
78
  return await refresh(); // Send the refresh request and return the new access token
77
79
  };
78
80
  };
@@ -5,10 +5,12 @@ export declare const authRefresh: (refreshToken?: {
5
5
  status: number;
6
6
  code: string;
7
7
  state: string;
8
+ session?: undefined;
8
9
  user?: undefined;
9
10
  } | {
10
11
  status: number;
11
12
  state: string;
13
+ session: Session;
12
14
  user: {
13
15
  id: string;
14
16
  username: string;
@@ -21,6 +23,14 @@ export declare const login: ({ username, password }: {
21
23
  username: string;
22
24
  password: string;
23
25
  }) => Promise<{
26
+ status: number;
27
+ session: {
28
+ user: {
29
+ id: string;
30
+ name: string;
31
+ locale: string | null;
32
+ };
33
+ };
24
34
  user: {
25
35
  id: string;
26
36
  username: string;
@@ -1 +1 @@
1
- {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../../src/auth/lib/actions.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE1C,eAAO,MAAM,WAAW,GAAU,eAAe;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;EA4GjE,CAAA;AAED,eAAO,MAAM,KAAK,GAAU,wBAAwB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;;;;;;;;EA+FzF,CAAA;AAED,eAAO,MAAM,aAAa,GAAU,UAAU,OAAO,GAAG,IAAI,qBA0B3D,CAAA"}
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../../src/auth/lib/actions.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE1C,eAAO,MAAM,WAAW,GAAU,eAAe;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;EA4HjE,CAAA;AAED,eAAO,MAAM,KAAK,GAAU,wBAAwB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;EA+GzF,CAAA;AAED,eAAO,MAAM,aAAa,GAAU,UAAU,OAAO,GAAG,IAAI,qBA0B3D,CAAA"}
@@ -29,7 +29,7 @@ export const authRefresh = async (refreshToken) => {
29
29
  .where(and(eq(AccessTokensTable.adminId, decoded.id), eq(AccessTokensTable.refreshToken, refreshToken.value)))
30
30
  .limit(1);
31
31
  const adminTokenResult = result[0];
32
- if (!adminTokenResult) {
32
+ if (!adminTokenResult || !adminTokenResult.adminId || !adminTokenResult.username) {
33
33
  return {
34
34
  status: 401,
35
35
  code: 'invalid_refresh_token',
@@ -39,17 +39,24 @@ export const authRefresh = async (refreshToken) => {
39
39
  // Let's create a JWT access token and refresh token
40
40
  const accessToken = encodeJWT({
41
41
  id: decoded.id,
42
- iss: 'Lazemni CMS',
42
+ iss: 'nextjs-cms',
43
43
  aud: 'admin',
44
44
  sub: adminTokenResult.username,
45
45
  locale: adminTokenResult.locale || 'en',
46
46
  });
47
47
  const newRefreshToken = encodeRefreshToken({
48
48
  id: adminTokenResult.adminId,
49
- iss: 'Lazemni CMS',
49
+ iss: 'nextjs-cms',
50
50
  aud: 'admin',
51
51
  sub: adminTokenResult.username,
52
52
  });
53
+ if (!accessToken || !newRefreshToken) {
54
+ return {
55
+ status: 401,
56
+ code: 'failed_to_create_tokens',
57
+ state: 'logged_out',
58
+ };
59
+ }
53
60
  // Let's update the refresh token in the database
54
61
  await db
55
62
  .update(AccessTokensTable)
@@ -78,10 +85,17 @@ export const authRefresh = async (refreshToken) => {
78
85
  sameSite: true,
79
86
  maxAge: 60 * 60 * 2, // 2 hours
80
87
  });
88
+ const session = {
89
+ user: {
90
+ id: adminTokenResult.adminId,
91
+ name: adminTokenResult.username,
92
+ locale: adminTokenResult.locale ?? null,
93
+ },
94
+ };
81
95
  return {
82
96
  status: 200,
83
- // accessToken: accessToken,
84
97
  state: 'logged_in',
98
+ session,
85
99
  user: {
86
100
  id: adminTokenResult.adminId,
87
101
  username: adminTokenResult.username,
@@ -114,7 +128,7 @@ export const login = async ({ username, password }) => {
114
128
  .where(eq(AdminsTable.user, username))
115
129
  .limit(1);
116
130
  const admin = result[0];
117
- if (!admin) {
131
+ if (!admin || !admin.password || !admin.id || !admin.username) {
118
132
  throw new Error('Invalid credentials');
119
133
  }
120
134
  // Verify password with bcrypt
@@ -125,33 +139,41 @@ export const login = async ({ username, password }) => {
125
139
  // Let's create a JWT access token and refresh token
126
140
  const accessToken = encodeJWT({
127
141
  id: admin.id,
128
- iss: 'Lazemni CMS',
142
+ iss: 'nextjs-cms',
129
143
  aud: 'admin',
130
144
  sub: admin.username,
131
145
  locale: admin.locale || 'en',
132
146
  });
133
147
  const refreshToken = encodeRefreshToken({
134
148
  id: admin.id,
135
- iss: 'Lazemni CMS',
149
+ iss: 'nextjs-cms',
136
150
  aud: 'admin',
137
151
  sub: admin.username,
138
152
  });
139
- // Let's save the refresh token in the database
140
- await db
141
- .insert(AccessTokensTable)
142
- .values({
143
- accessToken,
144
- refreshToken,
145
- adminId: admin.id,
146
- expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
147
- })
148
- .onDuplicateKeyUpdate({
149
- set: {
153
+ if (!accessToken || !refreshToken) {
154
+ throw new Error('Failed to create access and refresh tokens');
155
+ }
156
+ try {
157
+ // Let's save the refresh token in the database
158
+ await db
159
+ .insert(AccessTokensTable)
160
+ .values({
150
161
  accessToken,
151
162
  refreshToken,
163
+ adminId: admin.id,
152
164
  expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
153
- },
154
- });
165
+ })
166
+ .onDuplicateKeyUpdate({
167
+ set: {
168
+ accessToken,
169
+ refreshToken,
170
+ expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
171
+ },
172
+ });
173
+ }
174
+ catch (error) {
175
+ throw new Error('Failed to save access and refresh tokens to database');
176
+ }
155
177
  const cookieStore = await cookies();
156
178
  // Let's send the refresh token as a cookie
157
179
  cookieStore.set({
@@ -173,6 +195,14 @@ export const login = async ({ username, password }) => {
173
195
  });
174
196
  // Now, return the access token in body
175
197
  return {
198
+ status: 200,
199
+ session: {
200
+ user: {
201
+ id: admin.id,
202
+ name: admin.username,
203
+ locale: admin.locale,
204
+ },
205
+ },
176
206
  user: {
177
207
  id: admin.id,
178
208
  username: admin.username,
@@ -45,6 +45,12 @@ export interface AuthClientConfig {
45
45
  * trigger session updates from places like `signIn` or `signOut`
46
46
  */
47
47
  _getSession: (...args: any[]) => any;
48
+ /**
49
+ * Directly set the session in the current tab/window,
50
+ * after being fetched from the server during a refresh request.
51
+ * This is to avoid the need to fetch the session from the server again.
52
+ */
53
+ _setSession: (session: Session | null) => void;
48
54
  }
49
55
  export declare const __AUTH: AuthClientConfig;
50
56
  export declare function logout(options?: {
@@ -54,7 +60,7 @@ export declare function login({ username, password }: {
54
60
  username: string;
55
61
  password: string;
56
62
  }): Promise<void>;
57
- export declare function refreshSession(): Promise<void>;
63
+ export declare function refreshSession(session: Session): Promise<void>;
58
64
  /**
59
65
  * Returns the current CSRF token.
60
66
  * required to make requests that changes state.
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/auth/react.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/auth/react.tsx"],"names":[],"mappings":"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"}
@@ -11,6 +11,7 @@ export const __AUTH = {
11
11
  _lastSync: 0,
12
12
  _session: undefined,
13
13
  _getSession: () => { },
14
+ _setSession: () => { },
14
15
  };
15
16
  export async function logout(options) {
16
17
  const { deleteCookies = true } = options ?? {};
@@ -51,26 +52,27 @@ export async function login({ username, password }) {
51
52
  const error = await response.json();
52
53
  throw new Error(error.error);
53
54
  }
55
+ const data = await response.json();
54
56
  /**
55
57
  * Broadcast a message to all other tabs/windows to tell them the user has logged in.
56
58
  * This is to ensure all tabs/windows trigger a session update.
57
59
  */
58
- broadcast().postMessage({ event: 'session', data: { trigger: 'login' } });
60
+ broadcast().postMessage({ event: 'session', data: { trigger: 'refetch' } });
59
61
  /**
60
62
  * Update the session in the current tab/window.
61
63
  */
62
- await __AUTH._getSession({ event: 'storage' });
64
+ __AUTH._setSession(data.session);
63
65
  }
64
- export async function refreshSession() {
66
+ export async function refreshSession(session) {
65
67
  /**
66
68
  * Broadcast a message to all other tabs/windows to tell them the session has been updated.
67
69
  * This is to ensure all tabs/windows trigger a session update.
68
70
  */
69
- broadcast().postMessage({ event: 'session', data: { trigger: 'refresh' } });
71
+ broadcast().postMessage({ event: 'session', data: { trigger: 'refetch' } });
70
72
  /**
71
73
  * Update the session in the current tab/window.
72
74
  */
73
- await __AUTH._getSession({ event: 'storage' });
75
+ __AUTH._setSession(session);
74
76
  }
75
77
  /**
76
78
  * Returns the current CSRF token.
@@ -90,8 +92,8 @@ function broadcast() {
90
92
  if (typeof BroadcastChannel === 'undefined') {
91
93
  return {
92
94
  postMessage: () => { },
93
- addEventListener: () => { },
94
- removeEventListener: () => { },
95
+ addEventListener: (_type, _listener) => { },
96
+ removeEventListener: (_type, _listener) => { },
95
97
  };
96
98
  }
97
99
  if (broadcastChannel === null) {
@@ -176,6 +178,11 @@ export function SessionProvider(props) {
176
178
  /** If session was passed, initialize as not loading */
177
179
  const [loading, setLoading] = React.useState(!hasInitialSession);
178
180
  React.useEffect(() => {
181
+ __AUTH._setSession = (session) => {
182
+ __AUTH._lastSync = now();
183
+ __AUTH._session = session;
184
+ setSession(session);
185
+ };
179
186
  async function generateInitialSession() {
180
187
  /**
181
188
  * Use the `axiosPrivate` instance to make a request to `/api/auth/session` to get the
@@ -209,18 +216,15 @@ export function SessionProvider(props) {
209
216
  */
210
217
  init()
211
218
  .then(async (session) => {
212
- if (session) {
219
+ /**
220
+ * Only update if we fetched a new session (i.e., no initial session was provided).
221
+ * When hasInitialSession is true, __AUTH._session and state are already set,
222
+ * so we skip the redundant update.
223
+ */
224
+ if (!hasInitialSession && session) {
213
225
  __AUTH._session = session;
214
226
  setSession(session);
215
227
  }
216
- else {
217
- /**
218
- * It's not necessary to log out the user and broadcast the logout event,
219
- * because there are no cookies set to delete.
220
- * But it's a good practice to do so.
221
- */
222
- // await logout()
223
- }
224
228
  })
225
229
  .then(() => {
226
230
  __AUTH._getSession = async ({ event } = {}) => {
@@ -235,11 +239,11 @@ export function SessionProvider(props) {
235
239
  return;
236
240
  }
237
241
  /**
238
- * If the event is a storage event, we should update the session and not broadcast the event.
239
- * Storage events can also be triggered when a broadcast message is sent from another
242
+ * If the event is a refetch event, we should update the session and not broadcast the event.
243
+ * Refetch events can be triggered when a broadcast message is sent from another
240
244
  * tab/window. (see below)
241
245
  */
242
- if (event === 'storage') {
246
+ if (event === 'refetch') {
243
247
  __AUTH._lastSync = now();
244
248
  __AUTH._session = await getSession({
245
249
  broadcast: false,
@@ -254,7 +258,7 @@ export function SessionProvider(props) {
254
258
  !event ||
255
259
  // If the client doesn't have a session then we don't need to call
256
260
  // the server to check if it does (if they have signed in via another
257
- // tab or window that will come through as a "storage" event anyway)
261
+ // tab or window that will come through as a "refetch" event anyway)
258
262
  __AUTH._session === null ||
259
263
  // Bail out early if the client session is not stale yet
260
264
  now() < __AUTH._lastSync) {
@@ -281,21 +285,39 @@ export function SessionProvider(props) {
281
285
  __AUTH._lastSync = 0;
282
286
  __AUTH._session = undefined;
283
287
  __AUTH._getSession = () => { };
288
+ __AUTH._setSession = () => { };
284
289
  };
285
290
  }, []);
286
291
  React.useEffect(() => {
287
- const handle = () => __AUTH._getSession({ event: 'storage' });
288
- // Listen for storage events and update session if event fired from
289
- // another window (but suppress firing another event to avoid a loop)
290
- // Fetch new session data but tell it not to fire another event to
291
- // avoid an infinite loop.
292
+ const handleBroadcastMessage = (event) => {
293
+ try {
294
+ const message = event.data;
295
+ // Only handle session-related broadcast messages
296
+ if (message.event !== 'session')
297
+ return;
298
+ // Handle logout events by updating the session directly
299
+ if (message.data?.trigger === 'logout') {
300
+ __AUTH._getSession({ event: 'logout' });
301
+ }
302
+ // Handle refetch events by updating the session directly
303
+ if (message.data?.trigger === 'refetch') {
304
+ __AUTH._getSession({ event: 'refetch' });
305
+ }
306
+ }
307
+ catch (error) {
308
+ // Ignore invalid JSON or malformed messages
309
+ console.warn('Failed to parse broadcast message:', error);
310
+ }
311
+ };
312
+ // Listen for broadcast events fired from another tab/window
313
+ // refetch/logout the current session data depending on the event
292
314
  // Note: We could pass session data through and do something like
293
315
  // `setData(message.data)` but that can cause problems depending
294
316
  // on how the session object is being used in the client; it is
295
317
  // more robust to have each window/tab fetch it's own copy of the
296
318
  // session object rather than share it across instances.
297
- broadcast().addEventListener('message', handle);
298
- return () => broadcast().removeEventListener('message', handle);
319
+ broadcast().addEventListener('message', handleBroadcastMessage);
320
+ return () => broadcast().removeEventListener('message', handleBroadcastMessage);
299
321
  }, []);
300
322
  React.useEffect(() => {
301
323
  const { refetchOnWindowFocus = true } = props;
@@ -335,7 +357,7 @@ export function SessionProvider(props) {
335
357
  setSession(newSession);
336
358
  broadcast().postMessage({
337
359
  event: 'session',
338
- data: { trigger: 'getSession' },
360
+ data: { trigger: 'refetch' },
339
361
  });
340
362
  }
341
363
  return newSession;
@@ -1 +1 @@
1
- {"version":3,"file":"trpc.d.ts","sourceRoot":"","sources":["../../src/auth/trpc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAM5C;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAAO,QAAQ,CAAC,aAAa,CAAC,GAAG,CA4E7D,CAAA"}
1
+ {"version":3,"file":"trpc.d.ts","sourceRoot":"","sources":["../../src/auth/trpc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAM5C;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAAO,QAAQ,CAAC,aAAa,CAAC,GAAG,CA6E7D,CAAA"}
package/dist/auth/trpc.js CHANGED
@@ -25,6 +25,7 @@ export const refreshTokenLink = () => {
25
25
  refreshPromise = (async () => {
26
26
  try {
27
27
  const response = await fetch('/api/auth/refresh');
28
+ const data = await response.json();
28
29
  if (response.status !== 200) {
29
30
  await logout({
30
31
  /**
@@ -34,7 +35,7 @@ export const refreshTokenLink = () => {
34
35
  });
35
36
  return;
36
37
  }
37
- await refreshSession();
38
+ await refreshSession(data.session);
38
39
  }
39
40
  catch (e) {
40
41
  await logout({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextjs-cms",
3
- "version": "0.5.55",
3
+ "version": "0.5.56",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "type": "module",
@@ -142,8 +142,8 @@
142
142
  "tsx": "^4.20.6",
143
143
  "typescript": "^5.9.2",
144
144
  "@lzcms/eslint-config": "0.3.0",
145
- "@lzcms/prettier-config": "0.1.0",
146
- "@lzcms/tsconfig": "0.1.0"
145
+ "@lzcms/tsconfig": "0.1.0",
146
+ "@lzcms/prettier-config": "0.1.0"
147
147
  },
148
148
  "license": "MIT",
149
149
  "keywords": [