feeds-fun 0.0.4
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/.eslintrc.cjs +15 -0
- package/.prettierrc.json +13 -0
- package/.vscode/extensions.json +3 -0
- package/README.md +52 -0
- package/env.d.ts +1 -0
- package/index.html +13 -0
- package/package.json +50 -0
- package/public/favicon.ico +0 -0
- package/src/App.vue +33 -0
- package/src/components/ConfigFlag.vue +22 -0
- package/src/components/ConfigSelector.vue +25 -0
- package/src/components/DiscoveryForm.vue +81 -0
- package/src/components/EntriesList.vue +51 -0
- package/src/components/EntryForList.vue +156 -0
- package/src/components/EntryInfo.vue +23 -0
- package/src/components/FeedForList.vue +115 -0
- package/src/components/FeedInfo.vue +35 -0
- package/src/components/FeedsCollections.vue +53 -0
- package/src/components/FeedsList.vue +27 -0
- package/src/components/FfunGithubButtons.vue +22 -0
- package/src/components/FfunTag.vue +95 -0
- package/src/components/OPMLUpload.vue +46 -0
- package/src/components/OpenaiTokensUsage.vue +61 -0
- package/src/components/RuleConstructor.vue +56 -0
- package/src/components/RuleScoreUpdater.vue +33 -0
- package/src/components/RulesList.vue +52 -0
- package/src/components/SimplePagination.vue +81 -0
- package/src/components/SupertokensLogin.vue +118 -0
- package/src/components/TagsFilter.vue +130 -0
- package/src/components/TagsFilterElement.vue +89 -0
- package/src/components/TagsList.vue +125 -0
- package/src/components/UserSetting.vue +129 -0
- package/src/inputs/Marker.vue +70 -0
- package/src/inputs/ScoreSelector.vue +38 -0
- package/src/layouts/SidePanelLayout.vue +231 -0
- package/src/layouts/WideLayout.vue +44 -0
- package/src/logic/api.ts +253 -0
- package/src/logic/constants.ts +8 -0
- package/src/logic/enums.ts +92 -0
- package/src/logic/settings.ts +37 -0
- package/src/logic/timer.ts +25 -0
- package/src/logic/types.ts +371 -0
- package/src/logic/utils.ts +39 -0
- package/src/main.ts +145 -0
- package/src/router/index.ts +61 -0
- package/src/stores/entries.ts +217 -0
- package/src/stores/globalSettings.ts +74 -0
- package/src/stores/globalState.ts +23 -0
- package/src/stores/supertokens.ts +144 -0
- package/src/stores/tags.ts +54 -0
- package/src/values/DateTime.vue +27 -0
- package/src/values/FeedId.vue +22 -0
- package/src/values/Score.vue +42 -0
- package/src/values/URL.vue +25 -0
- package/src/views/AuthView.vue +66 -0
- package/src/views/CollectionsView.vue +23 -0
- package/src/views/DiscoveryView.vue +26 -0
- package/src/views/FeedsView.vue +124 -0
- package/src/views/MainView.vue +67 -0
- package/src/views/NewsView.vue +96 -0
- package/src/views/RulesView.vue +33 -0
- package/src/views/SettingsView.vue +81 -0
- package/tsconfig.app.json +12 -0
- package/tsconfig.json +14 -0
- package/tsconfig.node.json +8 -0
- package/tsconfig.vitest.json +9 -0
- package/vite.config.ts +26 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import * as e from "@/logic/enums";
|
|
2
|
+
|
|
3
|
+
export type FeedId = string & {readonly __brand: unique symbol};
|
|
4
|
+
|
|
5
|
+
export function toFeedId(id: string): FeedId {
|
|
6
|
+
return id as FeedId;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type EntryId = string & {readonly __brand: unique symbol};
|
|
10
|
+
|
|
11
|
+
export function toEntryId(id: string): EntryId {
|
|
12
|
+
return id as EntryId;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type RuleId = string & {readonly __brand: unique symbol};
|
|
16
|
+
|
|
17
|
+
export function toRuleId(id: string): RuleId {
|
|
18
|
+
return id as RuleId;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type FeedsCollectionId = string & {readonly __brand: unique symbol};
|
|
22
|
+
|
|
23
|
+
export function toFeedsCollectionId(id: string): FeedsCollectionId {
|
|
24
|
+
return id as FeedsCollectionId;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type URL = string & {readonly __brand: unique symbol};
|
|
28
|
+
|
|
29
|
+
export function toURL(url: string): URL {
|
|
30
|
+
return url as URL;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class Feed {
|
|
34
|
+
readonly id: FeedId;
|
|
35
|
+
readonly title: string | null;
|
|
36
|
+
readonly description: string | null;
|
|
37
|
+
readonly url: URL;
|
|
38
|
+
readonly state: string;
|
|
39
|
+
readonly lastError: string | null;
|
|
40
|
+
readonly loadedAt: Date | null;
|
|
41
|
+
readonly linkedAt: Date;
|
|
42
|
+
readonly isOk: boolean;
|
|
43
|
+
|
|
44
|
+
constructor({
|
|
45
|
+
id,
|
|
46
|
+
title,
|
|
47
|
+
description,
|
|
48
|
+
url,
|
|
49
|
+
state,
|
|
50
|
+
lastError,
|
|
51
|
+
loadedAt,
|
|
52
|
+
linkedAt,
|
|
53
|
+
isOk
|
|
54
|
+
}: {
|
|
55
|
+
id: FeedId;
|
|
56
|
+
title: string | null;
|
|
57
|
+
description: string | null;
|
|
58
|
+
url: URL;
|
|
59
|
+
state: string;
|
|
60
|
+
lastError: string | null;
|
|
61
|
+
loadedAt: Date | null;
|
|
62
|
+
linkedAt: Date;
|
|
63
|
+
isOk: boolean;
|
|
64
|
+
}) {
|
|
65
|
+
this.id = id;
|
|
66
|
+
this.title = title;
|
|
67
|
+
this.description = description;
|
|
68
|
+
this.url = url;
|
|
69
|
+
this.state = state;
|
|
70
|
+
this.lastError = lastError;
|
|
71
|
+
this.loadedAt = loadedAt;
|
|
72
|
+
this.linkedAt = linkedAt;
|
|
73
|
+
this.isOk = isOk;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function feedFromJSON({
|
|
78
|
+
id,
|
|
79
|
+
title,
|
|
80
|
+
description,
|
|
81
|
+
url,
|
|
82
|
+
state,
|
|
83
|
+
lastError,
|
|
84
|
+
loadedAt,
|
|
85
|
+
linkedAt
|
|
86
|
+
}: {
|
|
87
|
+
id: string;
|
|
88
|
+
title: string;
|
|
89
|
+
description: string;
|
|
90
|
+
url: string;
|
|
91
|
+
state: string;
|
|
92
|
+
lastError: string | null;
|
|
93
|
+
loadedAt: string;
|
|
94
|
+
linkedAt: string;
|
|
95
|
+
}): Feed {
|
|
96
|
+
return {
|
|
97
|
+
id: toFeedId(id),
|
|
98
|
+
title: title !== null ? title : null,
|
|
99
|
+
description: description !== null ? description : null,
|
|
100
|
+
url: toURL(url),
|
|
101
|
+
state: state,
|
|
102
|
+
lastError: lastError,
|
|
103
|
+
loadedAt: loadedAt !== null ? new Date(loadedAt) : null,
|
|
104
|
+
linkedAt: new Date(linkedAt),
|
|
105
|
+
isOk: state === "loaded"
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export class Entry {
|
|
110
|
+
readonly id: EntryId;
|
|
111
|
+
readonly feedId: FeedId;
|
|
112
|
+
readonly title: string;
|
|
113
|
+
readonly url: URL;
|
|
114
|
+
readonly tags: string[];
|
|
115
|
+
readonly markers: e.Marker[];
|
|
116
|
+
readonly score: number;
|
|
117
|
+
readonly scoreToZero: number;
|
|
118
|
+
readonly publishedAt: Date;
|
|
119
|
+
readonly catalogedAt: Date;
|
|
120
|
+
body: string | null;
|
|
121
|
+
|
|
122
|
+
constructor({
|
|
123
|
+
id,
|
|
124
|
+
feedId,
|
|
125
|
+
title,
|
|
126
|
+
url,
|
|
127
|
+
tags,
|
|
128
|
+
markers,
|
|
129
|
+
score,
|
|
130
|
+
publishedAt,
|
|
131
|
+
catalogedAt,
|
|
132
|
+
body
|
|
133
|
+
}: {
|
|
134
|
+
id: EntryId;
|
|
135
|
+
feedId: FeedId;
|
|
136
|
+
title: string;
|
|
137
|
+
url: URL;
|
|
138
|
+
tags: string[];
|
|
139
|
+
markers: e.Marker[];
|
|
140
|
+
score: number;
|
|
141
|
+
publishedAt: Date;
|
|
142
|
+
catalogedAt: Date;
|
|
143
|
+
body: string | null;
|
|
144
|
+
}) {
|
|
145
|
+
this.id = id;
|
|
146
|
+
this.feedId = feedId;
|
|
147
|
+
this.title = title;
|
|
148
|
+
this.url = url;
|
|
149
|
+
this.tags = tags;
|
|
150
|
+
this.markers = markers;
|
|
151
|
+
this.score = score;
|
|
152
|
+
this.publishedAt = publishedAt;
|
|
153
|
+
this.catalogedAt = catalogedAt;
|
|
154
|
+
this.body = body;
|
|
155
|
+
|
|
156
|
+
this.scoreToZero = -Math.abs(score);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
setMarker(marker: e.Marker): void {
|
|
160
|
+
if (!this.hasMarker(marker)) {
|
|
161
|
+
this.markers.push(marker);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
removeMarker(marker: e.Marker): void {
|
|
166
|
+
if (this.hasMarker(marker)) {
|
|
167
|
+
this.markers.splice(this.markers.indexOf(marker), 1);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
hasMarker(marker: e.Marker): boolean {
|
|
172
|
+
return this.markers.includes(marker);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function entryFromJSON({
|
|
177
|
+
id,
|
|
178
|
+
feedId,
|
|
179
|
+
title,
|
|
180
|
+
url,
|
|
181
|
+
tags,
|
|
182
|
+
markers,
|
|
183
|
+
score,
|
|
184
|
+
publishedAt,
|
|
185
|
+
catalogedAt,
|
|
186
|
+
body
|
|
187
|
+
}: {
|
|
188
|
+
id: string;
|
|
189
|
+
feedId: string;
|
|
190
|
+
title: string;
|
|
191
|
+
url: string;
|
|
192
|
+
tags: string[];
|
|
193
|
+
markers: string[];
|
|
194
|
+
score: number;
|
|
195
|
+
publishedAt: string;
|
|
196
|
+
catalogedAt: string;
|
|
197
|
+
body: string | null;
|
|
198
|
+
}): Entry {
|
|
199
|
+
return new Entry({
|
|
200
|
+
id: toEntryId(id),
|
|
201
|
+
feedId: toFeedId(feedId),
|
|
202
|
+
title,
|
|
203
|
+
url: toURL(url),
|
|
204
|
+
tags: tags,
|
|
205
|
+
markers: markers.map((m: string) => {
|
|
206
|
+
if (m in e.reverseMarker) {
|
|
207
|
+
return e.reverseMarker[m];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
throw new Error(`Unknown marker: ${m}`);
|
|
211
|
+
}),
|
|
212
|
+
score: score,
|
|
213
|
+
publishedAt: new Date(publishedAt),
|
|
214
|
+
catalogedAt: new Date(catalogedAt),
|
|
215
|
+
body: body
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export type Rule = {
|
|
220
|
+
readonly id: RuleId;
|
|
221
|
+
readonly tags: string[];
|
|
222
|
+
readonly score: number;
|
|
223
|
+
readonly createdAt: Date;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
export function ruleFromJSON({
|
|
227
|
+
id,
|
|
228
|
+
tags,
|
|
229
|
+
score,
|
|
230
|
+
createdAt
|
|
231
|
+
}: {
|
|
232
|
+
id: string;
|
|
233
|
+
tags: string[];
|
|
234
|
+
score: number;
|
|
235
|
+
createdAt: string;
|
|
236
|
+
}): Rule {
|
|
237
|
+
return {
|
|
238
|
+
id: toRuleId(id),
|
|
239
|
+
tags: tags,
|
|
240
|
+
score: score,
|
|
241
|
+
createdAt: new Date(createdAt)
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export type EntryInfo = {
|
|
246
|
+
readonly title: string;
|
|
247
|
+
readonly body: string;
|
|
248
|
+
readonly url: URL;
|
|
249
|
+
readonly publishedAt: Date;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
export function entryInfoFromJSON({
|
|
253
|
+
title,
|
|
254
|
+
body,
|
|
255
|
+
url,
|
|
256
|
+
publishedAt
|
|
257
|
+
}: {
|
|
258
|
+
title: string;
|
|
259
|
+
body: string;
|
|
260
|
+
url: string;
|
|
261
|
+
publishedAt: string;
|
|
262
|
+
}): EntryInfo {
|
|
263
|
+
return {title, body, url: toURL(url), publishedAt: new Date(publishedAt)};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export type FeedInfo = {
|
|
267
|
+
readonly url: URL;
|
|
268
|
+
readonly title: string;
|
|
269
|
+
readonly description: string;
|
|
270
|
+
readonly entries: EntryInfo[];
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
export function feedInfoFromJSON({
|
|
274
|
+
url,
|
|
275
|
+
title,
|
|
276
|
+
description,
|
|
277
|
+
entries
|
|
278
|
+
}: {
|
|
279
|
+
url: string;
|
|
280
|
+
title: string;
|
|
281
|
+
description: string;
|
|
282
|
+
entries: any[];
|
|
283
|
+
}): FeedInfo {
|
|
284
|
+
return {
|
|
285
|
+
url: toURL(url),
|
|
286
|
+
title,
|
|
287
|
+
description,
|
|
288
|
+
entries: entries.map(entryInfoFromJSON)
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export type TagInfo = {
|
|
293
|
+
readonly uid: string;
|
|
294
|
+
readonly name: string | null;
|
|
295
|
+
readonly link: string | null;
|
|
296
|
+
readonly categories: string[];
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
export function tagInfoFromJSON({
|
|
300
|
+
uid,
|
|
301
|
+
name,
|
|
302
|
+
link,
|
|
303
|
+
categories
|
|
304
|
+
}: {
|
|
305
|
+
uid: string;
|
|
306
|
+
name: string | null;
|
|
307
|
+
link: string | null;
|
|
308
|
+
categories: string[];
|
|
309
|
+
}): TagInfo {
|
|
310
|
+
return {uid, name: name, link: link, categories};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function noInfoTag(uid: string): TagInfo {
|
|
314
|
+
return {uid, name: null, link: null, categories: []};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export type UserSetting = {
|
|
318
|
+
readonly kind: string;
|
|
319
|
+
readonly type: string;
|
|
320
|
+
value: string | number | boolean;
|
|
321
|
+
readonly name: string;
|
|
322
|
+
readonly description: string;
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
export function userSettingFromJSON({
|
|
326
|
+
kind,
|
|
327
|
+
type,
|
|
328
|
+
value,
|
|
329
|
+
name,
|
|
330
|
+
description
|
|
331
|
+
}: {
|
|
332
|
+
kind: string;
|
|
333
|
+
type: string;
|
|
334
|
+
value: string | number | boolean;
|
|
335
|
+
name: string;
|
|
336
|
+
description: string;
|
|
337
|
+
}): UserSetting {
|
|
338
|
+
return {kind, type, value, name, description};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export class ResourceHistoryRecord {
|
|
342
|
+
readonly intervalStartedAt: Date;
|
|
343
|
+
readonly used: number;
|
|
344
|
+
readonly reserved: number;
|
|
345
|
+
|
|
346
|
+
constructor({intervalStartedAt, used, reserved}: {intervalStartedAt: Date; used: number; reserved: number}) {
|
|
347
|
+
this.intervalStartedAt = intervalStartedAt;
|
|
348
|
+
this.used = used;
|
|
349
|
+
this.reserved = reserved;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
total(): number {
|
|
353
|
+
return this.used + this.reserved;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export function resourceHistoryRecordFromJSON({
|
|
358
|
+
intervalStartedAt,
|
|
359
|
+
used,
|
|
360
|
+
reserved
|
|
361
|
+
}: {
|
|
362
|
+
intervalStartedAt: string;
|
|
363
|
+
used: number;
|
|
364
|
+
reserved: number;
|
|
365
|
+
}): ResourceHistoryRecord {
|
|
366
|
+
return new ResourceHistoryRecord({
|
|
367
|
+
intervalStartedAt: new Date(intervalStartedAt),
|
|
368
|
+
used,
|
|
369
|
+
reserved
|
|
370
|
+
});
|
|
371
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
|
|
3
|
+
export function timeSince(date: Date) {
|
|
4
|
+
const now = new Date();
|
|
5
|
+
|
|
6
|
+
const secondsPast = Math.floor((now.getTime() - date.getTime()) / 1000);
|
|
7
|
+
|
|
8
|
+
if (secondsPast < 60) {
|
|
9
|
+
return "<min";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const minutesPast = Math.floor(secondsPast / 60);
|
|
13
|
+
|
|
14
|
+
if (minutesPast < 60) {
|
|
15
|
+
return `${minutesPast}min`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const hoursPast = Math.floor(minutesPast / 60);
|
|
19
|
+
|
|
20
|
+
if (hoursPast < 24) {
|
|
21
|
+
return `${hoursPast}h`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const daysPast = Math.floor(hoursPast / 24);
|
|
25
|
+
|
|
26
|
+
if (daysPast < 30) {
|
|
27
|
+
return `${daysPast}d`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const monthsPast = Math.floor(daysPast / 30);
|
|
31
|
+
|
|
32
|
+
if (monthsPast < 12) {
|
|
33
|
+
return `${monthsPast}m`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const yearsPast = Math.floor(monthsPast / 12);
|
|
37
|
+
|
|
38
|
+
return `${yearsPast}y`;
|
|
39
|
+
}
|
package/src/main.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {createApp} from "vue";
|
|
2
|
+
import {createPinia} from "pinia";
|
|
3
|
+
import * as Sentry from "@sentry/vue";
|
|
4
|
+
|
|
5
|
+
import App from "./App.vue";
|
|
6
|
+
import router from "./router";
|
|
7
|
+
|
|
8
|
+
import FeedsList from "./components/FeedsList.vue";
|
|
9
|
+
import EntriesList from "./components/EntriesList.vue";
|
|
10
|
+
import RulesList from "./components/RulesList.vue";
|
|
11
|
+
import TagsList from "./components/TagsList.vue";
|
|
12
|
+
import ConfigSelector from "./components/ConfigSelector.vue";
|
|
13
|
+
import ConfigFlag from "./components/ConfigFlag.vue";
|
|
14
|
+
import EntryForList from "./components/EntryForList.vue";
|
|
15
|
+
import RuleConstructor from "./components/RuleConstructor.vue";
|
|
16
|
+
import RuleScoreUpdater from "./components/RuleScoreUpdater.vue";
|
|
17
|
+
import TagsFilter from "./components/TagsFilter.vue";
|
|
18
|
+
import TagsFilterElement from "./components/TagsFilterElement.vue";
|
|
19
|
+
import DiscoveryForm from "./components/DiscoveryForm.vue";
|
|
20
|
+
import FeedInfo from "./components/FeedInfo.vue";
|
|
21
|
+
import EntryInfo from "./components/EntryInfo.vue";
|
|
22
|
+
import OpmlUpload from "./components/OPMLUpload.vue";
|
|
23
|
+
import FeedForList from "./components/FeedForList.vue";
|
|
24
|
+
import SupertokensLogin from "./components/SupertokensLogin.vue";
|
|
25
|
+
import FeedsCollections from "./components/FeedsCollections.vue";
|
|
26
|
+
import FfunGithubButtons from "./components/FfunGithubButtons.vue";
|
|
27
|
+
import FfunTag from "./components/FfunTag.vue";
|
|
28
|
+
import SimplePagination from "./components/SimplePagination.vue";
|
|
29
|
+
import UserSetting from "./components/UserSetting.vue";
|
|
30
|
+
import OpenaiTokensUsage from "./components/OpenaiTokensUsage.vue";
|
|
31
|
+
|
|
32
|
+
import ScoreSelector from "./inputs/ScoreSelector.vue";
|
|
33
|
+
import InputMarker from "./inputs/Marker.vue";
|
|
34
|
+
|
|
35
|
+
import ValueUrl from "./values/URL.vue";
|
|
36
|
+
import ValueFeedId from "./values/FeedId.vue";
|
|
37
|
+
import ValueDateTime from "./values/DateTime.vue";
|
|
38
|
+
import ValueScore from "./values/Score.vue";
|
|
39
|
+
|
|
40
|
+
import WideLayout from "./layouts/WideLayout.vue";
|
|
41
|
+
import SidePanelLayout from "./layouts/SidePanelLayout.vue";
|
|
42
|
+
|
|
43
|
+
import {useSupertokens} from "@/stores/supertokens";
|
|
44
|
+
|
|
45
|
+
import VueCountdown from "@chenfengyuan/vue-countdown";
|
|
46
|
+
import GithubButton from "vue-github-button";
|
|
47
|
+
|
|
48
|
+
const app = createApp(App);
|
|
49
|
+
|
|
50
|
+
app.component("FeedsList", FeedsList);
|
|
51
|
+
app.component("EntriesList", EntriesList);
|
|
52
|
+
app.component("RulesList", RulesList);
|
|
53
|
+
app.component("TagsList", TagsList);
|
|
54
|
+
app.component("ConfigSelector", ConfigSelector);
|
|
55
|
+
app.component("ConfigFlag", ConfigFlag);
|
|
56
|
+
app.component("EntryForList", EntryForList);
|
|
57
|
+
app.component("RuleConstructor", RuleConstructor);
|
|
58
|
+
app.component("RuleScoreUpdater", RuleScoreUpdater);
|
|
59
|
+
app.component("TagsFilter", TagsFilter);
|
|
60
|
+
app.component("TagsFilterElement", TagsFilterElement);
|
|
61
|
+
app.component("DiscoveryForm", DiscoveryForm);
|
|
62
|
+
app.component("FeedInfo", FeedInfo);
|
|
63
|
+
app.component("EntryInfo", EntryInfo);
|
|
64
|
+
app.component("OpmlUpload", OpmlUpload);
|
|
65
|
+
app.component("FeedForList", FeedForList);
|
|
66
|
+
app.component("SupertokensLogin", SupertokensLogin);
|
|
67
|
+
app.component("FeedsCollections", FeedsCollections);
|
|
68
|
+
app.component("FfunGithubButtons", FfunGithubButtons);
|
|
69
|
+
app.component("FfunTag", FfunTag);
|
|
70
|
+
app.component("SimplePagination", SimplePagination);
|
|
71
|
+
app.component("UserSetting", UserSetting);
|
|
72
|
+
app.component("OpenaiTokensUsage", OpenaiTokensUsage);
|
|
73
|
+
|
|
74
|
+
app.component("ScoreSelector", ScoreSelector);
|
|
75
|
+
app.component("InputMarker", InputMarker);
|
|
76
|
+
|
|
77
|
+
app.component("ValueUrl", ValueUrl);
|
|
78
|
+
app.component("ValueFeedId", ValueFeedId);
|
|
79
|
+
app.component("ValueDateTime", ValueDateTime);
|
|
80
|
+
app.component("ValueScore", ValueScore);
|
|
81
|
+
|
|
82
|
+
app.component("WideLayout", WideLayout);
|
|
83
|
+
app.component("SidePanelLayout", SidePanelLayout);
|
|
84
|
+
|
|
85
|
+
app.component("vue-countdown", VueCountdown);
|
|
86
|
+
app.component("github-button", GithubButton);
|
|
87
|
+
|
|
88
|
+
app.use(createPinia());
|
|
89
|
+
app.use(router);
|
|
90
|
+
|
|
91
|
+
if (settings.sentryEnable) {
|
|
92
|
+
Sentry.init({
|
|
93
|
+
app,
|
|
94
|
+
dsn: settings.sentryDsn,
|
|
95
|
+
environment: settings.environment,
|
|
96
|
+
sampleRate: settings.sentrySampleRate,
|
|
97
|
+
attachStacktrace: true,
|
|
98
|
+
enableTracing: false,
|
|
99
|
+
integrations: []
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
app.mount("#app");
|
|
104
|
+
|
|
105
|
+
import * as api from "@/logic/api";
|
|
106
|
+
import * as settings from "@/logic/settings";
|
|
107
|
+
|
|
108
|
+
// must be copy of smart_url from backend
|
|
109
|
+
function smartUrl(domain: string, port: number) {
|
|
110
|
+
if (port == 80) {
|
|
111
|
+
return `http://${domain}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (port == 443) {
|
|
115
|
+
return `https://${domain}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return `http://${domain}:${port}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let supertokens: ReturnType<typeof useSupertokens> | null = null;
|
|
122
|
+
|
|
123
|
+
if (settings.authMode === settings.AuthMode.Supertokens) {
|
|
124
|
+
supertokens = useSupertokens();
|
|
125
|
+
|
|
126
|
+
supertokens.init({
|
|
127
|
+
apiDomain: smartUrl(settings.appDomain, settings.appPort),
|
|
128
|
+
apiBasePath: settings.authSupertokensApiBasePath,
|
|
129
|
+
appName: settings.appName,
|
|
130
|
+
resendAfter: settings.authSupertokensResendAfter
|
|
131
|
+
});
|
|
132
|
+
} else if (settings.authMode === settings.AuthMode.SingleUser) {
|
|
133
|
+
} else {
|
|
134
|
+
throw `Unknown auth mode: ${settings.authMode}`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function onSessionLost() {
|
|
138
|
+
if (supertokens !== null) {
|
|
139
|
+
await supertokens.logout();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
router.push({name: "main", params: {}});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
api.init({onSessionLost: onSessionLost});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {createRouter, createWebHistory} from "vue-router";
|
|
2
|
+
import MainView from "../views/MainView.vue";
|
|
3
|
+
import AuthView from "../views/AuthView.vue";
|
|
4
|
+
import FeedsView from "../views/FeedsView.vue";
|
|
5
|
+
import NewsView from "../views/NewsView.vue";
|
|
6
|
+
import RulesView from "../views/RulesView.vue";
|
|
7
|
+
import DiscoveryView from "../views/DiscoveryView.vue";
|
|
8
|
+
import CollectionsView from "../views/CollectionsView.vue";
|
|
9
|
+
import SettingsView from "../views/SettingsView.vue";
|
|
10
|
+
import * as e from "@/logic/enums";
|
|
11
|
+
|
|
12
|
+
// lazy view loading does not work with router.push function
|
|
13
|
+
// first attempt to router.push into not loaded view, will cause its loading, but will not change components
|
|
14
|
+
|
|
15
|
+
const router = createRouter({
|
|
16
|
+
history: createWebHistory(import.meta.env.BASE_URL),
|
|
17
|
+
routes: [
|
|
18
|
+
{
|
|
19
|
+
path: "/",
|
|
20
|
+
name: "main",
|
|
21
|
+
component: MainView
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
path: "/auth",
|
|
25
|
+
name: "auth",
|
|
26
|
+
component: AuthView
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
path: "/feeds",
|
|
30
|
+
name: e.MainPanelMode.Feeds,
|
|
31
|
+
component: FeedsView
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
path: "/news",
|
|
35
|
+
name: e.MainPanelMode.Entries,
|
|
36
|
+
component: NewsView
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
path: "/rules",
|
|
40
|
+
name: e.MainPanelMode.Rules,
|
|
41
|
+
component: RulesView
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
path: "/discovery",
|
|
45
|
+
name: e.MainPanelMode.Discovery,
|
|
46
|
+
component: DiscoveryView
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
path: "/collections",
|
|
50
|
+
name: e.MainPanelMode.Collections,
|
|
51
|
+
component: CollectionsView
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
path: "/settings",
|
|
55
|
+
name: e.MainPanelMode.Settings,
|
|
56
|
+
component: SettingsView
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export default router;
|