feat: add Microsoft Clarity analytics provider

Add Microsoft Clarity as a fourth analytics provider option alongside
Umami, Google Analytics, and Custom. Clarity provides free heatmaps,
session recordings, and user analytics.

- admin.types.ts: Add 'clarity' to AnalyticsProvider union type
- api/admin/site/route.ts: Add 'clarity' to validation
- admin/page.tsx: Add Clarity option to dropdown + Project ID input UI
- layout.tsx: Inject Clarity tracking script with project ID
This commit is contained in:
mtvpls-turso
2026-07-11 19:01:16 +00:00
parent d98f5082a5
commit 392211264f
4 changed files with 37 additions and 5 deletions
+26 -2
View File
@@ -408,7 +408,7 @@ interface SiteConfig {
OIDCClientSecret?: string;
OIDCButtonText?: string;
AnalyticsEnabled?: boolean;
AnalyticsProvider?: 'umami' | 'google' | 'custom';
AnalyticsProvider?: 'umami' | 'google' | 'clarity' | 'custom';
AnalyticsScriptUrl?: string;
AnalyticsWebsiteId?: string;
AnalyticsCustomScript?: string;
@@ -11597,13 +11597,14 @@ const SiteConfigComponent = ({
onChange={(e) =>
setSiteSettings((prev) => ({
...prev,
AnalyticsProvider: e.target.value as 'umami' | 'google' | 'custom',
AnalyticsProvider: e.target.value as 'umami' | 'google' | 'clarity' | 'custom',
}))
}
className='mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 text-sm dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200'
>
<option value='umami'>Umami</option>
<option value='google'>Google Analytics</option>
<option value='clarity'>Microsoft Clarity+</option>
<option value='custom'></option>
</select>
</div>
@@ -11676,6 +11677,29 @@ const SiteConfigComponent = ({
</div>
)}
{siteSettings.AnalyticsProvider === 'clarity' && (
<div>
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300'>
Project ID
</label>
<input
type='text'
value={siteSettings.AnalyticsWebsiteId}
onChange={(e) =>
setSiteSettings((prev) => ({
...prev,
AnalyticsWebsiteId: e.target.value,
}))
}
placeholder='e.g. abc1234567'
className='mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 text-sm dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200'
/>
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
Microsoft Clarity Project ID clarity.microsoft.com
</p>
</div>
)}
{siteSettings.AnalyticsProvider === 'custom' && (
<div>
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300'>
+2 -1
View File
@@ -144,7 +144,7 @@ export async function POST(request: NextRequest) {
OIDCButtonText?: string;
OIDCMinTrustLevel?: number;
AnalyticsEnabled?: boolean;
AnalyticsProvider?: 'umami' | 'google' | 'custom';
AnalyticsProvider?: 'umami' | 'google' | 'clarity' | 'custom';
AnalyticsScriptUrl?: string;
AnalyticsWebsiteId?: string;
AnalyticsCustomScript?: string;
@@ -239,6 +239,7 @@ export async function POST(request: NextRequest) {
(AnalyticsProvider !== undefined &&
AnalyticsProvider !== 'umami' &&
AnalyticsProvider !== 'google' &&
AnalyticsProvider !== 'clarity' &&
AnalyticsProvider !== 'custom') ||
(AnalyticsScriptUrl !== undefined && typeof AnalyticsScriptUrl !== 'string') ||
(AnalyticsWebsiteId !== undefined && typeof AnalyticsWebsiteId !== 'string') ||
+8 -1
View File
@@ -116,7 +116,7 @@ export default async function RootLayout({
let webLiveEnabled = false;
let customAdFilterVersion = 0;
let analyticsEnabled = false;
let analyticsProvider: 'umami' | 'google' | 'custom' = 'umami';
let analyticsProvider: 'umami' | 'google' | 'clarity' | 'custom' = 'umami';
let analyticsScriptUrl = '';
let analyticsWebsiteId = '';
let analyticsCustomScript = '';
@@ -372,6 +372,13 @@ export default async function RootLayout({
/>
</>
)}
{analyticsEnabled && analyticsProvider === 'clarity' && analyticsWebsiteId && (
<script
dangerouslySetInnerHTML={{
__html: `(function(c,l,a,r,i,t,y){c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);})(window,document,"clarity","script","${analyticsWebsiteId}");`,
}}
/>
)}
{analyticsEnabled && analyticsProvider === 'custom' && analyticsCustomScript && (
<script
dangerouslySetInnerHTML={{ __html: analyticsCustomScript }}
+1 -1
View File
@@ -75,7 +75,7 @@ export interface AdminConfig {
OIDCMinTrustLevel?: number; // 最低信任等级(仅LinuxDo网站有效,为0时不判断)
// 流量统计配置
AnalyticsEnabled?: boolean; // 是否启用流量统计
AnalyticsProvider?: 'umami' | 'google' | 'custom'; // 统计服务提供商
AnalyticsProvider?: 'umami' | 'google' | 'clarity' | 'custom'; // 统计服务提供商
AnalyticsScriptUrl?: string; // 脚本URLUmami: umami.js地址; GA: gtag URL; 自定义: 脚本src
AnalyticsWebsiteId?: string; // 网站IDUmami: website_id; GA: Measurement ID如G-XXXX; 自定义: 留空)
AnalyticsCustomScript?: string; // 自定义统计代码(仅custom模式使用,完整的HTML脚本内容)