Harden platform security, reliability, and CI after full audit.

Close deployment IDOR and gate stub payment endpoints, add production
secret validation, health probes, Redis-backed build progress, GitHub
Actions CI, expanded tests, billing/k8s refactors, and ops runbooks.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
keyhan
2026-06-29 20:59:49 +03:30
parent a87bc49393
commit 837f0fa63f
83 changed files with 3953 additions and 1308 deletions
@@ -3,6 +3,7 @@
import { useState, useEffect, useRef, useCallback } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import api from '@/lib/api';
import { queryKeys } from '@/lib/query-keys';
import { notify } from '@/lib/notify';
import { useT } from '@/i18n/I18nProvider';
import { useLocalizedRouter } from '@/i18n/navigation';
@@ -114,7 +115,7 @@ export function ManagedServiceResourcesPanel({
const isDatabase = app.productType === 'managed_database';
const { data: walletData } = useQuery<{ balance: number }>({
queryKey: ['wallet'],
queryKey: queryKeys.walletBalance,
queryFn: () => api.get('/billing/wallet').then((r) => r.data),
});
@@ -181,7 +182,7 @@ export function ManagedServiceResourcesPanel({
queryClient.invalidateQueries({ queryKey: ['resources', serviceId] });
queryClient.invalidateQueries({ queryKey: ['db-storage', serviceId] });
queryClient.invalidateQueries({ queryKey: ['storage-usage', serviceId] });
queryClient.invalidateQueries({ queryKey: ['wallet'] });
queryClient.invalidateQueries({ queryKey: queryKeys.walletBalance });
setShowUpgradeConfirm(false);
setUpgradeCostData(null);
setPendingUpgradePayload(null);
+7 -1
View File
@@ -29,7 +29,13 @@ export function Providers({ children }: { children: React.ReactNode }) {
loadUser();
}, [loadUser]);
if (!mounted) return null;
if (!mounted) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-950">
<div className="w-8 h-8 border-2 border-primary-500 border-t-transparent rounded-full animate-spin" />
</div>
);
}
return (
<QueryClientProvider client={queryClient}>
+24
View File
@@ -0,0 +1,24 @@
import clsx from 'clsx';
type BadgeTone = 'gray' | 'green' | 'yellow' | 'red' | 'blue' | 'purple';
const toneClass: Record<BadgeTone, string> = {
gray: 'badge-gray',
green: 'badge-green',
yellow: 'badge-yellow',
red: 'badge-red',
blue: 'badge-blue',
purple: 'badge-purple',
};
export function Badge({
tone = 'gray',
className,
children,
}: {
tone?: BadgeTone;
className?: string;
children: React.ReactNode;
}) {
return <span className={clsx('badge', toneClass[tone], className)}>{children}</span>;
}
+24
View File
@@ -0,0 +1,24 @@
import clsx from 'clsx';
import type { ButtonHTMLAttributes } from 'react';
type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'ghost';
const variantClass: Record<ButtonVariant, string> = {
primary: 'btn-primary',
secondary: 'btn-secondary',
danger: 'btn-danger',
ghost: 'btn-ghost',
};
export function Button({
variant = 'primary',
className,
children,
...props
}: ButtonHTMLAttributes<HTMLButtonElement> & { variant?: ButtonVariant }) {
return (
<button type="button" className={clsx(variantClass[variant], className)} {...props}>
{children}
</button>
);
}
+10
View File
@@ -0,0 +1,10 @@
import clsx from 'clsx';
import type { HTMLAttributes } from 'react';
export function Card({ className, children, ...props }: HTMLAttributes<HTMLDivElement>) {
return (
<div className={clsx('card', className)} {...props}>
{children}
</div>
);
}
@@ -0,0 +1,23 @@
import type { ReactNode } from 'react';
export function PageHeader({
title,
description,
actions,
}: {
title: ReactNode;
description?: ReactNode;
actions?: ReactNode;
}) {
return (
<div className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between mb-8">
<div>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">{title}</h1>
{description ? (
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">{description}</p>
) : null}
</div>
{actions ? <div className="flex flex-wrap items-center gap-2">{actions}</div> : null}
</div>
);
}