From e372ef2295859c3f317be2104e7a74744ebed2da Mon Sep 17 00:00:00 2001 From: kevin Date: Sat, 11 Apr 2026 19:44:51 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BC=98=E5=85=88=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 + config/config.go | 2 +- crawler/crawler.go | 269 ++++++++++++------ .../{index-CYFclJJJ.js => index-CJeEgrKF.js} | 2 +- dist/index.html | 2 +- sese-engine-ui | 2 +- storage/storage.go | 37 ++- 7 files changed, 210 insertions(+), 106 deletions(-) create mode 100644 .env rename dist/assets/{index-CYFclJJJ.js => index-CJeEgrKF.js} (78%) diff --git a/.env b/.env new file mode 100644 index 0000000..b31ba26 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +CC=C:\mingw64\bin\gcc.exe +CXX=C:\mingw64\bin\g++.exe diff --git a/config/config.go b/config/config.go index 0785fc5..6970822 100644 --- a/config/config.go +++ b/config/config.go @@ -124,7 +124,7 @@ func GetDefaultConfig() Config { MaxKeywordsPerPage: 250, MaxEpoch: 100, ExpectedProsperRatio: 0.6, - EntryURL: "https://zh.wikipedia.org/", + EntryURL: "http://127.0.0.1", MaxPageSize: 5 * 1024 * 1024, RecrawlMaxAge: 30 * 86400, // 30 天 RecrawlCheckInterval: 3600, // 1 小时 diff --git a/crawler/crawler.go b/crawler/crawler.go index 66b3dfb..f3d3a41 100644 --- a/crawler/crawler.go +++ b/crawler/crawler.go @@ -70,18 +70,17 @@ type Crawler struct { activeWorkers int64 // ---- Priority Worker(独立 goroutine,不受主 workers 限制)---- - priorityCh chan string // Priority URL 任务队列 - prioritySem chan struct{} // Priority 信号量(上限 priorityMaxWorkers) - priorityWg sync.WaitGroup // 等待所有 Priority goroutine 结束 - priorityMu sync.RWMutex // 保护 priorityStats - priorityStats struct { + priorityCh chan string // Priority URL 任务队列(用户手动添加) + priorityChildCh chan string // Priority 子链接队列(子 URL 继续由 priority worker 爬取) + prioritySem chan struct{} // Priority 信号量(上限 priorityMaxWorkers) + priorityWg sync.WaitGroup // 等待所有 Priority goroutine 结束 + priorityMu sync.RWMutex // 保护 priorityStats + priorityStats struct { pending int64 // 待处理的 Priority URL 数量(入队但未开始) active int64 // 正在处理的 Priority URL 数量 } - - // ---- Priority 子链接优先队列(来自 priority worker 的子链接会优先爬取)---- - priorityChildrenMu sync.Mutex - priorityChildren []string // Priority URL 产生的子链接(优先处理) + // 孙链接(子 URL 的子链接)进入普通 BFS 队列 + normalChildCh chan URLWeight // 孙链接 channel,由 Run 循环消费 // ---- 爬取状态暴露(供前端监控) ---- crawlStatusMu sync.RWMutex @@ -118,27 +117,40 @@ var globalPriorityStatus struct { active int64 } +// 全局二级优先队列待处理计数(goroutine 异步发送,无法用 len() 直接获取) +var globalPriorityLevel2 int64 + // GlobalPriorityStatus 返回当前全局 Priority Worker 状态。 func GlobalPriorityStatus() map[string]interface{} { return map[string]interface{}{ - "pending": atomic.LoadInt64(&globalPriorityStatus.pending), - "active": atomic.LoadInt64(&globalPriorityStatus.active), - "max_workers": priorityMaxWorkers, - "children_queue": atomic.LoadInt64(&globalPriorityChildren), - } + "active": atomic.LoadInt64(&globalPriorityStatus.active), + "max_workers": priorityMaxWorkers, + "level1": atomic.LoadInt64(&globalPriorityStatus.pending), + "level2": atomic.LoadInt64(&globalPriorityLevel2), + "level2_queue": atomic.LoadInt64(&globalPriorityLevel2), // 待爬取 + "level2_inflight": atomic.LoadInt64(&globalPriorityLevel2Inflight), // 正在爬取 +} } -// 全局 Priority 子链接队列长度(跨 Crawler 实例共享) -var globalPriorityChildren int64 - -// IncrementPriorityChildren 增加 priorityChildren 计数。 -func IncrementPriorityChildren(n int64) { - atomic.AddInt64(&globalPriorityChildren, n) +// IncrementPriorityLevel2 增加二级队列计数。 +func IncrementPriorityLevel2(n int64) { + atomic.AddInt64(&globalPriorityLevel2, n) } -// DecrementPriorityChildren 减少 priorityChildren 计数。 -func DecrementPriorityChildren(n int64) { - atomic.AddInt64(&globalPriorityChildren, -n) +// DecrementPriorityLevel2 减少二级队列计数。 +func DecrementPriorityLevel2(n int64) { + atomic.AddInt64(&globalPriorityLevel2, -n) +} + +// ---- 二级正在爬取的计数(drain 时启动,goroutine 结束时扣减)---- +var globalPriorityLevel2Inflight int64 + +func IncrementPriorityLevel2Inflight(n int64) { + atomic.AddInt64(&globalPriorityLevel2Inflight, n) +} + +func DecrementPriorityLevel2Inflight(n int64) { + atomic.AddInt64(&globalPriorityLevel2Inflight, -n) } // New 创建一个 Crawler 实例。 @@ -150,8 +162,10 @@ func New(db *storage.DB, a *analyzer.Analyzer, prosperMap map[string]float64) *C analyzer: a, prosperMap: prosperMap, visited: make(map[string]bool), - priorityCh: make(chan string, priorityQueueSize), - prioritySem: make(chan struct{}, priorityMaxWorkers), + priorityCh: make(chan string, priorityQueueSize), + priorityChildCh: make(chan string, priorityQueueSize), + prioritySem: make(chan struct{}, priorityMaxWorkers), + normalChildCh: make(chan URLWeight, priorityQueueSize), } // 启动 Priority Worker(独立 goroutine,不受主 workers 限制) go c.runPriorityWorker() @@ -225,54 +239,129 @@ func (c *Crawler) markVisited(url string) { // ---- Priority Worker(突破 workers 上限,立即爬取高优先级 URL)---- // runPriorityWorker 独立处理高优先级 URL,不受主 workers 限制。 -// 当用户插入 Priority URL 时,立即触发爬取(不等待 epoch 调度)。 +// 两级队列共享同一线程池:一级队列(手动 URL)优先处理完,再处理二级队列(子 URL)。 +// - 一级队列:visitURLUnlimited,无限爬子链接,子链接进入二级队列 +// - 二级队列:visitURL,有限爬子链接,子链接进入普通 BFS func (c *Crawler) runPriorityWorker() { - for url := range c.priorityCh { - c.prioritySem <- struct{}{} // 获取令牌(阻塞直到有空闲槽位) - c.priorityMu.Lock() - c.priorityStats.active++ - c.priorityStats.pending-- - c.priorityMu.Unlock() - atomic.AddInt64(&globalPriorityStatus.pending, -1) - atomic.AddInt64(&globalPriorityStatus.active, 1) - - c.priorityWg.Add(1) - go func(rawURL string) { - defer c.priorityWg.Done() - defer func() { <-c.prioritySem }() // 释放令牌 - defer func() { - c.priorityMu.Lock() - c.priorityStats.active-- - c.priorityMu.Unlock() - atomic.AddInt64(&globalPriorityStatus.active, -1) - }() - - // 关键:强制移除 visited 标记(即使未过期也要重新爬取) - c.visitedMu.Lock() - delete(c.visited, rawURL) - c.visitedMu.Unlock() - - log.Printf("[crawler] priority crawl started: %s", rawURL) - - // 直接调用 visitURLUnlimited,绕过队列调度和链接数限制 - hrefs := c.visitURLUnlimited(rawURL) - - // 将子链接加入优先队列(保持优先级) - // 注意:Priority URL 的子链接强制加入队列,即使已访问过也要重新爬取 - if len(hrefs) > 0 { - c.priorityChildrenMu.Lock() - for _, child := range hrefs { - c.priorityChildren = append(c.priorityChildren, child) + for { + // ---- 第一阶段:drain 一级队列全部取出 ---- + level1Batch := []string{} + // 先阻塞等第一个一级 URL + url := <-c.priorityCh + level1Batch = append(level1Batch, url) + // drain 剩余一级 URL(非阻塞) + for { + select { + case url = <-c.priorityCh: + level1Batch = append(level1Batch, url) + default: + goto processLevel1 } - IncrementPriorityChildren(int64(len(hrefs))) - c.priorityChildrenMu.Unlock() } - log.Printf("[crawler] priority crawl done: %s (%d child links)", rawURL, len(hrefs)) + processLevel1: + // batch 收集完成后才扣减 pending,此时 pending = len(level1Batch) + len(priorityCh) + // 这样 level1 能正确反映队列中待处理的 URL 总数 + log.Printf("[priority] drain level1 batch: count=%d, pending_before=%d, priorityCh_len=%d", + len(level1Batch), atomic.LoadInt64(&globalPriorityStatus.pending), len(c.priorityCh)) + atomic.AddInt64(&globalPriorityStatus.pending, -int64(len(level1Batch))) - // 清理已访问的 priority URL(防止重复爬取) - _ = c.db.RemovePriorityURL(rawURL) - }(url) + // ---- 启动全部一级 goroutine ---- + for _, u := range level1Batch { + c.prioritySem <- struct{}{} + c.priorityMu.Lock() + c.priorityStats.active++ + c.priorityMu.Unlock() + atomic.AddInt64(&globalPriorityStatus.active, 1) + c.priorityWg.Add(1) + go c.priorityCrawlLoop(u, 1) + } + + // 等待一级 goroutine 全部完成 + c.priorityWg.Wait() + + // ---- 第二阶段:drain 二级队列全部取出 ---- + level2Batch := []string{} + drained := int64(0) + for { + select { + case child := <-c.priorityChildCh: + level2Batch = append(level2Batch, child) + drained++ + // 实时扣减 level2,使 API 在 drain 期间仍能看到剩余未处理的量 + atomic.AddInt64(&globalPriorityLevel2, -1) + default: + goto processLevel2 + } + } + + processLevel2: + // drained 项已在 drain 循环中逐个从 level2 扣减 + // 启动 goroutine 后这些 URL 进入"正在爬取"状态 + IncrementPriorityLevel2Inflight(int64(len(level2Batch))) + log.Printf("[priority] drain level2 done: drained=%d, inflight_before_spawn=%d", + len(level2Batch), atomic.LoadInt64(&globalPriorityLevel2Inflight)) + + // ---- 启动全部二级 goroutine ---- + for _, u := range level2Batch { + c.prioritySem <- struct{}{} + c.priorityMu.Lock() + c.priorityStats.active++ + c.priorityMu.Unlock() + atomic.AddInt64(&globalPriorityStatus.active, 1) + c.priorityWg.Add(1) + go c.priorityCrawlLoop(u, 2) + } + + // 等待二级 goroutine 全部完成,才进入下一轮(再次处理一级队列) + c.priorityWg.Wait() + } +} + +// priorityCrawlLoop 爬取单个 URL: +// +// level=1(一级,手动 URL):visitURLUnlimited 无限爬子链接 → 二级队列 +// level=2(二级,子 URL):visitURLUnlimited 无限爬子链接 → 普通 BFS +func (c *Crawler) priorityCrawlLoop(rawURL string, level int) { + defer c.priorityWg.Done() + defer func() { <-c.prioritySem }() + defer func() { + c.priorityMu.Lock() + c.priorityStats.active-- + c.priorityMu.Unlock() + atomic.AddInt64(&globalPriorityStatus.active, -1) + if level == 2 { + DecrementPriorityLevel2Inflight(1) + } + }() + + // 标记 DB 中该 URL 为已访问,防止重启后再次被调度 + _ = c.db.MarkPriorityURLVisited(rawURL) + + // 两级都不限制子链接数量 + children := c.visitURLUnlimited(rawURL) + + log.Printf("[crawler] priority[%d] crawl done: %s (%d child links)", level, rawURL, len(children)) + + if len(children) == 0 { + return + } + + // 一级:子链接进二级队列;二级:子链接进普通 BFS + for _, child := range children { + if level == 1 { + select { + case c.priorityChildCh <- child: + IncrementPriorityLevel2(1) + default: + // 二级队列满,丢弃 + } + } else { + select { + case c.normalChildCh <- URLWeight{URL: child, Weight: 1.0}: + default: + } + } } } @@ -367,17 +456,6 @@ func (c *Crawler) Run(entryURL string, maxEpoch int) { cs.CompletedCount = 0 }) - // ---- 优先处理 priorityChildren 队列(来自 priority worker 的子链接)---- - var priorityQueue []string - c.priorityChildrenMu.Lock() - if len(c.priorityChildren) > 0 { - priorityQueue = c.priorityChildren - // 更新全局计数器:这些 URL 即将被处理 - DecrementPriorityChildren(int64(len(priorityQueue))) - log.Printf("[crawler] epoch %d/%d processing %d priority children first", ep+1, maxEpoch, len(priorityQueue)) - } - c.priorityChildrenMu.Unlock() - // 每轮开始前:拉取 priority URLs,插入队列前端 priorityAdded := c.fetchAndApplyPriorityURLs(&queue) if priorityAdded > 0 { @@ -440,8 +518,8 @@ func (c *Crawler) Run(entryURL string, maxEpoch int) { // 分配权重 w := 1.0 / float64(n) - // 孙链接(来自 priorityChildren)爬取后,子链接进入正常 BFS 队列(不再优先传递) - // 所有子链接统一进入 newLinks,经过 schedule() 调度 + // 子 URL 进入 newLinks 调度,由普通 BFS 下一轮处理。 + // (priority worker 通过 priorityChildCh 独立爬取子 URL,两者互不干扰) mu.Lock() for _, h := range children { newLinks = append(newLinks, URLWeight{URL: h, Weight: w}) @@ -451,13 +529,18 @@ func (c *Crawler) Run(entryURL string, maxEpoch int) { } wg.Wait() - // ---- 清空本轮已处理的 priorityQueue ---- - c.priorityChildrenMu.Lock() - if len(priorityQueue) > 0 { - c.priorityChildren = c.priorityChildren[len(priorityQueue):] - // 计数器已在提取时扣除,这里不需要额外操作 + // 消费孙链接 channel(孙链接来自 priority 爬取的子链接,进入普通 BFS 队列) + // 孙链接爬取是异步的,使用 timeout 确保全部到达后再调度 + timeout := time.After(5 * time.Second) + drained := false + for !drained { + select { + case gc := <-c.normalChildCh: + newLinks = append(newLinks, gc) + case <-timeout: + drained = true + } } - c.priorityChildrenMu.Unlock() // 更新已收录总数 c.visitedMu.RLock() @@ -487,7 +570,8 @@ func (c *Crawler) Run(entryURL string, maxEpoch int) { // visitURLRaw 抓取 URL 的核心逻辑,提取标题、描述、正文、子链接。 // 不包含链接数限制,用于优先级爬取。 -func (c *Crawler) visitURLRaw(rawURL string) (title, desc, text string, hrefs []string) { +// forceIndex=true 时强制跳过增量重爬检测,直接提取关键词写入索引(用于 Priority URL)。 +func (c *Crawler) visitURLRaw(rawURL string, forceIndex bool) (title, desc, text string, hrefs []string) { // recover 保护:防止任何模块(analyzer/storage/parser)的 panic 杀死 goroutine defer func() { if r := recover(); r != nil { @@ -535,7 +619,7 @@ func (c *Crawler) visitURLRaw(rawURL string) (title, desc, text string, hrefs [] // 增量重爬检测:查询上次爬取的哈希,内容未变则跳过关键词提取 isRecrawl := false oldEntry, _ := c.db.GetSnippet(res.FinalURL) - if oldEntry != nil && oldEntry.ContentHash != "" && oldEntry.ContentHash == contentHash { + if !forceIndex && oldEntry != nil && oldEntry.ContentHash != "" && oldEntry.ContentHash == contentHash { isRecrawl = true //log.Printf("[crawler] unchanged (recrawl skip): %s", res.FinalURL) } @@ -595,7 +679,7 @@ func (c *Crawler) visitURLRaw(rawURL string) (title, desc, text string, hrefs [] // 限制返回的链接数,防止下一轮队列爆炸。 func (c *Crawler) visitURL(rawURL string) (hrefs []string) { atomic.AddInt64(&c.stats.VisitedURLs, 1) // 计数器 +1 - _, _, _, hrefs = c.visitURLRaw(rawURL) + _, _, _, hrefs = c.visitURLRaw(rawURL, false) // 限制返回的链接数,防止下一轮队列爆炸 maxLinks := config.MaxPriorityChildren() @@ -606,10 +690,11 @@ func (c *Crawler) visitURL(rawURL string) (hrefs []string) { } // visitURLUnlimited 抓取一个 URL,返回页面中发现的子链接(无链接数限制)。 -// 用于手动添加的 Priority URL,保留全部子链接。 +// 用于手动添加的 Priority URL,保留全部子链接,并强制重建索引(跳过增量检测)。 func (c *Crawler) visitURLUnlimited(rawURL string) (hrefs []string) { atomic.AddInt64(&c.stats.VisitedURLs, 1) // 计数器 +1 - _, _, _, hrefs = c.visitURLRaw(rawURL) + // forceIndex=true:Priority URL 强制重新提取关键词,不受内容未变化影响 + _, _, _, hrefs = c.visitURLRaw(rawURL, true) return hrefs } diff --git a/dist/assets/index-CYFclJJJ.js b/dist/assets/index-CJeEgrKF.js similarity index 78% rename from dist/assets/index-CYFclJJJ.js rename to dist/assets/index-CJeEgrKF.js index 8be3c28..b32a918 100644 --- a/dist/assets/index-CYFclJJJ.js +++ b/dist/assets/index-CJeEgrKF.js @@ -3,4 +3,4 @@ var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],en `)}getSetCookie(){return this.get(`set-cookie`)||[]}get[Symbol.toStringTag](){return`AxiosHeaders`}static from(e){return e instanceof this?e:new this(e)}static concat(e,...t){let n=new this(e);return t.forEach(e=>n.set(e)),n}static accessor(e){let t=(this[sc]=this[sc]={accessors:{}}).accessors,n=this.prototype;function r(e){let r=cc(e);t[r]||(mc(n,e),t[r]=!0)}return J.isArray(e)?e.forEach(r):r(e),this}};Z.accessor([`Content-Type`,`Content-Length`,`Accept`,`Accept-Encoding`,`User-Agent`,`Authorization`]),J.reduceDescriptors(Z.prototype,({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(e){this[n]=e}}}),J.freezeMethods(Z);function hc(e,t){let n=this||ic,r=t||n,i=Z.from(r.headers),a=r.data;return J.forEach(e,function(e){a=e.call(n,a,i.normalize(),t?t.status:void 0)}),i.normalize(),a}function gc(e){return!!(e&&e.__CANCEL__)}var _c=class extends Y{constructor(e,t,n){super(e??`canceled`,Y.ERR_CANCELED,t,n),this.name=`CanceledError`,this.__CANCEL__=!0}};function vc(e,t,n){let r=n.config.validateStatus;!n.status||!r||r(n.status)?e(n):t(new Y(`Request failed with status code `+n.status,[Y.ERR_BAD_REQUEST,Y.ERR_BAD_RESPONSE][Math.floor(n.status/100)-4],n.config,n.request,n))}function yc(e){let t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||``}function bc(e,t){e||=10;let n=Array(e),r=Array(e),i=0,a=0,o;return t=t===void 0?1e3:t,function(s){let c=Date.now(),l=r[a];o||=c,n[i]=s,r[i]=c;let u=a,d=0;for(;u!==i;)d+=n[u++],u%=e;if(i=(i+1)%e,i===a&&(a=(a+1)%e),c-o{n=r,i=null,a&&=(clearTimeout(a),null),e(...t)};return[(...e)=>{let t=Date.now(),s=t-n;s>=r?o(e,t):(i=e,a||=setTimeout(()=>{a=null,o(i)},r-s))},()=>i&&o(i)]}var Sc=(e,t,n=3)=>{let r=0,i=bc(50,250);return xc(n=>{let a=n.loaded,o=n.lengthComputable?n.total:void 0,s=a-r,c=i(s),l=a<=o;r=a,e({loaded:a,total:o,progress:o?a/o:void 0,bytes:s,rate:c||void 0,estimated:c&&o&&l?(o-a)/c:void 0,event:n,lengthComputable:o!=null,[t?`download`:`upload`]:!0})},n)},Cc=(e,t)=>{let n=e!=null;return[r=>t[0]({lengthComputable:n,total:e,loaded:r}),t[1]]},wc=e=>(...t)=>J.asap(()=>e(...t)),Tc=X.hasStandardBrowserEnv?((e,t)=>n=>(n=new URL(n,X.origin),e.protocol===n.protocol&&e.host===n.host&&(t||e.port===n.port)))(new URL(X.origin),X.navigator&&/(msie|trident)/i.test(X.navigator.userAgent)):()=>!0,Ec=X.hasStandardBrowserEnv?{write(e,t,n,r,i,a,o){if(typeof document>`u`)return;let s=[`${e}=${encodeURIComponent(t)}`];J.isNumber(n)&&s.push(`expires=${new Date(n).toUTCString()}`),J.isString(r)&&s.push(`path=${r}`),J.isString(i)&&s.push(`domain=${i}`),a===!0&&s.push(`secure`),J.isString(o)&&s.push(`SameSite=${o}`),document.cookie=s.join(`; `)},read(e){if(typeof document>`u`)return null;let t=document.cookie.match(RegExp(`(?:^|; )`+e+`=([^;]*)`));return t?decodeURIComponent(t[1]):null},remove(e){this.write(e,``,Date.now()-864e5,`/`)}}:{write(){},read(){return null},remove(){}};function Dc(e){return typeof e==`string`?/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e):!1}function Oc(e,t){return t?e.replace(/\/?\/$/,``)+`/`+t.replace(/^\/+/,``):e}function kc(e,t,n){let r=!Dc(t);return e&&(r||n==0)?Oc(e,t):t}var Ac=e=>e instanceof Z?{...e}:e;function jc(e,t){t||={};let n={};function r(e,t,n,r){return J.isPlainObject(e)&&J.isPlainObject(t)?J.merge.call({caseless:r},e,t):J.isPlainObject(t)?J.merge({},t):J.isArray(t)?t.slice():t}function i(e,t,n,i){if(!J.isUndefined(t))return r(e,t,n,i);if(!J.isUndefined(e))return r(void 0,e,n,i)}function a(e,t){if(!J.isUndefined(t))return r(void 0,t)}function o(e,t){if(!J.isUndefined(t))return r(void 0,t);if(!J.isUndefined(e))return r(void 0,e)}function s(n,i,a){if(a in t)return r(n,i);if(a in e)return r(void 0,n)}let c={url:a,method:a,data:a,baseURL:o,transformRequest:o,transformResponse:o,paramsSerializer:o,timeout:o,timeoutMessage:o,withCredentials:o,withXSRFToken:o,adapter:o,responseType:o,xsrfCookieName:o,xsrfHeaderName:o,onUploadProgress:o,onDownloadProgress:o,decompress:o,maxContentLength:o,maxBodyLength:o,beforeRedirect:o,transport:o,httpAgent:o,httpsAgent:o,cancelToken:o,socketPath:o,responseEncoding:o,validateStatus:s,headers:(e,t,n)=>i(Ac(e),Ac(t),n,!0)};return J.forEach(Object.keys({...e,...t}),function(r){if(r===`__proto__`||r===`constructor`||r===`prototype`)return;let a=J.hasOwnProp(c,r)?c[r]:i,o=a(e[r],t[r],r);J.isUndefined(o)&&a!==s||(n[r]=o)}),n}var Mc=e=>{let t=jc({},e),{data:n,withXSRFToken:r,xsrfHeaderName:i,xsrfCookieName:a,headers:o,auth:s}=t;if(t.headers=o=Z.from(o),t.url=Us(kc(t.baseURL,t.url,t.allowAbsoluteUrls),e.params,e.paramsSerializer),s&&o.set(`Authorization`,`Basic `+btoa((s.username||``)+`:`+(s.password?unescape(encodeURIComponent(s.password)):``))),J.isFormData(n)){if(X.hasStandardBrowserEnv||X.hasStandardBrowserWebWorkerEnv)o.setContentType(void 0);else if(J.isFunction(n.getHeaders)){let e=n.getHeaders(),t=[`content-type`,`content-length`];Object.entries(e).forEach(([e,n])=>{t.includes(e.toLowerCase())&&o.set(e,n)})}}if(X.hasStandardBrowserEnv&&(r&&J.isFunction(r)&&(r=r(t)),r||r!==!1&&Tc(t.url))){let e=i&&a&&Ec.read(a);e&&o.set(i,e)}return t},Nc=typeof XMLHttpRequest<`u`&&function(e){return new Promise(function(t,n){let r=Mc(e),i=r.data,a=Z.from(r.headers).normalize(),{responseType:o,onUploadProgress:s,onDownloadProgress:c}=r,l,u,d,f,p;function m(){f&&f(),p&&p(),r.cancelToken&&r.cancelToken.unsubscribe(l),r.signal&&r.signal.removeEventListener(`abort`,l)}let h=new XMLHttpRequest;h.open(r.method.toUpperCase(),r.url,!0),h.timeout=r.timeout;function g(){if(!h)return;let r=Z.from(`getAllResponseHeaders`in h&&h.getAllResponseHeaders());vc(function(e){t(e),m()},function(e){n(e),m()},{data:!o||o===`text`||o===`json`?h.responseText:h.response,status:h.status,statusText:h.statusText,headers:r,config:e,request:h}),h=null}`onloadend`in h?h.onloadend=g:h.onreadystatechange=function(){!h||h.readyState!==4||h.status===0&&!(h.responseURL&&h.responseURL.indexOf(`file:`)===0)||setTimeout(g)},h.onabort=function(){h&&=(n(new Y(`Request aborted`,Y.ECONNABORTED,e,h)),null)},h.onerror=function(t){let r=new Y(t&&t.message?t.message:`Network Error`,Y.ERR_NETWORK,e,h);r.event=t||null,n(r),h=null},h.ontimeout=function(){let t=r.timeout?`timeout of `+r.timeout+`ms exceeded`:`timeout exceeded`,i=r.transitional||Gs;r.timeoutErrorMessage&&(t=r.timeoutErrorMessage),n(new Y(t,i.clarifyTimeoutError?Y.ETIMEDOUT:Y.ECONNABORTED,e,h)),h=null},i===void 0&&a.setContentType(null),`setRequestHeader`in h&&J.forEach(a.toJSON(),function(e,t){h.setRequestHeader(t,e)}),J.isUndefined(r.withCredentials)||(h.withCredentials=!!r.withCredentials),o&&o!==`json`&&(h.responseType=r.responseType),c&&([d,p]=Sc(c,!0),h.addEventListener(`progress`,d)),s&&h.upload&&([u,f]=Sc(s),h.upload.addEventListener(`progress`,u),h.upload.addEventListener(`loadend`,f)),(r.cancelToken||r.signal)&&(l=t=>{h&&=(n(!t||t.type?new _c(null,e,h):t),h.abort(),null)},r.cancelToken&&r.cancelToken.subscribe(l),r.signal&&(r.signal.aborted?l():r.signal.addEventListener(`abort`,l)));let _=yc(r.url);if(_&&X.protocols.indexOf(_)===-1){n(new Y(`Unsupported protocol `+_+`:`,Y.ERR_BAD_REQUEST,e));return}h.send(i||null)})},Pc=(e,t)=>{let{length:n}=e=e?e.filter(Boolean):[];if(t||n){let n=new AbortController,r,i=function(e){if(!r){r=!0,o();let t=e instanceof Error?e:this.reason;n.abort(t instanceof Y?t:new _c(t instanceof Error?t.message:t))}},a=t&&setTimeout(()=>{a=null,i(new Y(`timeout of ${t}ms exceeded`,Y.ETIMEDOUT))},t),o=()=>{e&&=(a&&clearTimeout(a),a=null,e.forEach(e=>{e.unsubscribe?e.unsubscribe(i):e.removeEventListener(`abort`,i)}),null)};e.forEach(e=>e.addEventListener(`abort`,i));let{signal:s}=n;return s.unsubscribe=()=>J.asap(o),s}},Fc=function*(e,t){let n=e.byteLength;if(!t||n{let i=Ic(e,t),a=0,o,s=e=>{o||(o=!0,r&&r(e))};return new ReadableStream({async pull(e){try{let{done:t,value:r}=await i.next();if(t){s(),e.close();return}let o=r.byteLength;n&&n(a+=o),e.enqueue(new Uint8Array(r))}catch(e){throw s(e),e}},cancel(e){return s(e),i.return()}},{highWaterMark:2})},zc=64*1024,{isFunction:Bc}=J,Vc=(({Request:e,Response:t})=>({Request:e,Response:t}))(J.global),{ReadableStream:Hc,TextEncoder:Uc}=J.global,Wc=(e,...t)=>{try{return!!e(...t)}catch{return!1}},Gc=e=>{e=J.merge.call({skipUndefined:!0},Vc,e);let{fetch:t,Request:n,Response:r}=e,i=t?Bc(t):typeof fetch==`function`,a=Bc(n),o=Bc(r);if(!i)return!1;let s=i&&Bc(Hc),c=i&&(typeof Uc==`function`?(e=>t=>e.encode(t))(new Uc):async e=>new Uint8Array(await new n(e).arrayBuffer())),l=a&&s&&Wc(()=>{let e=!1,t=new Hc,r=new n(X.origin,{body:t,method:`POST`,get duplex(){return e=!0,`half`}}).headers.has(`Content-Type`);return t.cancel(),e&&!r}),u=o&&s&&Wc(()=>J.isReadableStream(new r(``).body)),d={stream:u&&(e=>e.body)};i&&[`text`,`arrayBuffer`,`blob`,`formData`,`stream`].forEach(e=>{!d[e]&&(d[e]=(t,n)=>{let r=t&&t[e];if(r)return r.call(t);throw new Y(`Response type '${e}' is not supported`,Y.ERR_NOT_SUPPORT,n)})});let f=async e=>{if(e==null)return 0;if(J.isBlob(e))return e.size;if(J.isSpecCompliantForm(e))return(await new n(X.origin,{method:`POST`,body:e}).arrayBuffer()).byteLength;if(J.isArrayBufferView(e)||J.isArrayBuffer(e))return e.byteLength;if(J.isURLSearchParams(e)&&(e+=``),J.isString(e))return(await c(e)).byteLength},p=async(e,t)=>J.toFiniteNumber(e.getContentLength())??f(t);return async e=>{let{url:i,method:o,data:s,signal:c,cancelToken:f,timeout:m,onDownloadProgress:h,onUploadProgress:g,responseType:_,headers:v,withCredentials:y=`same-origin`,fetchOptions:b}=Mc(e),x=t||fetch;_=_?(_+``).toLowerCase():`text`;let S=Pc([c,f&&f.toAbortSignal()],m),C=null,w=S&&S.unsubscribe&&(()=>{S.unsubscribe()}),ee;try{if(g&&l&&o!==`get`&&o!==`head`&&(ee=await p(v,s))!==0){let e=new n(i,{method:`POST`,body:s,duplex:`half`}),t;if(J.isFormData(s)&&(t=e.headers.get(`content-type`))&&v.setContentType(t),e.body){let[t,n]=Cc(ee,Sc(wc(g)));s=Rc(e.body,zc,t,n)}}J.isString(y)||(y=y?`include`:`omit`);let t=a&&`credentials`in n.prototype,c={...b,signal:S,method:o.toUpperCase(),headers:v.normalize().toJSON(),body:s,duplex:`half`,credentials:t?y:void 0};C=a&&new n(i,c);let f=await(a?x(C,b):x(i,c)),m=u&&(_===`stream`||_===`response`);if(u&&(h||m&&w)){let e={};[`status`,`statusText`,`headers`].forEach(t=>{e[t]=f[t]});let t=J.toFiniteNumber(f.headers.get(`content-length`)),[n,i]=h&&Cc(t,Sc(wc(h),!0))||[];f=new r(Rc(f.body,zc,n,()=>{i&&i(),w&&w()}),e)}_||=`text`;let te=await d[J.findKey(d,_)||`text`](f,e);return!m&&w&&w(),await new Promise((t,n)=>{vc(t,n,{data:te,headers:Z.from(f.headers),status:f.status,statusText:f.statusText,config:e,request:C})})}catch(t){throw w&&w(),t&&t.name===`TypeError`&&/Load failed|fetch/i.test(t.message)?Object.assign(new Y(`Network Error`,Y.ERR_NETWORK,e,C,t&&t.response),{cause:t.cause||t}):Y.from(t,t&&t.code,e,C,t&&t.response)}}},Kc=new Map,qc=e=>{let t=e&&e.env||{},{fetch:n,Request:r,Response:i}=t,a=[r,i,n],o=a.length,s,c,l=Kc;for(;o--;)s=a[o],c=l.get(s),c===void 0&&l.set(s,c=o?new Map:Gc(t)),l=c;return c};qc();var Jc={http:null,xhr:Nc,fetch:{get:qc}};J.forEach(Jc,(e,t)=>{if(e){try{Object.defineProperty(e,`name`,{value:t})}catch{}Object.defineProperty(e,`adapterName`,{value:t})}});var Yc=e=>`- ${e}`,Xc=e=>J.isFunction(e)||e===null||e===!1;function Zc(e,t){e=J.isArray(e)?e:[e];let{length:n}=e,r,i,a={};for(let o=0;o`adapter ${e} `+(t===!1?`is not supported by the environment`:`is not available in the build`));throw new Y(`There is no suitable adapter to dispatch the request `+(n?e.length>1?`since : `+e.map(Yc).join(` `):` `+Yc(e[0]):`as no adapter specified`),`ERR_NOT_SUPPORT`)}return i}var Qc={getAdapter:Zc,adapters:Jc};function $c(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new _c(null,e)}function el(e){return $c(e),e.headers=Z.from(e.headers),e.data=hc.call(e,e.transformRequest),[`post`,`put`,`patch`].indexOf(e.method)!==-1&&e.headers.setContentType(`application/x-www-form-urlencoded`,!1),Qc.getAdapter(e.adapter||ic.adapter,e)(e).then(function(t){return $c(e),t.data=hc.call(e,e.transformResponse,t),t.headers=Z.from(t.headers),t},function(t){return gc(t)||($c(e),t&&t.response&&(t.response.data=hc.call(e,e.transformResponse,t.response),t.response.headers=Z.from(t.response.headers))),Promise.reject(t)})}var tl=`1.14.0`,nl={};[`object`,`boolean`,`number`,`function`,`string`,`symbol`].forEach((e,t)=>{nl[e]=function(n){return typeof n===e||`a`+(t<1?`n `:` `)+e}});var rl={};nl.transitional=function(e,t,n){function r(e,t){return`[Axios v`+tl+`] Transitional option '`+e+`'`+t+(n?`. `+n:``)}return(n,i,a)=>{if(e===!1)throw new Y(r(i,` has been removed`+(t?` in `+t:``)),Y.ERR_DEPRECATED);return t&&!rl[i]&&(rl[i]=!0,console.warn(r(i,` has been deprecated since v`+t+` and will be removed in the near future`))),e?e(n,i,a):!0}},nl.spelling=function(e){return(t,n)=>(console.warn(`${n} is likely a misspelling of ${e}`),!0)};function il(e,t,n){if(typeof e!=`object`)throw new Y(`options must be an object`,Y.ERR_BAD_OPTION_VALUE);let r=Object.keys(e),i=r.length;for(;i-- >0;){let a=r[i],o=t[a];if(o){let t=e[a],n=t===void 0||o(t,a,e);if(n!==!0)throw new Y(`option `+a+` must be `+n,Y.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new Y(`Unknown option `+a,Y.ERR_BAD_OPTION)}}var al={assertOptions:il,validators:nl},ol=al.validators,sl=class{constructor(e){this.defaults=e||{},this.interceptors={request:new Ws,response:new Ws}}async request(e,t){try{return await this._request(e,t)}catch(e){if(e instanceof Error){let t={};Error.captureStackTrace?Error.captureStackTrace(t):t=Error();let n=t.stack?t.stack.replace(/^.+\n/,``):``;try{e.stack?n&&!String(e.stack).endsWith(n.replace(/^.+\n.+\n/,``))&&(e.stack+=` -`+n):e.stack=n}catch{}}throw e}}_request(e,t){typeof e==`string`?(t||={},t.url=e):t=e||{},t=jc(this.defaults,t);let{transitional:n,paramsSerializer:r,headers:i}=t;n!==void 0&&al.assertOptions(n,{silentJSONParsing:ol.transitional(ol.boolean),forcedJSONParsing:ol.transitional(ol.boolean),clarifyTimeoutError:ol.transitional(ol.boolean),legacyInterceptorReqResOrdering:ol.transitional(ol.boolean)},!1),r!=null&&(J.isFunction(r)?t.paramsSerializer={serialize:r}:al.assertOptions(r,{encode:ol.function,serialize:ol.function},!0)),t.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls===void 0?t.allowAbsoluteUrls=!0:t.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls),al.assertOptions(t,{baseUrl:ol.spelling(`baseURL`),withXsrfToken:ol.spelling(`withXSRFToken`)},!0),t.method=(t.method||this.defaults.method||`get`).toLowerCase();let a=i&&J.merge(i.common,i[t.method]);i&&J.forEach([`delete`,`get`,`head`,`post`,`put`,`patch`,`common`],e=>{delete i[e]}),t.headers=Z.concat(a,i);let o=[],s=!0;this.interceptors.request.forEach(function(e){if(typeof e.runWhen==`function`&&e.runWhen(t)===!1)return;s&&=e.synchronous;let n=t.transitional||Gs;n&&n.legacyInterceptorReqResOrdering?o.unshift(e.fulfilled,e.rejected):o.push(e.fulfilled,e.rejected)});let c=[];this.interceptors.response.forEach(function(e){c.push(e.fulfilled,e.rejected)});let l,u=0,d;if(!s){let e=[el.bind(this),void 0];for(e.unshift(...o),e.push(...c),d=e.length,l=Promise.resolve(t);u{if(!n._listeners)return;let t=n._listeners.length;for(;t-- >0;)n._listeners[t](e);n._listeners=null}),this.promise.then=e=>{let t,r=new Promise(e=>{n.subscribe(e),t=e}).then(e);return r.cancel=function(){n.unsubscribe(t)},r},e(function(e,r,i){n.reason||(n.reason=new _c(e,r,i),t(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){if(this.reason){e(this.reason);return}this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;let t=this._listeners.indexOf(e);t!==-1&&this._listeners.splice(t,1)}toAbortSignal(){let e=new AbortController,t=t=>{e.abort(t)};return this.subscribe(t),e.signal.unsubscribe=()=>this.unsubscribe(t),e.signal}static source(){let t;return{token:new e(function(e){t=e}),cancel:t}}};function ll(e){return function(t){return e.apply(null,t)}}function ul(e){return J.isObject(e)&&e.isAxiosError===!0}var dl={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(dl).forEach(([e,t])=>{dl[t]=e});function fl(e){let t=new sl(e),n=Co(sl.prototype.request,t);return J.extend(n,sl.prototype,t,{allOwnKeys:!0}),J.extend(n,t,null,{allOwnKeys:!0}),n.create=function(t){return fl(jc(e,t))},n}var Q=fl(ic);Q.Axios=sl,Q.CanceledError=_c,Q.CancelToken=cl,Q.isCancel=gc,Q.VERSION=tl,Q.toFormData=Rs,Q.AxiosError=Y,Q.Cancel=Q.CanceledError,Q.all=function(e){return Promise.all(e)},Q.spread=ll,Q.isAxiosError=ul,Q.mergeConfig=jc,Q.AxiosHeaders=Z,Q.formToJSON=e=>nc(J.isHTMLForm(e)?new FormData(e):e),Q.getAdapter=Qc.getAdapter,Q.HttpStatusCode=dl,Q.default=Q;var $=``;async function pl(e=50){let{data:t}=await Q.get(`${$}/admin/recent`,{params:{limit:e},timeout:15e3});return t}async function ml(){let{data:e}=await Q.get(`${$}/admin/stats`,{timeout:15e3});return e}async function hl(){let{data:e}=await Q.get(`${$}/admin/priority`,{timeout:15e3});return e}async function gl(e){let{data:t}=await Q.post(`${$}/admin/priority`,{url:e},{timeout:15e3});return t}async function _l(e){let{data:t}=await Q.delete(`${$}/admin/priority`,{params:{url:e},timeout:15e3});return t}async function vl(){let{data:e}=await Q.post(`${$}/admin/flush`,null,{timeout:6e4});return e}async function yl(){let{data:e}=await Q.get(`${$}/admin/flush/status`,{timeout:5e3});return e}async function bl(){let{data:e}=await Q.get(`${$}/admin/workers`,{timeout:1e4});return e}async function xl(e){let{data:t}=await Q.post(`${$}/admin/workers`,{workers:e},{timeout:1e4});return t}async function Sl(){let{data:e}=await Q.get(`${$}/admin/backlink`,{timeout:1e4});return e}async function Cl(){let{data:e}=await Q.post(`${$}/admin/backlink`,null,{timeout:1e4});return e}async function wl(){let{data:e}=await Q.get(`${$}/admin/priority/status`,{timeout:1e4});return e}async function Tl(){let{data:e}=await Q.get(`${$}/admin/crawl/status`,{timeout:1e4});return e}async function El(e){let{data:t}=await Q.get(`${$}/admin/url/keywords`,{params:{url:e},timeout:5e3});return t}async function Dl(){let{data:e}=await Q.get(`${$}/admin/url/keywords/stats`,{timeout:5e3});return e}var Ol={class:`p-4 md:p-8`},kl={key:0,class:`flex items-center justify-center h-48`},Al={key:1,class:`bg-red-900/30 border border-red-800 rounded-lg p-4 text-red-300`},jl={class:`grid grid-cols-2 md:grid-cols-5 gap-3 md:gap-5 mb-6 md:mb-8`},Ml={class:`bg-gray-900 border border-gray-800 rounded-xl p-5`},Nl={class:`text-3xl font-bold text-white`},Pl={class:`bg-gray-900 border border-gray-800 rounded-xl p-5`},Fl={class:`text-3xl font-bold text-white`},Il={class:`bg-gray-900 border border-gray-800 rounded-xl p-5`},Ll={class:`text-3xl font-bold text-white`},Rl={class:`bg-gray-900 border border-gray-800 rounded-xl p-5`},zl={class:`bg-gray-900 border border-gray-800 rounded-xl p-5 flex flex-col justify-between`},Bl=[`disabled`],Vl={key:0,class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5 mb-6 md:mb-8`},Hl={class:`grid grid-cols-2 md:grid-cols-4 gap-4`},Ul={class:`bg-gray-800/50 rounded-lg p-4 text-center`},Wl={class:`bg-gray-800/50 rounded-lg p-4 text-center`},Gl={class:`bg-gray-800/50 rounded-lg p-4 text-center`},Kl={class:`bg-gray-800/50 rounded-lg p-4 text-center`},ql={class:`text-2xl font-bold text-blue-400`},Jl={key:1,class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5 mb-6 md:mb-8`},Yl={class:`flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-4`},Xl={class:`flex items-center gap-2`},Zl={class:`grid grid-cols-2 md:grid-cols-4 gap-4`},Ql={class:`text-center`},$l={class:`text-3xl font-bold text-white`},eu={class:`text-lg text-gray-500`},tu={class:`text-center`},nu={class:`text-center`},ru={class:`text-3xl font-bold text-green-400`},iu={class:`text-xs text-gray-600 mt-1`},au={class:`text-center`},ou={class:`text-3xl font-bold text-white`},su={key:0,class:`mt-4`},cu={class:`bg-gray-800 rounded-full h-2 overflow-hidden`},lu={class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5 mb-6 md:mb-8`},uu={class:`flex flex-col md:flex-row md:items-center md:justify-between gap-4`},du={class:`flex items-baseline gap-4`},fu={class:`text-3xl font-bold text-white`},pu={class:`flex items-center gap-2`},mu=[`disabled`],hu=[`disabled`],gu=[`disabled`],_u={key:2,class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5 mb-6 md:mb-8`},vu={class:`flex flex-col md:flex-row md:items-center md:justify-between gap-4`},yu={class:`flex-1`},bu={class:`grid grid-cols-1 sm:grid-cols-2 gap-3`},xu={class:`text-lg font-bold text-white`},Su=[`title`],Cu=[`disabled`],wu={class:`grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-5`},Tu={class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5`},Eu={class:`space-y-2`},Du=[`title`],Ou={class:`flex-1 bg-gray-800 rounded-full h-4 md:h-5 overflow-hidden`},ku={class:`w-12 md:w-16 text-xs text-gray-500 text-right shrink-0`},Au={class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5`},ju={class:`space-y-2`},Mu={class:`w-8 md:w-10 text-xs text-gray-400 shrink-0 font-mono`},Nu={class:`flex-1 bg-gray-800 rounded-full h-4 md:h-5 overflow-hidden`},Pu={class:`w-12 md:w-16 text-xs text-gray-500 text-right shrink-0`},Fu={__name:`Dashboard`,setup(e){let t=F(null),n=F(!0),r=F(!1),i=F(null),a=null,o=F(0),s=F(0),c=F(0),l=F(!1),u=F(null),d=F(null);ar(async()=>{await Promise.all([f(),p(),h(),g(),_()]),a=setInterval(()=>{f(),p(),h(),g(),_()},5e3)}),lr(()=>{a&&clearInterval(a)});async function f(){try{t.value=await ml(),i.value=null}catch(e){i.value=`无法加载统计数据,可能人服务器未启动或端口不对`,console.error(e)}finally{n.value=!1}}async function p(){try{let e=await bl();o.value=e.configured,s.value=e.active,c.value=e.configured}catch(e){console.error(`Failed to load workers:`,e)}}async function m(){let e=parseInt(c.value,10);if(isNaN(e)||e<1||e>500){i.value=`线程数必须在 1~500 之间`;return}l.value=!0;try{await xl(e),o.value=e,i.value=null}catch(e){i.value=`修改线程数失败: `+e.message}finally{l.value=!1}}async function h(){try{backlinkStatus.value=await Sl()}catch(e){console.error(`Failed to load backlink status:`,e)}}async function g(){try{u.value=await wl()}catch(e){console.error(`Failed to load priority status:`,e)}}async function _(){try{d.value=await Tl()}catch(e){console.error(`Failed to load crawl status:`,e)}}async function v(){backlinkTriggering.value=!0;try{await Cl(),backlinkStatus.value=await Sl(),i.value=null}catch(e){i.value=`触发反链计算失败: `+e.message}finally{backlinkTriggering.value=!1}}function y(e){if(!e)return`--`;let t=new Date(e),n=t-new Date,r=Math.floor(n/6e4),i=Math.floor(r/60),a=Math.floor(i/24),o=t.toLocaleTimeString(`zh-CN`,{hour:`2-digit`,minute:`2-digit`}),s=t.toLocaleDateString(`zh-CN`,{month:`short`,day:`numeric`});return a>0?`${s} ${o} (${a}天后)`:i>0?`${s} ${o} (${i}小时后)`:r>0?`${o} (${r}分钟后)`:`${o} (即将执行)`}function b(e){return e?new Date(e).toLocaleString(`zh-CN`,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`}):`从未执行`}async function x(){r.value=!0;try{for(await vl();await new Promise(e=>setTimeout(e,1e3)),(await yl()).flushing;);t.value=await ml()}catch(e){i.value=`刷盘失败: `+e.message}finally{r.value=!1}}function S(e){return!e&&e!==0?`0`:Number(e).toLocaleString()}function C(e){return e?Object.entries(e).sort((e,t)=>t[1]-e[1]).slice(0,10):[]}function w(e){let t={zh:`#e53e3e`,en:`#3182ce`,ja:`#e53e3e`,ko:`#3182ce`,fr:`#38a169`,de:`#d69e2e`,es:`#38a169`,ru:`#805ad5`,other:`#718096`};return t[e]||t.other}return(e,a)=>(H(),U(`div`,Ol,[a[29]||=W(`h1`,{class:`text-xl md:text-2xl font-semibold text-white mb-6 md:mb-8`},`概览`,-1),n.value?(H(),U(`div`,kl,[...a[3]||=[W(`div`,{class:`text-gray-400 animate-pulse`},`加载中...`,-1)]])):i.value?(H(),U(`div`,Al,O(i.value),1)):t.value?(H(),U(B,{key:2},[W(`div`,jl,[W(`div`,Ml,[a[4]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`已爬取 URL`,-1),W(`div`,Nl,O(S(t.value.total_urls)),1)]),W(`div`,Pl,[a[5]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`总词数`,-1),W(`div`,Fl,O(S(t.value.total_words)),1)]),W(`div`,Il,[a[6]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`域名数量`,-1),W(`div`,Ll,O(S(t.value.total_domains)),1)]),W(`div`,Rl,[a[7]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`待重爬`,-1),W(`div`,{class:D([`text-3xl font-bold`,t.value.recrawl_eligible>0?`text-orange-400`:`text-green-400`])},O(S(t.value.recrawl_eligible)),3)]),W(`div`,zl,[W(`div`,null,[a[8]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`待刷盘`,-1),W(`div`,{class:D([`text-3xl font-bold`,t.value.pending>0?`text-yellow-400`:`text-green-400`])},O(S(t.value.pending)),3)]),W(`button`,{class:`mt-3 w-full bg-blue-700 hover:bg-blue-600 disabled:bg-gray-700 disabled:text-gray-500 text-white text-sm font-medium py-1.5 px-3 rounded transition-colors cursor-pointer`,disabled:r.value||!t.value.pending,onClick:x},O(r.value?`刷盘中...`:`立即刷盘`),9,Bl)])]),u.value?(H(),U(`div`,Vl,[a[13]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider mb-4`},`优先爬取队列`,-1),W(`div`,Hl,[W(`div`,Ul,[a[9]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`优先子链接`,-1),W(`div`,{class:D([`text-2xl font-bold`,(u.value.children_queue||0)>0?`text-yellow-400`:`text-gray-500`])},O(S(u.value.children_queue||0)),3)]),W(`div`,Wl,[a[10]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`手动待爬`,-1),W(`div`,{class:D([`text-2xl font-bold`,u.value.pending>0?`text-yellow-400`:`text-gray-500`])},O(S(u.value.pending)),3)]),W(`div`,Gl,[a[11]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`手动爬取中`,-1),W(`div`,{class:D([`text-2xl font-bold`,u.value.active>0?`text-orange-400`:`text-gray-500`])},O(S(u.value.active)),3)]),W(`div`,Kl,[a[12]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`优先上限`,-1),W(`div`,ql,O(u.value.max_workers),1)])])])):G(``,!0),d.value&&d.value.max_epoch>0?(H(),U(`div`,Jl,[W(`div`,Yl,[a[15]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider`},`爬取进度`,-1),W(`div`,Xl,[a[14]||=W(`span`,{class:`text-xs text-gray-500`},`状态:`,-1),W(`span`,{class:D([`px-2 py-0.5 rounded text-xs font-medium`,d.value.is_running?`bg-green-900/50 text-green-400`:`bg-gray-800 text-gray-500`])},O(d.value.is_running?`运行中`:`已停止`),3)])]),W(`div`,Zl,[W(`div`,Ql,[a[16]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`当前轮次`,-1),W(`div`,$l,[Ui(O(d.value.current_epoch),1),W(`span`,eu,`/`+O(d.value.max_epoch),1)])]),W(`div`,tu,[a[17]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`本轮队列`,-1),W(`div`,{class:D([`text-3xl font-bold`,d.value.queue_length>0?`text-blue-400`:`text-gray-500`])},O(S(d.value.queue_length)),3)]),W(`div`,nu,[a[18]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`已完成`,-1),W(`div`,ru,O(S(d.value.completed_count)),1),W(`div`,iu,O(d.value.queue_length>0?Math.round(d.value.completed_count/d.value.queue_length*100)+`%`:`--`),1)]),W(`div`,au,[a[19]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`已收录总计`,-1),W(`div`,ou,O(S(d.value.visited_total)),1)])]),d.value.queue_length>0?(H(),U(`div`,su,[W(`div`,cu,[W(`div`,{class:`h-full bg-gradient-to-r from-blue-600 to-green-500 rounded-full transition-all duration-500`,style:me({width:`${d.value.completed_count/d.value.queue_length*100}%`})},null,4)])])):G(``,!0)])):G(``,!0),W(`div`,lu,[W(`div`,uu,[W(`div`,null,[a[23]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider mb-2`},`爬虫线程`,-1),W(`div`,du,[W(`div`,null,[a[20]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`实际运行`,-1),W(`div`,{class:D([`text-3xl font-bold`,s.value>0?`text-green-400`:`text-gray-500`])},O(s.value),3)]),a[22]||=W(`div`,{class:`text-gray-700 text-xl`},`/`,-1),W(`div`,null,[a[21]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`设定上限`,-1),W(`div`,fu,O(o.value),1)])])]),W(`div`,pu,[W(`button`,{class:`w-9 h-9 flex items-center justify-center rounded-lg bg-gray-800 hover:bg-gray-700 text-gray-300 hover:text-white text-lg font-bold transition-colors cursor-pointer`,onClick:a[0]||=e=>c.value=Math.max(1,c.value-1),disabled:l.value},`−`,8,mu),jn(W(`input`,{type:`number`,"onUpdate:modelValue":a[1]||=e=>c.value=e,min:`1`,max:`500`,class:`w-20 h-9 text-center bg-gray-800 border border-gray-700 rounded-lg text-white text-sm focus:outline-none focus:border-blue-500 transition-colors [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none`,onKeyup:go(m,[`enter`])},null,544),[[so,c.value,void 0,{number:!0}]]),W(`button`,{class:`w-9 h-9 flex items-center justify-center rounded-lg bg-gray-800 hover:bg-gray-700 text-gray-300 hover:text-white text-lg font-bold transition-colors cursor-pointer`,onClick:a[2]||=e=>c.value=Math.min(500,c.value+1),disabled:l.value},`+`,8,hu),W(`button`,{class:`h-9 px-4 bg-blue-700 hover:bg-blue-600 disabled:bg-gray-700 disabled:text-gray-500 text-white text-sm font-medium rounded-lg transition-colors cursor-pointer`,disabled:l.value||c.value===o.value,onClick:m},O(l.value?`保存中...`:`应用`),9,gu)])])]),e.backlinkStatus?(H(),U(`div`,_u,[W(`div`,vu,[W(`div`,yu,[a[26]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider mb-3`},`反链计算(PageRank)`,-1),W(`div`,bu,[W(`div`,null,[a[24]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`下次自动执行`,-1),W(`div`,{class:D([`text-lg font-bold`,e.backlinkStatus.running?`text-yellow-400`:`text-white`])},O(e.backlinkStatus.running?`计算中...`:y(e.backlinkStatus.next_run)),3)]),W(`div`,null,[a[25]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`上次完成`,-1),W(`div`,xu,O(b(e.backlinkStatus.last_run)),1)])]),e.backlinkStatus.last_error?(H(),U(`div`,{key:0,class:`mt-2 text-xs text-red-400 truncate`,title:e.backlinkStatus.last_error},` 上次错误:`+O(e.backlinkStatus.last_error),9,Su)):G(``,!0)]),W(`button`,{class:`h-9 px-4 bg-blue-700 hover:bg-blue-600 disabled:bg-gray-700 disabled:text-gray-500 text-white text-sm font-medium rounded-lg transition-colors cursor-pointer whitespace-nowrap`,disabled:e.backlinkTriggering||e.backlinkStatus.running,onClick:v},O(e.backlinkTriggering?`已触发...`:e.backlinkStatus.running?`计算中...`:`立即执行`),9,Cu)])])):G(``,!0),W(`div`,wu,[W(`div`,Tu,[a[27]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 mb-3 md:mb-4 uppercase tracking-wider`},`域名分布 Top 10`,-1),W(`div`,Eu,[(H(!0),U(B,null,L(C(t.value.domains),([e,n])=>(H(),U(`div`,{key:e,class:`flex items-center gap-2 md:gap-3`},[W(`div`,{class:`w-24 md:w-36 text-xs text-gray-400 truncate shrink-0`,title:e},O(e),9,Du),W(`div`,Ou,[W(`div`,{class:`h-full bg-blue-600 rounded-full transition-all duration-500`,style:me({width:`${n/t.value.domains[Object.keys(t.value.domains)[0]]*100}%`})},null,4)]),W(`div`,ku,O(S(n)),1)]))),128))])]),W(`div`,Au,[a[28]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 mb-3 md:mb-4 uppercase tracking-wider`},`语种分布`,-1),W(`div`,ju,[(H(!0),U(B,null,L(Object.entries(t.value.languages||{}).sort((e,t)=>t[1]-e[1]),([e,n])=>(H(),U(`div`,{key:e,class:`flex items-center gap-2 md:gap-3`},[W(`div`,Mu,O(e),1),W(`div`,Nu,[W(`div`,{class:`h-full rounded-full transition-all duration-500`,style:me({width:`${n/t.value.total_urls*100}%`,backgroundColor:w(e)})},null,4)]),W(`div`,Pu,O(S(n)),1)]))),128))])])])],64)):G(``,!0)]))}},Iu={class:`p-4 md:p-8`},Lu={class:`flex flex-col md:flex-row md:items-center justify-between mb-4 md:mb-6 gap-3`},Ru={class:`text-sm text-gray-500`},zu={class:`flex items-center gap-2 md:gap-3`},Bu={class:`bg-gray-900 border border-gray-700 rounded-lg px-3 py-2 flex items-center gap-2`},Vu={class:`flex flex-col`},Hu={class:`text-sm font-medium text-gray-300`},Uu={class:`w-12 h-8 relative`},Wu={viewBox:`0 0 36 36`,class:`w-full h-full transform -rotate-90`},Gu=[`stroke-dasharray`],Ku={class:`absolute inset-0 flex items-center justify-center text-[8px] font-medium text-gray-400`},qu=[`value`],Ju={class:`flex flex-col sm:flex-row items-stretch sm:items-center gap-3 mb-4 md:mb-5`},Yu={class:`relative flex-1 max-w-full sm:max-w-sm`},Xu={key:0,class:`flex items-center justify-center h-48`},Zu={key:1,class:`bg-red-900/30 border border-red-800 rounded-lg p-4 text-red-300`},Qu={key:2,class:`bg-gray-900 border border-gray-800 rounded-xl overflow-hidden`},$u={class:`hidden md:table w-full text-sm`},ed={class:`px-5 py-3.5`},td=[`href`],nd={class:`font-medium text-gray-200 group-hover:text-white line-clamp-2`},rd={class:`text-xs text-gray-600 mt-0.5 break-all line-clamp-1`},id={key:0,class:`text-xs text-gray-500 mt-1 line-clamp-1`},ad=[`onClick`],od={key:0,class:`text-gray-500`},sd={key:1,class:`mt-2`},cd={class:`flex flex-wrap gap-1.5`},ld=[`title`],ud={class:`text-gray-500 text-[10px] ml-0.5`},dd={class:`text-xs text-gray-600 mt-1`},fd={key:1,class:`text-xs text-gray-600`},pd={class:`px-5 py-3.5`},md={class:`text-gray-400 text-xs font-mono`},hd={class:`px-5 py-3.5`},gd={key:1,class:`text-xs text-gray-600`},_d={class:`px-5 py-3.5 text-gray-500 text-xs tabular-nums`},vd={class:`px-5 py-3.5 text-gray-500 text-xs`},yd={key:0},bd={class:`md:hidden divide-y divide-gray-800`},xd=[`href`],Sd={class:`font-medium text-gray-200 text-sm line-clamp-2 mb-1`},Cd={class:`text-xs text-gray-500 break-all line-clamp-1 mb-2`},wd={class:`flex items-center gap-2 text-xs mb-2`},Td={class:`text-gray-400 font-mono`},Ed={class:`text-gray-600 ml-auto`},Dd=[`onClick`],Od={key:0,class:`text-gray-500`},kd={key:0,class:`mb-2`},Ad={class:`flex flex-wrap gap-1`},jd=[`title`],Md={class:`text-gray-500 text-[9px] ml-0.5`},Nd={class:`text-[10px] text-gray-600 mt-1`},Pd={key:1,class:`text-xs text-gray-600`},Fd={key:0,class:`p-8 text-center text-gray-600`},Id={key:3,class:`mt-3 text-xs text-gray-600 text-right pb-4 md:pb-0`},Ld={__name:`RecentCrawls`,setup(e){let t=F([]),n=F(0),r=F(!0),i=F(null),a=F(``),o=F(``),s=[20,50,100,200],c=F(50),l=F(new Set),u=F({}),d=F(new Set),f=F({size:0,max_size:1e4,usage:0});ar(async()=>{await p()});async function p(){r.value=!0,i.value=null;try{let[e,r]=await Promise.all([pl(c.value),Dl().catch(()=>({size:0,max_size:1e4,usage:0}))]);t.value=e.items||[],n.value=e.total||0,f.value=r}catch(e){i.value=`无法加载数据,可能人服务器未启动`,console.error(e)}finally{r.value=!1}}async function m(e){c.value=e,await p()}let h=ga(()=>{let e=t.value;if(a.value){let t=a.value.toLowerCase();e=e.filter(e=>e.title?.toLowerCase().includes(t)||e.url?.toLowerCase().includes(t)||e.domain?.toLowerCase().includes(t))}return o.value&&(e=e.filter(e=>Object.keys(e.language||{}).includes(o.value))),e});function g(e){return e?new Date(e*1e3).toLocaleString(`zh-CN`,{year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1}):`-`}function _(e){return{zh:{label:`中文`,cls:`bg-red-900/60 text-red-300`},en:{label:`EN`,cls:`bg-blue-900/60 text-blue-300`},ja:{label:`日`,cls:`bg-pink-900/60 text-pink-300`},ko:{label:`한`,cls:`bg-blue-900/60 text-blue-300`},fr:{label:`FR`,cls:`bg-green-900/60 text-green-300`},de:{label:`DE`,cls:`bg-yellow-900/60 text-yellow-300`},es:{label:`ES`,cls:`bg-green-900/60 text-green-300`},ru:{label:`RU`,cls:`bg-purple-900/60 text-purple-300`}}[e]||{label:e,cls:`bg-gray-800 text-gray-400`}}function v(e){return e?Object.entries(e).sort((e,t)=>t[1]-e[1])[0]:null}async function y(e){if(l.value.has(e)){l.value.delete(e);return}if(l.value.add(e),!u.value[e]){d.value.add(e);try{let t=await El(e);u.value[e]=t.keywords||[]}catch(t){console.error(`Failed to load keywords:`,t),u.value[e]=[]}finally{d.value.delete(e)}}}return(e,t)=>(H(),U(`div`,Iu,[W(`div`,Lu,[W(`div`,null,[t[4]||=W(`h1`,{class:`text-xl md:text-2xl font-semibold text-white mb-1`},`最近爬取`,-1),W(`p`,Ru,`共 `+O(n.value.toLocaleString())+` 条记录`,1)]),W(`div`,zu,[W(`div`,Bu,[W(`div`,Vu,[t[5]||=W(`span`,{class:`text-[10px] text-gray-500 uppercase`},`关键词缓存`,-1),W(`span`,Hu,O(f.value.size.toLocaleString())+` / `+O(f.value.max_size.toLocaleString()),1)]),W(`div`,Uu,[(H(),U(`svg`,Wu,[t[6]||=W(`path`,{class:`text-gray-800`,d:`M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831`,fill:`none`,stroke:`currentColor`,"stroke-width":`4`},null,-1),W(`path`,{class:D(f.value.usage>.9?`text-red-500`:f.value.usage>.7?`text-yellow-500`:`text-green-500`),d:`M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831`,fill:`none`,stroke:`currentColor`,"stroke-width":`4`,"stroke-dasharray":`${(f.value.usage*100).toFixed(0)}, 100`},null,10,Gu)])),W(`span`,Ku,O((f.value.usage*100).toFixed(0))+`% `,1)])]),jn(W(`select`,{"onUpdate:modelValue":t[0]||=e=>c.value=e,onChange:t[1]||=e=>m(c.value),class:`bg-gray-900 border border-gray-700 text-gray-300 text-sm rounded-lg px-3 py-2 focus:border-blue-500 focus:outline-none`},[(H(),U(B,null,L(s,e=>W(`option`,{key:e,value:e},`显示 `+O(e)+` 条`,9,qu)),64))],544),[[co,c.value]]),W(`button`,{onClick:p,class:`bg-blue-600 hover:bg-blue-700 text-white text-sm px-4 py-2 rounded-lg transition-colors`},` 刷新 `)])]),W(`div`,Ju,[W(`div`,Yu,[jn(W(`input`,{"onUpdate:modelValue":t[2]||=e=>a.value=e,type:`text`,placeholder:`搜索标题、URL、域名...`,class:`w-full bg-gray-900 border border-gray-700 text-gray-200 text-sm rounded-lg pl-10 pr-4 py-2 focus:border-blue-500 focus:outline-none placeholder-gray-600`},null,512),[[so,a.value]]),t[7]||=W(`span`,{class:`absolute left-3 top-1/2 -translate-y-1/2 text-gray-500`},`🔍`,-1)]),jn(W(`select`,{"onUpdate:modelValue":t[3]||=e=>o.value=e,class:`bg-gray-900 border border-gray-700 text-gray-300 text-sm rounded-lg px-3 py-2 focus:border-blue-500 focus:outline-none`},[...t[8]||=[Wi(``,9)]],512),[[co,o.value]])]),r.value?(H(),U(`div`,Xu,[...t[9]||=[W(`div`,{class:`text-gray-400 animate-pulse`},`加载中...`,-1)]])):i.value?(H(),U(`div`,Zu,O(i.value),1)):(H(),U(`div`,Qu,[W(`table`,$u,[t[11]||=W(`thead`,null,[W(`tr`,{class:`border-b border-gray-800`},[W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider`},`标题`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-28`},`域名`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-16`},`语种`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-20`},`字数`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-48`},`爬取时间`)])],-1),W(`tbody`,null,[(H(!0),U(B,null,L(h.value,e=>(H(),U(`tr`,{key:e.url,class:`border-b border-gray-800/50 hover:bg-gray-800/40 transition-colors group`},[W(`td`,ed,[W(`a`,{href:e.url,target:`_blank`,rel:`noopener noreferrer`,class:`block hover:opacity-80 transition-opacity`},[W(`div`,nd,O(e.title||`(无标题)`),1),W(`div`,rd,O(e.url),1)],8,td),e.description?(H(),U(`div`,id,O(e.description),1)):G(``,!0),W(`button`,{onClick:mo(t=>y(e.url),[`prevent`]),class:`mt-2 text-xs text-blue-400 hover:text-blue-300 flex items-center gap-1`},[W(`span`,null,O(l.value.has(e.url)?`▼`:`▶`)+` 关键词`,1),d.value.has(e.url)?(H(),U(`span`,od,`加载中...`)):G(``,!0)],8,ad),l.value.has(e.url)?(H(),U(`div`,sd,[u.value[e.url]?.length?(H(),U(B,{key:0},[W(`div`,cd,[(H(!0),U(B,null,L(u.value[e.url],e=>(H(),U(`span`,{key:e.word,class:`text-xs px-2 py-0.5 rounded bg-gray-800 text-gray-300 border border-gray-700`,title:`权重: ${e.weight.toFixed(4)}`},[Ui(O(e.word)+` `,1),W(`span`,ud,O(e.weight.toFixed(2)),1)],8,ld))),128))]),W(`div`,dd,` 共 `+O(u.value[e.url].length)+` 个关键词 `,1)],64)):d.value.has(e.url)?G(``,!0):(H(),U(`span`,fd,` 暂无关键词(服务重启后缓存已清空) `))])):G(``,!0)]),W(`td`,pd,[W(`span`,md,O(e.domain),1)]),W(`td`,hd,[v(e.language)?(H(),U(`span`,{key:0,class:D([`text-xs px-1.5 py-0.5 rounded font-medium`,_(v(e.language)[0]).cls])},O(_(v(e.language)[0]).label)+` `+O((v(e.language)[1]*100).toFixed(0))+`% `,3)):(H(),U(`span`,gd,`-`))]),W(`td`,_d,O(e.word_count.toLocaleString()),1),W(`td`,vd,O(g(e.crawled_at)),1)]))),128)),h.value.length?G(``,!0):(H(),U(`tr`,yd,[...t[10]||=[W(`td`,{colspan:`5`,class:`px-5 py-12 text-center text-gray-600`},` 没有找到匹配的记录 `,-1)]]))])]),W(`div`,bd,[(H(!0),U(B,null,L(h.value,e=>(H(),U(`div`,{key:e.url,class:`block p-4 hover:bg-gray-800/40 transition-colors`},[W(`a`,{href:e.url,target:`_blank`,rel:`noopener noreferrer`,class:`block`},[W(`div`,Sd,O(e.title||`(无标题)`),1),W(`div`,Cd,O(e.url),1)],8,xd),W(`div`,wd,[W(`span`,Td,O(e.domain),1),v(e.language)?(H(),U(`span`,{key:0,class:D([`px-1.5 py-0.5 rounded font-medium`,_(v(e.language)[0]).cls])},O(_(v(e.language)[0]).label),3)):G(``,!0),W(`span`,Ed,O(g(e.crawled_at)),1)]),W(`button`,{onClick:mo(t=>y(e.url),[`prevent`]),class:`text-xs text-blue-400 hover:text-blue-300 flex items-center gap-1 mb-2`},[W(`span`,null,O(l.value.has(e.url)?`▼`:`▶`)+` 关键词`,1),d.value.has(e.url)?(H(),U(`span`,Od,`加载中...`)):G(``,!0)],8,Dd),l.value.has(e.url)?(H(),U(`div`,kd,[u.value[e.url]?.length?(H(),U(B,{key:0},[W(`div`,Ad,[(H(!0),U(B,null,L(u.value[e.url],e=>(H(),U(`span`,{key:e.word,class:`text-[10px] px-1.5 py-0.5 rounded bg-gray-800 text-gray-300 border border-gray-700`,title:`权重: ${e.weight.toFixed(4)}`},[Ui(O(e.word)+` `,1),W(`span`,Md,O(e.weight.toFixed(2)),1)],8,jd))),128))]),W(`div`,Nd,` 共 `+O(u.value[e.url].length)+` 个 `,1)],64)):d.value.has(e.url)?G(``,!0):(H(),U(`span`,Pd,` 暂无关键词 `))])):G(``,!0)]))),128)),h.value.length?G(``,!0):(H(),U(`div`,Fd,` 没有找到匹配的记录 `))])])),!r.value&&!i.value&&h.value.length?(H(),U(`div`,Id,` 筛选后 `+O(h.value.length)+` 条 / 共 `+O(n.value.toLocaleString())+` 条 `,1)):G(``,!0)]))}},Rd={class:`p-4 md:p-8`},zd={class:`bg-gray-900 rounded-xl p-4 md:p-6 mb-4 md:mb-6 border border-gray-800`},Bd={class:`flex flex-col sm:flex-row gap-3`},Vd=[`disabled`],Hd=[`disabled`],Ud={key:0,class:`mt-3 text-sm text-red-400`},Wd={key:1,class:`mt-3 text-sm text-green-400`},Gd={class:`bg-gray-900 rounded-xl border border-gray-800`},Kd={class:`px-4 md:px-6 py-3 md:py-4 border-b border-gray-800 flex items-center justify-between`},qd={class:`text-xs text-gray-500`},Jd={key:0,class:`p-8 text-center text-gray-500 text-sm`},Yd={key:1,class:`p-8 text-center text-red-400 text-sm`},Xd={key:2,class:`p-8 text-center text-gray-500 text-sm`},Zd={key:3,class:`hidden md:table w-full text-sm`},Qd={class:`divide-y divide-gray-800`},$d={class:`px-6 py-3`},ef={class:`text-gray-300 break-all`},tf={class:`px-6 py-3`},nf={key:0,class:`inline-block px-2 py-0.5 text-xs rounded bg-purple-900 text-purple-300`},rf={key:1,class:`inline-block px-2 py-0.5 text-xs rounded bg-blue-900 text-blue-300`},af={class:`px-6 py-3 text-gray-500`},of={class:`px-6 py-3`},sf=[`onClick`],cf={class:`md:hidden divide-y divide-gray-800`},lf={class:`flex-1 min-w-0`},uf={class:`text-gray-300 text-sm break-all mb-1`},df={class:`flex items-center gap-2`},ff={key:0,class:`inline-block px-2 py-0.5 text-xs rounded bg-purple-900 text-purple-300`},pf={key:1,class:`inline-block px-2 py-0.5 text-xs rounded bg-blue-900 text-blue-300`},mf={class:`text-xs text-gray-500`},hf=[`onClick`],gf={__name:`PriorityCrawl`,setup(e){let t=F([]),n=F(!0),r=F(null),i=F(!1),a=F(null),o=F(!1),s=F(``);async function c(){n.value=!0,r.value=null;try{t.value=(await hl()).items||[]}catch(e){r.value=`加载失败,请检查人服务器是否启动`,console.error(e)}finally{n.value=!1}}async function l(){let e=s.value.trim();if(e){i.value=!0,a.value=null,o.value=!1;try{await gl(e),s.value=``,o.value=!0,setTimeout(()=>{o.value=!1},3e3),await c()}catch(e){a.value=e?.response?.data?.error||`添加失败`}finally{i.value=!1}}}async function u(e){try{await _l(e),await c()}catch(e){r.value=`删除失败`,console.error(e)}}function d(e){return e?new Date(e*1e3).toLocaleString(`zh-CN`,{month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`}):`-`}return ar(c),(e,c)=>(H(),U(`div`,Rd,[c[3]||=W(`h1`,{class:`text-xl md:text-2xl font-semibold text-white mb-2`},`插入爬取`,-1),c[4]||=W(`p`,{class:`text-sm text-gray-500 mb-6 md:mb-8`},` 添加 URL 或域名,下一轮爬取时会优先抓取。纯域名会自动补全为 https://www.域名/。 `,-1),W(`div`,zd,[W(`div`,Bd,[jn(W(`input`,{"onUpdate:modelValue":c[0]||=e=>s.value=e,onKeyup:go(l,[`enter`]),type:`text`,placeholder:`输入 URL 或域名,例如 https://example.com 或 example.com`,class:`flex-1 bg-gray-800 border border-gray-700 rounded-lg px-4 py-2.5 text-gray-100 placeholder-gray-500 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 text-sm`,disabled:i.value},null,40,Vd),[[so,s.value]]),W(`button`,{onClick:l,disabled:i.value||!s.value.trim(),class:`px-6 py-2.5 bg-blue-600 hover:bg-blue-500 disabled:bg-gray-700 disabled:text-gray-500 text-white text-sm font-medium rounded-lg transition-colors cursor-pointer whitespace-nowrap`},O(i.value?`添加中...`:`插入队列`),9,Hd)]),a.value?(H(),U(`div`,Ud,O(a.value),1)):G(``,!0),o.value?(H(),U(`div`,Wd,` 已添加到优先队列,将在下一轮爬取时优先抓取 `)):G(``,!0)]),W(`div`,Gd,[W(`div`,Kd,[c[1]||=W(`span`,{class:`text-sm font-medium text-gray-300`},`待爬取队列`,-1),W(`span`,qd,O(t.value.length)+` 条`,1)]),n.value?(H(),U(`div`,Jd,` 加载中... `)):r.value?(H(),U(`div`,Yd,O(r.value),1)):t.value.length===0?(H(),U(`div`,Xd,` 暂无待爬取的优先 URL `)):(H(),U(`table`,Zd,[c[2]||=W(`thead`,null,[W(`tr`,{class:`text-left text-gray-500 text-xs border-b border-gray-800`},[W(`th`,{class:`px-6 py-3 font-medium`},`URL`),W(`th`,{class:`px-6 py-3 font-medium w-28`},`类型`),W(`th`,{class:`px-6 py-3 font-medium w-40`},`添加时间`),W(`th`,{class:`px-6 py-3 font-medium w-16`},`操作`)])],-1),W(`tbody`,Qd,[(H(!0),U(B,null,L(t.value,e=>(H(),U(`tr`,{key:e.url,class:`hover:bg-gray-800/50`},[W(`td`,$d,[W(`span`,ef,O(e.url),1)]),W(`td`,tf,[e.domain?(H(),U(`span`,nf,`域名`)):(H(),U(`span`,rf,`URL`))]),W(`td`,af,O(d(e.added_at)),1),W(`td`,of,[W(`button`,{onClick:t=>u(e.url),class:`text-red-400 hover:text-red-300 text-xs cursor-pointer`},` 删除 `,8,sf)])]))),128))])])),W(`div`,cf,[(H(!0),U(B,null,L(t.value,e=>(H(),U(`div`,{key:e.url,class:`p-4 hover:bg-gray-800/50 flex items-center justify-between gap-3`},[W(`div`,lf,[W(`div`,uf,O(e.url),1),W(`div`,df,[e.domain?(H(),U(`span`,ff,`域名`)):(H(),U(`span`,pf,`URL`)),W(`span`,mf,O(d(e.added_at)),1)])]),W(`button`,{onClick:t=>u(e.url),class:`text-red-400 hover:text-red-300 text-xs cursor-pointer shrink-0 px-2 py-1`},` 删除 `,8,hf)]))),128))])])]))}},_f={class:`flex flex-col h-full`},vf={class:`bg-gray-950 border-b border-gray-800 px-4 md:px-8 py-4 md:py-6`},yf={class:`max-w-3xl mx-auto`},bf={class:`relative`},xf={class:`flex-1 overflow-y-auto px-8 py-6`},Sf={class:`max-w-3xl mx-auto`},Cf={key:0,class:`flex items-center gap-3 text-gray-400 py-8`},wf={key:1,class:`bg-red-900/30 border border-red-800 rounded-lg p-4 text-red-300`},Tf={key:2,class:`py-16 text-center text-gray-600`},Ef={key:3,class:`py-16 text-center text-gray-600`},Df={key:4,class:`flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4 mb-4 md:mb-5 text-sm text-gray-500`},Of={class:`flex items-center gap-2`},kf={class:`text-gray-300`},Af={class:`flex flex-wrap gap-2 sm:ml-auto`},jf={class:`text-gray-300`},Mf={key:5,class:`space-y-2 md:space-y-1`},Nf=[`onClick`],Pf={class:`flex items-start gap-2 md:gap-3 mb-2`},Ff={class:`flex-1 min-w-0`},If={class:`text-blue-400 group-hover:text-blue-300 text-base md:text-lg leading-snug`},Lf={class:`text-xs text-gray-600 mt-0.5 truncate`},Rf={class:`flex flex-col items-end gap-1 shrink-0`},zf={class:`text-xs text-gray-600`},Bf={class:`w-12 md:w-14 bg-gray-800 rounded-full h-1.5 overflow-hidden`},Vf={class:`text-gray-400 text-sm leading-relaxed mb-2 md:mb-3`},Hf={class:`flex flex-wrap items-center gap-2 md:gap-3 text-xs`},Uf={class:`flex flex-wrap gap-1.5`},Wf=[`title`],Gf={class:`text-blue-400`},Kf={class:`text-gray-600 ml-1`},qf={key:0,class:`text-gray-600`},Jf={class:`text-gray-700 ml-0 md:ml-auto`},Yf={class:`text-xs text-gray-700 mt-1 truncate`},Xf={key:6,class:`mt-6 text-center`},Zf=``,Qf=10,$f={__name:`SearchView`,setup(e){let t=F(``),n=F([]),r=F(0),i=F({}),a=F(!1),o=F(null),s=F(0),c=null;async function l(e,t=!1){if(!e.trim()){n.value=[],r.value=0,i.value={};return}t||(s.value=0),a.value=!0,o.value=null;try{let a=encodeURIComponent(e),o=s.value*Qf,c=await fetch(`${Zf}/search?qh=${a}&slice=${o}:${o+Qf}`);if(!c.ok)throw Error(`HTTP ${c.status}`);let l=await c.json();t?n.value=[...n.value,...l.results||[]]:n.value=l.results||[],r.value=l.total||0,i.value=l.counts||{}}catch(e){o.value=`搜索失败,可能是人服务器未启动`,console.error(e)}finally{a.value=!1}}function u(){clearTimeout(c),c=setTimeout(()=>l(t.value),400)}function d(e){e.key===`Enter`&&(clearTimeout(c),l(t.value))}function f(e,t=120){return e?e.length>t?e.slice(0,t)+`…`:e:``}function p(e){return e?e.toLocaleString():`0`}function m(e){return Math.min(100,Math.round(e*100))}function h(e){return{zh:`中文`,en:`EN`,ja:`JA`,ko:`KO`,fr:`FR`,de:`DE`,es:`ES`,ru:`RU`}[e]||e?.toUpperCase()}function g(e){try{return new URL(e).hostname}catch{return e}}function _(e){window.open(e,`_blank`)}return(e,c)=>(H(),U(`div`,_f,[W(`div`,vf,[W(`div`,yf,[W(`div`,bf,[jn(W(`input`,{"onUpdate:modelValue":c[0]||=e=>t.value=e,onInput:u,onKeydown:d,type:`text`,placeholder:`输入关键词搜索,或用 site:example.com 限定域名`,class:`w-full bg-gray-900 border border-gray-700 rounded-xl md:rounded-2xl px-4 md:px-6 py-3 md:py-4 pr-20 md:pr-14 text-white text-base md:text-lg placeholder-gray-600 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition`},null,544),[[so,t.value]]),W(`button`,{onClick:c[1]||=e=>l(t.value),class:`absolute right-2 md:right-3 top-1/2 -translate-y-1/2 bg-blue-600 hover:bg-blue-500 text-white rounded-lg md:rounded-xl px-3 md:px-4 py-1.5 md:py-2 text-sm font-medium transition`},` 搜索 `)])])]),W(`div`,xf,[W(`div`,Sf,[a.value?(H(),U(`div`,Cf,[...c[3]||=[W(`div`,{class:`w-5 h-5 border-2 border-blue-400 border-t-transparent rounded-full animate-spin`},null,-1),W(`span`,null,`搜索中...`,-1)]])):o.value?(H(),U(`div`,wf,O(o.value),1)):t.value.trim()?n.value.length===0&&!a.value?(H(),U(`div`,Ef,` 未找到相关结果 `)):n.value.length>0?(H(),U(`div`,Df,[W(`div`,Of,[W(`span`,null,[c[4]||=Ui(`找到约 `,-1),W(`strong`,kf,O(p(r.value)),1),c[5]||=Ui(` 条结果`,-1)]),c[6]||=W(`span`,{class:`text-gray-700`},`|`,-1),W(`span`,null,O(n.value.length)+` 条已加载`,1)]),W(`div`,Af,[(H(!0),U(B,null,L(i.value,(e,t)=>(H(),U(`span`,{key:t,class:`inline-flex items-center gap-1 bg-gray-800 rounded px-2 py-0.5 text-xs text-gray-400`},[W(`span`,jf,O(t),1),W(`span`,null,O(p(e)),1)]))),128))])])):G(``,!0):(H(),U(`div`,Tf,` 输入关键词开始搜索 `)),n.value.length>0?(H(),U(`div`,Mf,[(H(!0),U(B,null,L(n.value,(e,t)=>(H(),U(`div`,{key:t,onClick:t=>_(e.url),class:`group block bg-gray-900/50 hover:bg-gray-900 border border-gray-800 hover:border-gray-700 rounded-xl p-4 md:p-5 cursor-pointer transition`},[W(`div`,Pf,[W(`div`,Ff,[W(`div`,If,O(e.snippet?.title||g(e.url)),1),W(`div`,Lf,O(g(e.url)),1)]),W(`div`,Rf,[W(`div`,zf,O(m(e.score))+`%`,1),W(`div`,Bf,[W(`div`,{class:`h-full bg-blue-500 rounded-full`,style:me({width:m(e.score)+`%`})},null,4)])])]),W(`p`,Vf,O(f(e.snippet?.description||e.snippet?.text)),1),W(`div`,Hf,[W(`div`,Uf,[(H(!0),U(B,null,L(e.relevance,(e,t)=>(H(),U(`span`,{key:t,class:`inline-flex items-center bg-gray-800 rounded px-1.5 py-0.5`,title:`${t}: ${(e*100).toFixed(1)}%`},[W(`span`,Gf,O(t),1),W(`span`,Kf,O((e*100).toFixed(0))+`%`,1)],8,Wf))),128))]),e.snippet?.language?(H(),U(`span`,qf,O(h(e.snippet.language)),1)):G(``,!0),W(`span`,Jf,O(p(e.domain_count))+` 个结果`,1)]),W(`div`,Yf,O(e.url),1)],8,Nf))),128))])):G(``,!0),n.value.length>0&&n.value.length{s.value++,l(t.value,!0)},class:`bg-gray-800 hover:bg-gray-700 text-gray-300 rounded-xl px-6 py-2.5 text-sm transition`},` 加载更多 (`+O(n.value.length)+`/`+O(p(r.value))+`) `,1)])):G(``,!0)])])]))}},ep={class:`flex h-screen bg-gray-950 text-gray-100 font-sans`},tp={class:`hidden md:flex w-56 bg-gray-900 border-r border-gray-800 flex-col shrink-0`},np={class:`flex-1 py-4 px-3 space-y-1`},rp=[`onClick`],ip={class:`flex-1 overflow-y-auto pb-16 md:pb-0`},ap={class:`md:hidden fixed bottom-0 left-0 right-0 bg-gray-900 border-t border-gray-800 flex justify-around items-center h-16 z-50`},op=[`onClick`],sp={class:`text-lg`},cp={class:`text-[10px]`};bo({__name:`App`,setup(e){let t=F(`dashboard`),n=[{id:`dashboard`,label:`概览`,icon:`📊`},{id:`recent`,label:`最近`,icon:`🕷️`},{id:`search`,label:`搜索`,icon:`🔍`},{id:`priority`,label:`插入`,icon:`🚀`}];return(e,r)=>(H(),U(`div`,ep,[W(`aside`,tp,[r[0]||=W(`div`,{class:`px-5 py-5 border-b border-gray-800`},[W(`div`,{class:`text-lg font-semibold text-white tracking-tight`},`SESE Admin`),W(`div`,{class:`text-xs text-gray-500 mt-0.5`},`爬取内容监控`)],-1),W(`nav`,np,[(H(),U(B,null,L(n,e=>W(`button`,{key:e.id,onClick:n=>t.value=e.id,class:D([`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors`,t.value===e.id?`bg-blue-600 text-white`:`text-gray-400 hover:text-white hover:bg-gray-800`])},[W(`span`,null,O(e.icon),1),Ui(` `+O(e.label),1)],10,rp)),64))]),r[1]||=W(`div`,{class:`px-5 py-4 border-t border-gray-800`},[W(`div`,{class:`text-xs text-gray-600`},`sese-engine v1.0`)],-1)]),W(`main`,ip,[t.value===`dashboard`?(H(),Pi(Fu,{key:0})):t.value===`recent`?(H(),Pi(Ld,{key:1})):t.value===`search`?(H(),Pi($f,{key:2})):t.value===`priority`?(H(),Pi(gf,{key:3})):G(``,!0)]),W(`nav`,ap,[(H(),U(B,null,L(n,e=>W(`button`,{key:e.id,onClick:n=>t.value=e.id,class:D([`flex flex-col items-center justify-center gap-0.5 flex-1 h-full transition-colors`,t.value===e.id?`text-blue-400`:`text-gray-500`])},[W(`span`,sp,O(e.icon),1),W(`span`,cp,O(e.label),1)],10,op)),64))])]))}}).mount(`#app`); \ No newline at end of file +`+n):e.stack=n}catch{}}throw e}}_request(e,t){typeof e==`string`?(t||={},t.url=e):t=e||{},t=jc(this.defaults,t);let{transitional:n,paramsSerializer:r,headers:i}=t;n!==void 0&&al.assertOptions(n,{silentJSONParsing:ol.transitional(ol.boolean),forcedJSONParsing:ol.transitional(ol.boolean),clarifyTimeoutError:ol.transitional(ol.boolean),legacyInterceptorReqResOrdering:ol.transitional(ol.boolean)},!1),r!=null&&(J.isFunction(r)?t.paramsSerializer={serialize:r}:al.assertOptions(r,{encode:ol.function,serialize:ol.function},!0)),t.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls===void 0?t.allowAbsoluteUrls=!0:t.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls),al.assertOptions(t,{baseUrl:ol.spelling(`baseURL`),withXsrfToken:ol.spelling(`withXSRFToken`)},!0),t.method=(t.method||this.defaults.method||`get`).toLowerCase();let a=i&&J.merge(i.common,i[t.method]);i&&J.forEach([`delete`,`get`,`head`,`post`,`put`,`patch`,`common`],e=>{delete i[e]}),t.headers=Z.concat(a,i);let o=[],s=!0;this.interceptors.request.forEach(function(e){if(typeof e.runWhen==`function`&&e.runWhen(t)===!1)return;s&&=e.synchronous;let n=t.transitional||Gs;n&&n.legacyInterceptorReqResOrdering?o.unshift(e.fulfilled,e.rejected):o.push(e.fulfilled,e.rejected)});let c=[];this.interceptors.response.forEach(function(e){c.push(e.fulfilled,e.rejected)});let l,u=0,d;if(!s){let e=[el.bind(this),void 0];for(e.unshift(...o),e.push(...c),d=e.length,l=Promise.resolve(t);u{if(!n._listeners)return;let t=n._listeners.length;for(;t-- >0;)n._listeners[t](e);n._listeners=null}),this.promise.then=e=>{let t,r=new Promise(e=>{n.subscribe(e),t=e}).then(e);return r.cancel=function(){n.unsubscribe(t)},r},e(function(e,r,i){n.reason||(n.reason=new _c(e,r,i),t(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){if(this.reason){e(this.reason);return}this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;let t=this._listeners.indexOf(e);t!==-1&&this._listeners.splice(t,1)}toAbortSignal(){let e=new AbortController,t=t=>{e.abort(t)};return this.subscribe(t),e.signal.unsubscribe=()=>this.unsubscribe(t),e.signal}static source(){let t;return{token:new e(function(e){t=e}),cancel:t}}};function ll(e){return function(t){return e.apply(null,t)}}function ul(e){return J.isObject(e)&&e.isAxiosError===!0}var dl={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(dl).forEach(([e,t])=>{dl[t]=e});function fl(e){let t=new sl(e),n=Co(sl.prototype.request,t);return J.extend(n,sl.prototype,t,{allOwnKeys:!0}),J.extend(n,t,null,{allOwnKeys:!0}),n.create=function(t){return fl(jc(e,t))},n}var Q=fl(ic);Q.Axios=sl,Q.CanceledError=_c,Q.CancelToken=cl,Q.isCancel=gc,Q.VERSION=tl,Q.toFormData=Rs,Q.AxiosError=Y,Q.Cancel=Q.CanceledError,Q.all=function(e){return Promise.all(e)},Q.spread=ll,Q.isAxiosError=ul,Q.mergeConfig=jc,Q.AxiosHeaders=Z,Q.formToJSON=e=>nc(J.isHTMLForm(e)?new FormData(e):e),Q.getAdapter=Qc.getAdapter,Q.HttpStatusCode=dl,Q.default=Q;var $=``;async function pl(e=50){let{data:t}=await Q.get(`${$}/admin/recent`,{params:{limit:e},timeout:15e3});return t}async function ml(){let{data:e}=await Q.get(`${$}/admin/stats`,{timeout:15e3});return e}async function hl(){let{data:e}=await Q.get(`${$}/admin/priority`,{timeout:15e3});return e}async function gl(e){let{data:t}=await Q.post(`${$}/admin/priority`,{url:e},{timeout:15e3});return t}async function _l(e){let{data:t}=await Q.delete(`${$}/admin/priority`,{params:{url:e},timeout:15e3});return t}async function vl(){let{data:e}=await Q.post(`${$}/admin/flush`,null,{timeout:6e4});return e}async function yl(){let{data:e}=await Q.get(`${$}/admin/flush/status`,{timeout:5e3});return e}async function bl(){let{data:e}=await Q.get(`${$}/admin/workers`,{timeout:1e4});return e}async function xl(e){let{data:t}=await Q.post(`${$}/admin/workers`,{workers:e},{timeout:1e4});return t}async function Sl(){let{data:e}=await Q.get(`${$}/admin/backlink`,{timeout:1e4});return e}async function Cl(){let{data:e}=await Q.post(`${$}/admin/backlink`,null,{timeout:1e4});return e}async function wl(){let{data:e}=await Q.get(`${$}/admin/priority/status`,{timeout:1e4});return e}async function Tl(){let{data:e}=await Q.get(`${$}/admin/crawl/status`,{timeout:1e4});return e}async function El(e){let{data:t}=await Q.get(`${$}/admin/url/keywords`,{params:{url:e},timeout:5e3});return t}async function Dl(){let{data:e}=await Q.get(`${$}/admin/url/keywords/stats`,{timeout:5e3});return e}var Ol={class:`p-4 md:p-8`},kl={key:0,class:`flex items-center justify-center h-48`},Al={key:1,class:`bg-red-900/30 border border-red-800 rounded-lg p-4 text-red-300`},jl={class:`grid grid-cols-2 md:grid-cols-5 gap-3 md:gap-5 mb-6 md:mb-8`},Ml={class:`bg-gray-900 border border-gray-800 rounded-xl p-5`},Nl={class:`text-3xl font-bold text-white`},Pl={class:`bg-gray-900 border border-gray-800 rounded-xl p-5`},Fl={class:`text-3xl font-bold text-white`},Il={class:`bg-gray-900 border border-gray-800 rounded-xl p-5`},Ll={class:`text-3xl font-bold text-white`},Rl={class:`bg-gray-900 border border-gray-800 rounded-xl p-5`},zl={class:`bg-gray-900 border border-gray-800 rounded-xl p-5 flex flex-col justify-between`},Bl=[`disabled`],Vl={key:0,class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5 mb-6 md:mb-8`},Hl={class:`grid grid-cols-2 md:grid-cols-4 gap-4`},Ul={class:`bg-gray-800/50 rounded-lg p-4 text-center`},Wl={class:`bg-gray-800/50 rounded-lg p-4 text-center`},Gl={class:`bg-gray-800/50 rounded-lg p-4 text-center`},Kl={class:`bg-gray-800/50 rounded-lg p-4 text-center`},ql={class:`text-2xl font-bold text-blue-400`},Jl={key:1,class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5 mb-6 md:mb-8`},Yl={class:`flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-4`},Xl={class:`flex items-center gap-2`},Zl={class:`grid grid-cols-2 md:grid-cols-4 gap-4`},Ql={class:`text-center`},$l={class:`text-3xl font-bold text-white`},eu={class:`text-lg text-gray-500`},tu={class:`text-center`},nu={class:`text-center`},ru={class:`text-3xl font-bold text-green-400`},iu={class:`text-xs text-gray-600 mt-1`},au={class:`text-center`},ou={class:`text-3xl font-bold text-white`},su={key:0,class:`mt-4`},cu={class:`bg-gray-800 rounded-full h-2 overflow-hidden`},lu={class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5 mb-6 md:mb-8`},uu={class:`flex flex-col md:flex-row md:items-center md:justify-between gap-4`},du={class:`flex items-baseline gap-4`},fu={class:`text-3xl font-bold text-white`},pu={class:`flex items-center gap-2`},mu=[`disabled`],hu=[`disabled`],gu=[`disabled`],_u={key:2,class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5 mb-6 md:mb-8`},vu={class:`flex flex-col md:flex-row md:items-center md:justify-between gap-4`},yu={class:`flex-1`},bu={class:`grid grid-cols-1 sm:grid-cols-2 gap-3`},xu={class:`text-lg font-bold text-white`},Su=[`title`],Cu=[`disabled`],wu={class:`grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-5`},Tu={class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5`},Eu={class:`space-y-2`},Du=[`title`],Ou={class:`flex-1 bg-gray-800 rounded-full h-4 md:h-5 overflow-hidden`},ku={class:`w-12 md:w-16 text-xs text-gray-500 text-right shrink-0`},Au={class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5`},ju={class:`space-y-2`},Mu={class:`w-8 md:w-10 text-xs text-gray-400 shrink-0 font-mono`},Nu={class:`flex-1 bg-gray-800 rounded-full h-4 md:h-5 overflow-hidden`},Pu={class:`w-12 md:w-16 text-xs text-gray-500 text-right shrink-0`},Fu={__name:`Dashboard`,setup(e){let t=F(null),n=F(!0),r=F(!1),i=F(null),a=null,o=F(0),s=F(0),c=F(0),l=F(!1),u=F(null),d=F(null);ar(async()=>{await Promise.all([f(),p(),h(),g(),_()]),a=setInterval(()=>{f(),p(),h(),g(),_()},5e3)}),lr(()=>{a&&clearInterval(a)});async function f(){try{t.value=await ml(),i.value=null}catch(e){i.value=`无法加载统计数据,可能人服务器未启动或端口不对`,console.error(e)}finally{n.value=!1}}async function p(){try{let e=await bl();o.value=e.configured,s.value=e.active,c.value=e.configured}catch(e){console.error(`Failed to load workers:`,e)}}async function m(){let e=parseInt(c.value,10);if(isNaN(e)||e<1||e>500){i.value=`线程数必须在 1~500 之间`;return}l.value=!0;try{await xl(e),o.value=e,i.value=null}catch(e){i.value=`修改线程数失败: `+e.message}finally{l.value=!1}}async function h(){try{backlinkStatus.value=await Sl()}catch(e){console.error(`Failed to load backlink status:`,e)}}async function g(){try{u.value=await wl()}catch(e){console.error(`Failed to load priority status:`,e)}}async function _(){try{d.value=await Tl()}catch(e){console.error(`Failed to load crawl status:`,e)}}async function v(){backlinkTriggering.value=!0;try{await Cl(),backlinkStatus.value=await Sl(),i.value=null}catch(e){i.value=`触发反链计算失败: `+e.message}finally{backlinkTriggering.value=!1}}function y(e){if(!e)return`--`;let t=new Date(e),n=t-new Date,r=Math.floor(n/6e4),i=Math.floor(r/60),a=Math.floor(i/24),o=t.toLocaleTimeString(`zh-CN`,{hour:`2-digit`,minute:`2-digit`}),s=t.toLocaleDateString(`zh-CN`,{month:`short`,day:`numeric`});return a>0?`${s} ${o} (${a}天后)`:i>0?`${s} ${o} (${i}小时后)`:r>0?`${o} (${r}分钟后)`:`${o} (即将执行)`}function b(e){return e?new Date(e).toLocaleString(`zh-CN`,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`}):`从未执行`}async function x(){r.value=!0;try{for(await vl();await new Promise(e=>setTimeout(e,1e3)),(await yl()).flushing;);t.value=await ml()}catch(e){i.value=`刷盘失败: `+e.message}finally{r.value=!1}}function S(e){return!e&&e!==0?`0`:Number(e).toLocaleString()}function C(e){return e?Object.entries(e).sort((e,t)=>t[1]-e[1]).slice(0,10):[]}function w(e){let t={zh:`#e53e3e`,en:`#3182ce`,ja:`#e53e3e`,ko:`#3182ce`,fr:`#38a169`,de:`#d69e2e`,es:`#38a169`,ru:`#805ad5`,other:`#718096`};return t[e]||t.other}return(e,a)=>(H(),U(`div`,Ol,[a[29]||=W(`h1`,{class:`text-xl md:text-2xl font-semibold text-white mb-6 md:mb-8`},`概览`,-1),n.value?(H(),U(`div`,kl,[...a[3]||=[W(`div`,{class:`text-gray-400 animate-pulse`},`加载中...`,-1)]])):i.value?(H(),U(`div`,Al,O(i.value),1)):t.value?(H(),U(B,{key:2},[W(`div`,jl,[W(`div`,Ml,[a[4]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`已爬取 URL`,-1),W(`div`,Nl,O(S(t.value.total_urls)),1)]),W(`div`,Pl,[a[5]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`总词数`,-1),W(`div`,Fl,O(S(t.value.total_words)),1)]),W(`div`,Il,[a[6]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`域名数量`,-1),W(`div`,Ll,O(S(t.value.total_domains)),1)]),W(`div`,Rl,[a[7]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`待重爬`,-1),W(`div`,{class:D([`text-3xl font-bold`,t.value.recrawl_eligible>0?`text-orange-400`:`text-green-400`])},O(S(t.value.recrawl_eligible)),3)]),W(`div`,zl,[W(`div`,null,[a[8]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`待刷盘`,-1),W(`div`,{class:D([`text-3xl font-bold`,t.value.pending>0?`text-yellow-400`:`text-green-400`])},O(S(t.value.pending)),3)]),W(`button`,{class:`mt-3 w-full bg-blue-700 hover:bg-blue-600 disabled:bg-gray-700 disabled:text-gray-500 text-white text-sm font-medium py-1.5 px-3 rounded transition-colors cursor-pointer`,disabled:r.value||!t.value.pending,onClick:x},O(r.value?`刷盘中...`:`立即刷盘`),9,Bl)])]),u.value?(H(),U(`div`,Vl,[a[13]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider mb-4`},`优先爬取队列`,-1),W(`div`,Hl,[W(`div`,Ul,[a[9]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`一级队列`,-1),W(`div`,{class:D([`text-2xl font-bold`,(u.value.level1||0)>0?`text-yellow-400`:`text-gray-500`])},O(S(u.value.level1||0)),3)]),W(`div`,Wl,[a[10]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`二级队列`,-1),W(`div`,{class:D([`text-2xl font-bold`,(u.value.level2_queue||0)+(u.value.level2_inflight||0)>0?`text-yellow-400`:`text-gray-500`])},O(S((u.value.level2_queue||0)+(u.value.level2_inflight||0))),3)]),W(`div`,Gl,[a[11]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`线程使用`,-1),W(`div`,{class:D([`text-2xl font-bold`,u.value.active>0?`text-orange-400`:`text-gray-500`])},O(S(u.value.active)),3)]),W(`div`,Kl,[a[12]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`线程总数`,-1),W(`div`,ql,O(u.value.max_workers),1)])])])):G(``,!0),d.value&&d.value.max_epoch>0?(H(),U(`div`,Jl,[W(`div`,Yl,[a[15]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider`},`爬取进度`,-1),W(`div`,Xl,[a[14]||=W(`span`,{class:`text-xs text-gray-500`},`状态:`,-1),W(`span`,{class:D([`px-2 py-0.5 rounded text-xs font-medium`,d.value.is_running?`bg-green-900/50 text-green-400`:`bg-gray-800 text-gray-500`])},O(d.value.is_running?`运行中`:`已停止`),3)])]),W(`div`,Zl,[W(`div`,Ql,[a[16]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`当前轮次`,-1),W(`div`,$l,[Ui(O(d.value.current_epoch),1),W(`span`,eu,`/`+O(d.value.max_epoch),1)])]),W(`div`,tu,[a[17]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`本轮队列`,-1),W(`div`,{class:D([`text-3xl font-bold`,d.value.queue_length>0?`text-blue-400`:`text-gray-500`])},O(S(d.value.queue_length)),3)]),W(`div`,nu,[a[18]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`已完成`,-1),W(`div`,ru,O(S(d.value.completed_count)),1),W(`div`,iu,O(d.value.queue_length>0?Math.round(d.value.completed_count/d.value.queue_length*100)+`%`:`--`),1)]),W(`div`,au,[a[19]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`已收录总计`,-1),W(`div`,ou,O(S(d.value.visited_total)),1)])]),d.value.queue_length>0?(H(),U(`div`,su,[W(`div`,cu,[W(`div`,{class:`h-full bg-gradient-to-r from-blue-600 to-green-500 rounded-full transition-all duration-500`,style:me({width:`${d.value.completed_count/d.value.queue_length*100}%`})},null,4)])])):G(``,!0)])):G(``,!0),W(`div`,lu,[W(`div`,uu,[W(`div`,null,[a[23]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider mb-2`},`爬虫线程`,-1),W(`div`,du,[W(`div`,null,[a[20]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`实际运行`,-1),W(`div`,{class:D([`text-3xl font-bold`,s.value>0?`text-green-400`:`text-gray-500`])},O(s.value),3)]),a[22]||=W(`div`,{class:`text-gray-700 text-xl`},`/`,-1),W(`div`,null,[a[21]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`设定上限`,-1),W(`div`,fu,O(o.value),1)])])]),W(`div`,pu,[W(`button`,{class:`w-9 h-9 flex items-center justify-center rounded-lg bg-gray-800 hover:bg-gray-700 text-gray-300 hover:text-white text-lg font-bold transition-colors cursor-pointer`,onClick:a[0]||=e=>c.value=Math.max(1,c.value-1),disabled:l.value},`−`,8,mu),jn(W(`input`,{type:`number`,"onUpdate:modelValue":a[1]||=e=>c.value=e,min:`1`,max:`500`,class:`w-20 h-9 text-center bg-gray-800 border border-gray-700 rounded-lg text-white text-sm focus:outline-none focus:border-blue-500 transition-colors [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none`,onKeyup:go(m,[`enter`])},null,544),[[so,c.value,void 0,{number:!0}]]),W(`button`,{class:`w-9 h-9 flex items-center justify-center rounded-lg bg-gray-800 hover:bg-gray-700 text-gray-300 hover:text-white text-lg font-bold transition-colors cursor-pointer`,onClick:a[2]||=e=>c.value=Math.min(500,c.value+1),disabled:l.value},`+`,8,hu),W(`button`,{class:`h-9 px-4 bg-blue-700 hover:bg-blue-600 disabled:bg-gray-700 disabled:text-gray-500 text-white text-sm font-medium rounded-lg transition-colors cursor-pointer`,disabled:l.value||c.value===o.value,onClick:m},O(l.value?`保存中...`:`应用`),9,gu)])])]),e.backlinkStatus?(H(),U(`div`,_u,[W(`div`,vu,[W(`div`,yu,[a[26]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider mb-3`},`反链计算(PageRank)`,-1),W(`div`,bu,[W(`div`,null,[a[24]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`下次自动执行`,-1),W(`div`,{class:D([`text-lg font-bold`,e.backlinkStatus.running?`text-yellow-400`:`text-white`])},O(e.backlinkStatus.running?`计算中...`:y(e.backlinkStatus.next_run)),3)]),W(`div`,null,[a[25]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`上次完成`,-1),W(`div`,xu,O(b(e.backlinkStatus.last_run)),1)])]),e.backlinkStatus.last_error?(H(),U(`div`,{key:0,class:`mt-2 text-xs text-red-400 truncate`,title:e.backlinkStatus.last_error},` 上次错误:`+O(e.backlinkStatus.last_error),9,Su)):G(``,!0)]),W(`button`,{class:`h-9 px-4 bg-blue-700 hover:bg-blue-600 disabled:bg-gray-700 disabled:text-gray-500 text-white text-sm font-medium rounded-lg transition-colors cursor-pointer whitespace-nowrap`,disabled:e.backlinkTriggering||e.backlinkStatus.running,onClick:v},O(e.backlinkTriggering?`已触发...`:e.backlinkStatus.running?`计算中...`:`立即执行`),9,Cu)])])):G(``,!0),W(`div`,wu,[W(`div`,Tu,[a[27]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 mb-3 md:mb-4 uppercase tracking-wider`},`域名分布 Top 10`,-1),W(`div`,Eu,[(H(!0),U(B,null,L(C(t.value.domains),([e,n])=>(H(),U(`div`,{key:e,class:`flex items-center gap-2 md:gap-3`},[W(`div`,{class:`w-24 md:w-36 text-xs text-gray-400 truncate shrink-0`,title:e},O(e),9,Du),W(`div`,Ou,[W(`div`,{class:`h-full bg-blue-600 rounded-full transition-all duration-500`,style:me({width:`${n/t.value.domains[Object.keys(t.value.domains)[0]]*100}%`})},null,4)]),W(`div`,ku,O(S(n)),1)]))),128))])]),W(`div`,Au,[a[28]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 mb-3 md:mb-4 uppercase tracking-wider`},`语种分布`,-1),W(`div`,ju,[(H(!0),U(B,null,L(Object.entries(t.value.languages||{}).sort((e,t)=>t[1]-e[1]),([e,n])=>(H(),U(`div`,{key:e,class:`flex items-center gap-2 md:gap-3`},[W(`div`,Mu,O(e),1),W(`div`,Nu,[W(`div`,{class:`h-full rounded-full transition-all duration-500`,style:me({width:`${n/t.value.total_urls*100}%`,backgroundColor:w(e)})},null,4)]),W(`div`,Pu,O(S(n)),1)]))),128))])])])],64)):G(``,!0)]))}},Iu={class:`p-4 md:p-8`},Lu={class:`flex flex-col md:flex-row md:items-center justify-between mb-4 md:mb-6 gap-3`},Ru={class:`text-sm text-gray-500`},zu={class:`flex items-center gap-2 md:gap-3`},Bu={class:`bg-gray-900 border border-gray-700 rounded-lg px-3 py-2 flex items-center gap-2`},Vu={class:`flex flex-col`},Hu={class:`text-sm font-medium text-gray-300`},Uu={class:`w-12 h-8 relative`},Wu={viewBox:`0 0 36 36`,class:`w-full h-full transform -rotate-90`},Gu=[`stroke-dasharray`],Ku={class:`absolute inset-0 flex items-center justify-center text-[8px] font-medium text-gray-400`},qu=[`value`],Ju={class:`flex flex-col sm:flex-row items-stretch sm:items-center gap-3 mb-4 md:mb-5`},Yu={class:`relative flex-1 max-w-full sm:max-w-sm`},Xu={key:0,class:`flex items-center justify-center h-48`},Zu={key:1,class:`bg-red-900/30 border border-red-800 rounded-lg p-4 text-red-300`},Qu={key:2,class:`bg-gray-900 border border-gray-800 rounded-xl overflow-hidden`},$u={class:`hidden md:table w-full text-sm`},ed={class:`px-5 py-3.5`},td=[`href`],nd={class:`font-medium text-gray-200 group-hover:text-white line-clamp-2`},rd={class:`text-xs text-gray-600 mt-0.5 break-all line-clamp-1`},id={key:0,class:`text-xs text-gray-500 mt-1 line-clamp-1`},ad=[`onClick`],od={key:0,class:`text-gray-500`},sd={key:1,class:`mt-2`},cd={class:`flex flex-wrap gap-1.5`},ld=[`title`],ud={class:`text-gray-500 text-[10px] ml-0.5`},dd={class:`text-xs text-gray-600 mt-1`},fd={key:1,class:`text-xs text-gray-600`},pd={class:`px-5 py-3.5`},md={class:`text-gray-400 text-xs font-mono`},hd={class:`px-5 py-3.5`},gd={key:1,class:`text-xs text-gray-600`},_d={class:`px-5 py-3.5 text-gray-500 text-xs tabular-nums`},vd={class:`px-5 py-3.5 text-gray-500 text-xs`},yd={key:0},bd={class:`md:hidden divide-y divide-gray-800`},xd=[`href`],Sd={class:`font-medium text-gray-200 text-sm line-clamp-2 mb-1`},Cd={class:`text-xs text-gray-500 break-all line-clamp-1 mb-2`},wd={class:`flex items-center gap-2 text-xs mb-2`},Td={class:`text-gray-400 font-mono`},Ed={class:`text-gray-600 ml-auto`},Dd=[`onClick`],Od={key:0,class:`text-gray-500`},kd={key:0,class:`mb-2`},Ad={class:`flex flex-wrap gap-1`},jd=[`title`],Md={class:`text-gray-500 text-[9px] ml-0.5`},Nd={class:`text-[10px] text-gray-600 mt-1`},Pd={key:1,class:`text-xs text-gray-600`},Fd={key:0,class:`p-8 text-center text-gray-600`},Id={key:3,class:`mt-3 text-xs text-gray-600 text-right pb-4 md:pb-0`},Ld={__name:`RecentCrawls`,setup(e){let t=F([]),n=F(0),r=F(!0),i=F(null),a=F(``),o=F(``),s=[20,50,100,200],c=F(50),l=F(new Set),u=F({}),d=F(new Set),f=F({size:0,max_size:1e4,usage:0});ar(async()=>{await p()});async function p(){r.value=!0,i.value=null;try{let[e,r]=await Promise.all([pl(c.value),Dl().catch(()=>({size:0,max_size:1e4,usage:0}))]);t.value=e.items||[],n.value=e.total||0,f.value=r}catch(e){i.value=`无法加载数据,可能人服务器未启动`,console.error(e)}finally{r.value=!1}}async function m(e){c.value=e,await p()}let h=ga(()=>{let e=t.value;if(a.value){let t=a.value.toLowerCase();e=e.filter(e=>e.title?.toLowerCase().includes(t)||e.url?.toLowerCase().includes(t)||e.domain?.toLowerCase().includes(t))}return o.value&&(e=e.filter(e=>Object.keys(e.language||{}).includes(o.value))),e});function g(e){return e?new Date(e*1e3).toLocaleString(`zh-CN`,{year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1}):`-`}function _(e){return{zh:{label:`中文`,cls:`bg-red-900/60 text-red-300`},en:{label:`EN`,cls:`bg-blue-900/60 text-blue-300`},ja:{label:`日`,cls:`bg-pink-900/60 text-pink-300`},ko:{label:`한`,cls:`bg-blue-900/60 text-blue-300`},fr:{label:`FR`,cls:`bg-green-900/60 text-green-300`},de:{label:`DE`,cls:`bg-yellow-900/60 text-yellow-300`},es:{label:`ES`,cls:`bg-green-900/60 text-green-300`},ru:{label:`RU`,cls:`bg-purple-900/60 text-purple-300`}}[e]||{label:e,cls:`bg-gray-800 text-gray-400`}}function v(e){return e?Object.entries(e).sort((e,t)=>t[1]-e[1])[0]:null}async function y(e){if(l.value.has(e)){l.value.delete(e);return}if(l.value.add(e),!u.value[e]){d.value.add(e);try{let t=await El(e);u.value[e]=t.keywords||[]}catch(t){console.error(`Failed to load keywords:`,t),u.value[e]=[]}finally{d.value.delete(e)}}}return(e,t)=>(H(),U(`div`,Iu,[W(`div`,Lu,[W(`div`,null,[t[4]||=W(`h1`,{class:`text-xl md:text-2xl font-semibold text-white mb-1`},`最近爬取`,-1),W(`p`,Ru,`共 `+O(n.value.toLocaleString())+` 条记录`,1)]),W(`div`,zu,[W(`div`,Bu,[W(`div`,Vu,[t[5]||=W(`span`,{class:`text-[10px] text-gray-500 uppercase`},`关键词缓存`,-1),W(`span`,Hu,O(f.value.size.toLocaleString())+` / `+O(f.value.max_size.toLocaleString()),1)]),W(`div`,Uu,[(H(),U(`svg`,Wu,[t[6]||=W(`path`,{class:`text-gray-800`,d:`M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831`,fill:`none`,stroke:`currentColor`,"stroke-width":`4`},null,-1),W(`path`,{class:D(f.value.usage>.9?`text-red-500`:f.value.usage>.7?`text-yellow-500`:`text-green-500`),d:`M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831`,fill:`none`,stroke:`currentColor`,"stroke-width":`4`,"stroke-dasharray":`${(f.value.usage*100).toFixed(0)}, 100`},null,10,Gu)])),W(`span`,Ku,O((f.value.usage*100).toFixed(0))+`% `,1)])]),jn(W(`select`,{"onUpdate:modelValue":t[0]||=e=>c.value=e,onChange:t[1]||=e=>m(c.value),class:`bg-gray-900 border border-gray-700 text-gray-300 text-sm rounded-lg px-3 py-2 focus:border-blue-500 focus:outline-none`},[(H(),U(B,null,L(s,e=>W(`option`,{key:e,value:e},`显示 `+O(e)+` 条`,9,qu)),64))],544),[[co,c.value]]),W(`button`,{onClick:p,class:`bg-blue-600 hover:bg-blue-700 text-white text-sm px-4 py-2 rounded-lg transition-colors`},` 刷新 `)])]),W(`div`,Ju,[W(`div`,Yu,[jn(W(`input`,{"onUpdate:modelValue":t[2]||=e=>a.value=e,type:`text`,placeholder:`搜索标题、URL、域名...`,class:`w-full bg-gray-900 border border-gray-700 text-gray-200 text-sm rounded-lg pl-10 pr-4 py-2 focus:border-blue-500 focus:outline-none placeholder-gray-600`},null,512),[[so,a.value]]),t[7]||=W(`span`,{class:`absolute left-3 top-1/2 -translate-y-1/2 text-gray-500`},`🔍`,-1)]),jn(W(`select`,{"onUpdate:modelValue":t[3]||=e=>o.value=e,class:`bg-gray-900 border border-gray-700 text-gray-300 text-sm rounded-lg px-3 py-2 focus:border-blue-500 focus:outline-none`},[...t[8]||=[Wi(``,9)]],512),[[co,o.value]])]),r.value?(H(),U(`div`,Xu,[...t[9]||=[W(`div`,{class:`text-gray-400 animate-pulse`},`加载中...`,-1)]])):i.value?(H(),U(`div`,Zu,O(i.value),1)):(H(),U(`div`,Qu,[W(`table`,$u,[t[11]||=W(`thead`,null,[W(`tr`,{class:`border-b border-gray-800`},[W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider`},`标题`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-28`},`域名`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-16`},`语种`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-20`},`字数`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-48`},`爬取时间`)])],-1),W(`tbody`,null,[(H(!0),U(B,null,L(h.value,e=>(H(),U(`tr`,{key:e.url,class:`border-b border-gray-800/50 hover:bg-gray-800/40 transition-colors group`},[W(`td`,ed,[W(`a`,{href:e.url,target:`_blank`,rel:`noopener noreferrer`,class:`block hover:opacity-80 transition-opacity`},[W(`div`,nd,O(e.title||`(无标题)`),1),W(`div`,rd,O(e.url),1)],8,td),e.description?(H(),U(`div`,id,O(e.description),1)):G(``,!0),W(`button`,{onClick:mo(t=>y(e.url),[`prevent`]),class:`mt-2 text-xs text-blue-400 hover:text-blue-300 flex items-center gap-1`},[W(`span`,null,O(l.value.has(e.url)?`▼`:`▶`)+` 关键词`,1),d.value.has(e.url)?(H(),U(`span`,od,`加载中...`)):G(``,!0)],8,ad),l.value.has(e.url)?(H(),U(`div`,sd,[u.value[e.url]?.length?(H(),U(B,{key:0},[W(`div`,cd,[(H(!0),U(B,null,L(u.value[e.url],e=>(H(),U(`span`,{key:e.word,class:`text-xs px-2 py-0.5 rounded bg-gray-800 text-gray-300 border border-gray-700`,title:`权重: ${e.weight.toFixed(4)}`},[Ui(O(e.word)+` `,1),W(`span`,ud,O(e.weight.toFixed(2)),1)],8,ld))),128))]),W(`div`,dd,` 共 `+O(u.value[e.url].length)+` 个关键词 `,1)],64)):d.value.has(e.url)?G(``,!0):(H(),U(`span`,fd,` 暂无关键词(服务重启后缓存已清空) `))])):G(``,!0)]),W(`td`,pd,[W(`span`,md,O(e.domain),1)]),W(`td`,hd,[v(e.language)?(H(),U(`span`,{key:0,class:D([`text-xs px-1.5 py-0.5 rounded font-medium`,_(v(e.language)[0]).cls])},O(_(v(e.language)[0]).label)+` `+O((v(e.language)[1]*100).toFixed(0))+`% `,3)):(H(),U(`span`,gd,`-`))]),W(`td`,_d,O(e.word_count.toLocaleString()),1),W(`td`,vd,O(g(e.crawled_at)),1)]))),128)),h.value.length?G(``,!0):(H(),U(`tr`,yd,[...t[10]||=[W(`td`,{colspan:`5`,class:`px-5 py-12 text-center text-gray-600`},` 没有找到匹配的记录 `,-1)]]))])]),W(`div`,bd,[(H(!0),U(B,null,L(h.value,e=>(H(),U(`div`,{key:e.url,class:`block p-4 hover:bg-gray-800/40 transition-colors`},[W(`a`,{href:e.url,target:`_blank`,rel:`noopener noreferrer`,class:`block`},[W(`div`,Sd,O(e.title||`(无标题)`),1),W(`div`,Cd,O(e.url),1)],8,xd),W(`div`,wd,[W(`span`,Td,O(e.domain),1),v(e.language)?(H(),U(`span`,{key:0,class:D([`px-1.5 py-0.5 rounded font-medium`,_(v(e.language)[0]).cls])},O(_(v(e.language)[0]).label),3)):G(``,!0),W(`span`,Ed,O(g(e.crawled_at)),1)]),W(`button`,{onClick:mo(t=>y(e.url),[`prevent`]),class:`text-xs text-blue-400 hover:text-blue-300 flex items-center gap-1 mb-2`},[W(`span`,null,O(l.value.has(e.url)?`▼`:`▶`)+` 关键词`,1),d.value.has(e.url)?(H(),U(`span`,Od,`加载中...`)):G(``,!0)],8,Dd),l.value.has(e.url)?(H(),U(`div`,kd,[u.value[e.url]?.length?(H(),U(B,{key:0},[W(`div`,Ad,[(H(!0),U(B,null,L(u.value[e.url],e=>(H(),U(`span`,{key:e.word,class:`text-[10px] px-1.5 py-0.5 rounded bg-gray-800 text-gray-300 border border-gray-700`,title:`权重: ${e.weight.toFixed(4)}`},[Ui(O(e.word)+` `,1),W(`span`,Md,O(e.weight.toFixed(2)),1)],8,jd))),128))]),W(`div`,Nd,` 共 `+O(u.value[e.url].length)+` 个 `,1)],64)):d.value.has(e.url)?G(``,!0):(H(),U(`span`,Pd,` 暂无关键词 `))])):G(``,!0)]))),128)),h.value.length?G(``,!0):(H(),U(`div`,Fd,` 没有找到匹配的记录 `))])])),!r.value&&!i.value&&h.value.length?(H(),U(`div`,Id,` 筛选后 `+O(h.value.length)+` 条 / 共 `+O(n.value.toLocaleString())+` 条 `,1)):G(``,!0)]))}},Rd={class:`p-4 md:p-8`},zd={class:`bg-gray-900 rounded-xl p-4 md:p-6 mb-4 md:mb-6 border border-gray-800`},Bd={class:`flex flex-col sm:flex-row gap-3`},Vd=[`disabled`],Hd=[`disabled`],Ud={key:0,class:`mt-3 text-sm text-red-400`},Wd={key:1,class:`mt-3 text-sm text-green-400`},Gd={class:`bg-gray-900 rounded-xl border border-gray-800`},Kd={class:`px-4 md:px-6 py-3 md:py-4 border-b border-gray-800 flex items-center justify-between`},qd={class:`text-xs text-gray-500`},Jd={key:0,class:`p-8 text-center text-gray-500 text-sm`},Yd={key:1,class:`p-8 text-center text-red-400 text-sm`},Xd={key:2,class:`p-8 text-center text-gray-500 text-sm`},Zd={key:3,class:`hidden md:table w-full text-sm`},Qd={class:`divide-y divide-gray-800`},$d={class:`px-6 py-3`},ef={class:`text-gray-300 break-all`},tf={class:`px-6 py-3`},nf={key:0,class:`inline-block px-2 py-0.5 text-xs rounded bg-purple-900 text-purple-300`},rf={key:1,class:`inline-block px-2 py-0.5 text-xs rounded bg-blue-900 text-blue-300`},af={class:`px-6 py-3 text-gray-500`},of={class:`px-6 py-3`},sf=[`onClick`],cf={class:`md:hidden divide-y divide-gray-800`},lf={class:`flex-1 min-w-0`},uf={class:`text-gray-300 text-sm break-all mb-1`},df={class:`flex items-center gap-2`},ff={key:0,class:`inline-block px-2 py-0.5 text-xs rounded bg-purple-900 text-purple-300`},pf={key:1,class:`inline-block px-2 py-0.5 text-xs rounded bg-blue-900 text-blue-300`},mf={class:`text-xs text-gray-500`},hf=[`onClick`],gf={__name:`PriorityCrawl`,setup(e){let t=F([]),n=F(!0),r=F(null),i=F(!1),a=F(null),o=F(!1),s=F(``);async function c(){n.value=!0,r.value=null;try{t.value=(await hl()).items||[]}catch(e){r.value=`加载失败,请检查人服务器是否启动`,console.error(e)}finally{n.value=!1}}async function l(){let e=s.value.trim();if(e){i.value=!0,a.value=null,o.value=!1;try{await gl(e),s.value=``,o.value=!0,setTimeout(()=>{o.value=!1},3e3),await c()}catch(e){a.value=e?.response?.data?.error||`添加失败`}finally{i.value=!1}}}async function u(e){try{await _l(e),await c()}catch(e){r.value=`删除失败`,console.error(e)}}function d(e){return e?new Date(e*1e3).toLocaleString(`zh-CN`,{month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`}):`-`}return ar(c),(e,c)=>(H(),U(`div`,Rd,[c[3]||=W(`h1`,{class:`text-xl md:text-2xl font-semibold text-white mb-2`},`插入爬取`,-1),c[4]||=W(`p`,{class:`text-sm text-gray-500 mb-6 md:mb-8`},` 添加 URL 或域名,下一轮爬取时会优先抓取。纯域名会自动补全为 https://www.域名/。 `,-1),W(`div`,zd,[W(`div`,Bd,[jn(W(`input`,{"onUpdate:modelValue":c[0]||=e=>s.value=e,onKeyup:go(l,[`enter`]),type:`text`,placeholder:`输入 URL 或域名,例如 https://example.com 或 example.com`,class:`flex-1 bg-gray-800 border border-gray-700 rounded-lg px-4 py-2.5 text-gray-100 placeholder-gray-500 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 text-sm`,disabled:i.value},null,40,Vd),[[so,s.value]]),W(`button`,{onClick:l,disabled:i.value||!s.value.trim(),class:`px-6 py-2.5 bg-blue-600 hover:bg-blue-500 disabled:bg-gray-700 disabled:text-gray-500 text-white text-sm font-medium rounded-lg transition-colors cursor-pointer whitespace-nowrap`},O(i.value?`添加中...`:`插入队列`),9,Hd)]),a.value?(H(),U(`div`,Ud,O(a.value),1)):G(``,!0),o.value?(H(),U(`div`,Wd,` 已添加到优先队列,将在下一轮爬取时优先抓取 `)):G(``,!0)]),W(`div`,Gd,[W(`div`,Kd,[c[1]||=W(`span`,{class:`text-sm font-medium text-gray-300`},`待爬取队列`,-1),W(`span`,qd,O(t.value.length)+` 条`,1)]),n.value?(H(),U(`div`,Jd,` 加载中... `)):r.value?(H(),U(`div`,Yd,O(r.value),1)):t.value.length===0?(H(),U(`div`,Xd,` 暂无待爬取的优先 URL `)):(H(),U(`table`,Zd,[c[2]||=W(`thead`,null,[W(`tr`,{class:`text-left text-gray-500 text-xs border-b border-gray-800`},[W(`th`,{class:`px-6 py-3 font-medium`},`URL`),W(`th`,{class:`px-6 py-3 font-medium w-28`},`类型`),W(`th`,{class:`px-6 py-3 font-medium w-40`},`添加时间`),W(`th`,{class:`px-6 py-3 font-medium w-16`},`操作`)])],-1),W(`tbody`,Qd,[(H(!0),U(B,null,L(t.value,e=>(H(),U(`tr`,{key:e.url,class:`hover:bg-gray-800/50`},[W(`td`,$d,[W(`span`,ef,O(e.url),1)]),W(`td`,tf,[e.domain?(H(),U(`span`,nf,`域名`)):(H(),U(`span`,rf,`URL`))]),W(`td`,af,O(d(e.added_at)),1),W(`td`,of,[W(`button`,{onClick:t=>u(e.url),class:`text-red-400 hover:text-red-300 text-xs cursor-pointer`},` 删除 `,8,sf)])]))),128))])])),W(`div`,cf,[(H(!0),U(B,null,L(t.value,e=>(H(),U(`div`,{key:e.url,class:`p-4 hover:bg-gray-800/50 flex items-center justify-between gap-3`},[W(`div`,lf,[W(`div`,uf,O(e.url),1),W(`div`,df,[e.domain?(H(),U(`span`,ff,`域名`)):(H(),U(`span`,pf,`URL`)),W(`span`,mf,O(d(e.added_at)),1)])]),W(`button`,{onClick:t=>u(e.url),class:`text-red-400 hover:text-red-300 text-xs cursor-pointer shrink-0 px-2 py-1`},` 删除 `,8,hf)]))),128))])])]))}},_f={class:`flex flex-col h-full`},vf={class:`bg-gray-950 border-b border-gray-800 px-4 md:px-8 py-4 md:py-6`},yf={class:`max-w-3xl mx-auto`},bf={class:`relative`},xf={class:`flex-1 overflow-y-auto px-8 py-6`},Sf={class:`max-w-3xl mx-auto`},Cf={key:0,class:`flex items-center gap-3 text-gray-400 py-8`},wf={key:1,class:`bg-red-900/30 border border-red-800 rounded-lg p-4 text-red-300`},Tf={key:2,class:`py-16 text-center text-gray-600`},Ef={key:3,class:`py-16 text-center text-gray-600`},Df={key:4,class:`flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4 mb-4 md:mb-5 text-sm text-gray-500`},Of={class:`flex items-center gap-2`},kf={class:`text-gray-300`},Af={class:`flex flex-wrap gap-2 sm:ml-auto`},jf={class:`text-gray-300`},Mf={key:5,class:`space-y-2 md:space-y-1`},Nf=[`onClick`],Pf={class:`flex items-start gap-2 md:gap-3 mb-2`},Ff={class:`flex-1 min-w-0`},If={class:`text-blue-400 group-hover:text-blue-300 text-base md:text-lg leading-snug`},Lf={class:`text-xs text-gray-600 mt-0.5 truncate`},Rf={class:`flex flex-col items-end gap-1 shrink-0`},zf={class:`text-xs text-gray-600`},Bf={class:`w-12 md:w-14 bg-gray-800 rounded-full h-1.5 overflow-hidden`},Vf={class:`text-gray-400 text-sm leading-relaxed mb-2 md:mb-3`},Hf={class:`flex flex-wrap items-center gap-2 md:gap-3 text-xs`},Uf={class:`flex flex-wrap gap-1.5`},Wf=[`title`],Gf={class:`text-blue-400`},Kf={class:`text-gray-600 ml-1`},qf={key:0,class:`text-gray-600`},Jf={class:`text-gray-700 ml-0 md:ml-auto`},Yf={class:`text-xs text-gray-700 mt-1 truncate`},Xf={key:6,class:`mt-6 text-center`},Zf=``,Qf=10,$f={__name:`SearchView`,setup(e){let t=F(``),n=F([]),r=F(0),i=F({}),a=F(!1),o=F(null),s=F(0),c=null;async function l(e,t=!1){if(!e.trim()){n.value=[],r.value=0,i.value={};return}t||(s.value=0),a.value=!0,o.value=null;try{let a=encodeURIComponent(e),o=s.value*Qf,c=await fetch(`${Zf}/search?qh=${a}&slice=${o}:${o+Qf}`);if(!c.ok)throw Error(`HTTP ${c.status}`);let l=await c.json();t?n.value=[...n.value,...l.results||[]]:n.value=l.results||[],r.value=l.total||0,i.value=l.counts||{}}catch(e){o.value=`搜索失败,可能是人服务器未启动`,console.error(e)}finally{a.value=!1}}function u(){clearTimeout(c),c=setTimeout(()=>l(t.value),400)}function d(e){e.key===`Enter`&&(clearTimeout(c),l(t.value))}function f(e,t=120){return e?e.length>t?e.slice(0,t)+`…`:e:``}function p(e){return e?e.toLocaleString():`0`}function m(e){return Math.min(100,Math.round(e*100))}function h(e){return{zh:`中文`,en:`EN`,ja:`JA`,ko:`KO`,fr:`FR`,de:`DE`,es:`ES`,ru:`RU`}[e]||e?.toUpperCase()}function g(e){try{return new URL(e).hostname}catch{return e}}function _(e){window.open(e,`_blank`)}return(e,c)=>(H(),U(`div`,_f,[W(`div`,vf,[W(`div`,yf,[W(`div`,bf,[jn(W(`input`,{"onUpdate:modelValue":c[0]||=e=>t.value=e,onInput:u,onKeydown:d,type:`text`,placeholder:`输入关键词搜索,或用 site:example.com 限定域名`,class:`w-full bg-gray-900 border border-gray-700 rounded-xl md:rounded-2xl px-4 md:px-6 py-3 md:py-4 pr-20 md:pr-14 text-white text-base md:text-lg placeholder-gray-600 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition`},null,544),[[so,t.value]]),W(`button`,{onClick:c[1]||=e=>l(t.value),class:`absolute right-2 md:right-3 top-1/2 -translate-y-1/2 bg-blue-600 hover:bg-blue-500 text-white rounded-lg md:rounded-xl px-3 md:px-4 py-1.5 md:py-2 text-sm font-medium transition`},` 搜索 `)])])]),W(`div`,xf,[W(`div`,Sf,[a.value?(H(),U(`div`,Cf,[...c[3]||=[W(`div`,{class:`w-5 h-5 border-2 border-blue-400 border-t-transparent rounded-full animate-spin`},null,-1),W(`span`,null,`搜索中...`,-1)]])):o.value?(H(),U(`div`,wf,O(o.value),1)):t.value.trim()?n.value.length===0&&!a.value?(H(),U(`div`,Ef,` 未找到相关结果 `)):n.value.length>0?(H(),U(`div`,Df,[W(`div`,Of,[W(`span`,null,[c[4]||=Ui(`找到约 `,-1),W(`strong`,kf,O(p(r.value)),1),c[5]||=Ui(` 条结果`,-1)]),c[6]||=W(`span`,{class:`text-gray-700`},`|`,-1),W(`span`,null,O(n.value.length)+` 条已加载`,1)]),W(`div`,Af,[(H(!0),U(B,null,L(i.value,(e,t)=>(H(),U(`span`,{key:t,class:`inline-flex items-center gap-1 bg-gray-800 rounded px-2 py-0.5 text-xs text-gray-400`},[W(`span`,jf,O(t),1),W(`span`,null,O(p(e)),1)]))),128))])])):G(``,!0):(H(),U(`div`,Tf,` 输入关键词开始搜索 `)),n.value.length>0?(H(),U(`div`,Mf,[(H(!0),U(B,null,L(n.value,(e,t)=>(H(),U(`div`,{key:t,onClick:t=>_(e.url),class:`group block bg-gray-900/50 hover:bg-gray-900 border border-gray-800 hover:border-gray-700 rounded-xl p-4 md:p-5 cursor-pointer transition`},[W(`div`,Pf,[W(`div`,Ff,[W(`div`,If,O(e.snippet?.title||g(e.url)),1),W(`div`,Lf,O(g(e.url)),1)]),W(`div`,Rf,[W(`div`,zf,O(m(e.score))+`%`,1),W(`div`,Bf,[W(`div`,{class:`h-full bg-blue-500 rounded-full`,style:me({width:m(e.score)+`%`})},null,4)])])]),W(`p`,Vf,O(f(e.snippet?.description||e.snippet?.text)),1),W(`div`,Hf,[W(`div`,Uf,[(H(!0),U(B,null,L(e.relevance,(e,t)=>(H(),U(`span`,{key:t,class:`inline-flex items-center bg-gray-800 rounded px-1.5 py-0.5`,title:`${t}: ${(e*100).toFixed(1)}%`},[W(`span`,Gf,O(t),1),W(`span`,Kf,O((e*100).toFixed(0))+`%`,1)],8,Wf))),128))]),e.snippet?.language?(H(),U(`span`,qf,O(h(e.snippet.language)),1)):G(``,!0),W(`span`,Jf,O(p(e.domain_count))+` 个结果`,1)]),W(`div`,Yf,O(e.url),1)],8,Nf))),128))])):G(``,!0),n.value.length>0&&n.value.length{s.value++,l(t.value,!0)},class:`bg-gray-800 hover:bg-gray-700 text-gray-300 rounded-xl px-6 py-2.5 text-sm transition`},` 加载更多 (`+O(n.value.length)+`/`+O(p(r.value))+`) `,1)])):G(``,!0)])])]))}},ep={class:`flex h-screen bg-gray-950 text-gray-100 font-sans`},tp={class:`hidden md:flex w-56 bg-gray-900 border-r border-gray-800 flex-col shrink-0`},np={class:`flex-1 py-4 px-3 space-y-1`},rp=[`onClick`],ip={class:`flex-1 overflow-y-auto pb-16 md:pb-0`},ap={class:`md:hidden fixed bottom-0 left-0 right-0 bg-gray-900 border-t border-gray-800 flex justify-around items-center h-16 z-50`},op=[`onClick`],sp={class:`text-lg`},cp={class:`text-[10px]`};bo({__name:`App`,setup(e){let t=F(`dashboard`),n=[{id:`dashboard`,label:`概览`,icon:`📊`},{id:`recent`,label:`最近`,icon:`🕷️`},{id:`search`,label:`搜索`,icon:`🔍`},{id:`priority`,label:`插入`,icon:`🚀`}];return(e,r)=>(H(),U(`div`,ep,[W(`aside`,tp,[r[0]||=W(`div`,{class:`px-5 py-5 border-b border-gray-800`},[W(`div`,{class:`text-lg font-semibold text-white tracking-tight`},`SESE Admin`),W(`div`,{class:`text-xs text-gray-500 mt-0.5`},`爬取内容监控`)],-1),W(`nav`,np,[(H(),U(B,null,L(n,e=>W(`button`,{key:e.id,onClick:n=>t.value=e.id,class:D([`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors`,t.value===e.id?`bg-blue-600 text-white`:`text-gray-400 hover:text-white hover:bg-gray-800`])},[W(`span`,null,O(e.icon),1),Ui(` `+O(e.label),1)],10,rp)),64))]),r[1]||=W(`div`,{class:`px-5 py-4 border-t border-gray-800`},[W(`div`,{class:`text-xs text-gray-600`},`sese-engine v1.0`)],-1)]),W(`main`,ip,[t.value===`dashboard`?(H(),Pi(Fu,{key:0})):t.value===`recent`?(H(),Pi(Ld,{key:1})):t.value===`search`?(H(),Pi($f,{key:2})):t.value===`priority`?(H(),Pi(gf,{key:3})):G(``,!0)]),W(`nav`,ap,[(H(),U(B,null,L(n,e=>W(`button`,{key:e.id,onClick:n=>t.value=e.id,class:D([`flex flex-col items-center justify-center gap-0.5 flex-1 h-full transition-colors`,t.value===e.id?`text-blue-400`:`text-gray-500`])},[W(`span`,sp,O(e.icon),1),W(`span`,cp,O(e.label),1)],10,op)),64))])]))}}).mount(`#app`); \ No newline at end of file diff --git a/dist/index.html b/dist/index.html index ad0e6e9..9a2c074 100644 --- a/dist/index.html +++ b/dist/index.html @@ -5,7 +5,7 @@ SESE 爬取管理 - + diff --git a/sese-engine-ui b/sese-engine-ui index 5c944b7..67a5f7c 160000 --- a/sese-engine-ui +++ b/sese-engine-ui @@ -1 +1 @@ -Subproject commit 5c944b7c90a36932fd4397627a92c6b6bad622a3 +Subproject commit 67a5f7cfab5b3d9580c531776f5b20f2809f387d diff --git a/storage/storage.go b/storage/storage.go index a1cb743..a052ad4 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -690,22 +690,14 @@ func (d *DB) GetPriorityURLs() ([]PriorityEntry, error) { } // AddPriorityURL 添加一条 priority 条目(key = URL,value = PriorityEntry)。 -// 若已存在(且未访问)则忽略。 +// 每次手动添加都会重新爬取,不做去重检查。 func (d *DB) AddPriorityURL(entry PriorityEntry) error { return d.db.Update(func(tx *bolt.Tx) error { - k := []byte(entry.URL) - existing := tx.Bucket(bucketPriority).Get(k) - if existing != nil { - var e PriorityEntry - if err := decompressUnmarshal(existing, &e); err == nil && !e.Visited { - return nil // 已存在且未访问,忽略 - } - } data, err := marshalCompress(entry) if err != nil { return err } - return tx.Bucket(bucketPriority).Put(k, data) + return tx.Bucket(bucketPriority).Put([]byte(entry.URL), data) }) } @@ -716,6 +708,31 @@ func (d *DB) RemovePriorityURL(url string) error { }) } +// MarkPriorityURLVisited 将指定 URL 的 priority 条目标记为已访问。 +// 用于 priority 爬取完成后标记,避免 RemovePriorityURL 后同一 URL 被重复添加。 +func (d *DB) MarkPriorityURLVisited(url string) error { + return d.db.Update(func(tx *bolt.Tx) error { + k := []byte(url) + v := tx.Bucket(bucketPriority).Get(k) + if v == nil { + return nil // 条目不存在,无需处理 + } + var e PriorityEntry + if err := decompressUnmarshal(v, &e); err != nil { + return nil + } + if e.Visited { + return nil // 已是 visited 状态 + } + e.Visited = true + data, err := marshalCompress(e) + if err != nil { + return err + } + return tx.Bucket(bucketPriority).Put(k, data) + }) +} + // ClearVisitedPriorityURLs 批量删除所有已标记为 visited 的条目(crawler 爬完后调用)。 func (d *DB) ClearVisitedPriorityURLs() error { return d.db.Update(func(tx *bolt.Tx) error {