weifuwu 0.17.6 → 0.17.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics.d.ts +3 -0
- package/dist/index.js +76 -39
- package/package.json +1 -1
package/dist/analytics.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { Handler, Middleware } from './types.ts';
|
|
2
2
|
export interface AnalyticsOptions {
|
|
3
3
|
excluded?: string[];
|
|
4
|
+
pg?: {
|
|
5
|
+
sql: (strings: TemplateStringsArray, ...values: any[]) => Promise<any[]>;
|
|
6
|
+
};
|
|
4
7
|
}
|
|
5
8
|
export declare function analytics(options?: AnalyticsOptions): {
|
|
6
9
|
middleware: Middleware;
|
package/dist/index.js
CHANGED
|
@@ -6870,32 +6870,53 @@ var MemStore = class {
|
|
|
6870
6870
|
devices: { mobile: Math.round(totalMobile / total * 1e3) / 10, desktop: Math.round(totalDesktop / total * 1e3) / 10 }
|
|
6871
6871
|
};
|
|
6872
6872
|
}
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
}
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
).
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6873
|
+
};
|
|
6874
|
+
async function queryPg(sql2, days) {
|
|
6875
|
+
const since = /* @__PURE__ */ new Date();
|
|
6876
|
+
since.setDate(since.getDate() - days);
|
|
6877
|
+
const daily = await sql2`
|
|
6878
|
+
SELECT date, SUM(count) as pv, COUNT(DISTINCT path) as uv
|
|
6879
|
+
FROM __analytics WHERE date >= ${since.toISOString().slice(0, 10)}
|
|
6880
|
+
GROUP BY date ORDER BY date
|
|
6881
|
+
`;
|
|
6882
|
+
const pageRows = await sql2`
|
|
6883
|
+
SELECT path, SUM(count) as pv
|
|
6884
|
+
FROM __analytics WHERE date >= ${since.toISOString().slice(0, 10)}
|
|
6885
|
+
GROUP BY path ORDER BY pv DESC LIMIT 20
|
|
6886
|
+
`;
|
|
6887
|
+
const totalRes = await sql2`
|
|
6888
|
+
SELECT COALESCE(SUM(count), 0) as total_pv,
|
|
6889
|
+
COALESCE(SUM(mobile), 0) as total_mobile,
|
|
6890
|
+
COALESCE(SUM(desktop), 0) as total_desktop
|
|
6891
|
+
FROM __analytics WHERE date >= ${since.toISOString().slice(0, 10)}
|
|
6892
|
+
`;
|
|
6893
|
+
const total = totalRes[0];
|
|
6894
|
+
const totalMobileDesktop = total.total_mobile + total.total_desktop || 1;
|
|
6895
|
+
return {
|
|
6896
|
+
total_pv: total.total_pv,
|
|
6897
|
+
total_uv: pageRows.length,
|
|
6898
|
+
daily: daily.map((d) => ({ date: d.date, pv: Number(d.pv), uv: Number(d.uv) })),
|
|
6899
|
+
top_pages: pageRows.map((p) => ({ path: p.path, pv: Number(p.pv) })),
|
|
6900
|
+
referrers: [],
|
|
6901
|
+
devices: {
|
|
6902
|
+
mobile: Math.round(total.total_mobile / totalMobileDesktop * 1e3) / 10,
|
|
6903
|
+
desktop: Math.round(total.total_desktop / totalMobileDesktop * 1e3) / 10
|
|
6904
|
+
}
|
|
6905
|
+
};
|
|
6906
|
+
}
|
|
6907
|
+
function renderDashboard(days, data) {
|
|
6908
|
+
const { total_pv, total_uv, daily, top_pages, referrers } = data;
|
|
6909
|
+
const maxPv = Math.max(...daily.map((d) => d.pv), 1);
|
|
6910
|
+
const bars = daily.map(
|
|
6911
|
+
(d) => `<div class="bar-wrap"><div class="bar" style="height:${d.pv / maxPv * 100}%"></div><span class="bar-label">${d.date.slice(5)}</span></div>`
|
|
6912
|
+
).join("");
|
|
6913
|
+
const rows = top_pages.map(
|
|
6914
|
+
(p, i) => `<tr><td class="num">${i + 1}</td><td class="path">${p.path}</td><td class="num">${p.pv}</td></tr>`
|
|
6915
|
+
).join("");
|
|
6916
|
+
const refRows = referrers.map(
|
|
6917
|
+
(r) => `<tr><td>${r.domain}</td><td class="num">${r.count}</td></tr>`
|
|
6918
|
+
).join("");
|
|
6919
|
+
return `<!DOCTYPE html>
|
|
6899
6920
|
<html lang="en">
|
|
6900
6921
|
<head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>Analytics - weifuwu</title>
|
|
6901
6922
|
<style>
|
|
@@ -6924,36 +6945,52 @@ tr:hover td{background:#f8faff}
|
|
|
6924
6945
|
<div class="cards">
|
|
6925
6946
|
<div class="card"><div class="val">${total_pv}</div><div class="lbl">Page Views (${days}d)</div></div>
|
|
6926
6947
|
<div class="card"><div class="val">${total_uv}</div><div class="lbl">Unique Pages</div></div>
|
|
6927
|
-
<div class="card"><div class="val">${devices.mobile}%</div><div class="lbl">Mobile</div></div>
|
|
6928
|
-
<div class="card"><div class="val">${devices.desktop}%</div><div class="lbl">Desktop</div></div>
|
|
6948
|
+
<div class="card"><div class="val">${data.devices.mobile}%</div><div class="lbl">Mobile</div></div>
|
|
6949
|
+
<div class="card"><div class="val">${data.devices.desktop}%</div><div class="lbl">Desktop</div></div>
|
|
6929
6950
|
</div>
|
|
6930
6951
|
<div class="section"><h2>Daily Page Views</h2><div class="chart">${bars}</div></div>
|
|
6931
6952
|
<div class="section"><h2>Top Pages</h2>
|
|
6932
6953
|
<table><thead><tr><th style="width:32px">#</th><th>Path</th><th style="width:64px">Views</th></tr></thead><tbody>${rows}</tbody></table></div>
|
|
6933
6954
|
${referrers.length ? `<div class="section"><h2>Referrers</h2><table><thead><tr><th>Domain</th><th style="width:64px">Views</th></tr></thead><tbody>${refRows}</tbody></table></div>` : ""}
|
|
6934
6955
|
</body></html>`;
|
|
6935
|
-
|
|
6936
|
-
};
|
|
6956
|
+
}
|
|
6937
6957
|
function analytics(options) {
|
|
6938
6958
|
const excluded = options?.excluded ?? DEFAULT_EXCLUDED;
|
|
6939
|
-
const
|
|
6959
|
+
const pg = options?.pg;
|
|
6960
|
+
const store = pg ? null : new MemStore();
|
|
6940
6961
|
const middleware = async (req, ctx, next) => {
|
|
6941
6962
|
const url = new URL(req.url);
|
|
6942
6963
|
const path2 = url.pathname;
|
|
6943
|
-
if (
|
|
6944
|
-
|
|
6945
|
-
|
|
6964
|
+
if (excluded.some((e) => path2.startsWith(e))) return next(req, ctx);
|
|
6965
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6966
|
+
const ref = req.headers.get("referer") || "";
|
|
6967
|
+
const ua = req.headers.get("user-agent") || "";
|
|
6968
|
+
const mobile = /mobile|android|iphone|ipad/i.test(ua);
|
|
6969
|
+
if (pg) {
|
|
6970
|
+
await pg.sql`
|
|
6971
|
+
INSERT INTO __analytics (date, path, count, mobile, desktop)
|
|
6972
|
+
VALUES (${date}, ${path2}, 1, ${mobile ? 1 : 0}, ${mobile ? 0 : 1})
|
|
6973
|
+
ON CONFLICT (date, path) DO UPDATE SET
|
|
6974
|
+
count = __analytics.count + 1,
|
|
6975
|
+
mobile = __analytics.mobile + ${mobile ? 1 : 0},
|
|
6976
|
+
desktop = __analytics.desktop + ${mobile ? 0 : 1}
|
|
6977
|
+
`;
|
|
6978
|
+
} else {
|
|
6946
6979
|
const refDomain = ref ? new URL(ref).hostname.replace(/^www\./, "") : "";
|
|
6947
|
-
const ua = req.headers.get("user-agent") || "";
|
|
6948
|
-
const mobile = /mobile|android|iphone|ipad/i.test(ua);
|
|
6949
6980
|
store.record(path2, date, refDomain, mobile);
|
|
6950
6981
|
}
|
|
6951
6982
|
return next(req, ctx);
|
|
6952
6983
|
};
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6984
|
+
const handler = async (req) => {
|
|
6985
|
+
const url = new URL(req.url);
|
|
6986
|
+
const days = Math.min(Math.max(Number(url.searchParams.get("days")) || 7, 1), 365);
|
|
6987
|
+
const data = pg ? await queryPg(pg.sql, days) : store.query(days);
|
|
6988
|
+
if (url.pathname === "/__analytics/data") return Response.json(data);
|
|
6989
|
+
return new Response(renderDashboard(days, data), {
|
|
6990
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
6991
|
+
});
|
|
6956
6992
|
};
|
|
6993
|
+
return { middleware, handler };
|
|
6957
6994
|
}
|
|
6958
6995
|
|
|
6959
6996
|
// preferences.ts
|