rssany 0.3.1 → 0.3.3
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/.env.example +52 -52
- package/README.md +156 -147
- package/app/plugins/builtin/email.rssany.js +84 -84
- package/app/plugins/builtin/rss.rssany.js +164 -164
- package/app/plugins/builtin/xiaohongshu.rssany.js +59 -2
- package/app/statics/README.md +7 -7
- package/app/webui/build/200.html +36 -36
- package/app/webui/build/_app/immutable/assets/0.BLOTwIuF.css +1 -0
- package/app/webui/build/_app/immutable/assets/10.CmGYYZFR.css +1 -0
- package/app/webui/build/_app/immutable/assets/11.Dkz3VS_N.css +1 -0
- package/app/webui/build/_app/immutable/assets/14.BCCBoMGj.css +1 -0
- package/app/webui/build/_app/immutable/assets/6.Cm_jpHOq.css +1 -0
- package/app/webui/build/_app/immutable/assets/7.CJ3BjogD.css +1 -0
- package/app/webui/build/_app/immutable/assets/9.CATKVZ-n.css +1 -0
- package/app/webui/build/_app/immutable/assets/{SourcesList.D5Lso0bo.css → SourcesList.ke66uOSi.css} +1 -1
- package/app/webui/build/_app/immutable/assets/chevron-down.CV-KWLNP.css +1 -0
- package/app/webui/build/_app/immutable/chunks/{CGCMIfh3.js → 4TuV_psf.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/{DAdOEnFb.js → B0czyjwj.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/{CFwxUBGi.js → B553hBXT.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/B8StT3Do.js +6 -0
- package/app/webui/build/_app/immutable/chunks/BI_ale1m.js +1 -0
- package/app/webui/build/_app/immutable/chunks/BK0ygNWX.js +2 -0
- package/app/webui/build/_app/immutable/chunks/BKm6QCwp.js +1 -0
- package/app/webui/build/_app/immutable/chunks/BT6b4LcZ.js +36 -0
- package/app/webui/build/_app/immutable/chunks/BZY5aksi.js +36 -0
- package/app/webui/build/_app/immutable/chunks/{C8umpVpB.js → BnqaikL8.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/BsQ08Wq_.js +1 -0
- package/app/webui/build/_app/immutable/chunks/C9wTDiHH.js +1 -0
- package/app/webui/build/_app/immutable/chunks/{B-CeeY89.js → CAKuIoAf.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/CEWi_rGa.js +1 -0
- package/app/webui/build/_app/immutable/chunks/{ChUctqXA.js → Cc7aBSsN.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/{BAJAS8BI.js → D8G961Hm.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/{CS53ooo0.js → DIeahUKq.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/DO5OXNYS.js +1 -0
- package/app/webui/build/_app/immutable/chunks/Dg_D3pjF.js +1 -0
- package/app/webui/build/_app/immutable/chunks/{Dyvi1wBH.js → DptdhtA1.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/{ClknbeNl.js → FDS7fbwH.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/{CqYSO3Dx.js → GeNMTUn1.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/{DCEayuDt.js → IhDlsCxD.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/Nd0ktDhd.js +1 -0
- package/app/webui/build/_app/immutable/chunks/{D6kzEN_P.js → SvdgnirT.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/WW6La7Nt.js +2 -0
- package/app/webui/build/_app/immutable/chunks/{DsxvjlCC.js → pd_p3yYy.js} +5 -5
- package/app/webui/build/_app/immutable/chunks/rNwPv4DZ.js +1 -0
- package/app/webui/build/_app/immutable/entry/app.BKLBG-4w.js +2 -0
- package/app/webui/build/_app/immutable/entry/start.D-X6pVtx.js +1 -0
- package/app/webui/build/_app/immutable/nodes/{0.DK_mcVDm.js → 0.CJDC_3s9.js} +3 -3
- package/app/webui/build/_app/immutable/nodes/1.DsKocFSb.js +1 -0
- package/app/webui/build/_app/immutable/nodes/10.BeejAn8z.js +1 -0
- package/app/webui/build/_app/immutable/nodes/11.D--uwkk0.js +3 -0
- package/app/webui/build/_app/immutable/nodes/12.BLyQ6rUu.js +1 -0
- package/app/webui/build/_app/immutable/nodes/13.Cl0WQK13.js +1 -0
- package/app/webui/build/_app/immutable/nodes/14.T9l5Rh19.js +1 -0
- package/app/webui/build/_app/immutable/nodes/15.DHfwIlBx.js +1 -0
- package/app/webui/build/_app/immutable/nodes/{16.zfSe93Ab.js → 16.BKDfR-KV.js} +2 -2
- package/app/webui/build/_app/immutable/nodes/17.DofB8HQB.js +1 -0
- package/app/webui/build/_app/immutable/nodes/2.BOYqXdCa.js +1 -0
- package/app/webui/build/_app/immutable/nodes/3.B9ucbp_W.js +1 -0
- package/app/webui/build/_app/immutable/nodes/5.9zgwFV6I.js +2 -0
- package/app/webui/build/_app/immutable/nodes/6.Bs32Ieii.js +2 -0
- package/app/webui/build/_app/immutable/nodes/7.Cigxrk0v.js +1 -0
- package/app/webui/build/_app/immutable/nodes/8.pG10rCF0.js +1 -0
- package/app/webui/build/_app/immutable/nodes/9.Bzqb3xHY.js +1 -0
- package/app/webui/build/_app/version.json +1 -1
- package/bin/rssany.js +55 -3
- package/dist/index.js +361 -99
- package/dist/index.js.map +1 -1
- package/package.json +107 -103
- package/scripts/dev.mjs +5 -1
- package/scripts/postinstall.mjs +44 -0
- package/scripts/reset.mjs +137 -135
- package/scripts/user-dir.mjs +52 -0
- package/app/webui/build/_app/immutable/assets/0.DsKls1SN.css +0 -1
- package/app/webui/build/_app/immutable/assets/10.Dj8_pmut.css +0 -1
- package/app/webui/build/_app/immutable/assets/13.Qu_tY6H9.css +0 -1
- package/app/webui/build/_app/immutable/assets/5.B-dPiwB7.css +0 -1
- package/app/webui/build/_app/immutable/assets/6.B27N7pdA.css +0 -1
- package/app/webui/build/_app/immutable/assets/8.Cgji2b15.css +0 -1
- package/app/webui/build/_app/immutable/assets/9.BsCIAvn3.css +0 -1
- package/app/webui/build/_app/immutable/chunks/6prdYIKP.js +0 -1
- package/app/webui/build/_app/immutable/chunks/B2cyTHdf.js +0 -2
- package/app/webui/build/_app/immutable/chunks/B6WG2Sd3.js +0 -1
- package/app/webui/build/_app/immutable/chunks/BA4Gucnq.js +0 -1
- package/app/webui/build/_app/immutable/chunks/BkD3yAYe.js +0 -1
- package/app/webui/build/_app/immutable/chunks/C4uF_YIK.js +0 -1
- package/app/webui/build/_app/immutable/chunks/CBY2biv-.js +0 -1
- package/app/webui/build/_app/immutable/chunks/CVW0ymE1.js +0 -1
- package/app/webui/build/_app/immutable/chunks/DJ2e04vK.js +0 -36
- package/app/webui/build/_app/immutable/chunks/DL3Q5sfb.js +0 -1
- package/app/webui/build/_app/immutable/chunks/DVa8Y-mQ.js +0 -1
- package/app/webui/build/_app/immutable/chunks/DkamXS6W.js +0 -36
- package/app/webui/build/_app/immutable/chunks/DoRPmqLn.js +0 -2
- package/app/webui/build/_app/immutable/chunks/_qj9U-za.js +0 -1
- package/app/webui/build/_app/immutable/chunks/vtBo8kBV.js +0 -1
- package/app/webui/build/_app/immutable/entry/app.RFfWi3_i.js +0 -2
- package/app/webui/build/_app/immutable/entry/start.DU_kyeGS.js +0 -1
- package/app/webui/build/_app/immutable/nodes/1.0PRrU2uQ.js +0 -1
- package/app/webui/build/_app/immutable/nodes/10.CsxzlUER.js +0 -1
- package/app/webui/build/_app/immutable/nodes/11.D-PkhIRW.js +0 -1
- package/app/webui/build/_app/immutable/nodes/12.GGf-JLUY.js +0 -1
- package/app/webui/build/_app/immutable/nodes/13.DWWcH27k.js +0 -6
- package/app/webui/build/_app/immutable/nodes/14.COwSLwDN.js +0 -1
- package/app/webui/build/_app/immutable/nodes/15.nDN_AHrs.js +0 -1
- package/app/webui/build/_app/immutable/nodes/2.AJd2163d.js +0 -1
- package/app/webui/build/_app/immutable/nodes/3.CEVEHuaH.js +0 -1
- package/app/webui/build/_app/immutable/nodes/4.BT_N8pCh.js +0 -2
- package/app/webui/build/_app/immutable/nodes/5.BZScQ2CH.js +0 -2
- package/app/webui/build/_app/immutable/nodes/6.CkFk8X--.js +0 -1
- package/app/webui/build/_app/immutable/nodes/7.CuQJk7te.js +0 -1
- package/app/webui/build/_app/immutable/nodes/8.DIavWJnU.js +0 -1
- package/app/webui/build/_app/immutable/nodes/9.Db30M8x0.js +0 -1
- /package/app/webui/build/_app/immutable/assets/{11.qYZMiTb0.css → 12.qYZMiTb0.css} +0 -0
- /package/app/webui/build/_app/immutable/assets/{12.DfJcfUWl.css → 13.DfJcfUWl.css} +0 -0
- /package/app/webui/build/_app/immutable/assets/{14.DfMfOrS3.css → 15.DfMfOrS3.css} +0 -0
- /package/app/webui/build/_app/immutable/assets/{15.nNGjXhCQ.css → 17.nNGjXhCQ.css} +0 -0
- /package/app/webui/build/_app/immutable/assets/{4.Di6rvlY-.css → 5.Di6rvlY-.css} +0 -0
- /package/app/webui/build/_app/immutable/assets/{7.CrNxmd8B.css → 8.CrNxmd8B.css} +0 -0
- /package/app/webui/build/_app/immutable/nodes/{17.BtYZF6FM.js → 18.BtYZF6FM.js} +0 -0
- /package/app/webui/build/_app/immutable/nodes/{18.BIzqhTqv.js → 4.BIzqhTqv.js} +0 -0
package/package.json
CHANGED
|
@@ -1,103 +1,107 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "rssany",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "Universal RSS/Atom/JSON Feed pipeline — fetches, extracts, parses and converts any web content into consumable feeds with plugin support",
|
|
5
|
-
"author": "Joo",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"main": "./dist/index.js",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": "./dist/index.js"
|
|
10
|
-
},
|
|
11
|
-
"bin": {
|
|
12
|
-
"rssany": "./bin/rssany.js"
|
|
13
|
-
},
|
|
14
|
-
"files": [
|
|
15
|
-
"dist",
|
|
16
|
-
"bin",
|
|
17
|
-
"
|
|
18
|
-
"app/plugins/
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"scripts/
|
|
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
|
-
"@types/
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
"hono": "^
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
|
|
103
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "rssany",
|
|
3
|
+
"version": "0.3.3",
|
|
4
|
+
"description": "Universal RSS/Atom/JSON Feed pipeline — fetches, extracts, parses and converts any web content into consumable feeds with plugin support",
|
|
5
|
+
"author": "Joo",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"rssany": "./bin/rssany.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"bin",
|
|
17
|
+
"scripts/user-dir.mjs",
|
|
18
|
+
"app/plugins/builtin",
|
|
19
|
+
"app/plugins/site.rssany.js",
|
|
20
|
+
"statics",
|
|
21
|
+
"app/webui/build",
|
|
22
|
+
".env.example",
|
|
23
|
+
"README.md",
|
|
24
|
+
"init",
|
|
25
|
+
"scripts/reset.mjs",
|
|
26
|
+
"scripts/postinstall.mjs",
|
|
27
|
+
"scripts/user-dir.mjs",
|
|
28
|
+
"scripts/dev.mjs"
|
|
29
|
+
],
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=20 <24"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "vite build",
|
|
35
|
+
"dev": "node scripts/dev.mjs",
|
|
36
|
+
"dev:backend": "cross-env PORT=3999 tsx app/index.ts",
|
|
37
|
+
"start": "node dist/index.js",
|
|
38
|
+
"serve:route": "node scripts/serve-route.mjs",
|
|
39
|
+
"serve:app": "npx tsx app/index.ts",
|
|
40
|
+
"test": "vitest",
|
|
41
|
+
"test:run": "vitest run --passWithNoTests",
|
|
42
|
+
"lint": "eslint .",
|
|
43
|
+
"lint:fix": "eslint . --fix",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"reset": "node scripts/reset.mjs",
|
|
46
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
47
|
+
"proxy-browser": "tsx scripts/proxy-browser.ts",
|
|
48
|
+
"webui:install": "cd app/webui && npm install",
|
|
49
|
+
"webui:dev": "cd app/webui && npm run build:watch",
|
|
50
|
+
"webui:build": "cd app/webui && npm run build",
|
|
51
|
+
"webui:watch": "cd app/webui && npm run build:watch",
|
|
52
|
+
"dev:all": "npm run dev",
|
|
53
|
+
"build:all": "npm run build && npm run webui:build",
|
|
54
|
+
"prepublishOnly": "npm run build:all",
|
|
55
|
+
"docker:build": "bash scripts/docker-build.sh",
|
|
56
|
+
"docker:build:tag": "bash scripts/docker-build.sh",
|
|
57
|
+
"landing:install": "cd landing && npm install",
|
|
58
|
+
"landing:dev": "cd landing && npm run dev",
|
|
59
|
+
"landing:build": "cd landing && npm run build",
|
|
60
|
+
"deploy": "node scripts/deploy-landing.mjs",
|
|
61
|
+
"deploy:landing": "node scripts/deploy-landing.mjs"
|
|
62
|
+
},
|
|
63
|
+
"keywords": [
|
|
64
|
+
"rss",
|
|
65
|
+
"atom",
|
|
66
|
+
"json-feed",
|
|
67
|
+
"subscription",
|
|
68
|
+
"pipeline"
|
|
69
|
+
],
|
|
70
|
+
"license": "MIT",
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@eslint/js": "^9.15.0",
|
|
73
|
+
"@types/jsdom": "^28.0.3",
|
|
74
|
+
"@types/mailparser": "^3.4.6",
|
|
75
|
+
"@types/node": "^25.2.0",
|
|
76
|
+
"@types/node-cron": "^3.0.11",
|
|
77
|
+
"@types/nodemailer": "^7.0.11",
|
|
78
|
+
"concurrently": "^9.2.1",
|
|
79
|
+
"cross-env": "^7.0.3",
|
|
80
|
+
"eslint": "^9.15.0",
|
|
81
|
+
"globals": "^15.12.0",
|
|
82
|
+
"ssh2": "^1.17.0",
|
|
83
|
+
"tsx": "^4.19.0",
|
|
84
|
+
"typescript": "~5.6.0",
|
|
85
|
+
"typescript-eslint": "^8.15.0",
|
|
86
|
+
"vite": "^6.4.3",
|
|
87
|
+
"vitest": "^4.1.8"
|
|
88
|
+
},
|
|
89
|
+
"dependencies": {
|
|
90
|
+
"@hono/node-server": "^1.19.10",
|
|
91
|
+
"@mozilla/readability": "^0.6.0",
|
|
92
|
+
"cron-parser": "^5.0.0",
|
|
93
|
+
"dotenv": "^16.4.7",
|
|
94
|
+
"hono": "^4.12.12",
|
|
95
|
+
"https-proxy-agent": "^7.0.6",
|
|
96
|
+
"imapflow": "^1.2.10",
|
|
97
|
+
"jsdom": "^29.1.1",
|
|
98
|
+
"mailparser": "^3.9.3",
|
|
99
|
+
"marked": "^17.0.3",
|
|
100
|
+
"node-cron": "^4.2.1",
|
|
101
|
+
"node-html-parser": "^7.0.2",
|
|
102
|
+
"nodemailer": "^8.0.2",
|
|
103
|
+
"openai": "^6.42.0",
|
|
104
|
+
"puppeteer-core": "^24.36.0",
|
|
105
|
+
"rss-parser": "^3.13.0"
|
|
106
|
+
}
|
|
107
|
+
}
|
package/scripts/dev.mjs
CHANGED
|
@@ -73,7 +73,11 @@ function waitForInitialWebuiBuild(child) {
|
|
|
73
73
|
}, 500);
|
|
74
74
|
child.stdout?.on("data", (chunk) => {
|
|
75
75
|
const text = chunk.toString();
|
|
76
|
-
if (
|
|
76
|
+
if (
|
|
77
|
+
text.includes('Wrote site to "build"') ||
|
|
78
|
+
text.includes("Wrote site to 'build'") ||
|
|
79
|
+
text.includes("[webui:watch] build complete")
|
|
80
|
+
) {
|
|
77
81
|
finish();
|
|
78
82
|
}
|
|
79
83
|
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 全局安装后输出用户数据目录位置({npm prefix}/var/rssany)。
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { execSync } from "node:child_process";
|
|
7
|
+
import { dirname, join } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { isGlobalNpmInstall, resolveDefaultUserDir, resolveNpmPrefixFromPackageRoot } from "./user-dir.mjs";
|
|
10
|
+
|
|
11
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const packageRoot = join(scriptDir, "..");
|
|
13
|
+
|
|
14
|
+
function getGlobalNpmPrefix() {
|
|
15
|
+
try {
|
|
16
|
+
return execSync("npm prefix -g", { encoding: "utf8", env: process.env }).trim();
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function isSystemOwnedPrefix(prefix) {
|
|
23
|
+
if (!prefix) return false;
|
|
24
|
+
const normalized = prefix.replace(/\\/g, "/");
|
|
25
|
+
return normalized === "/usr/local" || normalized.startsWith("/usr/local/");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function main() {
|
|
29
|
+
if (!isGlobalNpmInstall(packageRoot)) return;
|
|
30
|
+
|
|
31
|
+
const userDir = resolveDefaultUserDir(packageRoot);
|
|
32
|
+
const npmPrefix = resolveNpmPrefixFromPackageRoot(packageRoot) ?? getGlobalNpmPrefix();
|
|
33
|
+
console.log(`[rssany] 用户数据目录: ${userDir}`);
|
|
34
|
+
if (npmPrefix) {
|
|
35
|
+
console.log(`[rssany] npm 全局 prefix: ${npmPrefix}`);
|
|
36
|
+
}
|
|
37
|
+
if (isSystemOwnedPrefix(getGlobalNpmPrefix())) {
|
|
38
|
+
console.log(
|
|
39
|
+
"[rssany] 若此前 npm install -g 报 EACCES,请先执行: npm config set prefix \"$HOME/.local\",并将 \"$HOME/.local/bin\" 加入 PATH",
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
await main();
|
package/scripts/reset.mjs
CHANGED
|
@@ -1,136 +1,138 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* 停止占用 HTTP 服务端口的进程,并删除用户数据目录(与 app 中 PORT / RSSANY_USER_DIR 约定一致)。
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 停止占用 HTTP 服务端口的进程,并删除用户数据目录(与 app 中 PORT / RSSANY_USER_DIR 约定一致)。
|
|
4
4
|
* 用法:npm run reset 或 PORT=3000 npm run reset
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { execFileSync } from "node:child_process";
|
|
8
|
-
import { existsSync, rmSync } from "node:fs";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { config } from "dotenv";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
.
|
|
34
|
-
.
|
|
35
|
-
.map(
|
|
36
|
-
.filter(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (
|
|
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
|
-
console.
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
console.
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
console.log(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { execFileSync } from "node:child_process";
|
|
8
|
+
import { existsSync, rmSync } from "node:fs";
|
|
9
|
+
import { join, dirname } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { config } from "dotenv";
|
|
12
|
+
import { resolveDefaultUserDir } from "./user-dir.mjs";
|
|
13
|
+
|
|
14
|
+
config({ path: join(process.cwd(), ".env") });
|
|
15
|
+
|
|
16
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const packageRoot = join(scriptDir, "..");
|
|
18
|
+
|
|
19
|
+
const DEFAULT_PORT = 18473;
|
|
20
|
+
const port = Number(process.env.PORT) || DEFAULT_PORT;
|
|
21
|
+
const userDir = resolveDefaultUserDir(packageRoot);
|
|
22
|
+
|
|
23
|
+
function pidsListeningWin32(p) {
|
|
24
|
+
const cmd = `Get-NetTCPConnection -LocalPort ${p} -State Listen -ErrorAction SilentlyContinue | ForEach-Object { $_.OwningProcess } | Sort-Object -Unique`;
|
|
25
|
+
try {
|
|
26
|
+
const out = execFileSync("powershell.exe", ["-NoProfile", "-Command", cmd], {
|
|
27
|
+
encoding: "utf8",
|
|
28
|
+
windowsHide: true,
|
|
29
|
+
});
|
|
30
|
+
return [
|
|
31
|
+
...new Set(
|
|
32
|
+
out
|
|
33
|
+
.trim()
|
|
34
|
+
.split(/\r?\n/)
|
|
35
|
+
.map((s) => s.trim())
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
.map(Number)
|
|
38
|
+
.filter((n) => Number.isFinite(n) && n > 0),
|
|
39
|
+
),
|
|
40
|
+
];
|
|
41
|
+
} catch {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function pidsListeningNetstatWin32(p) {
|
|
47
|
+
const pids = new Set();
|
|
48
|
+
try {
|
|
49
|
+
const out = execFileSync("netstat", ["-ano"], { encoding: "utf8", windowsHide: true });
|
|
50
|
+
const needle = `:${p}`;
|
|
51
|
+
for (const line of out.split(/\r?\n/)) {
|
|
52
|
+
if (!line.includes("LISTENING") || !line.includes(needle)) continue;
|
|
53
|
+
const m = line.trim().match(/LISTENING\s+(\d+)\s*$/);
|
|
54
|
+
if (m) pids.add(Number(m[1]));
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
// ignore
|
|
58
|
+
}
|
|
59
|
+
return [...pids];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function pidsListeningUnix(p) {
|
|
63
|
+
const argsList = [
|
|
64
|
+
["-nP", `-iTCP:${p}`, "-sTCP:LISTEN", "-t"],
|
|
65
|
+
["-ti", `:${p}`],
|
|
66
|
+
];
|
|
67
|
+
for (const args of argsList) {
|
|
68
|
+
try {
|
|
69
|
+
const out = execFileSync("lsof", args, { encoding: "utf8" });
|
|
70
|
+
const pids = out
|
|
71
|
+
.trim()
|
|
72
|
+
.split(/\n/)
|
|
73
|
+
.map((s) => Number(s.trim()))
|
|
74
|
+
.filter((n) => Number.isFinite(n) && n > 0);
|
|
75
|
+
if (pids.length) return [...new Set(pids)];
|
|
76
|
+
} catch {
|
|
77
|
+
// try next
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function killPidsWin32(pids) {
|
|
84
|
+
for (const pid of pids) {
|
|
85
|
+
if (pid === process.pid) continue;
|
|
86
|
+
try {
|
|
87
|
+
execFileSync("taskkill", ["/F", "/PID", String(pid)], { stdio: "inherit", windowsHide: true });
|
|
88
|
+
console.log(`已结束进程 PID ${pid}`);
|
|
89
|
+
} catch {
|
|
90
|
+
console.warn(`无法结束进程 PID ${pid}(可能已退出)`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function killPidsUnix(pids) {
|
|
96
|
+
for (const pid of pids) {
|
|
97
|
+
if (pid === process.pid) continue;
|
|
98
|
+
try {
|
|
99
|
+
process.kill(pid, "SIGTERM");
|
|
100
|
+
console.log(`已发送 SIGTERM 至 PID ${pid}`);
|
|
101
|
+
} catch {
|
|
102
|
+
try {
|
|
103
|
+
process.kill(pid, "SIGKILL");
|
|
104
|
+
console.log(`已发送 SIGKILL 至 PID ${pid}`);
|
|
105
|
+
} catch {
|
|
106
|
+
console.warn(`无法结束进程 PID ${pid}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function main() {
|
|
113
|
+
console.log(`端口: ${port}(来自 PORT 或默认 ${DEFAULT_PORT})`);
|
|
114
|
+
console.log(`用户数据目录: ${userDir}`);
|
|
115
|
+
|
|
116
|
+
let pids =
|
|
117
|
+
process.platform === "win32" ? pidsListeningWin32(port) : pidsListeningUnix(port);
|
|
118
|
+
if (process.platform === "win32" && pids.length === 0) {
|
|
119
|
+
pids = pidsListeningNetstatWin32(port);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (pids.length === 0) {
|
|
123
|
+
console.log("未发现占用该端口的监听进程。");
|
|
124
|
+
} else {
|
|
125
|
+
console.log(`将结束占用端口的进程: ${pids.join(", ")}`);
|
|
126
|
+
if (process.platform === "win32") killPidsWin32(pids);
|
|
127
|
+
else killPidsUnix(pids);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!existsSync(userDir)) {
|
|
131
|
+
console.log("用户数据目录不存在,跳过删除。");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
rmSync(userDir, { recursive: true, force: true });
|
|
135
|
+
console.log("已删除用户数据目录。");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
main();
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// 与 app/config/userDir.ts 保持同步(bin/、scripts/ 运行时直接加载)
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
const LEGACY_HOME_DIR = ".rssany";
|
|
6
|
+
|
|
7
|
+
export function resolveNpmPrefixFromPackageRoot(packageRoot) {
|
|
8
|
+
const normalized = packageRoot.replace(/\\/g, "/");
|
|
9
|
+
const libSuffix = "/lib/node_modules/rssany";
|
|
10
|
+
if (normalized.endsWith(libSuffix)) {
|
|
11
|
+
return packageRoot.slice(0, packageRoot.length - libSuffix.length);
|
|
12
|
+
}
|
|
13
|
+
const flatSuffix = "/node_modules/rssany";
|
|
14
|
+
if (normalized.endsWith(flatSuffix)) {
|
|
15
|
+
return packageRoot.slice(0, packageRoot.length - flatSuffix.length);
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isGlobalNpmInstall(packageRoot) {
|
|
21
|
+
const normalized = packageRoot.replace(/\\/g, "/");
|
|
22
|
+
if (normalized.endsWith("/lib/node_modules/rssany")) return true;
|
|
23
|
+
const globalPatterns = [
|
|
24
|
+
/\/npm\/node_modules\/rssany$/,
|
|
25
|
+
/\/\.local\/lib\/node_modules\/rssany$/,
|
|
26
|
+
/\/\.local\/node_modules\/rssany$/,
|
|
27
|
+
/\/\.npm-global\/lib\/node_modules\/rssany$/,
|
|
28
|
+
/\/\.nvm\/versions\/node\/[^/]+\/lib\/node_modules\/rssany$/,
|
|
29
|
+
/\/\.fnm\/node-versions\/[^/]+\/installation\/lib\/node_modules\/rssany$/,
|
|
30
|
+
];
|
|
31
|
+
return globalPatterns.some((pattern) => pattern.test(normalized));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function resolveDefaultUserDir(packageRoot) {
|
|
35
|
+
const env = process.env.RSSANY_USER_DIR?.trim();
|
|
36
|
+
if (env) return env;
|
|
37
|
+
|
|
38
|
+
const npmPrefix = resolveNpmPrefixFromPackageRoot(packageRoot);
|
|
39
|
+
if (npmPrefix && isGlobalNpmInstall(packageRoot)) {
|
|
40
|
+
return join(npmPrefix, "var", "rssany");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!packageRoot.replace(/\\/g, "/").includes("/node_modules/")) {
|
|
44
|
+
return join(packageRoot, ".rssany");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return join(homedir(), LEGACY_HOME_DIR);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function getLegacyHomeUserDir() {
|
|
51
|
+
return join(homedir(), LEGACY_HOME_DIR);
|
|
52
|
+
}
|