Add waitlist schema for marketing (FRE-635)

- Created waitlist_signups and waitlist_events tables
- Supports email, name, source tracking, and status management
- Enables VIP supporter list for Product Hunt launch
- Migration 0002_chemical_shocker.sql generated
- Fixed brand color in product-hunt-assets-brief.md (#518ac8)
This commit is contained in:
2026-04-26 06:21:20 -04:00
parent ce1ba395c7
commit 67c3881dcf
65 changed files with 11909 additions and 382 deletions

View File

@@ -42,7 +42,7 @@ export async function recordKPI(
metadata: metadata ? JSON.stringify(metadata) : null,
};
const result = await db.insert(kpiSnapshots).values(snapshot).returning();
return result[0];
return result[0]!;
}
export async function getLatestKPI(

View File

@@ -1,3 +1,3 @@
export { useAuth, useAuthActions, requireAuth, ClerkProvider } from './clerk-provider';
export { useAuth, useAuthActions, RequireAuth, ClerkProvider } from './clerk-provider';
export { getClerk, loadClerk, getClerkUrls } from './clerk-client';
export type { User, UserRole, Team, TeamMember, Project, ProjectStatus, ProjectCollaborator, AuthState, ClerkConfig } from './types';

View File

@@ -3,6 +3,6 @@ export {
AuthActionsContext,
useAuth,
useAuthActions,
requireAuth,
RequireAuth,
ClerkProvider as AuthProvider,
} from './clerk-provider';

View File

@@ -189,7 +189,7 @@ export class WebSocketConnection implements WebSocketConnectionWithPresence {
/**
* Send auth token via awareness state after connection
* Security: Token not exposed in URL/logs, only sent over secure WebSocket
* Security: Only send userId and projectId (not full JWT) to avoid credential broadcast
*/
private sendAuthToken(): void {
if (!this.provider || !this.provider.awareness) {
@@ -197,13 +197,19 @@ export class WebSocketConnection implements WebSocketConnectionWithPresence {
return;
}
// Store token in awareness state (sent to server, not in URL)
this.provider.awareness.setLocalStateField('auth', {
token: this.options.authToken,
timestamp: Date.now(),
});
console.log('[WebSocketConnection] Auth token sent via awareness state');
// Parse JWT to extract userId and projectId (don't broadcast full token)
try {
const jwtPayload = this.options.authToken.split('.')[1];
const payload = JSON.parse(atob(jwtPayload || ''));
this.provider.awareness.setLocalStateField('auth', {
userId: payload.userId,
projectId: payload.projectId,
timestamp: Date.now(),
});
console.log('[WebSocketConnection] Auth credentials sent via awareness (userId, projectId only)');
} catch (error) {
console.error('[WebSocketConnection] Failed to parse JWT token:', error);
}
}
/**