Database Yedeği, Uygulama Hata ayıklama
This commit is contained in:
265
COMPLETE_DATABASE_SETUP_20251230.sql
Normal file
265
COMPLETE_DATABASE_SETUP_20251230.sql
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
-- Düğün Salonu Yönetim Sistemi - COMPLETE DATABASE SETUP
|
||||||
|
-- Date: 2025-12-30
|
||||||
|
-- Includes: Base Schema, CMS, Expenses, Storage, Security (2FA, Logs, Rate Limits)
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- 0. EXTENSIONS
|
||||||
|
-- ==========================================
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- Required for Master OTP hashing
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- 1. BASE SCHEMA (Core Tables)
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
-- Profiles
|
||||||
|
create table if not exists profiles (
|
||||||
|
id uuid references auth.users on delete cascade not null primary key,
|
||||||
|
role text check (role in ('admin', 'staff')) default 'staff',
|
||||||
|
full_name text,
|
||||||
|
master_code_hash text, -- Added for Master OTP
|
||||||
|
created_at timestamp with time zone default timezone('utc'::text, now()) not null
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Customers
|
||||||
|
create table if not exists customers (
|
||||||
|
id uuid default uuid_generate_v4() primary key,
|
||||||
|
full_name text not null,
|
||||||
|
phone text,
|
||||||
|
email text,
|
||||||
|
city text,
|
||||||
|
district text,
|
||||||
|
address text,
|
||||||
|
notes text,
|
||||||
|
created_at timestamp with time zone default timezone('utc'::text, now()) not null
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Halls
|
||||||
|
create table if not exists halls (
|
||||||
|
id uuid default uuid_generate_v4() primary key,
|
||||||
|
name text not null,
|
||||||
|
capacity int,
|
||||||
|
description text,
|
||||||
|
features text[],
|
||||||
|
logo_url text, -- Added for Hall Logo
|
||||||
|
created_at timestamp with time zone default timezone('utc'::text, now()) not null
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Packages
|
||||||
|
create table if not exists packages (
|
||||||
|
id uuid default uuid_generate_v4() primary key,
|
||||||
|
name text not null,
|
||||||
|
description text,
|
||||||
|
price decimal(10,2) not null,
|
||||||
|
is_active boolean default true,
|
||||||
|
created_at timestamp with time zone default timezone('utc'::text, now()) not null
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Reservations
|
||||||
|
create table if not exists reservations (
|
||||||
|
id uuid default uuid_generate_v4() primary key,
|
||||||
|
hall_id uuid references halls(id) on delete set null,
|
||||||
|
customer_id uuid references customers(id) on delete set null,
|
||||||
|
package_id uuid references packages(id) on delete set null,
|
||||||
|
start_time timestamp with time zone not null,
|
||||||
|
end_time timestamp with time zone not null,
|
||||||
|
status text check (status in ('pending', 'confirmed', 'cancelled', 'completed')) default 'pending',
|
||||||
|
notes text,
|
||||||
|
price decimal(10,2), -- Snapshot price
|
||||||
|
groom_region text,
|
||||||
|
bride_region text,
|
||||||
|
created_by uuid references auth.users(id),
|
||||||
|
created_at timestamp with time zone default timezone('utc'::text, now()) not null
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Payments
|
||||||
|
create table if not exists payments (
|
||||||
|
id uuid default uuid_generate_v4() primary key,
|
||||||
|
reservation_id uuid references reservations(id) on delete cascade,
|
||||||
|
amount decimal(10,2) not null,
|
||||||
|
payment_type text check (payment_type in ('deposit', 'full', 'remaining')),
|
||||||
|
payment_method text check (payment_method in ('cash', 'credit_card', 'transfer')),
|
||||||
|
status text check (status in ('pending', 'paid', 'refunded')) default 'pending',
|
||||||
|
paid_at timestamp with time zone,
|
||||||
|
created_at timestamp with time zone default timezone('utc'::text, now()) not null
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- 2. SECURITY TABLES (New)
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
-- Auth Codes (2FA OTP)
|
||||||
|
CREATE TABLE IF NOT EXISTS public.auth_codes (
|
||||||
|
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||||
|
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
|
||||||
|
code TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL,
|
||||||
|
expires_at TIMESTAMP WITH TIME ZONE NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Auth Logs (Audit & Security)
|
||||||
|
CREATE TABLE IF NOT EXISTS public.auth_logs (
|
||||||
|
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||||
|
user_id UUID REFERENCES auth.users(id) ON DELETE SET NULL,
|
||||||
|
event_type TEXT NOT NULL,
|
||||||
|
ip_address TEXT,
|
||||||
|
user_agent TEXT,
|
||||||
|
details JSONB DEFAULT '{}'::jsonb,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Rate Limits
|
||||||
|
CREATE TABLE IF NOT EXISTS public.rate_limits (
|
||||||
|
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||||
|
ip_address TEXT NOT NULL,
|
||||||
|
action TEXT NOT NULL,
|
||||||
|
count INTEGER DEFAULT 1,
|
||||||
|
last_attempt TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL,
|
||||||
|
blocked_until TIMESTAMP WITH TIME ZONE
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rate_limits_ip_action ON public.rate_limits(ip_address, action);
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- 3. CMS Tables
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
-- Site Contents
|
||||||
|
CREATE TABLE IF NOT EXISTS public.site_contents (
|
||||||
|
key TEXT PRIMARY KEY,
|
||||||
|
value TEXT,
|
||||||
|
type TEXT DEFAULT 'text',
|
||||||
|
section TEXT,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL,
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Services
|
||||||
|
CREATE TABLE IF NOT EXISTS public.services (
|
||||||
|
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
image_url TEXT,
|
||||||
|
"order" INTEGER DEFAULT 0,
|
||||||
|
is_active BOOLEAN DEFAULT true,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Gallery
|
||||||
|
CREATE TABLE IF NOT EXISTS public.gallery (
|
||||||
|
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||||
|
image_url TEXT NOT NULL,
|
||||||
|
caption TEXT,
|
||||||
|
category TEXT DEFAULT 'Genel',
|
||||||
|
"order" INTEGER DEFAULT 0,
|
||||||
|
video_url TEXT,
|
||||||
|
is_hero BOOLEAN DEFAULT false,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- 4. EXPENSES MODULE
|
||||||
|
-- ==========================================
|
||||||
|
create table if not exists expense_categories (
|
||||||
|
id uuid default uuid_generate_v4() primary key,
|
||||||
|
name text not null,
|
||||||
|
description text,
|
||||||
|
created_at timestamp with time zone default timezone('utc'::text, now()) not null
|
||||||
|
);
|
||||||
|
|
||||||
|
create table if not exists expenses (
|
||||||
|
id uuid default uuid_generate_v4() primary key,
|
||||||
|
category_id uuid references expense_categories(id) on delete set null,
|
||||||
|
amount decimal(10,2) not null,
|
||||||
|
description text,
|
||||||
|
date timestamp with time zone default timezone('utc'::text, now()) not null,
|
||||||
|
created_by uuid references auth.users(id),
|
||||||
|
created_at timestamp with time zone default timezone('utc'::text, now()) not null
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- 5. RLS POLICIES (Consolidated)
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
-- Enable RLS on all tables
|
||||||
|
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE customers ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE halls ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE packages ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE reservations ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE payments ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE auth_codes ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE auth_logs ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE rate_limits ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE site_contents ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE services ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE gallery ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE expense_categories ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE expenses ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- Standard Authenticated Access (Staff/Admin)
|
||||||
|
create policy "Authenticated Users Full Access" on profiles for all using (auth.role() = 'authenticated');
|
||||||
|
create policy "Authenticated Users Full Access" on customers for all using (auth.role() = 'authenticated');
|
||||||
|
create policy "Authenticated Users Full Access" on halls for all using (auth.role() = 'authenticated');
|
||||||
|
create policy "Authenticated Users Full Access" on packages for all using (auth.role() = 'authenticated');
|
||||||
|
create policy "Authenticated Users Full Access" on reservations for all using (auth.role() = 'authenticated');
|
||||||
|
create policy "Authenticated Users Full Access" on payments for all using (auth.role() = 'authenticated');
|
||||||
|
create policy "Authenticated Users Full Access" on expense_categories for all using (auth.role() = 'authenticated');
|
||||||
|
create policy "Authenticated Users Full Access" on expenses for all using (auth.role() = 'authenticated');
|
||||||
|
|
||||||
|
-- Security Tables Policies
|
||||||
|
CREATE POLICY "Users can see and insert own codes" ON public.auth_codes
|
||||||
|
FOR ALL USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id);
|
||||||
|
|
||||||
|
-- Logging Policies (Open for Logic)
|
||||||
|
create policy "Enable insert for all users" on public.auth_logs for insert with check (true);
|
||||||
|
create policy "Admins can view all logs" on public.auth_logs for select using (
|
||||||
|
exists (select 1 from public.profiles where profiles.id = auth.uid() and profiles.role = 'admin')
|
||||||
|
);
|
||||||
|
|
||||||
|
create policy "Enable all on rate_limits" on public.rate_limits for all using (true) with check (true);
|
||||||
|
|
||||||
|
-- CMS Public Read Access
|
||||||
|
CREATE POLICY "Public Read Content" ON public.site_contents FOR SELECT TO anon, authenticated USING (true);
|
||||||
|
CREATE POLICY "Authenticated Write Content" ON public.site_contents FOR ALL TO authenticated USING (true);
|
||||||
|
|
||||||
|
CREATE POLICY "Public Read Services" ON public.services FOR SELECT TO anon, authenticated USING (is_active = true OR auth.role() = 'authenticated');
|
||||||
|
CREATE POLICY "Authenticated Write Services" ON public.services FOR ALL TO authenticated USING (true);
|
||||||
|
|
||||||
|
CREATE POLICY "Public Read Gallery" ON public.gallery FOR SELECT TO anon, authenticated USING (true);
|
||||||
|
CREATE POLICY "Authenticated Write Gallery" ON public.gallery FOR ALL TO authenticated USING (true);
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- 6. TRIGGERS & FUNCTIONS
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
create or replace function public.handle_new_user()
|
||||||
|
returns trigger as $$
|
||||||
|
begin
|
||||||
|
insert into public.profiles (id, full_name, role)
|
||||||
|
values (new.id, new.raw_user_meta_data->>'full_name', 'staff')
|
||||||
|
on conflict (id) do nothing;
|
||||||
|
return new;
|
||||||
|
end;
|
||||||
|
$$ language plpgsql security definer;
|
||||||
|
|
||||||
|
drop trigger if exists on_auth_user_created on auth.users;
|
||||||
|
create trigger on_auth_user_created
|
||||||
|
after insert on auth.users
|
||||||
|
for each row execute procedure public.handle_new_user();
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- 7. DEFAULT DATA
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
-- Master Code for Admins (Default: 123456)
|
||||||
|
UPDATE public.profiles
|
||||||
|
SET master_code_hash = crypt('271210220792', gen_salt('bf'))
|
||||||
|
WHERE role = 'admin';
|
||||||
|
|
||||||
|
-- Initial Site Content
|
||||||
|
INSERT INTO public.site_contents (key, value, type, section) VALUES
|
||||||
|
('site_title', 'Rüya Düğün Salonu', 'text', 'general'),
|
||||||
|
('hero_title', 'Hayallerinizdeki Düğün İçin', 'text', 'home'),
|
||||||
|
('contact_phone', '+90 555 123 45 67', 'text', 'contact')
|
||||||
|
ON CONFLICT (key) DO NOTHING;
|
||||||
BIN
lint_analysis.txt
Normal file
BIN
lint_analysis.txt
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useEffect, forwardRef, useImperativeHandle } from 'react'
|
import { useState, useEffect, forwardRef, useImperativeHandle, useCallback } from 'react'
|
||||||
import { getNewCaptcha, CaptchaResponse } from '@/app/actions/captcha'
|
import { getNewCaptcha, CaptchaResponse } from '@/app/actions/captcha'
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
@@ -20,7 +20,7 @@ export const Captcha = forwardRef<CaptchaRef, CaptchaProps>(({ onVerify }, ref)
|
|||||||
const [input, setInput] = useState("")
|
const [input, setInput] = useState("")
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
const fetchCaptcha = async () => {
|
const fetchCaptcha = useCallback(async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const data = await getNewCaptcha()
|
const data = await getNewCaptcha()
|
||||||
@@ -32,11 +32,11 @@ export const Captcha = forwardRef<CaptchaRef, CaptchaProps>(({ onVerify }, ref)
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}, [onVerify])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchCaptcha()
|
fetchCaptcha()
|
||||||
}, [])
|
}, [fetchCaptcha])
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
reset: fetchCaptcha
|
reset: fetchCaptcha
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export async function checkRateLimit(action: string): Promise<{ blocked: boolean
|
|||||||
if (ip === '::1') ip = '127.0.0.1'
|
if (ip === '::1') ip = '127.0.0.1'
|
||||||
|
|
||||||
// Clean up old limits
|
// Clean up old limits
|
||||||
const windowStart = new Date(Date.now() - WINDOW_MINUTES * 60 * 1000).toISOString()
|
// Clean up old limits (logic simplified, variable unused)
|
||||||
|
|
||||||
// Check current limit
|
// Check current limit
|
||||||
const { data: limit } = await supabase
|
const { data: limit } = await supabase
|
||||||
|
|||||||
Reference in New Issue
Block a user