feat: add OpenTelemetry observability, Faro frontend monitoring, remove legacy Next.js app

- Add OpenTelemetry SDK with tracing, metrics, and OTLP export for API and worker
- Integrate Grafana Faro for frontend real-user monitoring
- Instrument health checks, database, Bull queues, and HTTP exception filter
- Add Grafana dashboard JSON for service overview
- Remove deprecated apps/web-nj (Next.js) — fully replaced by Vite+React frontend
- Update nginx config with OTEL collector proxy
- Minor UI fixes in schema viewer, category components, and subscription flow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sase Dev
2026-02-16 17:20:49 +00:00
parent 87d8eea298
commit dcbeb83ccc
113 changed files with 2745 additions and 7351 deletions

View File

@@ -4,6 +4,7 @@ import { Button } from "@sase/ui";
import { Input } from "@sase/ui";
import { Label } from "@sase/ui";
import { signIn } from "@/lib/auth-client";
import { startAction } from "@/lib/faro";
import { toast } from "@/lib/toast";
export const Route = createFileRoute("/_auth/login")({
@@ -18,6 +19,7 @@ function LoginPage() {
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
startAction("login", { method: "email" });
setLoading(true);
try {
@@ -104,7 +106,10 @@ function LoginPage() {
<Button
variant="outline"
className="w-full"
onClick={() => signIn.social({ provider: "google", callbackURL: "/dashboard/search" })}
onClick={() => {
startAction("login", { method: "google" });
signIn.social({ provider: "google", callbackURL: "/dashboard/search" });
}}
>
Google ile Giriş Yap
</Button>

View File

@@ -4,6 +4,7 @@ import { Button } from "@sase/ui";
import { Input } from "@sase/ui";
import { Label } from "@sase/ui";
import { signIn, signUp } from "@/lib/auth-client";
import { startAction } from "@/lib/faro";
import { toast } from "@/lib/toast";
import { ShieldCheck } from "lucide-react";
@@ -19,6 +20,7 @@ function RegisterPage() {
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
startAction("register", { method: "email" });
setLoading(true);
try {
@@ -107,7 +109,10 @@ function RegisterPage() {
<Button
variant="outline"
className="w-full"
onClick={() => signIn.social({ provider: "google", callbackURL: "/dashboard/subscription?welcome=1" })}
onClick={() => {
startAction("register", { method: "google" });
signIn.social({ provider: "google", callbackURL: "/dashboard/subscription?welcome=1" });
}}
>
Google ile Kayıt Ol
</Button>

View File

@@ -10,6 +10,7 @@ import {
AlertCircle,
} from "lucide-react";
import { api, ApiError } from "@/lib/api-client";
import { startAction } from "@/lib/faro";
import { toast } from "@/lib/toast";
// ─── HELPERS ──────────────────────────────────────────────────────────────────
@@ -125,6 +126,7 @@ function SearchPage() {
setError(null);
const cleanVin = vin.toUpperCase().trim();
startAction("vin-decode", { vin: cleanVin });
if (!isValidVin(cleanVin)) {
setError(
"Geçersiz şase. 17 karakter olmalı, I, O, Q harfleri kullanılamaz.",
@@ -234,6 +236,7 @@ function SearchPage() {
<button
type="button"
onClick={fillExampleVin}
data-faro-user-action-name="fill-example-vin"
className="text-muted-foreground underline underline-offset-4 transition hover:text-foreground"
>
Örnek şase deneyin &rarr;
@@ -348,6 +351,7 @@ function SearchPage() {
setVin(v.vin);
inputRef.current?.focus();
}}
data-faro-user-action-name="select-history-vin"
className="group flex items-center gap-4 rounded-2xl border border-border bg-background p-4 text-left transition-colors hover:bg-accent"
>
<div className="flex size-10 shrink-0 items-center justify-center rounded-xl bg-muted">

View File

@@ -1,6 +1,7 @@
import { lazy, Suspense, useEffect, useRef, useState } from "react";
import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { api } from "@/lib/api-client";
import { startAction } from "@/lib/faro";
import { useTranslation } from "@/lib/i18n";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@sase/ui";
import { Badge } from "@sase/ui";
@@ -232,6 +233,7 @@ function SubscriptionPage() {
return;
}
startAction("proceed-to-payment", { plan: selectedPlanKey, period: billingPeriod });
navigate({
to: "/dashboard/subscription/pay",
search: {
@@ -469,7 +471,10 @@ function SubscriptionPage() {
</Button>
<Button
variant="destructive"
onClick={() => cancelMutation.mutate()}
onClick={() => {
startAction("subscription-cancel");
cancelMutation.mutate();
}}
disabled={cancelMutation.isPending}
>
{cancelMutation.isPending
@@ -518,7 +523,10 @@ function SubscriptionPage() {
</ul>
<Button
className="bg-emerald-600 hover:bg-emerald-700 text-white"
onClick={() => trialMutation.mutate()}
onClick={() => {
startAction("trial-start");
trialMutation.mutate();
}}
disabled={trialMutation.isPending}
>
<ShieldCheck className="mr-2 h-4 w-4" />
@@ -549,6 +557,7 @@ function SubscriptionPage() {
<Button
variant={billingPeriod === "monthly" ? "default" : "outline"}
size="sm"
data-faro-user-action-name="billing-monthly"
onClick={() => setBillingPeriod("monthly")}
>
{t("common.monthly")}
@@ -556,6 +565,7 @@ function SubscriptionPage() {
<Button
variant={billingPeriod === "yearly" ? "default" : "outline"}
size="sm"
data-faro-user-action-name="billing-yearly"
onClick={() => setBillingPeriod("yearly")}
>
{t("common.yearly")}
@@ -575,6 +585,7 @@ function SubscriptionPage() {
return (
<Card
key={plan.key}
data-faro-user-action-name={`select-plan-${plan.key}`}
className={`relative cursor-pointer transition-all hover:shadow-md ${
isSelected ? "border-primary ring-2 ring-primary/20" : ""
} ${isCurrentPlan ? "border-green-500/50 bg-green-50/50 dark:bg-green-950/10" : ""} ${

View File

@@ -13,11 +13,11 @@ const SchemaViewer = lazy(() =>
function SchemaViewerFallback() {
return (
<div className="flex h-[600px] gap-4 rounded-lg border border-border">
<div className="flex w-[60%] items-center justify-center">
<div className="flex flex-col gap-4 rounded-lg border border-border md:h-[600px] md:flex-row">
<div className="flex h-[300px] items-center justify-center md:h-auto md:w-[60%]">
<Skeleton className="h-[80%] w-[80%]" />
</div>
<div className="w-[40%] space-y-3 p-4">
<div className="w-full space-y-3 p-4 md:w-[40%]">
<Skeleton className="h-6 w-1/2" />
{Array.from({ length: 8 }).map((_, i) => (
<Skeleton key={`schema-skel-${i}`} className="h-10 w-full" />

View File

@@ -452,13 +452,14 @@ function HomePage() {
<Link to="/login">
<Button
variant="outline"
data-faro-user-action-name="hero-login"
className="rounded-full border-border text-muted-foreground hover:bg-muted hover:text-foreground"
>
Giriş Yap
</Button>
</Link>
<Link to="/register">
<Button className="rounded-full bg-foreground text-background hover:bg-foreground/90">
<Button data-faro-user-action-name="hero-register" className="rounded-full bg-foreground text-background hover:bg-foreground/90">
7 Gün Ücretsiz Deneyin
</Button>
</Link>
@@ -594,6 +595,7 @@ function HomePage() {
</div>
<Button
onClick={handleVinSearch}
data-faro-user-action-name="hero-vin-search"
className="h-12 rounded-full bg-foreground px-8 text-sm font-semibold text-background hover:bg-foreground/90 sm:h-14 sm:text-base"
>
Ara
@@ -664,6 +666,7 @@ function HomePage() {
Şase numaranız yok mu?{" "}
<button
onClick={fillExampleVin}
data-faro-user-action-name="hero-free-trial"
className="text-foreground underline underline-offset-4 transition hover:text-foreground/80"
>
Örnek aramayı deneyin →