Skip to content

Commit ab2028f

Browse files
committed
feat(backend,frontend,ts-utils): handle session expired
1 parent 0b63cad commit ab2028f

File tree

4 files changed

+45
-19
lines changed

4 files changed

+45
-19
lines changed

apps/backend/src/traits.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -115,16 +115,16 @@ pub trait AuthProvider {
115115
auth_ctx
116116
.auth_token
117117
.clone()
118-
.ok_or_else(|| Error::new("The auth token is not present".to_owned()))
118+
.ok_or_else(|| Error::new("NO_AUTH_TOKEN".to_owned()))
119119
}
120120

121121
async fn user_id_from_ctx(&self, ctx: &Context<'_>) -> GraphqlResult<String> {
122+
// return Err(Error::new("NO_USER_ID".to_owned()));
122123
let auth_ctx = ctx.data_unchecked::<AuthContext>();
123-
if let Some(id) = auth_ctx.user_id.to_owned() {
124-
Ok(id)
125-
} else {
126-
Err(Error::new("User was not logged in"))
127-
}
124+
auth_ctx
125+
.user_id
126+
.clone()
127+
.ok_or_else(|| Error::new("NO_USER_ID".to_owned()))
128128
}
129129
}
130130

apps/frontend/app/lib/utilities.server.ts

+26-2
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ import {
1616
UserPreferencesDocument,
1717
} from "@ryot/generated/graphql/backend/graphql";
1818
import { UserDetailsDocument } from "@ryot/generated/graphql/backend/graphql";
19-
import { isEmpty } from "@ryot/ts-utils";
19+
import { intersection, isEmpty } from "@ryot/ts-utils";
2020
import { type CookieSerializeOptions, parse, serialize } from "cookie";
21-
import { GraphQLClient } from "graphql-request";
21+
import { ClientError, GraphQLClient } from "graphql-request";
2222
import { withoutHost } from "ufo";
2323
import { v4 as randomUUID } from "uuid";
2424
import { type ZodTypeAny, type output, z } from "zod";
@@ -33,8 +33,32 @@ import {
3333

3434
export const API_URL = process.env.API_URL || "http://localhost:8000/backend";
3535

36+
const RECOVERABLE_BACKEND_ERRORS = ["NO_AUTH_TOKEN", "NO_USER_ID"];
37+
3638
export const serverGqlService = new GraphQLClient(`${API_URL}/graphql`, {
3739
headers: { Connection: "keep-alive" },
40+
responseMiddleware: async (response) => {
41+
if (response instanceof ClientError) {
42+
const errors = response.response.errors?.map((e) => e.message) || [];
43+
const isRecoverable =
44+
intersection(RECOVERABLE_BACKEND_ERRORS, errors).length > 0;
45+
if (isRecoverable)
46+
throw Response.json(
47+
{},
48+
{
49+
headers: combineHeaders(
50+
getLogoutCookies(),
51+
{ Location: $path("/auth") },
52+
await createToastHeaders({
53+
type: "error",
54+
message: "Your session has expired",
55+
}),
56+
),
57+
status: 302,
58+
},
59+
);
60+
}
61+
},
3862
});
3963

4064
export const getCookieValue = (request: Request, cookieName: string) => {

libs/ts-utils/package.json

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
2-
"name": "@ryot/ts-utils",
3-
"main": "src/index.ts",
4-
"dependencies": {
5-
"@ryot/generated": "workspace:*",
6-
"dayjs": "1.11.11",
7-
"humanize-duration-ts": "2.1.1",
8-
"lodash": "4.17.21"
9-
},
10-
"devDependencies": {
11-
"@types/lodash": "4.17.6"
12-
}
2+
"name": "@ryot/ts-utils",
3+
"main": "src/index.ts",
4+
"dependencies": {
5+
"@ryot/generated": "workspace:*",
6+
"dayjs": "1.11.11",
7+
"humanize-duration-ts": "2.1.1",
8+
"lodash": "4.17.21"
9+
},
10+
"devDependencies": {
11+
"@types/lodash": "4.17.6"
12+
}
1313
}

libs/ts-utils/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
import camelCase from "lodash/camelCase";
88
import cloneDeep from "lodash/cloneDeep";
99
import groupBy from "lodash/groupBy";
10+
import intersection from "lodash/intersection";
1011
import isEmpty from "lodash/isEmpty";
1112
import isEqual from "lodash/isEqual";
1213
import isNumber from "lodash/isNumber";
@@ -75,6 +76,7 @@ export {
7576
camelCase,
7677
cloneDeep,
7778
groupBy,
79+
intersection,
7880
isEmpty,
7981
isEqual,
8082
isNumber,

0 commit comments

Comments
 (0)