-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlegadilo.js
153 lines (123 loc) · 4.05 KB
/
legadilo.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
const DEFAULT_LEGADILO_URL = "https://www.legadilo.eu";
export const DEFAULT_OPTIONS = {
instanceUrl: DEFAULT_LEGADILO_URL,
userEmail: "",
tokenId: "",
tokenSecret: "",
accessToken: "",
};
export const testCredentials = async ({ instanceUrl, userEmail, tokenId, tokenSecret }) => {
try {
const resp = await fetch(`${instanceUrl}/api/users/tokens/`, {
"Content-Type": "application/json",
method: "POST",
body: JSON.stringify({
email: userEmail,
application_token_uuid: tokenId,
application_token_secret: tokenSecret,
}),
});
await resp.json();
return resp.status === 200;
} catch (error) {
console.error(error);
return false;
}
};
export const saveArticle = async ({ link, title, content }) => {
if (!/^https?:\/\//.test(link)) {
throw new Error("Invalid url");
}
return await post("/api/reading/articles/", { link, title, content });
};
export const updateArticle = async (
articleId,
{ title, tags, readAt, isFavorite, isForLater, readingTime },
) =>
await patch(`/api/reading/articles/${articleId}/`, {
title,
tags,
reading_time: readingTime,
read_at: readAt,
is_favorite: isFavorite,
is_for_later: isForLater,
});
export const subscribeToFeed = async (link) => await post("/api/feeds/", { feed_url: link });
export const updateFeed = async (
feedId,
{ categoryId, tags, refreshDelay, articleRetentionTime },
) =>
await patch(`/api/feeds/${feedId}/`, {
category_id: categoryId,
tags,
refresh_delay: refreshDelay,
article_retention_time: articleRetentionTime,
});
export const listTags = async () => {
const response = await get("/api/reading/tags/");
return response.items;
};
export const listCategories = async () => {
const response = await get("/api/feeds/categories/");
return response.items;
};
const handleAuth =
(fetchFunc) =>
async (...fetchArgs) => {
const options = await loadOptions();
// If we don’t have a token yet, we create one.
if (!options.accessToken) {
await getNewAccessToken(options);
}
try {
return await fetchFunc(...fetchArgs);
} catch (error) {
// Error is unrelated to auth, let’s propagate immediately.
if (![401, 403].includes(error.cause)) {
throw error;
}
}
// The current token has probably expired. Let’s get a new one and retry. If it fails again,
// let the error propagate.
await getNewAccessToken(options);
return await fetchFunc(...fetchArgs);
};
const getNewAccessToken = async (options) => {
const data = await doFetch("/api/users/tokens/", {
method: "POST",
body: JSON.stringify({
email: options.userEmail,
application_token_uuid: options.tokenId,
application_token_secret: options.tokenSecret,
}),
});
await chrome.storage.local.set({ accessToken: data.access_token });
return data.access_token;
};
const post = handleAuth(
async (apiUrl, data) => await doFetch(apiUrl, { method: "POST", body: JSON.stringify(data) }),
);
const doFetch = async (url, fetchOptions) => {
const options = await loadOptions();
if (!fetchOptions.headers) {
fetchOptions.headers = {};
}
fetchOptions.headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${options.accessToken}`,
...fetchOptions.headers,
};
const resp = await fetch(`${options.instanceUrl}${url}`, fetchOptions);
if (!resp.ok) {
throw new Error(`Response status: ${resp.status} (${resp.statusText})`, { cause: resp.status });
}
return await resp.json();
};
const patch = handleAuth(
async (apiUrl, data) => await doFetch(apiUrl, { method: "PATCH", body: JSON.stringify(data) }),
);
const get = handleAuth(async (apiUrl) => await doFetch(apiUrl, { method: "GET" }));
export const loadOptions = async () => chrome.storage.local.get(DEFAULT_OPTIONS);
export const storeOptions = async ({ instanceUrl, userEmail, tokenId, tokenSecret }) =>
// Reset access token when options change.
chrome.storage.local.set({ instanceUrl, userEmail, tokenId, tokenSecret, accessToken: "" });