rssany 0.2.0 → 0.3.1
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/README.md +22 -22
- package/app/plugins/builtin/agi-eval-evaluation.rssany.js +6 -7
- package/app/plugins/builtin/amii-research-talent.rssany.js +6 -7
- package/app/plugins/builtin/anthropic-research.rssany.js +6 -8
- package/app/plugins/builtin/appen-resources.rssany.js +6 -7
- package/app/plugins/builtin/baai-wudao-paper-article.rssany.js +9 -10
- package/app/plugins/builtin/baaidata-csdn.rssany.js +6 -7
- package/app/plugins/builtin/baidu-research.rssany.js +5 -8
- package/app/plugins/builtin/brightdata-blog.rssany.js +6 -11
- package/app/plugins/builtin/bytedance-seed-research.rssany.js +5 -7
- package/app/plugins/builtin/email.rssany.js +9 -9
- package/app/plugins/builtin/five-radar.rssany.js +9 -11
- package/app/plugins/builtin/flageval-news.rssany.js +5 -7
- package/app/plugins/builtin/google-deepmind-research.rssany.js +6 -8
- package/app/plugins/builtin/google-research-datasets.rssany.js +6 -8
- package/app/plugins/builtin/google-research.rssany.js +6 -8
- package/app/plugins/builtin/hacker-news-newest.rssany.js +7 -9
- package/app/plugins/builtin/harvard-dataverse.rssany.js +6 -8
- package/app/plugins/builtin/huaweicloud-bbs-blogs.rssany.js +7 -9
- package/app/plugins/builtin/lingowhale.rssany.js +7 -9
- package/app/plugins/builtin/meituan-tech.rssany.js +7 -10
- package/app/plugins/builtin/meta-ai-publications.rssany.js +6 -11
- package/app/plugins/builtin/mila-quebec.rssany.js +6 -8
- package/app/plugins/builtin/mit-csail-research.rssany.js +7 -9
- package/app/plugins/builtin/moonshot.rssany.js +6 -8
- package/app/plugins/builtin/opendatalab-news.rssany.js +6 -7
- package/app/plugins/builtin/opendatalab.rssany.js +5 -6
- package/app/plugins/builtin/opendrivelab-autonomous-driving.rssany.js +6 -7
- package/app/plugins/builtin/opendrivelab-embodiedai.rssany.js +7 -8
- package/app/plugins/builtin/opendrivelab-publications.rssany.js +6 -8
- package/app/plugins/builtin/opendrivelab.rssany.js +7 -8
- package/app/plugins/builtin/paperswithcode.rssany.js +6 -8
- package/app/plugins/builtin/pjlab-adg-publications.rssany.js +7 -9
- package/app/plugins/builtin/rss.rssany.js +11 -12
- package/app/plugins/builtin/selectdataset.rssany.js +6 -8
- package/app/plugins/builtin/sensetime-tech-achievements.rssany.js +7 -8
- package/app/plugins/builtin/supervisely-blog.rssany.js +6 -8
- package/app/plugins/builtin/theinformation-briefings.rssany.js +7 -13
- package/app/plugins/builtin/uci-ml-repository.rssany.js +6 -7
- package/app/plugins/builtin/venturebeat.rssany.js +7 -9
- package/app/plugins/builtin/worldlabs.rssany.js +6 -8
- package/app/plugins/builtin/x.rssany.js +7 -9
- package/app/plugins/builtin/xiaohongshu.rssany.js +119 -56
- package/app/plugins/builtin/zhipu-research.rssany.js +5 -8
- package/app/plugins/site.rssany.js +25 -26
- package/{statics → app/statics}/README.md +7 -7
- package/app/webui/build/200.html +51 -0
- package/{webui/build/_app/immutable/assets/0.BB88QFoe.css → app/webui/build/_app/immutable/assets/0.DsKls1SN.css} +1 -1
- package/app/webui/build/_app/immutable/assets/13.Qu_tY6H9.css +1 -0
- package/app/webui/build/_app/immutable/assets/14.DfMfOrS3.css +1 -0
- package/app/webui/build/_app/immutable/assets/16.Cw9oSkcO.css +1 -0
- package/app/webui/build/_app/immutable/assets/4.Di6rvlY-.css +1 -0
- package/{webui/build/_app/immutable/assets/SourcesList.yTBBi3_m.css → app/webui/build/_app/immutable/assets/SourcesList.D5Lso0bo.css} +1 -1
- package/{webui/build/_app/immutable/assets/homeFeedPanelStore.CSvlNcpm.css → app/webui/build/_app/immutable/assets/homeFeedPanelStore.CE6xTfsa.css} +1 -1
- package/app/webui/build/_app/immutable/chunks/6prdYIKP.js +1 -0
- package/{webui/build/_app/immutable/chunks/Xy_fhzQq.js → app/webui/build/_app/immutable/chunks/B-CeeY89.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/B2cyTHdf.js +2 -0
- package/{webui/build/_app/immutable/chunks/DjNLq3TF.js → app/webui/build/_app/immutable/chunks/B6WG2Sd3.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/BA4Gucnq.js +1 -0
- package/{webui/build/_app/immutable/chunks/xtNWTdbD.js → app/webui/build/_app/immutable/chunks/BAJAS8BI.js} +1 -1
- package/{webui/build/_app/immutable/chunks/Dt2CddFe.js → app/webui/build/_app/immutable/chunks/BkD3yAYe.js} +1 -1
- package/{webui/build/_app/immutable/chunks/DFuhmi31.js → app/webui/build/_app/immutable/chunks/C4uF_YIK.js} +1 -1
- package/{webui/build/_app/immutable/chunks/Dw782Tjs.js → app/webui/build/_app/immutable/chunks/C8umpVpB.js} +1 -1
- package/{webui/build/_app/immutable/chunks/BQqoDzLx.js → app/webui/build/_app/immutable/chunks/CFwxUBGi.js} +1 -1
- package/{webui/build/_app/immutable/chunks/tB7QMF3U.js → app/webui/build/_app/immutable/chunks/CGCMIfh3.js} +1 -1
- package/{webui/build/_app/immutable/chunks/BK3WtZwv.js → app/webui/build/_app/immutable/chunks/CS53ooo0.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/CVW0ymE1.js +1 -0
- package/{webui/build/_app/immutable/chunks/B-OsL1Ct.js → app/webui/build/_app/immutable/chunks/ChUctqXA.js} +1 -1
- package/{webui/build/_app/immutable/chunks/D5GvRCv7.js → app/webui/build/_app/immutable/chunks/ClknbeNl.js} +1 -1
- package/{webui/build/_app/immutable/chunks/Bu9HsS-V.js → app/webui/build/_app/immutable/chunks/CqYSO3Dx.js} +1 -1
- package/{webui/build/_app/immutable/chunks/CWNeClHp.js → app/webui/build/_app/immutable/chunks/D6kzEN_P.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/DAdOEnFb.js +1 -0
- package/{webui/build/_app/immutable/chunks/Cihqbfi5.js → app/webui/build/_app/immutable/chunks/DCEayuDt.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/DJ2e04vK.js +36 -0
- package/{webui/build/_app/immutable/chunks/DEDI7Ecm.js → app/webui/build/_app/immutable/chunks/DL3Q5sfb.js} +1 -1
- package/{webui/build/_app/immutable/chunks/CVzlFH44.js → app/webui/build/_app/immutable/chunks/DVa8Y-mQ.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/DkamXS6W.js +36 -0
- package/app/webui/build/_app/immutable/chunks/DoRPmqLn.js +2 -0
- package/app/webui/build/_app/immutable/chunks/DsxvjlCC.js +13 -0
- package/{webui/build/_app/immutable/chunks/Bp63qm3L.js → app/webui/build/_app/immutable/chunks/Dyvi1wBH.js} +1 -1
- package/{webui/build/_app/immutable/chunks/CmjOpds-.js → app/webui/build/_app/immutable/chunks/_qj9U-za.js} +1 -1
- package/app/webui/build/_app/immutable/chunks/vtBo8kBV.js +1 -0
- package/app/webui/build/_app/immutable/entry/app.RFfWi3_i.js +2 -0
- package/app/webui/build/_app/immutable/entry/start.DU_kyeGS.js +1 -0
- package/{webui/build/_app/immutable/nodes/0.I1lQdWMl.js → app/webui/build/_app/immutable/nodes/0.DK_mcVDm.js} +1 -1
- package/app/webui/build/_app/immutable/nodes/1.0PRrU2uQ.js +1 -0
- package/{webui/build/_app/immutable/nodes/10.CvfUsqsw.js → app/webui/build/_app/immutable/nodes/10.CsxzlUER.js} +1 -1
- package/app/webui/build/_app/immutable/nodes/11.D-PkhIRW.js +1 -0
- package/{webui/build/_app/immutable/nodes/12.DVFJuIWI.js → app/webui/build/_app/immutable/nodes/12.GGf-JLUY.js} +1 -1
- package/app/webui/build/_app/immutable/nodes/13.DWWcH27k.js +6 -0
- package/app/webui/build/_app/immutable/nodes/14.COwSLwDN.js +1 -0
- package/app/webui/build/_app/immutable/nodes/15.nDN_AHrs.js +1 -0
- package/app/webui/build/_app/immutable/nodes/16.zfSe93Ab.js +24 -0
- package/app/webui/build/_app/immutable/nodes/2.AJd2163d.js +1 -0
- package/app/webui/build/_app/immutable/nodes/3.CEVEHuaH.js +1 -0
- package/app/webui/build/_app/immutable/nodes/4.BT_N8pCh.js +2 -0
- package/{webui/build/_app/immutable/nodes/5.B6fR3n6J.js → app/webui/build/_app/immutable/nodes/5.BZScQ2CH.js} +1 -1
- package/{webui/build/_app/immutable/nodes/6.j2O5Mwjv.js → app/webui/build/_app/immutable/nodes/6.CkFk8X--.js} +1 -1
- package/app/webui/build/_app/immutable/nodes/7.CuQJk7te.js +1 -0
- package/{webui/build/_app/immutable/nodes/8.Bw_d63B_.js → app/webui/build/_app/immutable/nodes/8.DIavWJnU.js} +1 -1
- package/{webui/build/_app/immutable/nodes/9.pMMi5PP6.js → app/webui/build/_app/immutable/nodes/9.Db30M8x0.js} +1 -1
- package/app/webui/build/_app/version.json +1 -0
- package/app/webui/build/apple-touch-icon.png +0 -0
- package/app/webui/build/favicon.ico +0 -0
- package/app/webui/build/favicon.png +0 -0
- package/bin/rssany.js +226 -6
- package/dist/index.js +209 -152
- package/dist/index.js.map +1 -1
- package/package.json +22 -16
- package/scripts/dev.mjs +114 -0
- package/scripts/reset.mjs +1 -1
- package/init/config.json +0 -17
- package/init/sources.json +0 -353
- package/statics/401.html +0 -56
- package/statics/404.html +0 -12
- package/statics/image.png +0 -0
- package/webui/build/200.html +0 -49
- package/webui/build/_app/immutable/assets/13.BhO9zvFi.css +0 -1
- package/webui/build/_app/immutable/assets/14.CujIhjQK.css +0 -1
- package/webui/build/_app/immutable/assets/16.PP9XLDf7.css +0 -1
- package/webui/build/_app/immutable/assets/4.9wPHhVwv.css +0 -1
- package/webui/build/_app/immutable/chunks/5LVkDJzw.js +0 -1
- package/webui/build/_app/immutable/chunks/B2Q1a1-H.js +0 -2
- package/webui/build/_app/immutable/chunks/BbWUOQ_m.js +0 -1
- package/webui/build/_app/immutable/chunks/Bns1MuyM.js +0 -36
- package/webui/build/_app/immutable/chunks/DMWEh-Ek.js +0 -2
- package/webui/build/_app/immutable/chunks/bvuf_jZd.js +0 -36
- package/webui/build/_app/immutable/chunks/lk5LaiqA.js +0 -1
- package/webui/build/_app/immutable/chunks/mW5RwvnK.js +0 -13
- package/webui/build/_app/immutable/entry/app.BVkrDt5l.js +0 -2
- package/webui/build/_app/immutable/entry/start.D3Q-BMMd.js +0 -1
- package/webui/build/_app/immutable/nodes/1.BiQQfx2j.js +0 -1
- package/webui/build/_app/immutable/nodes/11.B4LHPNL6.js +0 -1
- package/webui/build/_app/immutable/nodes/13.nT3SOzEB.js +0 -1
- package/webui/build/_app/immutable/nodes/14.DfaAf0f8.js +0 -1
- package/webui/build/_app/immutable/nodes/15.CMzkX9OK.js +0 -1
- package/webui/build/_app/immutable/nodes/16.zPgTQNze.js +0 -24
- package/webui/build/_app/immutable/nodes/2.BYWOpaxy.js +0 -1
- package/webui/build/_app/immutable/nodes/3.B8Viux9S.js +0 -1
- package/webui/build/_app/immutable/nodes/4.DTSxpKm7.js +0 -2
- package/webui/build/_app/immutable/nodes/7.Bd2USIrl.js +0 -1
- package/webui/build/_app/version.json +0 -1
- /package/{webui → app/webui}/build/_app/env.js +0 -0
- /package/{webui → app/webui}/build/_app/immutable/assets/10.Dj8_pmut.css +0 -0
- /package/{webui → app/webui}/build/_app/immutable/assets/11.qYZMiTb0.css +0 -0
- /package/{webui → app/webui}/build/_app/immutable/assets/12.DfJcfUWl.css +0 -0
- /package/{webui → app/webui}/build/_app/immutable/assets/15.nNGjXhCQ.css +0 -0
- /package/{webui → app/webui}/build/_app/immutable/assets/5.B-dPiwB7.css +0 -0
- /package/{webui → app/webui}/build/_app/immutable/assets/6.B27N7pdA.css +0 -0
- /package/{webui → app/webui}/build/_app/immutable/assets/7.CrNxmd8B.css +0 -0
- /package/{webui → app/webui}/build/_app/immutable/assets/8.Cgji2b15.css +0 -0
- /package/{webui → app/webui}/build/_app/immutable/assets/9.BsCIAvn3.css +0 -0
- /package/{webui → app/webui}/build/_app/immutable/assets/BackToParentRoute.DGk-X5ow.css +0 -0
- /package/{webui → app/webui}/build/_app/immutable/chunks/BUApaBEI.js +0 -0
- /package/{webui → app/webui}/build/_app/immutable/chunks/Bfc47y5P.js +0 -0
- /package/{webui → app/webui}/build/_app/immutable/chunks/CBY2biv-.js +0 -0
- /package/{webui → app/webui}/build/_app/immutable/chunks/hp4PFHFv.js +0 -0
- /package/{webui → app/webui}/build/_app/immutable/nodes/17.BtYZF6FM.js +0 -0
- /package/{webui → app/webui}/build/_app/immutable/nodes/18.BIzqhTqv.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as m,a as f}from"../chunks/CS53ooo0.js";import"../chunks/BA4Gucnq.js";import{f as n}from"../chunks/vtBo8kBV.js";import{s as p}from"../chunks/ChUctqXA.js";function d(t,a){var o=m(),r=n(o);p(r,a,"default",{}),f(t,o)}export{d as component};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as t,f as r}from"../chunks/CS53ooo0.js";import"../chunks/BA4Gucnq.js";import{d as m,$ as s}from"../chunks/vtBo8kBV.js";import{h as p}from"../chunks/DL3Q5sfb.js";import{S as n}from"../chunks/DkamXS6W.js";import{P as f}from"../chunks/DoRPmqLn.js";var i=r('<meta name="description" content="Subscribed sources: labels, counts, pull and manage feeds."/>');function S(e){p("1uha8ag",o=>{var a=i();m(()=>{s.title=`Sources — ${f}`}),t(o,a)}),n(e,{})}export{S as component};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as u,f as p}from"../chunks/CS53ooo0.js";import"../chunks/BA4Gucnq.js";import{o as V,a as W}from"../chunks/Dyvi1wBH.js";import{at as Z,au as ee,d as te,$ as ae,av as s,aw as v,i as r,ax as t,ay as se,az as b,a5 as z,z as re,u as P,aA as Y,aB as le}from"../chunks/vtBo8kBV.js";import{s as g}from"../chunks/B2cyTHdf.js";import{i as ie}from"../chunks/BkD3yAYe.js";import{e as y,i as D}from"../chunks/CFwxUBGi.js";import{h as ne}from"../chunks/DL3Q5sfb.js";import{P as oe,s as de,a as ce}from"../chunks/DoRPmqLn.js";import{i as ve}from"../chunks/DVa8Y-mQ.js";import{a as fe}from"../chunks/CBY2biv-.js";var ue=p("<span></span>"),pe=p('<div class="scheduler-row svelte-1jef3w8"><div class="scheduler-left svelte-1jef3w8"><span class="scheduler-name svelte-1jef3w8"> </span> <span class="scheduler-meta svelte-1jef3w8"> </span></div> <div class="scheduler-slots svelte-1jef3w8"></div></div>'),me=p('<section class="scheduler-section svelte-1jef3w8"><h3 class="section-title svelte-1jef3w8">调度任务</h3> <div class="scheduler-card svelte-1jef3w8"></div></section>'),he=p('<a class="card svelte-1jef3w8"><div class="card-main svelte-1jef3w8"><span class="card-label svelte-1jef3w8"> </span> <span class="card-desc svelte-1jef3w8"> </span></div> <span class="card-arrow svelte-1jef3w8">›</span></a>'),_e=p('<section class="links-section svelte-1jef3w8"><h3 class="section-title svelte-1jef3w8"> </h3> <div class="links svelte-1jef3w8"></div></section>'),we=p('<div class="feed-wrap svelte-1jef3w8"><div class="feed-col svelte-1jef3w8"><div class="settings-toolbar-block svelte-1jef3w8"><div class="admin-feed-header settings-header svelte-1jef3w8"><div class="admin-feed-header__left"><h2>设置</h2> <p class="admin-feed-header__desc">管理入口与调试工具</p></div></div></div> <div class="settings-body-scroll svelte-1jef3w8"><div class="body svelte-1jef3w8"><!> <!></div></div></div></div>');function Ne(U,q){Z(q,!1);function B(l){if(l===void 0)return"";if(l===0)return"· 正在执行";if(l===-1)return"· 无定时任务";const e=new Date(l),o=Date.now(),i=l-o;if(i<=0)return"· 即将执行";if(i<6e4)return`· 约 ${Math.round(i/1e3)} 秒后`;if(i<36e5)return`· 约 ${Math.round(i/6e4)} 分钟后`;const c=e.toLocaleTimeString("zh-CN",{hour:"2-digit",minute:"2-digit"}),n=new Date(o),m=e.getDate()===n.getDate()&&e.getMonth()===n.getMonth()&&e.getFullYear()===n.getFullYear(),a=new Date(n);a.setDate(a.getDate()+1);const d=e.getDate()===a.getDate()&&e.getMonth()===a.getMonth()&&e.getFullYear()===a.getFullYear();return m?`· 下次 ${c}`:d?`· 下次 明天 ${c}`:`· 下次 ${e.toLocaleDateString("zh-CN",{month:"numeric",day:"numeric"})} ${c}`}const E=[{title:"管理",links:[{href:"/admin/tags",label:"标签",desc:"系统标签库,新入库条目由 LLM 自动匹配打标签"},{href:"/admin/pipeline",label:"Pipeline",desc:"入库前处理(打标签、翻译),支持顺序与开关"}]},{title:"集成",links:[{href:"/admin/llm",label:"LLM",desc:"OpenAI 兼容 API(解析、Pipeline、标签与翻译);可替代 .env 中的 OPENAI_*"},{href:"/admin/proxy",label:"代理",desc:"全局 HTTP(S) 代理;单源在 sources.json 或插件中配置的代理优先"},{href:"/admin/deliver",label:"投递",desc:"配置下游 URL 与可选 Bearer 令牌;非空 URL 时在写库与 Pipeline 后额外 POST 条目"}]},{title:"调试",links:[{href:"/admin/parse",label:"Parse",desc:"从列表页解析条目,返回 JSON"}]}];let j=re({}),M=null;async function L(){try{const e=await(await fe("/api/scheduler/stats")).text();z(j,e.trim()?JSON.parse(e):{})}catch{z(j,{})}}V(()=>{L(),M=setInterval(L,2e3)}),W(()=>{M&&clearInterval(M)});const J=5;ve();var S=we();ne("1jef3w8",l=>{te(()=>{ae.title=`设置 - ${oe}`})});var O=s(S),N=v(s(O),2),k=s(N),A=s(k);{var H=l=>{var e=me(),o=v(s(e),2);y(o,5,()=>Object.entries(r(j)),D,(i,c)=>{var n=Y(()=>le(r(c),2));let m=()=>r(n)[0],a=()=>r(n)[1];const d=P(()=>a().running),$=P(()=>a().completedCount??0),h=P(()=>Math.min(a().concurrency,J));var _=pe(),f=s(_),w=s(f),G=s(w,!0);t(w);var C=v(w,2),K=s(C);t(C),t(f);var T=v(f,2);y(T,5,()=>Array(r(h)),D,(x,ge,Q)=>{var F=ue();let I;b(()=>I=ce(F,1,"slot svelte-1jef3w8",null,I,{filled:Q<r(d)})),u(x,F)}),t(T),t(_),b(x=>{g(G,m()),g(K,`执行中 ${r(d)??""}/${a().concurrency??""} · 排队 ${a().queued??""} · 定时 ${a().scheduledCount??""} · 已完成 ${r($)??""}
|
|
2
|
+
${x??""}`)},[()=>B(a().nextRunTime)]),u(i,_)}),t(o),t(e),u(l,e)},R=Y(()=>Object.keys(r(j)).length>0);ie(A,l=>{r(R)&&l(H)})}var X=v(A,2);y(X,1,()=>E,D,(l,e)=>{var o=_e(),i=s(o),c=s(i,!0);t(i);var n=v(i,2);y(n,5,()=>r(e).links,D,(m,a)=>{var d=he(),$=s(d),h=s($),_=s(h,!0);t(h);var f=v(h,2),w=s(f,!0);t(f),t($),se(2),t(d),b(()=>{de(d,"href",r(a).href),g(_,r(a).label),g(w,r(a).desc)}),u(m,d)}),t(n),t(o),b(()=>g(c,r(e).title)),u(l,o)}),t(k),t(N),t(O),t(S),u(U,S),ee()}export{Ne as component};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as p,f as d}from"../chunks/
|
|
1
|
+
import{a as p,f as d}from"../chunks/CS53ooo0.js";import"../chunks/BA4Gucnq.js";import{o as Y}from"../chunks/Dyvi1wBH.js";import{at as Z,az as ee,au as te,d as se,i as s,z as h,av as o,aw as c,$ as ae,ax as r,ay as P,a5 as a,f as C}from"../chunks/vtBo8kBV.js";import{e as J,s as x}from"../chunks/B2cyTHdf.js";import{i as N}from"../chunks/BkD3yAYe.js";import{h as oe}from"../chunks/DL3Q5sfb.js";import{P as re,r as R}from"../chunks/DoRPmqLn.js";import{b as A}from"../chunks/C8umpVpB.js";import{i as ie}from"../chunks/DVa8Y-mQ.js";import{B as ne}from"../chunks/_qj9U-za.js";import{b as S}from"../chunks/CBY2biv-.js";import{s as m}from"../chunks/CGCMIfh3.js";var ce=d('<p class="hint svelte-bhaf7m">加载中…</p>'),le=d('<input type="url" class="url-input svelte-bhaf7m" placeholder="https://agidaily.cc/api/gateway"/> <p class="hint svelte-bhaf7m">保存后生效;测试仅向 <code>…/test</code> 发一条合并的连通性请求(示例 items + 当前 sources),不写本地库。</p>',1),me=d('<p class="hint svelte-bhaf7m">加载中…</p>'),fe=d('<input type="password" class="url-input svelte-bhaf7m" placeholder="与下游 token.txt 内容一致,留空则不发送 Authorization" autocomplete="off"/> <p class="hint svelte-bhaf7m">非空时出站请求携带 Bearer。清空并保存可从配置中移除 token 字段。</p>',1),ve=d(`<div class="feed-wrap svelte-bhaf7m"><div class="feed-col svelte-bhaf7m"><div class="body svelte-bhaf7m"><!> <p class="intro svelte-bhaf7m">填写<strong>Gateway 基址</strong>(到 <code class="svelte-bhaf7m">/api/gateway</code> 为止,不要带 <code class="svelte-bhaf7m">/items</code>)。非空时:入库与 Pipeline
|
|
2
2
|
完成后会 <code class="svelte-bhaf7m"> </code>(体为 <code class="svelte-bhaf7m">sourceRef</code> + <code class="svelte-bhaf7m">items</code>);<code class="svelte-bhaf7m">sources.json</code> 变更后会 <code class="svelte-bhaf7m">POST …/sources</code>(正文与本地信源文件一致)。下游按固定路径接收即可。</p> <section class="form-section svelte-bhaf7m"><h3 class="section-title svelte-bhaf7m">Gateway 基址</h3> <!></section> <section class="form-section svelte-bhaf7m"><h3 class="section-title svelte-bhaf7m">Gateway 令牌(可选)</h3> <!></section> <section class="form-section svelte-bhaf7m"><div class="btn-row svelte-bhaf7m"><button type="button" class="btn btn-primary svelte-bhaf7m"> </button> <button type="button" class="btn btn-secondary svelte-bhaf7m"> </button></div></section></div></div></div>`);function Se(M,U){Z(U,!1);let i=h(""),n=h(""),l=h(!0),b=h(!1),u=h(!1);async function $(){a(l,!0);try{const e=await S("/api/deliver");a(i,(e==null?void 0:e.gateway)??""),a(n,(e==null?void 0:e.token)??"")}catch{a(i,""),a(n,"")}finally{a(l,!1)}}async function D(){a(b,!0);try{const e=await S("/api/deliver",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({gateway:s(i).trim(),token:s(n).trim()})});if(!(e!=null&&e.ok))throw new Error((e==null?void 0:e.message)??"保存失败");a(i,(e==null?void 0:e.gateway)??s(i)),a(n,(e==null?void 0:e.token)??s(n)),m("已保存","success")}catch(e){m("保存失败: "+(e instanceof Error?e.message:String(e)),"error")}finally{a(b,!1)}}async function F(){const e=s(i).trim();if(!e){m("请先填写 Gateway 基址","error");return}a(u,!0);try{const t=await S("/api/deliver/test",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({gateway:e,token:s(n).trim()})});t!=null&&t.ok?m("Gateway 测试投递成功(POST …/test)","success"):m("测试失败: "+((t==null?void 0:t.message)??"未知错误"),"error")}catch(t){m("测试失败: "+(t instanceof Error?t.message:String(t)),"error")}finally{a(u,!1)}}Y($),ie();var _=ve();oe("bhaf7m",e=>{se(()=>{ae.title=`投递 - ${re}`})});var O=o(_),G=o(O),E=o(G);ne(E,{});var g=c(E,2),j=c(o(g),7),q=o(j);r(j),P(9),r(g);var w=c(g,2),H=c(o(w),2);{var I=e=>{var t=ce();p(e,t)},K=e=>{var t=le(),v=C(t);R(v),P(2),A(v,()=>s(i),T=>a(i,T)),p(e,t)};N(H,e=>{s(l)?e(I):e(K,-1)})}r(w);var k=c(w,2),L=c(o(k),2);{var Q=e=>{var t=me();p(e,t)},V=e=>{var t=fe(),v=C(t);R(v),P(2),A(v,()=>s(n),T=>a(n,T)),p(e,t)};N(L,e=>{s(l)?e(Q):e(V,-1)})}r(k);var z=c(k,2),B=o(z),f=o(B),W=o(f,!0);r(f);var y=c(f,2),X=o(y,!0);r(y),r(B),r(z),r(G),r(O),r(_),ee(e=>{x(q,`POST ${(s(i)||"…")??""}/items`),f.disabled=s(b)||s(l),x(W,s(b)?"保存中…":"保存"),y.disabled=e,x(X,s(u)?"测试中…":"测试 Gateway")},[()=>s(u)||s(l)||!s(i).trim()]),J("click",f,D),J("click",y,F),p(M,_),te()}export{Se as component};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as v,f as p,t as ve}from"../chunks/
|
|
1
|
+
import{a as v,f as p,t as ve}from"../chunks/CS53ooo0.js";import"../chunks/BA4Gucnq.js";import{o as ce}from"../chunks/Dyvi1wBH.js";import{at as pe,az as R,au as me,d as fe,av as i,aw as n,$ as de,i as o,ax as l,a5 as s,z as c,f as ue,ay as j}from"../chunks/vtBo8kBV.js";import{e as z,s as $}from"../chunks/B2cyTHdf.js";import{i as b}from"../chunks/BkD3yAYe.js";import{h as ge}from"../chunks/DL3Q5sfb.js";import{P as _e,r as U,s as E}from"../chunks/DoRPmqLn.js";import{b as O}from"../chunks/C8umpVpB.js";import{i as ye}from"../chunks/DVa8Y-mQ.js";import{B as he}from"../chunks/_qj9U-za.js";import{b as C}from"../chunks/CBY2biv-.js";import{s as w}from"../chunks/CGCMIfh3.js";var be=p('<p class="hint svelte-1a0dvmg">加载中…</p>'),we=p('<input type="url" class="text-input svelte-1a0dvmg" placeholder="https://api.openai.com/v1" autocomplete="off"/>'),xe=p('<input type="text" class="text-input svelte-1a0dvmg" placeholder="gpt-4o-mini" autocomplete="off"/>'),Ke=p('<p class="hint svelte-1a0dvmg">加载中…</p>'),Pe=p("当前<strong>可用</strong>(文件或环境变量)。",1),Ae=p("未检测到 Key,请填写或设置 <code>OPENAI_API_KEY</code>。",1),Ie=p('<input type="password" class="text-input svelte-1a0dvmg" autocomplete="new-password"/> <p class="hint svelte-1a0dvmg"><!> <!></p>',1),ke=p('<div class="feed-wrap svelte-1a0dvmg"><div class="feed-col svelte-1a0dvmg"><div class="body svelte-1a0dvmg"><!> <p class="intro svelte-1a0dvmg">与 OpenAI 兼容的 Chat Completions API,用于列表解析、正文提取、Pipeline 打标签与翻译等。</p> <section class="form-section svelte-1a0dvmg"><h3 class="section-title svelte-1a0dvmg">API Base URL</h3> <!></section> <section class="form-section svelte-1a0dvmg"><h3 class="section-title svelte-1a0dvmg">模型</h3> <!></section> <section class="form-section svelte-1a0dvmg"><h3 class="section-title svelte-1a0dvmg">API Key</h3> <!></section> <section class="form-section svelte-1a0dvmg"><div class="btn-row svelte-1a0dvmg"><button type="button" class="btn btn-primary svelte-1a0dvmg"> </button> <button type="button" class="btn btn-secondary svelte-1a0dvmg"> </button></div></section></div></div></div>');function je(D,Y){pe(Y,!1);let m=c(""),f=c(""),u=c(""),g=c(!1),_=c(!1),d=c(!0),x=c(!1),K=c(!1);async function q(){s(d,!0);try{const e=await C("/api/llm");s(m,(e==null?void 0:e.baseUrl)??""),s(f,(e==null?void 0:e.model)??""),s(g,!!(e!=null&&e.hasApiKey)),s(_,!!(e!=null&&e.apiKeyInFile)),s(u,"")}catch{s(m,""),s(f,""),s(g,!1),s(_,!1)}finally{s(d,!1)}}async function G(){s(x,!0);try{const e={baseUrl:o(m).trim(),model:o(f).trim()},a=o(u).trim();a&&(e.apiKey=a);const t=await C("/api/llm",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!(t!=null&&t.ok))throw new Error((t==null?void 0:t.message)??"保存失败");s(m,(t==null?void 0:t.baseUrl)??o(m)),s(f,(t==null?void 0:t.model)??o(f)),s(g,!!(t!=null&&t.hasApiKey)),s(_,!!(t!=null&&t.apiKeyInFile)),s(u,""),w("已保存","success")}catch(e){w("保存失败: "+(e instanceof Error?e.message:String(e)),"error")}finally{s(x,!1)}}async function H(){s(K,!0);try{const e=await C("/api/llm/test",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({})});if(e!=null&&e.ok){const a=typeof e.reply=="string"?e.reply.trim():"";w(a?`连接成功:${a.slice(0,120)}${a.length>120?"…":""}`:"连接成功","success")}else w("测试失败: "+((e==null?void 0:e.message)??"未知错误"),"error")}catch(e){w("测试失败: "+(e instanceof Error?e.message:String(e)),"error")}finally{s(K,!1)}}ce(q),ye();var A=ke();ge("1a0dvmg",e=>{fe(()=>{de.title=`LLM - ${_e}`})});var S=i(A),F=i(S),N=i(F);he(N,{});var I=n(N,4),Q=n(i(I),2);{var V=e=>{var a=be();v(e,a)},W=e=>{var a=we();U(a),O(a,()=>o(m),t=>s(m,t)),v(e,a)};b(Q,e=>{o(d)?e(V):e(W,-1)})}l(I);var k=n(I,2),X=n(i(k),2);{var Z=e=>{var a=xe();U(a),E(a,"spellcheck",!1),O(a,()=>o(f),t=>s(f,t)),v(e,a)};b(X,e=>{o(d)||e(Z)})}l(k);var T=n(k,2),ee=n(i(T),2);{var te=e=>{var a=Ke();v(e,a)},se=e=>{var a=Ie(),t=ue(a);U(t),E(t,"spellcheck",!1);var L=n(t,2),M=i(L);{var re=r=>{var h=Pe();j(2),v(r,h)},ie=r=>{var h=Ae();j(2),v(r,h)};b(M,r=>{o(g)?r(re):r(ie,-1)})}var le=n(M,2);{var ne=r=>{var h=ve("已写入配置文件。");v(r,h)};b(le,r=>{o(_)&&r(ne)})}l(L),R(()=>E(t,"placeholder",o(_)?"已保存在配置中,留空表示不修改":"sk-...")),O(t,()=>o(u),r=>s(u,r)),v(e,a)};b(ee,e=>{o(d)?e(te):e(se,-1)})}l(T);var B=n(T,2),J=i(B),y=i(J),ae=i(y,!0);l(y);var P=n(y,2),oe=i(P,!0);l(P),l(J),l(B),l(F),l(S),l(A),R(()=>{y.disabled=o(x)||o(d),$(ae,o(x)?"保存中…":"保存"),P.disabled=o(K)||o(d)||!o(g),$(oe,o(K)?"测试中…":"测试连接")}),z("click",y,G),z("click",P,H),v(D,A),me()}export{je as component};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,f as m}from"../chunks/CS53ooo0.js";import"../chunks/BA4Gucnq.js";import{o as i}from"../chunks/Dyvi1wBH.js";import{at as f,au as n}from"../chunks/vtBo8kBV.js";import{s as l,a as u}from"../chunks/B6WG2Sd3.js";import{i as c}from"../chunks/DVa8Y-mQ.js";import{g}from"../chunks/CVW0ymE1.js";import{p as $}from"../chunks/CqYSO3Dx.js";var v=m('<p class="muted svelte-1w6l7vk">正在跳转…</p>');function b(o,t){f(t,!1);const a=()=>u($,"$page",r),[r,s]=l();i(()=>{g("/logs"+a().url.search,{replaceState:!0})}),c();var p=v();e(o,p),n(),s()}export{b as component};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as w,f as O}from"../chunks/
|
|
1
|
+
import{a as w,f as O}from"../chunks/CS53ooo0.js";import"../chunks/BA4Gucnq.js";import{at as M,az as T,i as s,au as W,d as X,av as t,a5 as o,z as d,$ as Y,ay as I,ax as r,aw as v}from"../chunks/vtBo8kBV.js";import{e as F,s as S}from"../chunks/B2cyTHdf.js";import{i as J}from"../chunks/BkD3yAYe.js";import{h as G}from"../chunks/DL3Q5sfb.js";import{P as K,r as q}from"../chunks/DoRPmqLn.js";import{b as C}from"../chunks/C8umpVpB.js";import{p as Q}from"../chunks/Bfc47y5P.js";import{i as V}from"../chunks/DVa8Y-mQ.js";import{B as Z}from"../chunks/_qj9U-za.js";var ee=O('<div class="result-error svelte-1x680s0" role="alert"> </div>'),se=O('<div class="result-wrap svelte-1x680s0"><p class="result-label svelte-1x680s0">解析结果(与下方接口一致)</p> <pre class="result-json svelte-1x680s0"> </pre></div>'),te=O('<div class="feed-wrap svelte-1x680s0"><div class="feed-col svelte-1x680s0"><div class="feed-header svelte-1x680s0"><!> <h2 class="svelte-1x680s0">Parse</h2> <p class="page-desc svelte-1x680s0">从列表页解析条目结构,返回 JSON,用于调试插件 Parser 规则</p></div> <div class="body svelte-1x680s0"><form><div class="url-row svelte-1x680s0"><input type="url" placeholder="输入列表页地址…" required="" autocomplete="url" class="svelte-1x680s0"/> <button type="submit" class="svelte-1x680s0"> </button></div> <div class="proxy-row svelte-1x680s0"><input type="text" placeholder="代理(可选),如 http://127.0.0.1:7890 — 覆盖信源与 HTTP_PROXY" autocomplete="off" spellcheck="false" class="svelte-1x680s0"/></div></form> <!> <!> <div class="info-box svelte-1x680s0"><p class="svelte-1x680s0">抓取在<strong>服务端</strong>执行,默认<strong>有头</strong> Chrome(与信源同一套 Puppeteer),会弹出可见窗口;本页 <code class="svelte-1x680s0">fetch</code> 仅用于展示 JSON。需要无头时在 URL 加 <code class="svelte-1x680s0">?headless=true</code>。</p> <p class="info-note svelte-1x680s0">返回中的 <code class="svelte-1x680s0">effectiveProxy</code> 为本次抓取实际选用的代理。若需在看得到的窗口里验证出口 IP,请在该 Puppeteer 窗口内打开 <code class="svelte-1x680s0">api.ipify.org</code> 等(勿用本机普通浏览器标签测代理)。</p></div></div></div></div>');function ue(z,B){M(B,!1);let i=d(""),m=d(""),n=d(!1),c=d(""),p=d("");async function D(){if(s(i).trim()){o(n,!0),o(c,""),o(p,"");try{const e=s(i).startsWith("http")?s(i):"https://"+s(i),a=new URLSearchParams,l=s(m).trim();l&&a.set("proxy",l);const u=a.toString(),b=await fetch("/admin/parse/"+encodeURIComponent(e)+(u?"?"+u:""),{credentials:"include"}),P=await b.text();if(!b.ok){o(c,P||`HTTP ${b.status}`);return}try{o(p,JSON.stringify(JSON.parse(P),null,2))}catch{o(p,P)}}catch(e){o(c,e instanceof Error?e.message:String(e))}finally{o(n,!1)}}}V();var x=te();G("1x680s0",e=>{X(()=>{Y.title=`Parse - ${K}`})});var R=t(x),_=t(R),E=t(_);Z(E,{}),I(4),r(_);var $=v(_,2),f=t($),h=t(f),g=t(h);q(g);var y=v(g,2),H=t(y,!0);r(y),r(h);var N=v(h,2),U=t(N);q(U),r(N),r(f);var k=v(f,2);{var L=e=>{var a=ee(),l=t(a,!0);r(a),T(()=>S(l,s(c))),w(e,a)};J(k,e=>{s(c)&&e(L)})}var j=v(k,2);{var A=e=>{var a=se(),l=v(t(a),2),u=t(l,!0);r(l),r(a),T(()=>S(u,s(p))),w(e,a)};J(j,e=>{s(p)&&e(A)})}I(2),r($),r(R),r(x),T(()=>{y.disabled=s(n),S(H,s(n)?"解析中…":"解析")}),C(g,()=>s(i),e=>o(i,e)),C(U,()=>s(m),e=>o(m,e)),F("submit",f,Q(D)),w(z,x),W()}export{ue as component};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as u,f as g}from"../chunks/
|
|
1
|
+
import{a as u,f as g}from"../chunks/CS53ooo0.js";import"../chunks/BA4Gucnq.js";import{o as se}from"../chunks/Dyvi1wBH.js";import{at as te,au as re,d as le,av as o,$ as ie,ay as B,ax as i,aw as c,i as a,a5 as r,z as b,az as T,aA as oe}from"../chunks/vtBo8kBV.js";import{s as I,e as f}from"../chunks/B2cyTHdf.js";import{i as O}from"../chunks/BkD3yAYe.js";import{e as ne,i as de}from"../chunks/CFwxUBGi.js";import{h as ve}from"../chunks/DL3Q5sfb.js";import{P as ce,r as fe,a as pe,b as me}from"../chunks/DoRPmqLn.js";import{i as ue}from"../chunks/DVa8Y-mQ.js";import{B as be}from"../chunks/_qj9U-za.js";import{b as $}from"../chunks/CBY2biv-.js";import{s as z}from"../chunks/CGCMIfh3.js";var ge=g('<div class="state svelte-jqd85f">加载中…</div>'),_e=g('<div role="listitem" draggable="true"><span class="drag-handle svelte-jqd85f" title="拖拽排序">⋮⋮</span> <label class="toggle-wrap svelte-jqd85f"><input type="checkbox" class="svelte-jqd85f"/> <span class="toggle-slider svelte-jqd85f"></span></label> <span class="step-label svelte-jqd85f"> </span></div>'),he=g('<button type="button" class="btn-add svelte-jqd85f">+ 添加步骤</button>'),je=g('<div class="body svelte-jqd85f"><section class="pipeline-section svelte-jqd85f"><h3 class="section-title svelte-jqd85f">Pipeline(入库前)</h3> <p class="hint svelte-jqd85f">对每条新入库条目执行,顺序由上到下。</p> <div class="step-list svelte-jqd85f" role="list"></div> <!></section> <div class="save-row svelte-jqd85f"><button type="button" class="btn btn-primary svelte-jqd85f"> </button></div></div>'),qe=g('<div class="feed-wrap svelte-jqd85f"><div class="feed-col svelte-jqd85f"><div class="feed-header svelte-jqd85f"><!> <h2 class="svelte-jqd85f">Pipeline</h2> <p class="page-desc svelte-jqd85f">入库前处理(打标签、翻译),可调整顺序与开关。</p></div> <!></div></div>');function $e(C,F){te(F,!1);const J={qualityFilter:"质量过滤",tagger:"打标签",translator:"翻译"};let t=b([]),_=b([]),q=b(!0),p=b(!1),m=b(null);async function L(){r(q,!0);try{const e=await $("/api/pipeline");r(t,(e==null?void 0:e.steps)??[]),r(_,(e==null?void 0:e.availableIds)??[])}catch{r(t,[]),r(_,[])}finally{r(q,!1)}}async function M(){r(p,!0);try{const e=await $("/api/pipeline",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({steps:a(t)})});if(!(e!=null&&e.ok))throw new Error((e==null?void 0:e.message)??"保存失败");r(t,(e==null?void 0:e.steps)??a(t)),z("已保存","success")}catch(e){z("保存失败: "+(e instanceof Error?e.message:String(e)),"error")}finally{r(p,!1)}}function N(e){r(t,a(t).map((s,l)=>l===e?{...s,enabled:!s.enabled}:s))}function R(e){r(m,e)}function U(e){e.preventDefault(),e.dataTransfer&&(e.dataTransfer.dropEffect="move")}function G(e){if(a(m)==null)return;const s=[...a(t)],[l]=s.splice(a(m),1);s.splice(e,0,l),r(t,s),r(m,null)}function H(){const e=new Set(a(t).map(l=>l.id)),s=a(_).find(l=>!e.has(l));s&&r(t,[...a(t),{id:s,enabled:!1}])}se(L),ue();var y=qe();ve("jqd85f",e=>{le(()=>{ie.title=`Pipeline - ${ce}`})});var k=o(y),w=o(k),K=o(w);be(K,{}),B(4),i(w);var Q=c(w,2);{var V=e=>{var s=ge();u(e,s)},W=e=>{var s=je(),l=o(s),x=c(o(l),4);ne(x,5,()=>a(t),de,(d,n,j)=>{var v=_e();let D;var P=c(o(v),2),S=o(P);fe(S),B(2),i(P);var A=c(P,2),ae=o(A,!0);i(A),i(v),T(()=>{D=pe(v,1,"step-row svelte-jqd85f",null,D,{dragging:a(m)===j}),me(S,a(n).enabled),I(ae,J[a(n).id]??a(n).id)}),f("change",S,()=>N(j)),f("dragstart",v,()=>R(j)),f("dragover",v,U),f("drop",v,()=>G(j)),u(d,v)}),i(x);var X=c(x,2);{var Y=d=>{var n=he();T(()=>n.disabled=a(p)),f("click",n,H),u(d,n)},Z=oe(()=>a(_).some(d=>!a(t).some(n=>n.id===d)));O(X,d=>{a(Z)&&d(Y)})}i(l);var E=c(l,2),h=o(E),ee=o(h,!0);i(h),i(E),i(s),T(()=>{h.disabled=a(p),I(ee,a(p)?"保存中…":"保存")}),f("click",h,M),u(e,s)};O(Q,e=>{a(q)?e(V):e(W,-1)})}i(k),i(y),u(C,y),re()}export{$e as component};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":"1780569808680"}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/rssany.js
CHANGED
|
@@ -1,6 +1,226 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { closeSync, openSync } from "node:fs";
|
|
4
|
+
import { access, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
5
|
+
import http from "node:http";
|
|
6
|
+
import { homedir, networkInterfaces } from "node:os";
|
|
7
|
+
import { dirname, join } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
const command = process.argv[2];
|
|
11
|
+
const binDir = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const packageRoot = join(binDir, "..");
|
|
13
|
+
const userDir = process.env.RSSANY_USER_DIR?.trim() || join(homedir(), ".rssany");
|
|
14
|
+
const pidPath = join(userDir, "rssany.pid");
|
|
15
|
+
const logPath = join(userDir, "rssany.log");
|
|
16
|
+
const port = Number(process.env.PORT) || 18473;
|
|
17
|
+
const serverOrigin = `http://127.0.0.1:${port}`;
|
|
18
|
+
|
|
19
|
+
async function pathExists(path) {
|
|
20
|
+
try {
|
|
21
|
+
await access(path);
|
|
22
|
+
return true;
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function readPid() {
|
|
29
|
+
try {
|
|
30
|
+
const raw = await readFile(pidPath, "utf-8");
|
|
31
|
+
const pid = Number(raw.trim());
|
|
32
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
33
|
+
} catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isProcessRunning(pid) {
|
|
39
|
+
try {
|
|
40
|
+
process.kill(pid, 0);
|
|
41
|
+
return true;
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getLanUrl() {
|
|
48
|
+
const lanIp = Object.values(networkInterfaces())
|
|
49
|
+
.flat()
|
|
50
|
+
.find((iface) => iface?.family === "IPv4" && !iface.internal)?.address;
|
|
51
|
+
return lanIp ? `http://${lanIp}:${port}/` : null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function printAddress(prefix = "RssAny 已启动") {
|
|
55
|
+
console.log(`${prefix}: http://127.0.0.1:${port}/`);
|
|
56
|
+
const lanUrl = getLanUrl();
|
|
57
|
+
if (lanUrl) console.log(`局域网访问: ${lanUrl}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function printUsage() {
|
|
61
|
+
console.log("用法: rssany <start|stop|reset|crawl>");
|
|
62
|
+
console.log(" rssany start 后台启动服务并输出访问地址");
|
|
63
|
+
console.log(" rssany stop 关闭后台服务并输出执行状态");
|
|
64
|
+
console.log(" rssany reset 重置本地数据");
|
|
65
|
+
console.log(" rssany crawl <ref> 按内部抓取链路拉取指定信源");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function canConnectToServer() {
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
const req = http.get(`${serverOrigin}/api/server-info`, (res) => {
|
|
71
|
+
res.resume();
|
|
72
|
+
resolve(true);
|
|
73
|
+
});
|
|
74
|
+
req.setTimeout(500, () => {
|
|
75
|
+
req.destroy();
|
|
76
|
+
resolve(false);
|
|
77
|
+
});
|
|
78
|
+
req.on("error", () => resolve(false));
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function waitForServer(timeoutMs = 5000) {
|
|
83
|
+
const startTime = Date.now();
|
|
84
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
85
|
+
if (await canConnectToServer()) return true;
|
|
86
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function start() {
|
|
92
|
+
await mkdir(userDir, { recursive: true });
|
|
93
|
+
|
|
94
|
+
const currentPid = await readPid();
|
|
95
|
+
if (currentPid && isProcessRunning(currentPid)) {
|
|
96
|
+
printAddress(`RssAny 已在运行 (pid ${currentPid})`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const entry = join(packageRoot, "dist", "index.js");
|
|
101
|
+
if (!(await pathExists(entry))) {
|
|
102
|
+
console.error("未找到 dist/index.js,请先构建项目或重新安装 rssany。");
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const logFd = openSync(logPath, "a");
|
|
108
|
+
const child = spawn(process.execPath, [entry], {
|
|
109
|
+
cwd: process.cwd(),
|
|
110
|
+
detached: true,
|
|
111
|
+
env: process.env,
|
|
112
|
+
stdio: ["ignore", logFd, logFd],
|
|
113
|
+
});
|
|
114
|
+
closeSync(logFd);
|
|
115
|
+
|
|
116
|
+
await writeFile(pidPath, `${child.pid}\n`, "utf-8");
|
|
117
|
+
console.log(`日志: ${logPath}`);
|
|
118
|
+
if (await waitForServer()) {
|
|
119
|
+
child.unref();
|
|
120
|
+
printAddress(`RssAny 已启动 (pid ${child.pid})`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
child.unref();
|
|
125
|
+
console.error(`RssAny 启动未完成,请查看日志: ${logPath}`);
|
|
126
|
+
process.exitCode = 1;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function stop() {
|
|
130
|
+
const pid = await readPid();
|
|
131
|
+
if (!pid) {
|
|
132
|
+
console.log("RssAny 未运行:没有找到 pid 文件。");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!isProcessRunning(pid)) {
|
|
137
|
+
await rm(pidPath, { force: true });
|
|
138
|
+
console.log(`RssAny 未运行:已清理失效 pid ${pid}。`);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
process.kill(pid, "SIGTERM");
|
|
143
|
+
await rm(pidPath, { force: true });
|
|
144
|
+
console.log(`RssAny 已发送停止信号 (pid ${pid})。`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function readCrawlRef(args) {
|
|
148
|
+
const refFlagIndex = args.findIndex((arg) => arg === "--ref");
|
|
149
|
+
if (refFlagIndex >= 0) return args[refFlagIndex + 1]?.trim() || "";
|
|
150
|
+
const refEquals = args.find((arg) => arg.startsWith("--ref="));
|
|
151
|
+
if (refEquals) return refEquals.slice("--ref=".length).trim();
|
|
152
|
+
return args.find((arg) => !arg.startsWith("-"))?.trim() || "";
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function postJson(path, body) {
|
|
156
|
+
const res = await fetch(`${serverOrigin}${path}`, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers: { "Content-Type": "application/json" },
|
|
159
|
+
body: JSON.stringify(body),
|
|
160
|
+
});
|
|
161
|
+
const data = await res.json().catch(() => ({}));
|
|
162
|
+
if (!res.ok) {
|
|
163
|
+
throw new Error(data.error || `HTTP ${res.status}`);
|
|
164
|
+
}
|
|
165
|
+
return data;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function getJson(path) {
|
|
169
|
+
const res = await fetch(`${serverOrigin}${path}`);
|
|
170
|
+
const data = await res.json().catch(() => ({}));
|
|
171
|
+
if (!res.ok) {
|
|
172
|
+
throw new Error(data.error || `HTTP ${res.status}`);
|
|
173
|
+
}
|
|
174
|
+
return data;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function pollTask(taskId, timeoutMs = 120000) {
|
|
178
|
+
const start = Date.now();
|
|
179
|
+
while (Date.now() - start < timeoutMs) {
|
|
180
|
+
const task = await getJson(`/api/tasks/${encodeURIComponent(taskId)}`);
|
|
181
|
+
if (task.status === "done") return task;
|
|
182
|
+
if (task.status === "error") {
|
|
183
|
+
throw new Error(task.error || "抓取失败");
|
|
184
|
+
}
|
|
185
|
+
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
186
|
+
}
|
|
187
|
+
throw new Error("抓取超时");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function crawl() {
|
|
191
|
+
const ref = readCrawlRef(process.argv.slice(3));
|
|
192
|
+
if (!ref) {
|
|
193
|
+
console.error("ref 不能为空。用法: rssany crawl <ref>");
|
|
194
|
+
process.exitCode = 1;
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (!(await canConnectToServer())) {
|
|
198
|
+
console.error(`RssAny 服务未运行,请先执行 rssany start。目标: ${serverOrigin}`);
|
|
199
|
+
process.exitCode = 1;
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
const { taskId } = await postJson("/api/tasks", { type: "source-pull", ref });
|
|
204
|
+
if (!taskId) throw new Error("后端未返回 taskId");
|
|
205
|
+
console.log(`crawl 已提交: ${ref}`);
|
|
206
|
+
console.log(`task: ${taskId}`);
|
|
207
|
+
await pollTask(taskId);
|
|
208
|
+
console.log("crawl 完成");
|
|
209
|
+
} catch (err) {
|
|
210
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
211
|
+
process.exitCode = 1;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (command === "reset") {
|
|
216
|
+
await import(new URL("../scripts/reset.mjs", import.meta.url));
|
|
217
|
+
} else if (command === "start") {
|
|
218
|
+
await start();
|
|
219
|
+
} else if (command === "stop") {
|
|
220
|
+
await stop();
|
|
221
|
+
} else if (command === "crawl") {
|
|
222
|
+
await crawl();
|
|
223
|
+
} else {
|
|
224
|
+
printUsage();
|
|
225
|
+
if (command) process.exitCode = 1;
|
|
226
|
+
}
|