update
Browse files- README.md +141 -12
- apps/server/package.json +1 -1
- apps/server/src/app.controller.ts +10 -2
- apps/server/src/app.service.ts +0 -5
- apps/server/src/trpc/trpc.router.ts +7 -7
- apps/web/index.html +1 -0
- apps/web/package.json +1 -1
- apps/web/src/pages/accounts/index.tsx +1 -3
- apps/web/src/pages/feeds/index.tsx +53 -12
- apps/web/src/provider/trpc.tsx +11 -6
- apps/web/src/utils/env.ts +3 -0
- apps/web/src/vite-env.d.ts +1 -0
- package.json +1 -1
README.md
CHANGED
@@ -1,12 +1,141 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div align="center">
|
2 |
+
<img src="https://raw.githubusercontent.com/cooderl/wewe-rss/main/assets/logo.png" width="80" alt="预览"/>
|
3 |
+
|
4 |
+
<h1 align="center"><a href="https://github.com/cooderl/wewe-rss">WeWe RSS</a></h1>
|
5 |
+
|
6 |
+
更优雅的微信公众号订阅方式。
|
7 |
+
|
8 |
+
![主界面](https://raw.githubusercontent.com/cooderl/wewe-rss/main/assets/preview1.png)
|
9 |
+
|
10 |
+
</div>
|
11 |
+
|
12 |
+
## 功能
|
13 |
+
|
14 |
+
- [x] 支持微信公众号订阅(基于微信读书)
|
15 |
+
- [x] 后台自动定时更新内容
|
16 |
+
- [x] 微信公众号RSS生成(支持`.atom`\.`rss`\.`json`格式)
|
17 |
+
- [x] 支持全文内容输出,让阅读无障碍
|
18 |
+
- [x] 所有订阅源导出OPML
|
19 |
+
|
20 |
+
## 部署
|
21 |
+
|
22 |
+
### 一键部署(待完善添加模板)
|
23 |
+
|
24 |
+
你可以通过以下平台一键部署,只需填写本项目的URL即可。
|
25 |
+
|
26 |
+
[Zeabur](https://zeabur.com/)
|
27 |
+
|
28 |
+
[Railway](https://railway.app/)
|
29 |
+
|
30 |
+
[Hugging Face部署参考](https://github.com/cooderl/wewe-rss/issues/32)
|
31 |
+
|
32 |
+
### Docker Compose 部署
|
33 |
+
|
34 |
+
可参考 [docker-compose.yml](https://github.com/cooderl/wewe-rss/blob/main/docker-compose.yml) 和 [docker-compose.sqlite.yml](https://github.com/cooderl/wewe-rss/blob/main/docker-compose.sqlite.yml)
|
35 |
+
|
36 |
+
### Docker 命令启动
|
37 |
+
|
38 |
+
#### Sqlite
|
39 |
+
|
40 |
+
```sh
|
41 |
+
docker run -d \
|
42 |
+
--name wewe-rss \
|
43 |
+
-p 4000:4000 \
|
44 |
+
-e DATABASE_TYPE=sqlite \
|
45 |
+
-e AUTH_CODE=123567 \
|
46 |
+
-v $(pwd)/data:/app/data \
|
47 |
+
cooderl/wewe-rss-sqlite:latest
|
48 |
+
```
|
49 |
+
|
50 |
+
#### Mysql
|
51 |
+
|
52 |
+
1. 创建docker网络
|
53 |
+
|
54 |
+
```sh
|
55 |
+
docker network create wewe-rss
|
56 |
+
```
|
57 |
+
|
58 |
+
2. 启动 MySQL 数据库
|
59 |
+
|
60 |
+
```sh
|
61 |
+
docker run -d \
|
62 |
+
--name db \
|
63 |
+
-e MYSQL_ROOT_PASSWORD=123456 \
|
64 |
+
-e TZ='Asia/Shanghai' \
|
65 |
+
-e MYSQL_DATABASE='wewe-rss' \
|
66 |
+
-v db_data:/var/lib/mysql \
|
67 |
+
--network wewe-rss \
|
68 |
+
mysql:latest --default-authentication-plugin=mysql_native_password
|
69 |
+
```
|
70 |
+
|
71 |
+
3. 启动 Server
|
72 |
+
|
73 |
+
```sh
|
74 |
+
docker run -d \
|
75 |
+
--name wewe-rss \
|
76 |
+
-p 4000:4000 \
|
77 |
+
-e DATABASE_URL='mysql://root:123456@db:3306/wewe-rss?schema=public&connect_timeout=30&pool_timeout=30&socket_timeout=30' \
|
78 |
+
-e AUTH_CODE=123567 \
|
79 |
+
--network wewe-rss \
|
80 |
+
cooderl/wewe-rss:latest
|
81 |
+
|
82 |
+
```
|
83 |
+
|
84 |
+
[Nginx配置参考](https://raw.githubusercontent.com/cooderl/wewe-rss/main/assets/nginx.example.conf)
|
85 |
+
|
86 |
+
### 本地部署
|
87 |
+
|
88 |
+
如果你想本地部署,请使用 `pnpm install && pnpm run -r build && pnpm run start:server` 命令(可以配合 pm2 来守护进程,防止被杀死)。
|
89 |
+
|
90 |
+
## 环境变量
|
91 |
+
|
92 |
+
- `DATABASE_URL` (**必填项**)数据库地址,例如 `mysql://root:123456@127.0.0.1:3306/wewe-rss`。
|
93 |
+
|
94 |
+
- `DATABASE_TYPE` 数据库类型,使用 `sqlite` 时需要填写 `sqlite`。
|
95 |
+
|
96 |
+
- `AUTH_CODE` 服务端接口请求授权码,(`/feeds`路径不需要)。
|
97 |
+
|
98 |
+
- `SERVER_ORIGIN_URL` 服务端访问地址,用于生成RSS的完整路径(外网访问时,设置为服务器的公网 IP 或者域名地址)。
|
99 |
+
|
100 |
+
- `MAX_REQUEST_PER_MINUTE` 每分钟最大请求次数,默认 60。
|
101 |
+
|
102 |
+
- `FEED_MODE` 输出模式,可选值 `fulltext`(RSS全文模式会使接口响应会变慢,占用更多内存)。
|
103 |
+
|
104 |
+
- `CRON_EXPRESSION` 定时更新订阅源Cron表达式,默认为 `35 5,17 * * *`。
|
105 |
+
|
106 |
+
|
107 |
+
## 使用方式
|
108 |
+
|
109 |
+
1. 进入账号管理,点击添加账号,微信扫码登录微信读书账号。
|
110 |
+
<img width="400" src="./assets/preview2.png"/>
|
111 |
+
|
112 |
+
1. 进入公众号源,点击添加,通过提交微信公众号分享链接,订阅微信公众号。
|
113 |
+
**(添加频率过高容易被封控,等24小时解封)**
|
114 |
+
<img width="400" src="./assets/preview3.png"/>
|
115 |
+
|
116 |
+
|
117 |
+
## 本地开发
|
118 |
+
|
119 |
+
1. 安装 nodejs 18 和 pnpm;
|
120 |
+
2. 修改环境变量`cp ./apps/web/.env.local.example ./apps/web/.env`和`cp ./apps/server/.env.local.example ./apps/server/.env`
|
121 |
+
3. 执行 `pnpm install && pnpm dev` 即可。⚠️ 注意:此命令仅用于本地开发,不要用于部署!
|
122 |
+
4. 前端访问 `http://localhost:5173` ,后端访问 `http://localhost:4000`
|
123 |
+
|
124 |
+
## 风险声明
|
125 |
+
|
126 |
+
为了确保本项目的持久运行,某些接口请求将通过`weread.111965.xyz`进行转发。请放心,该转发服务不会保存任何数据。
|
127 |
+
|
128 |
+
## 打赏
|
129 |
+
|
130 |
+
如果您觉得我们的项目有价值,并希望帮助我们继续发展,可以用以下几种加密货币打赏:
|
131 |
+
|
132 |
+
BTC(Bitcoin): `1DGU9zRC8cvexq3W92Kzxqg5sNnbWPz9fE`
|
133 |
+
|
134 |
+
ETH(Ethereum, ERC20): `0x6bb8cef666c346ac3926fd32edd27d8246dcece0`
|
135 |
+
|
136 |
+
USDT(Tron, TRC20): `TLsukYHcXN34RXABZwppRE5AuPp8AWY7Wv`
|
137 |
+
|
138 |
+
|
139 |
+
## License
|
140 |
+
|
141 |
+
[MIT](https://raw.githubusercontent.com/cooderl/wewe-rss/main/LICENSE) @cooderl
|
apps/server/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
{
|
2 |
"name": "server",
|
3 |
-
"version": "1.
|
4 |
"description": "",
|
5 |
"author": "",
|
6 |
"private": true,
|
|
|
1 |
{
|
2 |
"name": "server",
|
3 |
+
"version": "1.8.0",
|
4 |
"description": "",
|
5 |
"author": "",
|
6 |
"private": true,
|
apps/server/src/app.controller.ts
CHANGED
@@ -1,9 +1,14 @@
|
|
1 |
import { Controller, Get, Redirect, Render } from '@nestjs/common';
|
2 |
import { AppService } from './app.service';
|
|
|
|
|
3 |
|
4 |
@Controller()
|
5 |
export class AppController {
|
6 |
-
constructor(
|
|
|
|
|
|
|
7 |
|
8 |
@Get()
|
9 |
getHello(): string {
|
@@ -23,9 +28,12 @@ export class AppController {
|
|
23 |
@Render('index.hbs')
|
24 |
dashRender() {
|
25 |
const { originUrl: weweRssServerOriginUrl } =
|
26 |
-
this.
|
|
|
|
|
27 |
return {
|
28 |
weweRssServerOriginUrl,
|
|
|
29 |
};
|
30 |
}
|
31 |
}
|
|
|
1 |
import { Controller, Get, Redirect, Render } from '@nestjs/common';
|
2 |
import { AppService } from './app.service';
|
3 |
+
import { ConfigService } from '@nestjs/config';
|
4 |
+
import { ConfigurationType } from './configuration';
|
5 |
|
6 |
@Controller()
|
7 |
export class AppController {
|
8 |
+
constructor(
|
9 |
+
private readonly appService: AppService,
|
10 |
+
private readonly configService: ConfigService,
|
11 |
+
) {}
|
12 |
|
13 |
@Get()
|
14 |
getHello(): string {
|
|
|
28 |
@Render('index.hbs')
|
29 |
dashRender() {
|
30 |
const { originUrl: weweRssServerOriginUrl } =
|
31 |
+
this.configService.get<ConfigurationType['feed']>('feed')!;
|
32 |
+
const { code } = this.configService.get<ConfigurationType['auth']>('auth')!;
|
33 |
+
|
34 |
return {
|
35 |
weweRssServerOriginUrl,
|
36 |
+
enabledAuthCode: !!code,
|
37 |
};
|
38 |
}
|
39 |
}
|
apps/server/src/app.service.ts
CHANGED
@@ -1,6 +1,5 @@
|
|
1 |
import { Injectable } from '@nestjs/common';
|
2 |
import { ConfigService } from '@nestjs/config';
|
3 |
-
import { ConfigurationType } from './configuration';
|
4 |
|
5 |
@Injectable()
|
6 |
export class AppService {
|
@@ -12,8 +11,4 @@ export class AppService {
|
|
12 |
</div>
|
13 |
`;
|
14 |
}
|
15 |
-
|
16 |
-
getFeedConfig() {
|
17 |
-
return this.configService.get<ConfigurationType['feed']>('feed')!;
|
18 |
-
}
|
19 |
}
|
|
|
1 |
import { Injectable } from '@nestjs/common';
|
2 |
import { ConfigService } from '@nestjs/config';
|
|
|
3 |
|
4 |
@Injectable()
|
5 |
export class AppService {
|
|
|
11 |
</div>
|
12 |
`;
|
13 |
}
|
|
|
|
|
|
|
|
|
14 |
}
|
apps/server/src/trpc/trpc.router.ts
CHANGED
@@ -22,12 +22,12 @@ export class TrpcRouter {
|
|
22 |
list: this.trpcService.protectedProcedure
|
23 |
.input(
|
24 |
z.object({
|
25 |
-
limit: z.number().min(1).max(
|
26 |
cursor: z.string().nullish(),
|
27 |
}),
|
28 |
)
|
29 |
.query(async ({ input }) => {
|
30 |
-
const limit = input.limit ??
|
31 |
const { cursor } = input;
|
32 |
|
33 |
const items = await this.prismaService.account.findMany({
|
@@ -132,12 +132,12 @@ export class TrpcRouter {
|
|
132 |
list: this.trpcService.protectedProcedure
|
133 |
.input(
|
134 |
z.object({
|
135 |
-
limit: z.number().min(1).max(
|
136 |
cursor: z.string().nullish(),
|
137 |
}),
|
138 |
)
|
139 |
.query(async ({ input }) => {
|
140 |
-
const limit = input.limit ??
|
141 |
const { cursor } = input;
|
142 |
|
143 |
const items = await this.prismaService.feed.findMany({
|
@@ -247,13 +247,13 @@ export class TrpcRouter {
|
|
247 |
list: this.trpcService.protectedProcedure
|
248 |
.input(
|
249 |
z.object({
|
250 |
-
limit: z.number().min(1).max(
|
251 |
cursor: z.string().nullish(),
|
252 |
mpId: z.string().nullish(),
|
253 |
}),
|
254 |
)
|
255 |
.query(async ({ input }) => {
|
256 |
-
const limit = input.limit ??
|
257 |
const { cursor, mpId } = input;
|
258 |
|
259 |
const items = await this.prismaService.article.findMany({
|
@@ -401,7 +401,7 @@ export class TrpcRouter {
|
|
401 |
const authCode =
|
402 |
this.configService.get<ConfigurationType['auth']>('auth')!.code;
|
403 |
|
404 |
-
if (req.headers.authorization !== authCode) {
|
405 |
return {
|
406 |
errorMsg: 'authCode不正确!',
|
407 |
};
|
|
|
22 |
list: this.trpcService.protectedProcedure
|
23 |
.input(
|
24 |
z.object({
|
25 |
+
limit: z.number().min(1).max(500).nullish(),
|
26 |
cursor: z.string().nullish(),
|
27 |
}),
|
28 |
)
|
29 |
.query(async ({ input }) => {
|
30 |
+
const limit = input.limit ?? 500;
|
31 |
const { cursor } = input;
|
32 |
|
33 |
const items = await this.prismaService.account.findMany({
|
|
|
132 |
list: this.trpcService.protectedProcedure
|
133 |
.input(
|
134 |
z.object({
|
135 |
+
limit: z.number().min(1).max(500).nullish(),
|
136 |
cursor: z.string().nullish(),
|
137 |
}),
|
138 |
)
|
139 |
.query(async ({ input }) => {
|
140 |
+
const limit = input.limit ?? 500;
|
141 |
const { cursor } = input;
|
142 |
|
143 |
const items = await this.prismaService.feed.findMany({
|
|
|
247 |
list: this.trpcService.protectedProcedure
|
248 |
.input(
|
249 |
z.object({
|
250 |
+
limit: z.number().min(1).max(500).nullish(),
|
251 |
cursor: z.string().nullish(),
|
252 |
mpId: z.string().nullish(),
|
253 |
}),
|
254 |
)
|
255 |
.query(async ({ input }) => {
|
256 |
+
const limit = input.limit ?? 500;
|
257 |
const { cursor, mpId } = input;
|
258 |
|
259 |
const items = await this.prismaService.article.findMany({
|
|
|
401 |
const authCode =
|
402 |
this.configService.get<ConfigurationType['auth']>('auth')!.code;
|
403 |
|
404 |
+
if (authCode && req.headers.authorization !== authCode) {
|
405 |
return {
|
406 |
errorMsg: 'authCode不正确!',
|
407 |
};
|
apps/web/index.html
CHANGED
@@ -11,6 +11,7 @@
|
|
11 |
<div id="root"></div>
|
12 |
<script>
|
13 |
window.__WEWE_RSS_SERVER_ORIGIN_URL__ = '{{ weweRssServerOriginUrl }}';
|
|
|
14 |
</script>
|
15 |
<script type="module" src="/src/main.tsx"></script>
|
16 |
</body>
|
|
|
11 |
<div id="root"></div>
|
12 |
<script>
|
13 |
window.__WEWE_RSS_SERVER_ORIGIN_URL__ = '{{ weweRssServerOriginUrl }}';
|
14 |
+
window.__WEWE_RSS_ENABLED_AUTH_CODE__ = {{ enabledAuthCode }};
|
15 |
</script>
|
16 |
<script type="module" src="/src/main.tsx"></script>
|
17 |
</body>
|
apps/web/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
{
|
2 |
"name": "web",
|
3 |
"private": true,
|
4 |
-
"version": "1.
|
5 |
"type": "module",
|
6 |
"scripts": {
|
7 |
"dev": "vite",
|
|
|
1 |
{
|
2 |
"name": "web",
|
3 |
"private": true,
|
4 |
+
"version": "1.8.0",
|
5 |
"type": "module",
|
6 |
"scripts": {
|
7 |
"dev": "vite",
|
apps/web/src/pages/accounts/index.tsx
CHANGED
@@ -25,9 +25,7 @@ import { statusMap } from '@web/constants';
|
|
25 |
const AccountPage = () => {
|
26 |
const { isOpen, onOpen, onClose, onOpenChange } = useDisclosure();
|
27 |
|
28 |
-
const { refetch, data, isFetching } = trpc.account.list.useQuery({
|
29 |
-
limit: 100,
|
30 |
-
});
|
31 |
|
32 |
const { mutateAsync: updateAccount } = trpc.account.edit.useMutation({});
|
33 |
|
|
|
25 |
const AccountPage = () => {
|
26 |
const { isOpen, onOpen, onClose, onOpenChange } = useDisclosure();
|
27 |
|
28 |
+
const { refetch, data, isFetching } = trpc.account.list.useQuery({});
|
|
|
|
|
29 |
|
30 |
const { mutateAsync: updateAccount } = trpc.account.edit.useMutation({});
|
31 |
|
apps/web/src/pages/feeds/index.tsx
CHANGED
@@ -30,9 +30,7 @@ const Feeds = () => {
|
|
30 |
|
31 |
const { isOpen, onOpen, onOpenChange, onClose } = useDisclosure();
|
32 |
const { refetch: refetchFeedList, data: feedData } = trpc.feed.list.useQuery(
|
33 |
-
{
|
34 |
-
limit: 100,
|
35 |
-
},
|
36 |
{
|
37 |
refetchOnWindowFocus: true,
|
38 |
},
|
@@ -93,6 +91,38 @@ const Feeds = () => {
|
|
93 |
return feedData?.items.find((item) => item.id === currentMpId);
|
94 |
}, [currentMpId, feedData?.items]);
|
95 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
return (
|
97 |
<>
|
98 |
<div className="h-full flex justify-between">
|
@@ -238,15 +268,26 @@ const Feeds = () => {
|
|
238 |
</Tooltip>
|
239 |
</div>
|
240 |
) : (
|
241 |
-
<
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
250 |
)}
|
251 |
</div>
|
252 |
<div className="p-2 overflow-y-auto">
|
|
|
30 |
|
31 |
const { isOpen, onOpen, onOpenChange, onClose } = useDisclosure();
|
32 |
const { refetch: refetchFeedList, data: feedData } = trpc.feed.list.useQuery(
|
33 |
+
{},
|
|
|
|
|
34 |
{
|
35 |
refetchOnWindowFocus: true,
|
36 |
},
|
|
|
91 |
return feedData?.items.find((item) => item.id === currentMpId);
|
92 |
}, [currentMpId, feedData?.items]);
|
93 |
|
94 |
+
const handleExportOpml = async (ev) => {
|
95 |
+
ev.preventDefault();
|
96 |
+
ev.stopPropagation();
|
97 |
+
if (!feedData?.items?.length) {
|
98 |
+
console.warn('没有订阅源');
|
99 |
+
return;
|
100 |
+
}
|
101 |
+
|
102 |
+
let opmlContent = `<?xml version="1.0" encoding="UTF-8"?>
|
103 |
+
<opml version="2.0">
|
104 |
+
<head>
|
105 |
+
<title>WeWeRSS 所有订阅源</title>
|
106 |
+
</head>
|
107 |
+
<body>
|
108 |
+
`;
|
109 |
+
|
110 |
+
feedData?.items.forEach((sub) => {
|
111 |
+
opmlContent += ` <outline text="${sub.mpName}" type="rss" xmlUrl="${window.location.origin}/feeds/${sub.id}.atom" htmlUrl="${window.location.origin}/feeds/${sub.id}.atom"/>\n`;
|
112 |
+
});
|
113 |
+
|
114 |
+
opmlContent += ` </body>
|
115 |
+
</opml>`;
|
116 |
+
|
117 |
+
const blob = new Blob([opmlContent], { type: 'text/xml;charset=utf-8;' });
|
118 |
+
const link = document.createElement('a');
|
119 |
+
link.href = URL.createObjectURL(blob);
|
120 |
+
link.download = 'WeWeRSS-All.opml';
|
121 |
+
document.body.appendChild(link);
|
122 |
+
link.click();
|
123 |
+
document.body.removeChild(link);
|
124 |
+
};
|
125 |
+
|
126 |
return (
|
127 |
<>
|
128 |
<div className="h-full flex justify-between">
|
|
|
268 |
</Tooltip>
|
269 |
</div>
|
270 |
) : (
|
271 |
+
<div className="flex gap-2">
|
272 |
+
<Link
|
273 |
+
href="#"
|
274 |
+
color="foreground"
|
275 |
+
onClick={handleExportOpml}
|
276 |
+
size="sm"
|
277 |
+
>
|
278 |
+
导出OPML
|
279 |
+
</Link>
|
280 |
+
<Divider orientation="vertical" />
|
281 |
+
<Link
|
282 |
+
size="sm"
|
283 |
+
showAnchorIcon
|
284 |
+
target="_blank"
|
285 |
+
href={`${serverOriginUrl}/feeds/all.atom`}
|
286 |
+
color="foreground"
|
287 |
+
>
|
288 |
+
RSS
|
289 |
+
</Link>
|
290 |
+
</div>
|
291 |
)}
|
292 |
</div>
|
293 |
<div className="p-2 overflow-y-auto">
|
apps/web/src/provider/trpc.tsx
CHANGED
@@ -5,13 +5,19 @@ import { useState } from 'react';
|
|
5 |
import { toast } from 'sonner';
|
6 |
import { isTRPCClientError, trpc } from '../utils/trpc';
|
7 |
import { getAuthCode, setAuthCode } from '../utils/auth';
|
8 |
-
import { serverOriginUrl } from '../utils/env';
|
9 |
|
10 |
export const TrpcProvider: React.FC<{ children: React.ReactNode }> = ({
|
11 |
children,
|
12 |
}) => {
|
13 |
const navigate = useNavigate();
|
14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
const [queryClient] = useState(
|
16 |
() =>
|
17 |
new QueryClient({
|
@@ -38,8 +44,7 @@ export const TrpcProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
38 |
description: error.message,
|
39 |
});
|
40 |
|
41 |
-
|
42 |
-
navigate('/login');
|
43 |
} else {
|
44 |
toast.error('请求失败!', {
|
45 |
description: error.message,
|
@@ -56,8 +61,7 @@ export const TrpcProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
56 |
toast.error('无权限', {
|
57 |
description: error.message,
|
58 |
});
|
59 |
-
|
60 |
-
navigate('/login');
|
61 |
} else {
|
62 |
toast.error('请求失败!', {
|
63 |
description: error.message,
|
@@ -82,9 +86,10 @@ export const TrpcProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
82 |
const token = getAuthCode();
|
83 |
|
84 |
if (!token) {
|
85 |
-
|
86 |
return {};
|
87 |
}
|
|
|
88 |
return token
|
89 |
? {
|
90 |
Authorization: `${token}`,
|
|
|
5 |
import { toast } from 'sonner';
|
6 |
import { isTRPCClientError, trpc } from '../utils/trpc';
|
7 |
import { getAuthCode, setAuthCode } from '../utils/auth';
|
8 |
+
import { enabledAuthCode, serverOriginUrl } from '../utils/env';
|
9 |
|
10 |
export const TrpcProvider: React.FC<{ children: React.ReactNode }> = ({
|
11 |
children,
|
12 |
}) => {
|
13 |
const navigate = useNavigate();
|
14 |
|
15 |
+
const handleNoAuth = () => {
|
16 |
+
if (enabledAuthCode) {
|
17 |
+
setAuthCode('');
|
18 |
+
navigate('/login');
|
19 |
+
}
|
20 |
+
};
|
21 |
const [queryClient] = useState(
|
22 |
() =>
|
23 |
new QueryClient({
|
|
|
44 |
description: error.message,
|
45 |
});
|
46 |
|
47 |
+
handleNoAuth();
|
|
|
48 |
} else {
|
49 |
toast.error('请求失败!', {
|
50 |
description: error.message,
|
|
|
61 |
toast.error('无权限', {
|
62 |
description: error.message,
|
63 |
});
|
64 |
+
handleNoAuth();
|
|
|
65 |
} else {
|
66 |
toast.error('请求失败!', {
|
67 |
description: error.message,
|
|
|
86 |
const token = getAuthCode();
|
87 |
|
88 |
if (!token) {
|
89 |
+
handleNoAuth();
|
90 |
return {};
|
91 |
}
|
92 |
+
|
93 |
return token
|
94 |
? {
|
95 |
Authorization: `${token}`,
|
apps/web/src/utils/env.ts
CHANGED
@@ -5,3 +5,6 @@ export const serverOriginUrl = isProd
|
|
5 |
: import.meta.env.VITE_SERVER_ORIGIN_URL;
|
6 |
|
7 |
export const appVersion = __APP_VERSION__;
|
|
|
|
|
|
|
|
5 |
: import.meta.env.VITE_SERVER_ORIGIN_URL;
|
6 |
|
7 |
export const appVersion = __APP_VERSION__;
|
8 |
+
|
9 |
+
export const enabledAuthCode =
|
10 |
+
window.__WEWE_RSS_ENABLED_AUTH_CODE__ === false ? false : true;
|
apps/web/src/vite-env.d.ts
CHANGED
@@ -7,6 +7,7 @@ interface ImportMetaEnv {
|
|
7 |
|
8 |
interface Window {
|
9 |
__WEWE_RSS_SERVER_ORIGIN_URL__?: string;
|
|
|
10 |
}
|
11 |
|
12 |
declare const __APP_VERSION__: string;
|
|
|
7 |
|
8 |
interface Window {
|
9 |
__WEWE_RSS_SERVER_ORIGIN_URL__?: string;
|
10 |
+
__WEWE_RSS_ENABLED_AUTH_CODE__?: boolean;
|
11 |
}
|
12 |
|
13 |
declare const __APP_VERSION__: string;
|
package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
{
|
2 |
"name": "wewe-rss",
|
3 |
-
"version": "1.
|
4 |
"private": true,
|
5 |
"author": "cooderl <cooder@111965.xyz>",
|
6 |
"description": "",
|
|
|
1 |
{
|
2 |
"name": "wewe-rss",
|
3 |
+
"version": "1.8.0",
|
4 |
"private": true,
|
5 |
"author": "cooderl <cooder@111965.xyz>",
|
6 |
"description": "",
|