Skip to content

Commit 43e8bb9

Browse files
committed
refactor
1 parent 8d236a3 commit 43e8bb9

16 files changed

+218
-213
lines changed
Lines changed: 6 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,10 @@
11
import json
2-
import time
3-
from http.cookiejar import Cookie
4-
from typing import cast
52

6-
import browser_cookie3
73
import tornado
8-
from tornado.httpclient import AsyncHTTPClient
9-
from tornado.httputil import HTTPHeaders
104

11-
from ..utils.utils import first
5+
from ..utils.utils import get_leetcode_cookie
126
from .base_handler import BaseHandler
137

14-
BROWSER_COOKIE_METHOD_MAP = {
15-
"chrome": browser_cookie3.chrome,
16-
"chromium": browser_cookie3.chromium,
17-
"opera": browser_cookie3.opera,
18-
"opera_gx": browser_cookie3.opera_gx,
19-
"brave": browser_cookie3.brave,
20-
"edge": browser_cookie3.edge,
21-
"vivaldi": browser_cookie3.vivaldi,
22-
"firefox": browser_cookie3.firefox,
23-
"librewolf": browser_cookie3.librewolf,
24-
"safari": browser_cookie3.safari,
25-
"arc": browser_cookie3.arc,
26-
}
27-
288

299
class GetCookieHandler(BaseHandler):
3010
route = r"cookies"
@@ -33,71 +13,12 @@ class GetCookieHandler(BaseHandler):
3313
def get(self):
3414
self.log.debug("Loading all cookies for LeetCode...")
3515
browser = self.get_query_argument("browser", "", strip=True)
36-
if not browser:
37-
self.set_status(400)
38-
self.finish(json.dumps({"message": "Browser parameter is required"}))
39-
return
40-
41-
if browser not in BROWSER_COOKIE_METHOD_MAP:
42-
self.set_status(400)
43-
self.finish(json.dumps({"message": f"Unsupported browser: {browser}"}))
44-
return
45-
4616
try:
47-
cj = BROWSER_COOKIE_METHOD_MAP[browser](domain_name="leetcode.com")
48-
except browser_cookie3.BrowserCookieError as e:
49-
self.set_status(418)
50-
self.finish(
51-
json.dumps(
52-
{
53-
"message": "Failed to retrieve cookies. Maybe not installed the browser?"
54-
}
55-
)
17+
cookie = get_leetcode_cookie(
18+
browser, self.settings, self.request.headers.get("User-Agent", "")
5619
)
57-
return
20+
self.set_cookie("leetcode_browser", browser, expires=cookie["expires"])
21+
self.finish(json.dumps(cookie))
5822
except Exception as e:
59-
self.set_status(418)
23+
self.set_status(400)
6024
self.finish(json.dumps({"message": str(e.args)}))
61-
return
62-
63-
cookie_session = first(cj, lambda c: c.name == "LEETCODE_SESSION")
64-
cookie_csrf = first(cj, lambda c: c.name == "csrftoken")
65-
exist = bool(cookie_session and cookie_csrf)
66-
expired = exist and (
67-
cast(Cookie, cookie_session).is_expired()
68-
or cast(Cookie, cookie_csrf).is_expired()
69-
)
70-
checked = exist and not expired
71-
72-
resp = {"exist": exist, "expired": expired, "checked": checked}
73-
74-
if checked:
75-
cookie_session_expires = cast(Cookie, cookie_session).expires
76-
max_age = (
77-
cookie_session_expires - int(time.time())
78-
if cookie_session_expires is not None
79-
else 3600 * 24 * 14
80-
)
81-
self.set_cookie("leetcode_browser", browser, max_age=max_age)
82-
self.settings.update(
83-
leetcode_browser=browser,
84-
leetcode_cookiejar=cj,
85-
leetcode_headers=HTTPHeaders(
86-
{
87-
"Cookie": "; ".join(f"{c.name}={c.value}" for c in cj),
88-
"Content-Type": "application/json",
89-
"Origin": "https://leetcode.com",
90-
"Referer": "https://leetcode.com/",
91-
"X-CsrfToken": (
92-
cookie_csrf.value
93-
if cookie_csrf and cookie_csrf.value
94-
else ""
95-
),
96-
}
97-
),
98-
)
99-
AsyncHTTPClient.configure(
100-
None, defaults=dict(user_agent=self.request.headers.get("user-agent"))
101-
)
102-
103-
self.finish(json.dumps(resp))

jupyterlab_leetcode/handlers/leetcode_handler.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from tornado.websocket import WebSocketHandler
1212

1313
from ..utils.notebook_generator import NotebookGenerator
14-
from ..utils.utils import first, request
14+
from ..utils.utils import first, get_leetcode_cookie, request
1515
from .base_handler import BaseHandler
1616

1717
LEETCODE_URL = "https://leetcode.com"
@@ -22,6 +22,21 @@
2222
class LeetCodeHandler(BaseHandler):
2323
"""Base handler for LeetCode-related requests."""
2424

25+
async def prepare(self) -> None:
26+
await super().prepare()
27+
if not self.settings.get("leetcode_headers"):
28+
browser = self.get_cookie("leetcode_browser")
29+
if not browser:
30+
self.set_status(400)
31+
self.finish(
32+
json.dumps({"message": "LeetCode browser cookie is required"})
33+
)
34+
return
35+
36+
get_leetcode_cookie(
37+
browser, self.settings, self.request.headers.get("User-Agent", "")
38+
)
39+
2540
@overload
2641
async def graphql(self, name: str, query: Mapping[str, Any]) -> None: ...
2742

@@ -187,7 +202,7 @@ async def post(self):
187202
body = cast("dict[str, str|int]", body)
188203
skip = cast(int, body.get("skip", 0))
189204
limit = cast(int, body.get("limit", 0))
190-
keyword = cast(str, body.get("keyword", ""))
205+
query = cast("dict[str, Any]", body.get("query", ""))
191206
sortField = cast(str, body.get("sortField", "CUSTOM"))
192207
sortOrder = cast(str, body.get("sortOrder", "ASCENDING"))
193208

@@ -234,7 +249,7 @@ async def post(self):
234249
"variables": {
235250
"skip": skip,
236251
"limit": limit,
237-
"searchKeyword": keyword,
252+
"searchKeyword": query["keyword"],
238253
"categorySlug": "algorithms",
239254
"filters": {
240255
"filterCombineType": "ALL",

jupyterlab_leetcode/utils/utils.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import json
2+
import time
23
from collections.abc import Callable, Iterable, Mapping
3-
from typing import Any, TypeVar
4+
from http.cookiejar import Cookie
5+
from typing import Any, TypeVar, cast
46

7+
import browser_cookie3
58
from tornado.concurrent import Future
69
from tornado.httpclient import AsyncHTTPClient, HTTPRequest, HTTPResponse
710
from tornado.httputil import HTTPHeaders
@@ -26,3 +29,61 @@ def request(
2629
)
2730

2831
return client.fetch(req)
32+
33+
34+
BROWSER_COOKIE_METHOD_MAP = {
35+
"chrome": browser_cookie3.chrome,
36+
"chromium": browser_cookie3.chromium,
37+
"opera": browser_cookie3.opera,
38+
"opera_gx": browser_cookie3.opera_gx,
39+
"brave": browser_cookie3.brave,
40+
"edge": browser_cookie3.edge,
41+
"vivaldi": browser_cookie3.vivaldi,
42+
"firefox": browser_cookie3.firefox,
43+
"librewolf": browser_cookie3.librewolf,
44+
"safari": browser_cookie3.safari,
45+
"arc": browser_cookie3.arc,
46+
}
47+
48+
49+
def get_leetcode_cookie(browser: str, settings: dict[str, Any], ua: str):
50+
if not browser:
51+
raise ValueError("Browser parameter is required")
52+
53+
if browser not in BROWSER_COOKIE_METHOD_MAP:
54+
raise ValueError(f"Unsupported browser: {browser}")
55+
try:
56+
cj = BROWSER_COOKIE_METHOD_MAP[browser](domain_name="leetcode.com")
57+
except browser_cookie3.BrowserCookieError as e:
58+
raise Exception(
59+
"Failed to retrieve cookies. Maybe not installed the browser?"
60+
) from e
61+
except Exception as e:
62+
raise Exception(f"An error occurred: {str(e)}") from e
63+
64+
cookie_session = first(cj, lambda c: c.name == "LEETCODE_SESSION")
65+
cookie_csrf = first(cj, lambda c: c.name == "csrftoken")
66+
exist = bool(cookie_session and cookie_csrf)
67+
expired = exist and (
68+
cast(Cookie, cookie_session).is_expired()
69+
or cast(Cookie, cookie_csrf).is_expired()
70+
)
71+
checked = exist and not expired
72+
73+
expires = cast(Cookie, cookie_session).expires
74+
75+
settings.update(
76+
leetcode_headers=HTTPHeaders(
77+
{
78+
"Cookie": "; ".join(f"{c.name}={c.value}" for c in cj),
79+
"Content-Type": "application/json",
80+
"Origin": "https://leetcode.com",
81+
"Referer": "https://leetcode.com/",
82+
"X-CsrfToken": (
83+
cookie_csrf.value if cookie_csrf and cookie_csrf.value else ""
84+
),
85+
}
86+
),
87+
)
88+
AsyncHTTPClient.configure(None, defaults=dict(user_agent=ua))
89+
return {"exist": exist, "expired": expired, "checked": checked, "expires": expires}

src/components/BrowserMenu.tsx

Lines changed: 24 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React from 'react';
22
import { Notification } from '@jupyterlab/apputils';
33
import {
44
IconChevronDown,
@@ -14,115 +14,105 @@ import {
1414
import { Button, Menu, useMantineTheme } from '@mantine/core';
1515
import { getCookie } from '../services/cookie';
1616

17+
const BrowserIconProps = { size: 16, stroke: 1.5 };
18+
1719
const Browsers = [
1820
{
1921
name: 'Chrome',
2022
icon: (color: string) => (
21-
<IconBrandChrome size={16} color={color} stroke={1.5} />
23+
<IconBrandChrome color={color} {...BrowserIconProps} />
2224
)
2325
},
2426
{
2527
name: 'Firefox',
2628
icon: (color: string) => (
27-
<IconBrandFirefox size={16} color={color} stroke={1.5} />
29+
<IconBrandFirefox color={color} {...BrowserIconProps} />
2830
)
2931
},
3032
{
3133
name: 'Safari',
3234
icon: (color: string) => (
33-
<IconBrandSafari size={16} color={color} stroke={1.5} />
35+
<IconBrandSafari color={color} {...BrowserIconProps} />
3436
)
3537
},
3638
{
3739
name: 'Edge',
3840
icon: (color: string) => (
39-
<IconBrandEdge size={16} color={color} stroke={1.5} />
41+
<IconBrandEdge color={color} {...BrowserIconProps} />
4042
)
4143
},
4244
{
4345
name: 'Opera',
4446
icon: (color: string) => (
45-
<IconBrandOpera size={16} color={color} stroke={1.5} />
47+
<IconBrandOpera color={color} {...BrowserIconProps} />
4648
)
4749
},
4850
{
4951
name: 'Brave',
5052
icon: (color: string) => (
51-
<IconWorldWww size={16} color={color} stroke={1.5} />
53+
<IconWorldWww color={color} {...BrowserIconProps} />
5254
)
5355
},
5456
{
5557
name: 'Vivaldi',
5658
icon: (color: string) => (
57-
<IconBrandVivaldi size={16} color={color} stroke={1.5} />
59+
<IconBrandVivaldi color={color} {...BrowserIconProps} />
5860
)
5961
},
6062
{
6163
name: 'Chromium',
6264
icon: (color: string) => (
63-
<IconBrandChrome size={16} color={color} stroke={1.5} />
65+
<IconBrandChrome color={color} {...BrowserIconProps} />
6466
)
6567
},
6668
{
6769
name: 'Arc',
6870
icon: (color: string) => (
69-
<IconBrandArc size={16} color={color} stroke={1.5} />
71+
<IconBrandArc color={color} {...BrowserIconProps} />
7072
)
7173
},
7274
{
7375
name: 'LibreWolf',
7476
icon: (color: string) => (
75-
<IconWorldWww size={16} color={color} stroke={1.5} />
77+
<IconWorldWww color={color} {...BrowserIconProps} />
7678
)
7779
},
7880
{
7981
name: 'Opera GX',
8082
icon: (color: string) => (
81-
<IconBrandOpera size={16} color={color} stroke={1.5} />
83+
<IconBrandOpera color={color} {...BrowserIconProps} />
8284
)
8385
}
8486
];
8587

86-
const normalizeBrowserName = (name: string) =>
87-
name.toLowerCase().replace(/\s+/g, '_');
88-
8988
const BrowserMenu: React.FC<{
90-
className?: string;
91-
setCookieLoggedIn: (b: string) => void;
92-
}> = ({ className, setCookieLoggedIn }) => {
93-
const [browser, setBrowser] = useState('');
94-
const [checked, setChecked] = useState(false);
95-
96-
useEffect(() => {
89+
onCheckSuccess: () => void;
90+
}> = ({ onCheckSuccess }) => {
91+
const checkBrowser = (browser: string) => {
9792
if (!browser) {
9893
return;
9994
}
100-
if (browser === 'safari') {
95+
if (browser === 'Safari') {
10196
Notification.error(
10297
'Safari does not support getting cookies from the browser. Please use another browser.',
10398
{ autoClose: 3000 }
10499
);
105100
return;
106101
}
107102

108-
getCookie(browser)
103+
getCookie(browser.toLowerCase().replace(/\s+/g, '_'))
109104
.then(resp => {
110105
if (!resp['checked']) {
111106
Notification.error(
112-
`Failed to check cookie for ${browser}. Please ensure you are logged in to LeetCode in this browser.`,
107+
`Failed to check cookie for ${browser}. Please ensure you are logged in to LeetCode in ${browser}.`,
113108
{ autoClose: 3000 }
114109
);
110+
return;
115111
}
116-
setChecked(resp['checked']);
112+
onCheckSuccess();
117113
})
118114
.catch(e => Notification.error(e.message, { autoClose: 3000 }));
119-
}, [browser]);
120-
121-
useEffect(() => {
122-
if (checked) {
123-
setCookieLoggedIn(browser);
124-
}
125-
}, [checked]);
115+
};
126116

127117
const theme = useMantineTheme();
128118
return (
@@ -139,7 +129,6 @@ const BrowserMenu: React.FC<{
139129
pr={12}
140130
radius="md"
141131
size="md"
142-
className={className}
143132
>
144133
Load from browser
145134
</Button>
@@ -150,7 +139,7 @@ const BrowserMenu: React.FC<{
150139
<Menu.Item
151140
key={name}
152141
leftSection={icon(theme.colors.blue[6])}
153-
onClick={() => setBrowser(normalizeBrowserName(name))}
142+
onClick={() => checkBrowser(name)}
154143
>
155144
{name}
156145
</Menu.Item>

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy