diff --git a/docs/programming.md b/docs/programming.md
index ba19721ec..519a7feb6 100644
--- a/docs/programming.md
+++ b/docs/programming.md
@@ -172,6 +172,36 @@ GitHub 官方也提供了一些 RSS:
+## LWN.net
+
+### Security alerts
+
+
+
+| 发行版 | 标识 |
+| :--------------- | :----------------- |
+| Arch Linux | `Arch_Linux` |
+| CentOS | `CentOS` |
+| Debian | `Debian` |
+| Fedora | `Fedora` |
+| Gentoo | `Gentoo` |
+| Mageia | `Mageia` |
+| openSUSE | `openSUSE` |
+| Oracle | `Oracle` |
+| Red Hat | `Red_Hat` |
+| Scientific Linux | `Scientific_Linux` |
+| Slackware | `Slackware` |
+| SUSE | `SUSE` |
+| Ubuntu | `Ubuntu` |
+
+::: tip 提示
+
+注意标识大小写
+
+:::
+
+
+
## segmentfault
### 频道
diff --git a/lib/router.js b/lib/router.js
index ee0b458d4..70314bb90 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -1439,4 +1439,7 @@ router.get('/8btc/:authorid', require('./routes/8btc/author'));
// VueVlog
router.get('/vuevideo/:userid', require('./routes/vuevideo/user'));
+// LWN.net Alerts
+router.get('/lwn/alerts/:distributor', require('./routes/lwn/alerts'));
+
module.exports = router;
diff --git a/lib/routes/lwn/alerts.js b/lib/routes/lwn/alerts.js
new file mode 100644
index 000000000..ee8db6793
--- /dev/null
+++ b/lib/routes/lwn/alerts.js
@@ -0,0 +1,57 @@
+const url = require('url');
+const got = require('@/utils/got');
+const date = require('@/utils/date');
+const cheerio = require('cheerio');
+
+module.exports = async (ctx) => {
+ const distributor = ctx.params.distributor;
+ const listUrl = `https://lwn.net/Alerts/${distributor}/`;
+ const res = await got({
+ method: 'get',
+ url: listUrl,
+ });
+ const $ = cheerio.load(res.data);
+ const title = $('.PageHeadline').text();
+ const list = $('.ArticleText').find('table tbody tr:not(:first-of-type)');
+ const count = [];
+ for (let i = 0; i < list.length; i++) {
+ count.push(i);
+ }
+ const resultItem = await Promise.all(
+ count.map(async (i) => {
+ const each = $(list[i]);
+ const originalUrl = each.find('td > a').attr('href');
+ const link = url.resolve('https://lwn.net', originalUrl);
+ const pubDate = date(each.find('td:nth-of-type(3)').text());
+
+ const cacheKey = `LWN.net:${link}`;
+ const cacheValue = await ctx.cache.get(cacheKey);
+
+ let item;
+ if (cacheValue) {
+ item = JSON.parse(cacheValue);
+ } else {
+ const detail = await got({
+ method: 'get',
+ url: link,
+ });
+ const content = cheerio.load(detail.data);
+ item = {
+ title: content('.PageHeadline').text(),
+ description: content('.ArticleText').html(),
+ link: link,
+ pubDate: pubDate,
+ };
+ ctx.cache.set(cacheKey, JSON.stringify(item));
+ }
+
+ return Promise.resolve(item);
+ })
+ );
+
+ ctx.state.data = {
+ title: title,
+ link: listUrl,
+ item: resultItem,
+ };
+};