feat: onboarding trial flow, Google sign-in, dark mode hotspot fixes

- Move trial creation from auth hook to dedicated /subscriptions/trial endpoint
  with eligibility checks (3-day Full Paket trial)
- Add animated onboarding progress (Remotion) with confetti on completion
- Register redirects to subscription page with welcome flow
- Add Google social login/signup support with config injection
- Improve hotspot overlay visibility in dark mode
- Show "Panele Git" on landing page when authenticated
- Turkish translations for API error messages
- Add toast utility wrapper, UUID generation for auth IDs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sase Dev
2026-02-15 14:53:57 +00:00
parent a5ee147675
commit 87d8eea298
50 changed files with 1116 additions and 298 deletions

View File

@@ -16,6 +16,8 @@ export class AuthModule implements OnModuleInit {
const databaseUrl = this.configService.get<string>("database.url")!;
const secret = this.configService.get<string>("auth.secret")!;
const baseUrl = this.configService.get<string>("auth.url")!;
createAuth(databaseUrl, secret, baseUrl);
const googleClientId = this.configService.get<string>("auth.googleClientId");
const googleClientSecret = this.configService.get<string>("auth.googleClientSecret");
createAuth(databaseUrl, secret, baseUrl, { googleClientId, googleClientSecret });
}
}

View File

@@ -1,13 +1,19 @@
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { drizzle } from "drizzle-orm/postgres-js";
import { eq, asc } from "drizzle-orm";
import postgres from "postgres";
import { randomUUID } from "crypto";
import * as schema from "../database/schema/core";
let authInstance: ReturnType<typeof betterAuth> | null = null;
export function createAuth(databaseUrl: string, secret: string, baseUrl: string) {
interface SocialCredentials {
googleClientId?: string;
googleClientSecret?: string;
}
export function createAuth(databaseUrl: string, secret: string, baseUrl: string, social?: SocialCredentials) {
if (authInstance) return authInstance;
const client = postgres(databaseUrl, { max: 5 });
@@ -18,6 +24,11 @@ export function createAuth(databaseUrl: string, secret: string, baseUrl: string)
provider: "pg",
usePlural: true,
}),
advanced: {
database: {
generateId: () => randomUUID(),
},
},
secret,
baseURL: baseUrl,
basePath: "/api/auth",
@@ -25,13 +36,16 @@ export function createAuth(databaseUrl: string, secret: string, baseUrl: string)
enabled: true,
minPasswordLength: 8,
},
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID || "",
clientSecret: process.env.GOOGLE_CLIENT_SECRET || "",
enabled: !!(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET),
},
},
...(social?.googleClientId && social?.googleClientSecret
? {
socialProviders: {
google: {
clientId: social.googleClientId,
clientSecret: social.googleClientSecret,
},
},
}
: {}),
session: {
cookieCache: {
enabled: true,
@@ -57,40 +71,6 @@ export function createAuth(databaseUrl: string, secret: string, baseUrl: string)
},
},
},
databaseHooks: {
user: {
create: {
after: async (user) => {
try {
// Find the lowest-tier plan for trial
const [plan] = await db
.select()
.from(schema.plans)
.where(eq(schema.plans.isActive, true))
.orderBy(asc(schema.plans.priceMonthly))
.limit(1);
if (!plan) return;
const now = new Date();
const endDate = new Date(now);
endDate.setDate(endDate.getDate() + 7);
await db.insert(schema.userSubscriptions).values({
userId: user.id,
planId: plan.id,
status: "trial",
billingPeriod: "monthly",
startDate: now,
endDate,
});
} catch (err) {
console.error("Failed to create trial subscription:", err);
}
},
},
},
},
trustedOrigins: [
...(process.env.CORS_ORIGIN || "http://localhost:3000").split(","),
"http://localhost:4000",