diff --git a/.env.example b/.env.example index adaa2c7..2f63600 100644 --- a/.env.example +++ b/.env.example @@ -6,7 +6,7 @@ KV_REST_API_TOKEN= DATABASE_URL= BETTER_AUTH_SECRET= -BETTER_AUTH_URL= +BETTER_AUTH_URL="https://vercount.one" # 默认情况下,直接使用官方服务器的服务 GITHUB_CLIENT_ID= GITHUB_CLIENT_SECRET= \ No newline at end of file diff --git a/README.md b/README.md index 515e81d..3b40246 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,14 @@ Vercount 旨在解决这些问题,让网站计数器更快、更稳定、更 最终解释权归 EvanNotFound 所有。 -## 自托管 +## 自托管(部署到vercel) -Coming soon... \ No newline at end of file +1. fork本仓库 +2. 补全环境变量 + +- `GITHUB_CLIENT_ID` 和 `GITHUB_CLIENT_SECRET`:https://github.com/settings/developers +- `DATABASE_URL`:https://console.neon.tech/app/projects/create +- `KV_REST_API_URL` 和 `KV_REST_API_TOKEN`:https://console.upstash.com/redis/create +- `BETTER_AUTH_URL`: https://项目名.vercel.app + +3. 部署项目 diff --git a/package.json b/package.json index 5f3d462..2b6f5c3 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "terser ./src/lib/client.js -o ./public/js/client.min.js --compress --timings --mangle --toplevel && next dev --turbopack", - "build": "terser ./src/lib/client.js -o ./public/js/client.min.js --compress --timings --mangle --toplevel && next build", + "dev": "node ./scripts/build-client.js && terser ./src/lib/client.build.js -o ./public/js/client.min.js --compress --timings --mangle --toplevel && next dev --turbopack", + "build": "node ./scripts/build-client.js && terser ./src/lib/client.build.js -o ./public/js/client.min.js --compress --timings --mangle --toplevel && next build", "start": "terser ./src/lib/client.js -o ./public/js/client.min.js --compress --timings --mangle --toplevel && next start", "lint": "next lint", "db:generate": "npx drizzle-kit generate", diff --git a/public/js/client.min.js b/public/js/client.min.js deleted file mode 100644 index 277d13d..0000000 --- a/public/js/client.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(){"use strict";const t=[];let e=!1,n=null;const o="visitorCountData",a=async()=>{const t=window.location.href;if(!t.startsWith("http"))return null;const e=new AbortController,n=setTimeout((()=>e.abort()),5e3);try{const o=await fetch("https://events.vercount.one/api/v2/log",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:t}),signal:e.signal});if(clearTimeout(n),!o.ok)throw new Error(`HTTP ${o.status}`);return(t=>"success"===t?.status&&t.data?t.data:"error"===t?.status?(console.warn("API error:",t.message),t.data||{site_uv:0,site_pv:0,page_pv:0}):t||{site_uv:0,site_pv:0,page_pv:0})(await o.json())}catch(t){return clearTimeout(n),"AbortError"===t.name?console.warn("Request timeout"):console.warn("API error:",t.message),null}},s=()=>{if(n)return n;return n={},["site_pv","page_pv","site_uv"].forEach((t=>{n[`busuanzi_value_${t}`]=document.getElementById(`busuanzi_value_${t}`),n[`busuanzi_container_${t}`]=document.getElementById(`busuanzi_container_${t}`),n[`vercount_value_${t}`]=document.getElementById(`vercount_value_${t}`),n[`vercount_container_${t}`]=document.getElementById(`vercount_container_${t}`)})),n},r=t=>{const e=s();["site_pv","page_pv","site_uv"].forEach((n=>{const o=String(t[n]||"0"),a=e[`busuanzi_value_${n}`],s=e[`vercount_value_${n}`];a&&(a.textContent=o),s&&(s.textContent=o)}))},u=()=>{const t=s();["site_pv","page_pv","site_uv"].forEach((e=>{const n=t[`busuanzi_container_${e}`],o=t[`vercount_container_${e}`];n&&(n.style.display="inline"),o&&(o.style.display="inline")}))};var i;i=async()=>{(()=>{const t=s();["site_pv","page_pv","site_uv"].forEach((e=>{const n=t[`busuanzi_container_${e}`],o=t[`vercount_container_${e}`];n&&(n.style.display="none"),o&&(o.style.display="none")}))})();const t=await a();if(t)r(t),(t=>{try{localStorage.setItem(o,JSON.stringify(t))}catch(t){}})(t),u();else{const t=(()=>{try{const t=localStorage.getItem(o);return t?JSON.parse(t):null}catch(t){return null}})();t&&(r(t),u())}},e||"loading"!==document.readyState?i():(t.push(i),document.addEventListener("DOMContentLoaded",(()=>{e=!0,t.forEach((t=>t()))}),{once:!0}))}(); \ No newline at end of file diff --git a/scripts/build-client.js b/scripts/build-client.js new file mode 100644 index 0000000..3428eeb --- /dev/null +++ b/scripts/build-client.js @@ -0,0 +1,33 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); + +// Get base URL from env +const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || process.env.BETTER_AUTH_URL || ''; + +// Only replace if baseUrl is set and NOT vercount.one +const shouldReplace = baseUrl && !baseUrl.includes('vercount.one'); + +const clientSrc = path.resolve(__dirname, '../src/lib/client.js'); +const clientTmp = path.resolve(__dirname, '../src/lib/client.build.js'); + +if (shouldReplace) { + let origin = baseUrl; + try { + origin = new URL(baseUrl).origin; + } catch (e) { + origin = baseUrl.replace(/\/$/, ''); + } + + console.log(`[build-client] Using custom domain: ${origin}`); + + // Create temporary build version of client.js + let clientContent = fs.readFileSync(clientSrc, 'utf8'); + clientContent = clientContent.replace(/https:\/\/events\.vercount\.one/g, origin); + fs.writeFileSync(clientTmp, clientContent, 'utf8'); +} else { + console.log('[build-client] Using default: vercount.one'); + // Just copy original + fs.copyFileSync(clientSrc, clientTmp); +} + diff --git a/src/env.ts b/src/env.ts index 063f7c5..e23da43 100644 --- a/src/env.ts +++ b/src/env.ts @@ -15,9 +15,11 @@ export const env = createEnv({ KV_REST_API_TOKEN: z.string(), }, client: { + NEXT_PUBLIC_BASE_URL: z.string().url().optional(), }, // only for client env variables experimental__runtimeEnv: { + NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL, } }); \ No newline at end of file