lytx 0.3.0
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/.env.example +37 -0
- package/README.md +486 -0
- package/alchemy.run.ts +155 -0
- package/cli/bootstrap-admin.ts +284 -0
- package/cli/deploy-staging.ts +692 -0
- package/cli/import-events.ts +628 -0
- package/cli/import-sites.ts +518 -0
- package/cli/index.ts +609 -0
- package/cli/init-db.ts +269 -0
- package/cli/migrate-to-durable-objects.ts +564 -0
- package/cli/migration-worker.ts +300 -0
- package/cli/performance-test.ts +588 -0
- package/cli/pg/client.ts +4 -0
- package/cli/pg/new-site.ts +153 -0
- package/cli/rollback-durable-objects.ts +622 -0
- package/cli/seed-data.ts +459 -0
- package/cli/setup.js +18 -0
- package/cli/setup.ts +463 -0
- package/cli/validate-migration.ts +200 -0
- package/cli/wrangler-migration.jsonc +28 -0
- package/db/adapter.ts +166 -0
- package/db/analytics_engine/client.ts +0 -0
- package/db/analytics_engine/sites.ts +0 -0
- package/db/client.ts +16 -0
- package/db/d1/client.ts +8 -0
- package/db/d1/drizzle.config.ts +35 -0
- package/db/d1/migrations/0000_true_maelstrom.sql +165 -0
- package/db/d1/migrations/0001_wonderful_bloodaxe.sql +12 -0
- package/db/d1/migrations/0002_late_frightful_four.sql +1 -0
- package/db/d1/migrations/0003_cuddly_obadiah_stane.sql +16 -0
- package/db/d1/migrations/0004_mute_stardust.sql +1 -0
- package/db/d1/migrations/0005_awesome_silvermane.sql +3 -0
- package/db/d1/migrations/0006_volatile_shriek.sql +2 -0
- package/db/d1/migrations/0007_superb_lila_cheney.sql +1 -0
- package/db/d1/migrations/0008_bitter_longshot.sql +17 -0
- package/db/d1/migrations/0009_wonderful_madame_masque.sql +28 -0
- package/db/d1/migrations/meta/0000_snapshot.json +1112 -0
- package/db/d1/migrations/meta/0001_snapshot.json +1187 -0
- package/db/d1/migrations/meta/0002_snapshot.json +1194 -0
- package/db/d1/migrations/meta/0003_snapshot.json +1296 -0
- package/db/d1/migrations/meta/0004_snapshot.json +1303 -0
- package/db/d1/migrations/meta/0005_snapshot.json +1325 -0
- package/db/d1/migrations/meta/0006_snapshot.json +1339 -0
- package/db/d1/migrations/meta/0007_snapshot.json +1347 -0
- package/db/d1/migrations/meta/0008_snapshot.json +1464 -0
- package/db/d1/migrations/meta/0009_snapshot.json +1648 -0
- package/db/d1/migrations/meta/_journal.json +76 -0
- package/db/d1/schema.ts +407 -0
- package/db/d1/sites.ts +374 -0
- package/db/d1/teamAiUsage.ts +101 -0
- package/db/d1/teams.ts +127 -0
- package/db/durable/drizzle.config.ts +8 -0
- package/db/durable/durableObjectClient.ts +480 -0
- package/db/durable/events.ts +100 -0
- package/db/durable/migrations/0000_fair_bucky.sql +38 -0
- package/db/durable/migrations/meta/0000_snapshot.json +278 -0
- package/db/durable/migrations/meta/_journal.json +13 -0
- package/db/durable/migrations/migrations.js +10 -0
- package/db/durable/schema.ts +5 -0
- package/db/durable/siteDurableObject.ts +1352 -0
- package/db/durable/types.ts +53 -0
- package/db/postgres/client.ts +13 -0
- package/db/postgres/drizzle.config.ts +12 -0
- package/db/postgres/migrations/0000_brainy_sprite.sql +116 -0
- package/db/postgres/migrations/meta/0000_snapshot.json +681 -0
- package/db/postgres/migrations/meta/_journal.json +13 -0
- package/db/postgres/schema.ts +145 -0
- package/db/postgres/sites.ts +118 -0
- package/db/tranformReports.ts +595 -0
- package/db/types.ts +55 -0
- package/endpoints/api_worker.tsx +1854 -0
- package/endpoints/site_do_worker.ts +11 -0
- package/index.d.ts +63 -0
- package/index.ts +83 -0
- package/lib/auth.ts +279 -0
- package/lib/geojson/world_countries.json +45307 -0
- package/lib/random_name.ts +41 -0
- package/lib/sendMail.ts +252 -0
- package/package.json +142 -0
- package/public/favicon.ico +0 -0
- package/public/images/android-chrome-192x192.png +0 -0
- package/public/images/android-chrome-512x512.png +0 -0
- package/public/images/apple-touch-icon.png +0 -0
- package/public/images/favicon-16x16.png +0 -0
- package/public/images/favicon-32x32.png +0 -0
- package/public/images/lytx_dark_dashboard.png +0 -0
- package/public/images/lytx_light_dashboard.png +0 -0
- package/public/images/safari-pinned-tab.svg +4 -0
- package/public/logo.png +0 -0
- package/public/site.webmanifest +26 -0
- package/public/sw.js +107 -0
- package/src/Document.tsx +86 -0
- package/src/api/ai_api.ts +1156 -0
- package/src/api/authMiddleware.ts +45 -0
- package/src/api/auth_api.ts +465 -0
- package/src/api/event_labels_api.ts +193 -0
- package/src/api/events_api.ts +210 -0
- package/src/api/queueWorker.ts +303 -0
- package/src/api/reports_api.ts +278 -0
- package/src/api/seed_api.ts +288 -0
- package/src/api/sites_api.ts +904 -0
- package/src/api/tag_api.ts +458 -0
- package/src/api/tag_api_v2.ts +289 -0
- package/src/api/team_api.ts +456 -0
- package/src/app/Dashboard.tsx +1339 -0
- package/src/app/Events.tsx +974 -0
- package/src/app/Explore.tsx +312 -0
- package/src/app/Layout.tsx +58 -0
- package/src/app/Settings.tsx +1302 -0
- package/src/app/components/DashboardCard.tsx +118 -0
- package/src/app/components/EditableCell.tsx +123 -0
- package/src/app/components/EventForm.tsx +93 -0
- package/src/app/components/MarketingFooter.tsx +49 -0
- package/src/app/components/MarketingNav.tsx +150 -0
- package/src/app/components/Nav.tsx +755 -0
- package/src/app/components/NewSiteSetup.tsx +298 -0
- package/src/app/components/SQLEditor.tsx +740 -0
- package/src/app/components/SiteSelector.tsx +126 -0
- package/src/app/components/SiteTag.tsx +42 -0
- package/src/app/components/SiteTagInstallCard.tsx +241 -0
- package/src/app/components/WorldMapCard.tsx +337 -0
- package/src/app/components/charts/ChartComponents.tsx +1481 -0
- package/src/app/components/charts/EventFunnel.tsx +45 -0
- package/src/app/components/charts/EventSummary.tsx +194 -0
- package/src/app/components/charts/SankeyFlows.tsx +72 -0
- package/src/app/components/marketing/CheckIcon.tsx +16 -0
- package/src/app/components/marketing/MarketingLayout.tsx +23 -0
- package/src/app/components/marketing/SectionHeading.tsx +35 -0
- package/src/app/components/reports/AskAiWorkspace.tsx +371 -0
- package/src/app/components/reports/CreateReportStarter.tsx +74 -0
- package/src/app/components/reports/DashboardRouteFiltersContext.tsx +14 -0
- package/src/app/components/reports/DashboardToolbar.tsx +154 -0
- package/src/app/components/reports/DashboardWorkspaceLayout.tsx +63 -0
- package/src/app/components/reports/DashboardWorkspaceShell.tsx +118 -0
- package/src/app/components/reports/ReportBuilderWorkspace.tsx +76 -0
- package/src/app/components/reports/custom/CustomReportBuilderPage.tsx +1667 -0
- package/src/app/components/reports/custom/ReportWidgetChart.tsx +297 -0
- package/src/app/components/reports/custom/buildWidgetSql.ts +151 -0
- package/src/app/components/reports/custom/chartPalettes.ts +18 -0
- package/src/app/components/reports/custom/types.ts +50 -0
- package/src/app/components/reports/reportBuilderMenuItems.ts +17 -0
- package/src/app/components/reports/useDashboardToolbarControls.tsx +235 -0
- package/src/app/components/ui/AlertBanner.tsx +101 -0
- package/src/app/components/ui/Button.tsx +55 -0
- package/src/app/components/ui/Card.tsx +80 -0
- package/src/app/components/ui/Input.tsx +72 -0
- package/src/app/components/ui/Link.tsx +23 -0
- package/src/app/components/ui/ReportBuilderMenu.tsx +246 -0
- package/src/app/components/ui/ThemeToggle.tsx +54 -0
- package/src/app/constants.ts +6 -0
- package/src/app/headers.ts +33 -0
- package/src/app/providers/AuthProvider.tsx +189 -0
- package/src/app/providers/ClientProviders.tsx +18 -0
- package/src/app/providers/QueryProvider.tsx +23 -0
- package/src/app/providers/ThemeProvider.tsx +88 -0
- package/src/app/utils/chartThemes.ts +146 -0
- package/src/app/utils/keybinds.ts +96 -0
- package/src/app/utils/media.tsx +24 -0
- package/src/client.tsx +114 -0
- package/src/config/createLytxAppConfig.ts +252 -0
- package/src/config/resourceNames.ts +88 -0
- package/src/db/index.ts +67 -0
- package/src/index.css +285 -0
- package/src/lib/featureFlags.ts +69 -0
- package/src/pages/GetStarted.tsx +290 -0
- package/src/pages/Home.tsx +268 -0
- package/src/pages/Login.tsx +283 -0
- package/src/pages/PrivacyPolicy.tsx +120 -0
- package/src/pages/Signup.tsx +267 -0
- package/src/pages/TermsOfService.tsx +126 -0
- package/src/pages/VerifyEmail.tsx +56 -0
- package/src/session/durableObject.ts +7 -0
- package/src/session/siteSchema.ts +86 -0
- package/src/session/types.ts +36 -0
- package/src/templates/README.md +80 -0
- package/src/templates/cleanFunctions.js +44 -0
- package/src/templates/embedFunctions.js +52 -0
- package/src/templates/lytx-shared.ts +662 -0
- package/src/templates/lytxpixel-core.ts +144 -0
- package/src/templates/lytxpixel.ts +267 -0
- package/src/templates/lytxpixelBrowser.js +634 -0
- package/src/templates/lytxpixelBrowser.mjs +634 -0
- package/src/templates/parseData.js +12 -0
- package/src/templates/script.ts +31 -0
- package/src/templates/template.tsx +50 -0
- package/src/templates/test.js +3 -0
- package/src/templates/trackWebEvents.ts +177 -0
- package/src/templates/vendors/clickcease.ts +8 -0
- package/src/templates/vendors/google.ts +174 -0
- package/src/templates/vendors/linkedin.ts +23 -0
- package/src/templates/vendors/meta.ts +56 -0
- package/src/templates/vendors/quantcast.ts +22 -0
- package/src/templates/vendors/simplfi.ts +7 -0
- package/src/types/app-context.ts +16 -0
- package/src/utilities/dashboardParams.ts +188 -0
- package/src/utilities/dashboardQueries.ts +537 -0
- package/src/utilities/dashboardTransforms.ts +167 -0
- package/src/utilities/dataValidation.ts +414 -0
- package/src/utilities/detector.ts +73 -0
- package/src/utilities/encrypt.ts +103 -0
- package/src/utilities/index.ts +13 -0
- package/src/utilities/parser.ts +117 -0
- package/src/utilities/performanceMonitoring.ts +570 -0
- package/src/utilities/route_interuptors.ts +24 -0
- package/src/worker.tsx +675 -0
- package/tsconfig.json +78 -0
- package/types/env.d.ts +16 -0
- package/types/rw.d.ts +7 -0
- package/types/shims.d.ts +53 -0
- package/types/vite.d.ts +19 -0
- package/vite/vite-plugin-pixel-bundle.ts +126 -0
- package/vite.config.ts +53 -0
- package/worker-configuration.d.ts +8401 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a random name by combining an adjective and a noun with a hyphen
|
|
3
|
+
* @returns A string in the format "adjective-noun"
|
|
4
|
+
*/
|
|
5
|
+
export function randomName() {
|
|
6
|
+
const adjectives: string[] = [
|
|
7
|
+
'agile', 'atomic', 'awesome', 'blazing', 'bold', 'brave', 'bright', 'brilliant', 'busy',
|
|
8
|
+
'clever', 'cosmic', 'crisp', 'curious', 'dapper', 'dazzling', 'dynamic', 'eager', 'elegant',
|
|
9
|
+
'epic', 'fancy', 'fast', 'fluent', 'flying', 'friendly', 'fuzzy', 'gentle', 'glowing',
|
|
10
|
+
'happy', 'harmonic', 'hidden', 'hyper', 'iconic', 'infinite', 'jolly', 'keen', 'logical',
|
|
11
|
+
'magical', 'mighty', 'mini', 'modern', 'nimble', 'noble', 'novel', 'optimal', 'parallel',
|
|
12
|
+
'peaceful', 'perfect', 'prime', 'quantum', 'quick', 'quiet', 'rapid', 'reactive', 'resilient',
|
|
13
|
+
'robust', 'rustic', 'sacred', 'secure', 'sharp', 'shiny', 'silent', 'sleek', 'smooth',
|
|
14
|
+
'solid', 'speedy', 'stellar', 'super', 'swift', 'tidy', 'tiny', 'turbo', 'vibrant',
|
|
15
|
+
'vigilant', 'virtual', 'vivid', 'wild', 'wise', 'witty', 'wonderful', 'zealous'
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const nouns: string[] = [
|
|
19
|
+
'algorithm', 'api', 'app', 'array', 'atom', 'badge', 'bash', 'bit', 'block', 'bot', 'byte',
|
|
20
|
+
'cache', 'cat', 'chip', 'cli', 'cloud', 'code', 'commit', 'compiler', 'component', 'cookie',
|
|
21
|
+
'core', 'crab', 'dashboard', 'data', 'db', 'dev', 'docker', 'docs', 'dom', 'dragon', 'edge',
|
|
22
|
+
'element', 'falcon', 'feature', 'ferret', 'file', 'flow', 'flux', 'fox', 'framework', 'function',
|
|
23
|
+
'gator', 'gem', 'git', 'graph', 'grid', 'hawk', 'heap', 'hook', 'hub', 'image', 'index',
|
|
24
|
+
'iterator', 'jam', 'java', 'json', 'key', 'lab', 'lambda', 'leaf', 'lib', 'link', 'lint',
|
|
25
|
+
'lion', 'list', 'logic', 'loop', 'lynx', 'map', 'matrix', 'mesh', 'method', 'micro', 'module',
|
|
26
|
+
'monkey', 'net', 'node', 'object', 'octo', 'otter', 'package', 'panda', 'parser', 'path',
|
|
27
|
+
'pattern', 'phoenix', 'pixel', 'plugin', 'pod', 'pointer', 'pony', 'portal', 'proxy', 'puma',
|
|
28
|
+
'python', 'query', 'queue', 'rabbit', 'raccoon', 'react', 'repo', 'request', 'response',
|
|
29
|
+
'router', 'ruby', 'script', 'server', 'service', 'shell', 'sloth', 'snake', 'socket', 'spark',
|
|
30
|
+
'spec', 'sql', 'stack', 'state', 'static', 'stream', 'string', 'struct', 'style', 'syntax',
|
|
31
|
+
'system', 'table', 'tag', 'task', 'template', 'tiger', 'token', 'tool', 'tree', 'type',
|
|
32
|
+
'ui', 'util', 'vector', 'view', 'vm', 'web', 'widget', 'wolf', 'worker', 'yaml', 'zebra'
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
// Get random index for adjective and noun
|
|
36
|
+
const randomAdjectiveIndex = Math.floor(Math.random() * adjectives.length);
|
|
37
|
+
const randomNounIndex = Math.floor(Math.random() * nouns.length);
|
|
38
|
+
|
|
39
|
+
// Combine the randomly selected adjective and noun with a hyphen
|
|
40
|
+
return `${adjectives[randomAdjectiveIndex]}-${nouns[randomNounIndex]}`;
|
|
41
|
+
}
|
package/lib/sendMail.ts
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
|
|
3
|
+
import { env } from "cloudflare:workers";
|
|
4
|
+
import { IS_DEV } from "rwsdk/constants";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_EMAIL_FROM_PLACEHOLDER = "noreply@example.com";
|
|
7
|
+
let email_from_override: string | undefined;
|
|
8
|
+
|
|
9
|
+
export function setEmailFromAddress(value?: string) {
|
|
10
|
+
email_from_override = value?.trim() || undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getFromAddress(): string {
|
|
14
|
+
const from = email_from_override ?? env.EMAIL_FROM ?? DEFAULT_EMAIL_FROM_PLACEHOLDER;
|
|
15
|
+
const normalized = from.trim();
|
|
16
|
+
|
|
17
|
+
if (!normalized || normalized.toLowerCase() === DEFAULT_EMAIL_FROM_PLACEHOLDER) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
"EMAIL_FROM is not configured. Set EMAIL_FROM in your environment bindings or pass createLytxApp({ env: { EMAIL_FROM: \"noreply@yourdomain.com\" } }).",
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return normalized;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface SendEmailOptions {
|
|
27
|
+
from: string;
|
|
28
|
+
to: string | string[];
|
|
29
|
+
subject: string;
|
|
30
|
+
html?: string;
|
|
31
|
+
text?: string;
|
|
32
|
+
bcc?: string | string[];
|
|
33
|
+
cc?: string | string[];
|
|
34
|
+
reply_to?: string | string[];
|
|
35
|
+
headers?: Record<string, string>;
|
|
36
|
+
tags?: Array<{ name: string; value: string }>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeRecipients(value: string | string[]): string[] {
|
|
40
|
+
return Array.isArray(value) ? value : [value];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function redactRecipient(value: string): string {
|
|
44
|
+
const [local, domain] = value.split("@");
|
|
45
|
+
if (!domain) return "invalid-recipient";
|
|
46
|
+
const local_prefix = local?.slice(0, 2) ?? "";
|
|
47
|
+
return `${local_prefix}***@${domain}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function logEmailDebug(event: string, details: Record<string, unknown>) {
|
|
51
|
+
if (!IS_DEV) return;
|
|
52
|
+
console.log("[email][dev]", event, details);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function sendEmail(options: SendEmailOptions) {
|
|
56
|
+
logEmailDebug("attempt", {
|
|
57
|
+
from: options.from,
|
|
58
|
+
to: normalizeRecipients(options.to).map(redactRecipient),
|
|
59
|
+
subject: options.subject,
|
|
60
|
+
hasResendApiKey: Boolean(env.RESEND_API_KEY),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const response = await fetch("https://api.resend.com/emails", {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: {
|
|
66
|
+
Authorization: `Bearer ${env.RESEND_API_KEY}`,
|
|
67
|
+
"Content-Type": "application/json",
|
|
68
|
+
},
|
|
69
|
+
body: JSON.stringify(options),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
const error = await response.text();
|
|
74
|
+
logEmailDebug("failed", {
|
|
75
|
+
status: response.status,
|
|
76
|
+
error,
|
|
77
|
+
});
|
|
78
|
+
throw new Error(`Resend API error: ${response.status} - ${error}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const data = await response.json();
|
|
82
|
+
logEmailDebug("sent", {
|
|
83
|
+
status: response.status,
|
|
84
|
+
response: data,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return data;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface EmailTemplateOptions {
|
|
91
|
+
title: string;
|
|
92
|
+
message: string;
|
|
93
|
+
buttonLabel: string;
|
|
94
|
+
buttonUrl: string;
|
|
95
|
+
footerNote: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function buildEmailTemplate({
|
|
99
|
+
title,
|
|
100
|
+
message,
|
|
101
|
+
buttonLabel,
|
|
102
|
+
buttonUrl,
|
|
103
|
+
footerNote,
|
|
104
|
+
}: EmailTemplateOptions) {
|
|
105
|
+
return `
|
|
106
|
+
<!doctype html>
|
|
107
|
+
<html>
|
|
108
|
+
<head>
|
|
109
|
+
<meta charset="utf-8" />
|
|
110
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
111
|
+
<title>${title}</title>
|
|
112
|
+
<style>
|
|
113
|
+
@media only screen and (max-width: 600px) {
|
|
114
|
+
.email-card {
|
|
115
|
+
border-radius: 10px !important;
|
|
116
|
+
}
|
|
117
|
+
.email-pad {
|
|
118
|
+
padding-left: 20px !important;
|
|
119
|
+
padding-right: 20px !important;
|
|
120
|
+
}
|
|
121
|
+
.email-title {
|
|
122
|
+
font-size: 22px !important;
|
|
123
|
+
}
|
|
124
|
+
.email-body {
|
|
125
|
+
font-size: 16px !important;
|
|
126
|
+
}
|
|
127
|
+
.email-button {
|
|
128
|
+
font-size: 15px !important;
|
|
129
|
+
padding: 12px 18px !important;
|
|
130
|
+
}
|
|
131
|
+
.email-footnote {
|
|
132
|
+
font-size: 13px !important;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
</style>
|
|
136
|
+
</head>
|
|
137
|
+
<body style="margin:0;padding:0;background-color:#3c3c3c;">
|
|
138
|
+
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="background-color:#3c3c3c;">
|
|
139
|
+
<tr>
|
|
140
|
+
<td align="center" style="padding:32px 16px;">
|
|
141
|
+
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" class="email-card" style="max-width:560px;width:100%;background-color:#484743;border:1px solid #575353;border-radius:12px;overflow:hidden;">
|
|
142
|
+
<tr>
|
|
143
|
+
<td class="email-pad" style="padding:20px 28px 0 28px;">
|
|
144
|
+
<div style="font-family:'Space Grotesk','Avenir Next','Trebuchet MS',sans-serif;font-size:12px;letter-spacing:0.2em;text-transform:uppercase;color:#d1d5db;">LYTX</div>
|
|
145
|
+
</td>
|
|
146
|
+
</tr>
|
|
147
|
+
<tr>
|
|
148
|
+
<td class="email-pad" style="padding:8px 28px 8px 28px;">
|
|
149
|
+
<h1 class="email-title" style="margin:0;font-family:'Space Grotesk','Avenir Next','Trebuchet MS',sans-serif;font-size:24px;line-height:1.3;color:#ffffff;">${title}</h1>
|
|
150
|
+
</td>
|
|
151
|
+
</tr>
|
|
152
|
+
<tr>
|
|
153
|
+
<td class="email-pad" style="padding:0 28px 20px 28px;">
|
|
154
|
+
<p class="email-body" style="margin:0;font-family:'Space Grotesk','Avenir Next','Trebuchet MS',sans-serif;font-size:16px;line-height:1.6;color:#e5e7eb;">${message}</p>
|
|
155
|
+
</td>
|
|
156
|
+
</tr>
|
|
157
|
+
<tr>
|
|
158
|
+
<td class="email-pad" align="left" style="padding:0 28px 24px 28px;">
|
|
159
|
+
<a href="${buttonUrl}" class="email-button" style="display:inline-block;font-family:'Space Grotesk','Avenir Next','Trebuchet MS',sans-serif;font-size:15px;letter-spacing:0.02em;text-decoration:none;background-color:#6b7280;color:#ffffff;padding:12px 20px;border-radius:8px;">${buttonLabel}</a>
|
|
160
|
+
</td>
|
|
161
|
+
</tr>
|
|
162
|
+
<tr>
|
|
163
|
+
<td class="email-pad" style="padding:0 28px 20px 28px;">
|
|
164
|
+
<p class="email-footnote" style="margin:0;font-family:'Space Grotesk','Avenir Next','Trebuchet MS',sans-serif;font-size:13px;line-height:1.6;color:#9ca3af;">${footerNote}</p>
|
|
165
|
+
</td>
|
|
166
|
+
</tr>
|
|
167
|
+
<tr>
|
|
168
|
+
<td class="email-pad" style="padding:0 28px 28px 28px;">
|
|
169
|
+
<p class="email-footnote" style="margin:0;font-family:'Space Grotesk','Avenir Next','Trebuchet MS',sans-serif;font-size:13px;line-height:1.6;color:#9ca3af;">If the button does not work, paste this link into your browser:</p>
|
|
170
|
+
<p class="email-footnote" style="margin:6px 0 0 0;font-family:'Space Grotesk','Avenir Next','Trebuchet MS',sans-serif;font-size:13px;line-height:1.6;color:#e5e7eb;word-break:break-word;overflow-wrap:anywhere;">${buttonUrl}</p>
|
|
171
|
+
</td>
|
|
172
|
+
</tr>
|
|
173
|
+
</table>
|
|
174
|
+
</td>
|
|
175
|
+
</tr>
|
|
176
|
+
</table>
|
|
177
|
+
</body>
|
|
178
|
+
</html>
|
|
179
|
+
`;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function buildEmailText({
|
|
183
|
+
title,
|
|
184
|
+
message,
|
|
185
|
+
buttonLabel,
|
|
186
|
+
buttonUrl,
|
|
187
|
+
footerNote,
|
|
188
|
+
}: EmailTemplateOptions) {
|
|
189
|
+
return [
|
|
190
|
+
title,
|
|
191
|
+
"",
|
|
192
|
+
message,
|
|
193
|
+
"",
|
|
194
|
+
`${buttonLabel}: ${buttonUrl}`,
|
|
195
|
+
"",
|
|
196
|
+
footerNote,
|
|
197
|
+
].join("\n");
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export async function sendTeamInviteEmail(email: string, link: string) {
|
|
201
|
+
const template = {
|
|
202
|
+
title: "Join your Lytx team",
|
|
203
|
+
message: "You have been invited to join a Lytx team. Accept the invite to access shared dashboards and insights.",
|
|
204
|
+
buttonLabel: "Accept invitation",
|
|
205
|
+
buttonUrl: link,
|
|
206
|
+
footerNote: "If you were not expecting this invitation, you can safely ignore this email.",
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
return sendEmail({
|
|
210
|
+
from: getFromAddress(),
|
|
211
|
+
to: email,
|
|
212
|
+
subject: "You've been invited to join a Lytx team",
|
|
213
|
+
html: buildEmailTemplate(template),
|
|
214
|
+
text: buildEmailText(template),
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export async function newAccountInviteEmail(email: string, link: string) {
|
|
219
|
+
const template = {
|
|
220
|
+
title: "Create your Lytx account",
|
|
221
|
+
message: "You have been invited to join a Lytx team. Create your account to get started with your new workspace.",
|
|
222
|
+
buttonLabel: "Create account",
|
|
223
|
+
buttonUrl: link,
|
|
224
|
+
footerNote: "If you were not expecting this invitation, you can safely ignore this email.",
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
return sendEmail({
|
|
228
|
+
from: getFromAddress(),
|
|
229
|
+
to: email,
|
|
230
|
+
subject: "You've been invited to join a Lytx team",
|
|
231
|
+
html: buildEmailTemplate(template),
|
|
232
|
+
text: buildEmailText(template),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export async function sendVerificationEmail(email: string, link: string) {
|
|
237
|
+
const template = {
|
|
238
|
+
title: "Verify your email",
|
|
239
|
+
message: "Welcome to Lytx. Confirm your email to finish setting up your account and access your dashboard.",
|
|
240
|
+
buttonLabel: "Verify email",
|
|
241
|
+
buttonUrl: link,
|
|
242
|
+
footerNote: "If you did not create a Lytx account, you can ignore this email.",
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
return sendEmail({
|
|
246
|
+
from: getFromAddress(),
|
|
247
|
+
to: email,
|
|
248
|
+
subject: "Verify your Lytx account",
|
|
249
|
+
html: buildEmailTemplate(template),
|
|
250
|
+
text: buildEmailText(template),
|
|
251
|
+
});
|
|
252
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lytx",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"types": "./index.d.ts",
|
|
10
|
+
"main": "index.ts",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/lytx-io/lytx.git",
|
|
15
|
+
"directory": "core"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/lytx-io/lytx#readme",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/lytx-io/lytx/issues"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"analytics",
|
|
23
|
+
"cloudflare",
|
|
24
|
+
"redwoodsdk",
|
|
25
|
+
"web-analytics"
|
|
26
|
+
],
|
|
27
|
+
"files": [
|
|
28
|
+
"src/",
|
|
29
|
+
"lib/",
|
|
30
|
+
"db/",
|
|
31
|
+
"cli/",
|
|
32
|
+
"endpoints/",
|
|
33
|
+
"public/",
|
|
34
|
+
"types/",
|
|
35
|
+
"vite/",
|
|
36
|
+
"index.ts",
|
|
37
|
+
"README.md",
|
|
38
|
+
"tsconfig.json",
|
|
39
|
+
"vite.config.ts",
|
|
40
|
+
"alchemy.run.ts",
|
|
41
|
+
".env.example",
|
|
42
|
+
"worker-configuration.d.ts",
|
|
43
|
+
"index.d.ts"
|
|
44
|
+
],
|
|
45
|
+
"exports": {
|
|
46
|
+
".": {
|
|
47
|
+
"types": "./index.d.ts",
|
|
48
|
+
"import": "./index.ts",
|
|
49
|
+
"default": "./index.ts"
|
|
50
|
+
},
|
|
51
|
+
"./worker": {
|
|
52
|
+
"types": "./src/worker.tsx",
|
|
53
|
+
"import": "./src/worker.tsx",
|
|
54
|
+
"default": "./src/worker.tsx"
|
|
55
|
+
},
|
|
56
|
+
"./db/durable/siteDurableObject": {
|
|
57
|
+
"types": "./db/durable/siteDurableObject.ts",
|
|
58
|
+
"import": "./db/durable/siteDurableObject.ts",
|
|
59
|
+
"default": "./db/durable/siteDurableObject.ts"
|
|
60
|
+
},
|
|
61
|
+
"./resource-names": {
|
|
62
|
+
"types": "./src/config/resourceNames.ts",
|
|
63
|
+
"import": "./src/config/resourceNames.ts",
|
|
64
|
+
"default": "./src/config/resourceNames.ts"
|
|
65
|
+
},
|
|
66
|
+
"./package.json": "./package.json"
|
|
67
|
+
},
|
|
68
|
+
"bin": {
|
|
69
|
+
"lytx-setup": "./cli/setup.js"
|
|
70
|
+
},
|
|
71
|
+
"scripts": {
|
|
72
|
+
"dev": "bun alchemy dev --stage dev",
|
|
73
|
+
"lint": "oxlint .",
|
|
74
|
+
"lint:fix": "oxlint . --fix",
|
|
75
|
+
"build": "bun run vite build",
|
|
76
|
+
"clean": "rm -rf node_modules/.vite .vite",
|
|
77
|
+
"db_analytics:migrate": "bunx drizzle-kit generate --config=db/postgres/drizzle.config.ts",
|
|
78
|
+
"db:generate": "bunx drizzle-kit generate --config=db/d1/drizzle.config.ts",
|
|
79
|
+
"db:studio": "bunx drizzle-kit studio --config=db/d1/drizzle.config.ts",
|
|
80
|
+
"import-sites": "bun run cli/import-sites.ts",
|
|
81
|
+
"import-events": "bun run cli/import-events.ts",
|
|
82
|
+
"bootstrap-admin": "bun run cli/bootstrap-admin.ts",
|
|
83
|
+
"setup": "bun run cli/setup.ts",
|
|
84
|
+
"deploy": "bun alchemy deploy --env-file .env.prod --stage prod"
|
|
85
|
+
},
|
|
86
|
+
"devDependencies": {
|
|
87
|
+
"@cloudflare/vite-plugin": "^1.13.10",
|
|
88
|
+
"@tailwindcss/vite": "^4.1.8",
|
|
89
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
90
|
+
"@types/bun": "^1.2.21",
|
|
91
|
+
"@types/mustache": "^4.2.2",
|
|
92
|
+
"@types/node": "^24.0.0",
|
|
93
|
+
"@types/react": "^19.1.7",
|
|
94
|
+
"@types/react-dom": "^19.1.6",
|
|
95
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
96
|
+
"babel-plugin-react-compiler": "^1.0.0",
|
|
97
|
+
"blinkx.io-sveltekit": "^1.0.10",
|
|
98
|
+
"dotenv": "^17.2.1",
|
|
99
|
+
"drizzle-kit": "^0.31.8",
|
|
100
|
+
"oxlint": "^1.48.0",
|
|
101
|
+
"tailwindcss": "^4.1.8",
|
|
102
|
+
"typescript": "^5.3.2",
|
|
103
|
+
"wrangler": "^4.59.3"
|
|
104
|
+
},
|
|
105
|
+
"dependencies": {
|
|
106
|
+
"@ai-sdk/openai-compatible": "^2.0.26",
|
|
107
|
+
"@ai-sdk/react": "^3.0.70",
|
|
108
|
+
"@fontsource/montserrat": "^5.2.8",
|
|
109
|
+
"@hono/swagger-ui": "^0.5.3",
|
|
110
|
+
"@hono/zod-openapi": "^1.2.1",
|
|
111
|
+
"@hono/zod-validator": "^0.7.6",
|
|
112
|
+
"@nivo/bar": "^0.99.0",
|
|
113
|
+
"@nivo/core": "^0.99.0",
|
|
114
|
+
"@nivo/funnel": "^0.99.0",
|
|
115
|
+
"@nivo/line": "^0.99.0",
|
|
116
|
+
"@nivo/pie": "^0.99.0",
|
|
117
|
+
"@nivo/sankey": "^0.99.0",
|
|
118
|
+
"@paralleldrive/cuid2": "^2.2.2",
|
|
119
|
+
"@tanstack/react-query": "^5.90.2",
|
|
120
|
+
"@tanstack/react-table": "^8.21.3",
|
|
121
|
+
"ai": "^6.0.68",
|
|
122
|
+
"alchemy": "^0.84.0",
|
|
123
|
+
"better-auth": "^1.2.9",
|
|
124
|
+
"better-sqlite3": "^11.10.0",
|
|
125
|
+
"country-flag-icons": "^1.6.12",
|
|
126
|
+
"device-detector-js": "^3.0.3",
|
|
127
|
+
"drizzle-orm": "^0.44.7",
|
|
128
|
+
"esbuild": "^0.27.2",
|
|
129
|
+
"hono": "^4.11.7",
|
|
130
|
+
"modern-monaco": "^0.3.7",
|
|
131
|
+
"mustache": "^4.2.0",
|
|
132
|
+
"postgres": "^3.4.3",
|
|
133
|
+
"prism-react-renderer": "^2.4.1",
|
|
134
|
+
"react": "^19.3.0-canary-4fdf7cf2-20251003",
|
|
135
|
+
"react-dom": "^19.3.0-canary-4fdf7cf2-20251003",
|
|
136
|
+
"react-server-dom-webpack": "^19.3.0-canary-4fdf7cf2-20251003",
|
|
137
|
+
"react-simple-maps": "^3.0.0",
|
|
138
|
+
"rwsdk": "1.0.0-beta.49",
|
|
139
|
+
"vite": "^7.3.1",
|
|
140
|
+
"zod": "^4.3.6"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|