Upload 88 files
Browse files- README.md +161 -12
- apps/server/package.json +1 -1
- apps/server/src/app.controller.ts +13 -3
- apps/server/src/feeds/feeds.controller.ts +15 -0
- apps/server/src/feeds/feeds.service.ts +34 -3
- apps/web/index.html +2 -2
- apps/web/package.json +1 -1
- apps/web/src/components/Nav.tsx +5 -2
- docker-compose.yml +4 -4
- package.json +1 -1
- wewe-rss-dingtalk/main.py +2 -2
README.md
CHANGED
@@ -1,12 +1,161 @@
|
|
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] v2.x版本使用全新接口,更加稳定
|
15 |
+
- [x] 支持微信公众号订阅(基于微信读书)
|
16 |
+
- [x] 后台自动定时更新内容
|
17 |
+
- [x] 微信公众号RSS生成(支持`.atom`\.`rss`\.`json`格式)
|
18 |
+
- [x] 支持全文内容输出,让阅读无障碍
|
19 |
+
- [x] 所有订阅源导出OPML
|
20 |
+
- [x] 支持通过/feeds/all.(json|rss|atom)接口和/feeds/:feed对标题进行过滤:使用title_include和title_exclude参数,支持使用`|`实现多个关键词的 或 逻辑
|
21 |
+
> {{ORIGIN_URL}}/feeds/all.atom?title_include=张三
|
22 |
+
>
|
23 |
+
> {{ORIGIN_URL}}/feeds/MP_WXS_123.json?limit=30&title_include=张三|李四|王五&title_exclude=张三丰|赵六
|
24 |
+
- [x] 支持通过/feeds/:feed接口触发单个feedid更新:使用update=true参数(实时返回结果不包含更新后的articles,要获取更新后的articles需去掉update参数再请求一次)
|
25 |
+
> {{ORIGIN_URL}}/feeds/MP_WXS_123.rss?update=true
|
26 |
+
|
27 |
+
## 部署
|
28 |
+
|
29 |
+
### 一键部署(待完善添加模板)
|
30 |
+
|
31 |
+
你可以通过以下平台一键部署,只需填写本项目的URL即可。
|
32 |
+
|
33 |
+
[Zeabur](https://zeabur.com/)
|
34 |
+
|
35 |
+
[![Deploy on Zeabur](https://zeabur.com/button.svg)](https://zeabur.com/templates/DI9BBD)
|
36 |
+
|
37 |
+
[Railway](https://railway.app/)
|
38 |
+
|
39 |
+
[Hugging Face部署参考](https://github.com/cooderl/wewe-rss/issues/32)
|
40 |
+
|
41 |
+
### Docker Compose 部署
|
42 |
+
|
43 |
+
可参考 [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)
|
44 |
+
|
45 |
+
### Docker 命令启动
|
46 |
+
|
47 |
+
#### Sqlite
|
48 |
+
|
49 |
+
```sh
|
50 |
+
docker run -d \
|
51 |
+
--name wewe-rss \
|
52 |
+
-p 4000:4000 \
|
53 |
+
-e DATABASE_TYPE=sqlite \
|
54 |
+
-e AUTH_CODE=123567 \
|
55 |
+
-v $(pwd)/data:/app/data \
|
56 |
+
cooderl/wewe-rss-sqlite:latest
|
57 |
+
```
|
58 |
+
|
59 |
+
#### Mysql
|
60 |
+
|
61 |
+
1. 创建docker网络
|
62 |
+
|
63 |
+
```sh
|
64 |
+
docker network create wewe-rss
|
65 |
+
```
|
66 |
+
|
67 |
+
2. 启动 MySQL 数据库
|
68 |
+
|
69 |
+
```sh
|
70 |
+
docker run -d \
|
71 |
+
--name db \
|
72 |
+
-e MYSQL_ROOT_PASSWORD=123456 \
|
73 |
+
-e TZ='Asia/Shanghai' \
|
74 |
+
-e MYSQL_DATABASE='wewe-rss' \
|
75 |
+
-v db_data:/var/lib/mysql \
|
76 |
+
--network wewe-rss \
|
77 |
+
mysql:8.3.0 --mysql-native-password=ON
|
78 |
+
```
|
79 |
+
|
80 |
+
3. 启动 Server
|
81 |
+
|
82 |
+
```sh
|
83 |
+
docker run -d \
|
84 |
+
--name wewe-rss \
|
85 |
+
-p 4000:4000 \
|
86 |
+
-e DATABASE_URL='mysql://root:123456@db:3306/wewe-rss?schema=public&connect_timeout=30&pool_timeout=30&socket_timeout=30' \
|
87 |
+
-e AUTH_CODE=123567 \
|
88 |
+
--network wewe-rss \
|
89 |
+
cooderl/wewe-rss:latest
|
90 |
+
|
91 |
+
```
|
92 |
+
|
93 |
+
[Nginx配置参考](https://raw.githubusercontent.com/cooderl/wewe-rss/main/assets/nginx.example.conf)
|
94 |
+
|
95 |
+
### 本地部署
|
96 |
+
|
97 |
+
如果你想本地部署,请使用 `pnpm install && pnpm run -r build && pnpm run start:server` 命令(可以配合 pm2 来守护进程,防止被杀死)。
|
98 |
+
|
99 |
+
## 环境变量
|
100 |
+
|
101 |
+
- `DATABASE_URL` (**必填项**)数据库地址,例如 `mysql://root:123456@127.0.0.1:3306/wewe-rss`。
|
102 |
+
|
103 |
+
- `DATABASE_TYPE` 数据库类型,使用 `sqlite` 时需要填写 `sqlite`。
|
104 |
+
|
105 |
+
- `AUTH_CODE` 服务端接口请求授权码,如果设置为空字符或不设置将不启用。(`/feeds`路径不需要)
|
106 |
+
|
107 |
+
- `SERVER_ORIGIN_URL` 服务端访问地址,用于生成RSS的完整路径(外网访问时,设置为服务器的公网 IP 或者域名地址)。
|
108 |
+
|
109 |
+
- `MAX_REQUEST_PER_MINUTE` 每分钟最大请求次数,默认 60。
|
110 |
+
|
111 |
+
- `FEED_MODE` 输出模式,可选值 `fulltext`(RSS全文模式会使接口响应会变慢,占用更多内存)。
|
112 |
+
|
113 |
+
- `CRON_EXPRESSION` 定时更新订阅源Cron表达式,默认为 `35 5,17 * * *`。
|
114 |
+
|
115 |
+
## 支持钉钉通知
|
116 |
+
|
117 |
+
进入wewe-rss-dingtalk目录按照README.md指引部署
|
118 |
+
|
119 |
+
## 使用方式
|
120 |
+
|
121 |
+
1. 进入账号管理,点击添加账号,微信扫码登录微信读书账号。
|
122 |
+
<img width="400" src="./assets/preview2.png"/>
|
123 |
+
|
124 |
+
1. 进入公众号源,点击添加,通过提交微信公众号分享链接,订阅微信公众号。
|
125 |
+
**(添加频率过高容易被封控,等24小时解封)**
|
126 |
+
<img width="400" src="./assets/preview3.png"/>
|
127 |
+
|
128 |
+
|
129 |
+
## 账号状态说明
|
130 |
+
|
131 |
+
- 今日小黑屋
|
132 |
+
> 账号被封控,等一天恢复
|
133 |
+
> 如果账号正常,可以通过重启服务/容器清除小黑屋记录
|
134 |
+
|
135 |
+
- 禁用
|
136 |
+
> 不使用该账号
|
137 |
+
|
138 |
+
- 失效
|
139 |
+
> 账号登录状态失效,需要重新登录
|
140 |
+
|
141 |
+
## 本地开发
|
142 |
+
|
143 |
+
1. 安装 nodejs 18 和 pnpm;
|
144 |
+
2. 修改环境变量`cp ./apps/web/.env.local.example ./apps/web/.env`和`cp ./apps/server/.env.local.example ./apps/server/.env`
|
145 |
+
3. 执行 `pnpm install && pnpm dev` 即可。⚠️ 注意:此命令仅��于本地开发,不要用于部署!
|
146 |
+
4. 前端访问 `http://localhost:5173` ,后端访问 `http://localhost:4000`
|
147 |
+
|
148 |
+
## 贡献者
|
149 |
+
|
150 |
+
<a href="https://github.com/cooderl/wewe-rss/graphs/contributors">
|
151 |
+
<img src="https://contrib.rocks/image?repo=cooderl/wewe-rss" />
|
152 |
+
</a>
|
153 |
+
|
154 |
+
|
155 |
+
## 风险声明
|
156 |
+
|
157 |
+
为了确保本项目的持久运行,某些接口请求将通过`weread.111965.xyz`进行转发。请放心,该转发服务不会保存任何数据。
|
158 |
+
|
159 |
+
## License
|
160 |
+
|
161 |
+
[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": "2.
|
4 |
"description": "",
|
5 |
"author": "",
|
6 |
"private": true,
|
|
|
1 |
{
|
2 |
"name": "server",
|
3 |
+
"version": "2.4.1",
|
4 |
"description": "",
|
5 |
"author": "",
|
6 |
"private": true,
|
apps/server/src/app.controller.ts
CHANGED
@@ -1,7 +1,8 @@
|
|
1 |
-
import { Controller, Get,
|
2 |
import { AppService } from './app.service';
|
3 |
import { ConfigService } from '@nestjs/config';
|
4 |
import { ConfigurationType } from './configuration';
|
|
|
5 |
|
6 |
@Controller()
|
7 |
export class AppController {
|
@@ -21,8 +22,14 @@ export class AppController {
|
|
21 |
}
|
22 |
|
23 |
@Get('favicon.ico')
|
24 |
-
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
|
27 |
@Get('/dash*')
|
28 |
@Render('index.hbs')
|
@@ -34,6 +41,9 @@ export class AppController {
|
|
34 |
return {
|
35 |
weweRssServerOriginUrl,
|
36 |
enabledAuthCode: !!code,
|
|
|
|
|
|
|
37 |
};
|
38 |
}
|
39 |
}
|
|
|
1 |
+
import { Controller, Get, Response, Render } from '@nestjs/common';
|
2 |
import { AppService } from './app.service';
|
3 |
import { ConfigService } from '@nestjs/config';
|
4 |
import { ConfigurationType } from './configuration';
|
5 |
+
import { Response as Res } from 'express';
|
6 |
|
7 |
@Controller()
|
8 |
export class AppController {
|
|
|
22 |
}
|
23 |
|
24 |
@Get('favicon.ico')
|
25 |
+
getFavicon(
|
26 |
+
@Response() res: Res,
|
27 |
+
) {
|
28 |
+
const imgContent = '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'
|
29 |
+
const imgBuffer = Buffer.from(imgContent, 'base64');
|
30 |
+
res.setHeader('Content-Type', 'image/png');
|
31 |
+
res.send(imgBuffer);
|
32 |
+
}
|
33 |
|
34 |
@Get('/dash*')
|
35 |
@Render('index.hbs')
|
|
|
41 |
return {
|
42 |
weweRssServerOriginUrl,
|
43 |
enabledAuthCode: !!code,
|
44 |
+
iconUrl: weweRssServerOriginUrl
|
45 |
+
? `${weweRssServerOriginUrl}/favicon.ico`
|
46 |
+
: 'https://r2-assets.111965.xyz/wewe-rss.png'
|
47 |
};
|
48 |
}
|
49 |
}
|
apps/server/src/feeds/feeds.controller.ts
CHANGED
@@ -29,13 +29,18 @@ export class FeedsController {
|
|
29 |
@Response() res: Res,
|
30 |
@Query('limit', new DefaultValuePipe(30), ParseIntPipe) limit: number = 30,
|
31 |
@Query('mode') mode: string,
|
|
|
|
|
32 |
) {
|
33 |
const path = req.path;
|
34 |
const type = path.split('.').pop() || '';
|
|
|
35 |
const { content, mimeType } = await this.feedsService.handleGenerateFeed({
|
36 |
type,
|
37 |
limit,
|
38 |
mode,
|
|
|
|
|
39 |
});
|
40 |
|
41 |
res.setHeader('Content-Type', mimeType);
|
@@ -48,14 +53,24 @@ export class FeedsController {
|
|
48 |
@Param('feed') feed: string,
|
49 |
@Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number = 10,
|
50 |
@Query('mode') mode: string,
|
|
|
|
|
|
|
51 |
) {
|
52 |
const [id, type] = feed.split('.');
|
53 |
this.logger.log('getFeed: ', id);
|
|
|
|
|
|
|
|
|
|
|
54 |
const { content, mimeType } = await this.feedsService.handleGenerateFeed({
|
55 |
id,
|
56 |
type,
|
57 |
limit,
|
58 |
mode,
|
|
|
|
|
59 |
});
|
60 |
|
61 |
res.setHeader('Content-Type', mimeType);
|
|
|
29 |
@Response() res: Res,
|
30 |
@Query('limit', new DefaultValuePipe(30), ParseIntPipe) limit: number = 30,
|
31 |
@Query('mode') mode: string,
|
32 |
+
@Query('title_include') title_include: string,
|
33 |
+
@Query('title_exclude') title_exclude: string,
|
34 |
) {
|
35 |
const path = req.path;
|
36 |
const type = path.split('.').pop() || '';
|
37 |
+
|
38 |
const { content, mimeType } = await this.feedsService.handleGenerateFeed({
|
39 |
type,
|
40 |
limit,
|
41 |
mode,
|
42 |
+
title_include,
|
43 |
+
title_exclude,
|
44 |
});
|
45 |
|
46 |
res.setHeader('Content-Type', mimeType);
|
|
|
53 |
@Param('feed') feed: string,
|
54 |
@Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number = 10,
|
55 |
@Query('mode') mode: string,
|
56 |
+
@Query('title_include') title_include: string,
|
57 |
+
@Query('title_exclude') title_exclude: string,
|
58 |
+
@Query('update') update: boolean = false,
|
59 |
) {
|
60 |
const [id, type] = feed.split('.');
|
61 |
this.logger.log('getFeed: ', id);
|
62 |
+
|
63 |
+
if(update) {
|
64 |
+
this.feedsService.updateFeed(id);
|
65 |
+
}
|
66 |
+
|
67 |
const { content, mimeType } = await this.feedsService.handleGenerateFeed({
|
68 |
id,
|
69 |
type,
|
70 |
limit,
|
71 |
mode,
|
72 |
+
title_include,
|
73 |
+
title_exclude,
|
74 |
});
|
75 |
|
76 |
res.setHeader('Content-Type', mimeType);
|
apps/server/src/feeds/feeds.service.ts
CHANGED
@@ -6,7 +6,7 @@ import { feedMimeTypeMap, feedTypes } from '@server/constants';
|
|
6 |
import { ConfigService } from '@nestjs/config';
|
7 |
import { Article, Feed as FeedInfo } from '@prisma/client';
|
8 |
import { ConfigurationType } from '@server/configuration';
|
9 |
-
import { Feed } from 'feed';
|
10 |
import got, { Got } from 'got';
|
11 |
import { load } from 'cheerio';
|
12 |
import { minify } from 'html-minifier';
|
@@ -98,6 +98,7 @@ export class FeedsService {
|
|
98 |
|
99 |
const html = dirtyHtml
|
100 |
.replace(/data-src=/g, 'src=')
|
|
|
101 |
.replace(/visibility: hidden;/g, '');
|
102 |
|
103 |
const content =
|
@@ -215,11 +216,15 @@ export class FeedsService {
|
|
215 |
type,
|
216 |
limit,
|
217 |
mode,
|
|
|
|
|
218 |
}: {
|
219 |
id?: string;
|
220 |
type: string;
|
221 |
limit: number;
|
222 |
mode?: string;
|
|
|
|
|
223 |
}) {
|
224 |
if (!feedTypes.includes(type as any)) {
|
225 |
type = 'atom';
|
@@ -247,11 +252,15 @@ export class FeedsService {
|
|
247 |
take: limit,
|
248 |
});
|
249 |
|
|
|
|
|
250 |
feedInfo = {
|
251 |
id: 'all',
|
252 |
mpName: 'WeWe-RSS All',
|
253 |
mpIntro: 'WeWe-RSS 全部文章',
|
254 |
-
mpCover:
|
|
|
|
|
255 |
status: 1,
|
256 |
syncTime: 0,
|
257 |
updateTime: Math.floor(Date.now() / 1e3),
|
@@ -261,7 +270,18 @@ export class FeedsService {
|
|
261 |
}
|
262 |
|
263 |
this.logger.log('handleGenerateFeed articles: ' + articles.length);
|
264 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
265 |
|
266 |
switch (type) {
|
267 |
case 'rss':
|
@@ -288,4 +308,15 @@ export class FeedsService {
|
|
288 |
};
|
289 |
});
|
290 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
291 |
}
|
|
|
6 |
import { ConfigService } from '@nestjs/config';
|
7 |
import { Article, Feed as FeedInfo } from '@prisma/client';
|
8 |
import { ConfigurationType } from '@server/configuration';
|
9 |
+
import { Feed, Item } from 'feed';
|
10 |
import got, { Got } from 'got';
|
11 |
import { load } from 'cheerio';
|
12 |
import { minify } from 'html-minifier';
|
|
|
98 |
|
99 |
const html = dirtyHtml
|
100 |
.replace(/data-src=/g, 'src=')
|
101 |
+
.replace(/opacity: 0( !important)?;/g, '')
|
102 |
.replace(/visibility: hidden;/g, '');
|
103 |
|
104 |
const content =
|
|
|
216 |
type,
|
217 |
limit,
|
218 |
mode,
|
219 |
+
title_include,
|
220 |
+
title_exclude,
|
221 |
}: {
|
222 |
id?: string;
|
223 |
type: string;
|
224 |
limit: number;
|
225 |
mode?: string;
|
226 |
+
title_include?: string;
|
227 |
+
title_exclude?: string;
|
228 |
}) {
|
229 |
if (!feedTypes.includes(type as any)) {
|
230 |
type = 'atom';
|
|
|
252 |
take: limit,
|
253 |
});
|
254 |
|
255 |
+
const { originUrl } =
|
256 |
+
this.configService.get<ConfigurationType['feed']>('feed')!;
|
257 |
feedInfo = {
|
258 |
id: 'all',
|
259 |
mpName: 'WeWe-RSS All',
|
260 |
mpIntro: 'WeWe-RSS 全部文章',
|
261 |
+
mpCover: originUrl
|
262 |
+
? `${originUrl}/favicon.ico`
|
263 |
+
: 'https://r2-assets.111965.xyz/wewe-rss.png',
|
264 |
status: 1,
|
265 |
syncTime: 0,
|
266 |
updateTime: Math.floor(Date.now() / 1e3),
|
|
|
270 |
}
|
271 |
|
272 |
this.logger.log('handleGenerateFeed articles: ' + articles.length);
|
273 |
+
let feed = await this.renderFeed({ feedInfo, articles, type, mode });
|
274 |
+
|
275 |
+
if (title_include) {
|
276 |
+
const includes = title_include.split('|');
|
277 |
+
feed.items = feed.items.filter(
|
278 |
+
(i: Item) => includes.some((k) => i.title.includes(k)));
|
279 |
+
}
|
280 |
+
if (title_exclude) {
|
281 |
+
const excludes = title_exclude.split('|');
|
282 |
+
feed.items = feed.items.filter(
|
283 |
+
(i: Item) => !excludes.some((k) => i.title.includes(k)));
|
284 |
+
}
|
285 |
|
286 |
switch (type) {
|
287 |
case 'rss':
|
|
|
308 |
};
|
309 |
});
|
310 |
}
|
311 |
+
|
312 |
+
async updateFeed(id: string) {
|
313 |
+
try {
|
314 |
+
await this.trpcService.refreshMpArticlesAndUpdateFeed(id);
|
315 |
+
} catch (err) {
|
316 |
+
this.logger.error('updateFeed error', err);
|
317 |
+
} finally {
|
318 |
+
// wait 30s for next feed
|
319 |
+
await new Promise((resolve) => setTimeout(resolve, 30 * 1e3));
|
320 |
+
}
|
321 |
+
}
|
322 |
}
|
apps/web/index.html
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
<html lang="en">
|
3 |
<head>
|
4 |
<meta charset="UTF-8" />
|
5 |
-
<link rel="icon" href="
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
<title>WeWe RSS</title>
|
8 |
<meta name="description" content="更好的公众号订阅方式" />
|
@@ -11,7 +11,7 @@
|
|
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>
|
|
|
2 |
<html lang="en">
|
3 |
<head>
|
4 |
<meta charset="UTF-8" />
|
5 |
+
<link rel="icon" href="{{ iconUrl }}" />
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
<title>WeWe RSS</title>
|
8 |
<meta name="description" content="更好的公众号订阅方式" />
|
|
|
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": "2.
|
5 |
"type": "module",
|
6 |
"scripts": {
|
7 |
"dev": "vite",
|
|
|
1 |
{
|
2 |
"name": "web",
|
3 |
"private": true,
|
4 |
+
"version": "2.4.1",
|
5 |
"type": "module",
|
6 |
"scripts": {
|
7 |
"dev": "vite",
|
apps/web/src/components/Nav.tsx
CHANGED
@@ -11,7 +11,7 @@ import {
|
|
11 |
import { ThemeSwitcher } from './ThemeSwitcher';
|
12 |
import { GitHubIcon } from './GitHubIcon';
|
13 |
import { useLocation } from 'react-router-dom';
|
14 |
-
import { appVersion } from '@web/utils/env';
|
15 |
import { useEffect, useState } from 'react';
|
16 |
|
17 |
const navbarItemLink = [
|
@@ -74,7 +74,10 @@ const Nav = () => {
|
|
74 |
width={28}
|
75 |
alt="WeWe RSS"
|
76 |
className="mr-2"
|
77 |
-
src=
|
|
|
|
|
|
|
78 |
></Image>
|
79 |
</Badge>
|
80 |
<p className="font-bold text-inherit">WeWe RSS</p>
|
|
|
11 |
import { ThemeSwitcher } from './ThemeSwitcher';
|
12 |
import { GitHubIcon } from './GitHubIcon';
|
13 |
import { useLocation } from 'react-router-dom';
|
14 |
+
import { appVersion, serverOriginUrl } from '@web/utils/env';
|
15 |
import { useEffect, useState } from 'react';
|
16 |
|
17 |
const navbarItemLink = [
|
|
|
74 |
width={28}
|
75 |
alt="WeWe RSS"
|
76 |
className="mr-2"
|
77 |
+
src={ serverOriginUrl
|
78 |
+
? `${serverOriginUrl}/favicon.ico`
|
79 |
+
: 'https://r2-assets.111965.xyz/wewe-rss.png'
|
80 |
+
}
|
81 |
></Image>
|
82 |
</Badge>
|
83 |
<p className="font-bold text-inherit">WeWe RSS</p>
|
docker-compose.yml
CHANGED
@@ -2,15 +2,15 @@ version: '3.9'
|
|
2 |
|
3 |
services:
|
4 |
db:
|
5 |
-
image: mysql:
|
6 |
-
command: --
|
7 |
environment:
|
8 |
# 请修改为自己的密码
|
9 |
MYSQL_ROOT_PASSWORD: 123456
|
10 |
TZ: 'Asia/Shanghai'
|
11 |
MYSQL_DATABASE: 'wewe-rss'
|
12 |
-
|
13 |
-
|
14 |
volumes:
|
15 |
- db_data:/var/lib/mysql
|
16 |
healthcheck:
|
|
|
2 |
|
3 |
services:
|
4 |
db:
|
5 |
+
image: mysql:8.3.0
|
6 |
+
command: --mysql-native-password=ON
|
7 |
environment:
|
8 |
# 请修改为自己的密码
|
9 |
MYSQL_ROOT_PASSWORD: 123456
|
10 |
TZ: 'Asia/Shanghai'
|
11 |
MYSQL_DATABASE: 'wewe-rss'
|
12 |
+
# ports:
|
13 |
+
# - 13306:3306
|
14 |
volumes:
|
15 |
- db_data:/var/lib/mysql
|
16 |
healthcheck:
|
package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
{
|
2 |
"name": "wewe-rss",
|
3 |
-
"version": "2.
|
4 |
"private": true,
|
5 |
"author": "cooderl <cooder@111965.xyz>",
|
6 |
"description": "",
|
|
|
1 |
{
|
2 |
"name": "wewe-rss",
|
3 |
+
"version": "2.4.1",
|
4 |
"private": true,
|
5 |
"author": "cooderl <cooder@111965.xyz>",
|
6 |
"description": "",
|
wewe-rss-dingtalk/main.py
CHANGED
@@ -77,8 +77,8 @@ def send_dingtalk_msg(access_token, secret):
|
|
77 |
|
78 |
if __name__ == '__main__':
|
79 |
|
80 |
-
access_token = '
|
81 |
-
secret = '
|
82 |
|
83 |
while True:
|
84 |
send_dingtalk_msg(access_token, secret)
|
|
|
77 |
|
78 |
if __name__ == '__main__':
|
79 |
|
80 |
+
access_token = ''
|
81 |
+
secret = '' # 创建机器人时钉钉设置页面有提供
|
82 |
|
83 |
while True:
|
84 |
send_dingtalk_msg(access_token, secret)
|