- 新增访问统计:visitstats 服务与查询、visit_capture 中间件、爬虫/来源识别、管理端 analytics 页面 - 新增侧边栏:sidebar 服务与 handler、管理端 sidebar 配置页、前端侧边栏组件 - 新增 widget 嵌入代码生成(widgetCode)与 widgetApi - 遥测逻辑迁移至 handler 层,删除 service/telemetry - env 示例补充 Docker 部署可信代理 IP 说明
55 lines
1.8 KiB
TypeScript
55 lines
1.8 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, useRef } from "react";
|
||
import type { SidebarWidgetPublic, User } from "@/lib/api";
|
||
import { scopeWidgetCss } from "@/lib/widgetCode";
|
||
import { ensureWidgetApi } from "@/lib/widgetApi";
|
||
|
||
// 站长自定义工具块:HTML/CSS 随 SSR 直出(dangerouslySetInnerHTML),
|
||
// JS 在客户端挂载时注入执行、卸载时移除。站长可信内容,不做消毒/沙箱。
|
||
// CSS 经 scopeWidgetCss 限定在 `.j13-widget-{id}` 容器内,不污染页面其他区域;
|
||
// 工具内可直接用站点主题变量(var(--accent) 等)与 window.J13 接口。
|
||
export default function CustomWidgetBlock({
|
||
widget,
|
||
viewer,
|
||
}: {
|
||
widget: SidebarWidgetPublic;
|
||
viewer?: User | null;
|
||
}) {
|
||
const hostRef = useRef<HTMLDivElement>(null);
|
||
|
||
// 先于站长 JS 建立站点接口(声明顺序保证本 effect 先执行)
|
||
useEffect(() => {
|
||
ensureWidgetApi(viewer);
|
||
}, [viewer]);
|
||
|
||
useEffect(() => {
|
||
const host = hostRef.current;
|
||
if (!host || !widget.js) return;
|
||
const script = document.createElement("script");
|
||
script.dataset.widgetScript = String(widget.id);
|
||
script.textContent = widget.js;
|
||
host.appendChild(script);
|
||
return () => {
|
||
script.remove();
|
||
};
|
||
}, [widget.id, widget.js]);
|
||
|
||
return (
|
||
<section
|
||
className={`j13-widget-body j13-widget-${widget.id}`}
|
||
data-widget-id={widget.id}
|
||
data-widget-name={widget.name}
|
||
>
|
||
{widget.css ? (
|
||
<style dangerouslySetInnerHTML={{ __html: scopeWidgetCss(widget.css, widget.id) }} />
|
||
) : null}
|
||
{widget.html ? (
|
||
<div className="j13-widget-html" dangerouslySetInnerHTML={{ __html: widget.html }} />
|
||
) : null}
|
||
{/* 自定义 JS 的宿主节点:脚本由客户端创建后挂到此处执行 */}
|
||
<div ref={hostRef} />
|
||
</section>
|
||
);
|
||
}
|