first commit
Made-with: Cursor
This commit is contained in:
23
sqlc/queries/api_keys.sql
Normal file
23
sqlc/queries/api_keys.sql
Normal file
@@ -0,0 +1,23 @@
|
||||
-- name: CreateAPIKey :one
|
||||
INSERT INTO api_keys (id, user_id, name, key_hash, scopes)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id, user_id, name, key_hash, scopes, created_at, revoked_at;
|
||||
|
||||
-- name: ListAPIKeysByUser :many
|
||||
SELECT id, name, scopes, created_at, revoked_at
|
||||
FROM api_keys
|
||||
WHERE user_id = $1
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
-- name: GetAPIKeyByHash :one
|
||||
SELECT id, user_id, name, key_hash, scopes, created_at, revoked_at
|
||||
FROM api_keys
|
||||
WHERE key_hash = $1 AND revoked_at IS NULL;
|
||||
|
||||
-- name: RevokeAPIKey :exec
|
||||
UPDATE api_keys SET revoked_at = now()
|
||||
WHERE id = $1 AND user_id = $2 AND revoked_at IS NULL;
|
||||
|
||||
-- name: RevokeAllUserAPIKeys :exec
|
||||
UPDATE api_keys SET revoked_at = now()
|
||||
WHERE user_id = $1 AND revoked_at IS NULL;
|
||||
13
sqlc/queries/attachments.sql
Normal file
13
sqlc/queries/attachments.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
-- name: ListAttachmentsByEvent :many
|
||||
SELECT id, event_id, file_url
|
||||
FROM event_attachments
|
||||
WHERE event_id = $1
|
||||
ORDER BY id ASC;
|
||||
|
||||
-- name: CreateAttachment :one
|
||||
INSERT INTO event_attachments (id, event_id, file_url)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id, event_id, file_url;
|
||||
|
||||
-- name: DeleteAttachment :exec
|
||||
DELETE FROM event_attachments WHERE id = $1 AND event_id = $2;
|
||||
25
sqlc/queries/attendees.sql
Normal file
25
sqlc/queries/attendees.sql
Normal file
@@ -0,0 +1,25 @@
|
||||
-- name: CreateAttendee :one
|
||||
INSERT INTO event_attendees (id, event_id, user_id, email, status)
|
||||
VALUES ($1, $2, $3, $4, 'pending')
|
||||
RETURNING id, event_id, user_id, email, status;
|
||||
|
||||
-- name: ListAttendeesByEvent :many
|
||||
SELECT id, event_id, user_id, email, status
|
||||
FROM event_attendees
|
||||
WHERE event_id = $1
|
||||
ORDER BY id ASC;
|
||||
|
||||
-- name: UpdateAttendeeStatus :one
|
||||
UPDATE event_attendees
|
||||
SET status = $2
|
||||
WHERE id = $1
|
||||
RETURNING id, event_id, user_id, email, status;
|
||||
|
||||
-- name: DeleteAttendee :exec
|
||||
DELETE FROM event_attendees
|
||||
WHERE id = $1 AND event_id = $2;
|
||||
|
||||
-- name: GetAttendeeByID :one
|
||||
SELECT id, event_id, user_id, email, status
|
||||
FROM event_attendees
|
||||
WHERE id = $1;
|
||||
3
sqlc/queries/audit_logs.sql
Normal file
3
sqlc/queries/audit_logs.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
-- name: CreateAuditLog :exec
|
||||
INSERT INTO audit_logs (entity_type, entity_id, action, user_id)
|
||||
VALUES ($1, $2, $3, $4);
|
||||
23
sqlc/queries/booking_links.sql
Normal file
23
sqlc/queries/booking_links.sql
Normal file
@@ -0,0 +1,23 @@
|
||||
-- name: CreateBookingLink :one
|
||||
INSERT INTO booking_links (id, calendar_id, token, duration_minutes, buffer_minutes, timezone, working_hours, active)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetBookingLinkByToken :one
|
||||
SELECT * FROM booking_links
|
||||
WHERE token = $1;
|
||||
|
||||
-- name: GetBookingLinkByCalendar :one
|
||||
SELECT * FROM booking_links
|
||||
WHERE calendar_id = $1;
|
||||
|
||||
-- name: UpdateBookingLink :one
|
||||
UPDATE booking_links
|
||||
SET duration_minutes = COALESCE($2, duration_minutes),
|
||||
buffer_minutes = COALESCE($3, buffer_minutes),
|
||||
timezone = COALESCE($4, timezone),
|
||||
working_hours = COALESCE($5, working_hours),
|
||||
active = COALESCE($6, active),
|
||||
updated_at = now()
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
23
sqlc/queries/calendar_members.sql
Normal file
23
sqlc/queries/calendar_members.sql
Normal file
@@ -0,0 +1,23 @@
|
||||
-- name: UpsertCalendarMember :exec
|
||||
INSERT INTO calendar_members (calendar_id, user_id, role)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (calendar_id, user_id) DO UPDATE SET role = $3;
|
||||
|
||||
-- name: GetCalendarMemberRole :one
|
||||
SELECT role FROM calendar_members
|
||||
WHERE calendar_id = $1 AND user_id = $2;
|
||||
|
||||
-- name: ListCalendarMembers :many
|
||||
SELECT cm.user_id, u.email, cm.role
|
||||
FROM calendar_members cm
|
||||
JOIN users u ON u.id = cm.user_id
|
||||
WHERE cm.calendar_id = $1 AND u.deleted_at IS NULL
|
||||
ORDER BY cm.role ASC;
|
||||
|
||||
-- name: DeleteCalendarMember :exec
|
||||
DELETE FROM calendar_members
|
||||
WHERE calendar_id = $1 AND user_id = $2;
|
||||
|
||||
-- name: DeleteAllCalendarMembers :exec
|
||||
DELETE FROM calendar_members
|
||||
WHERE calendar_id = $1;
|
||||
33
sqlc/queries/calendars.sql
Normal file
33
sqlc/queries/calendars.sql
Normal file
@@ -0,0 +1,33 @@
|
||||
-- name: CreateCalendar :one
|
||||
INSERT INTO calendars (id, owner_id, name, color, is_public)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id, owner_id, name, color, is_public, public_token, created_at, updated_at;
|
||||
|
||||
-- name: GetCalendarByID :one
|
||||
SELECT id, owner_id, name, color, is_public, public_token, created_at, updated_at
|
||||
FROM calendars
|
||||
WHERE id = $1 AND deleted_at IS NULL;
|
||||
|
||||
-- name: ListCalendarsByUser :many
|
||||
SELECT c.id, c.owner_id, c.name, c.color, c.is_public, c.created_at, c.updated_at, cm.role
|
||||
FROM calendars c
|
||||
JOIN calendar_members cm ON cm.calendar_id = c.id
|
||||
WHERE cm.user_id = $1 AND c.deleted_at IS NULL
|
||||
ORDER BY c.created_at ASC;
|
||||
|
||||
-- name: UpdateCalendar :one
|
||||
UPDATE calendars
|
||||
SET name = COALESCE(sqlc.narg('name')::TEXT, name),
|
||||
color = COALESCE(sqlc.narg('color')::TEXT, color),
|
||||
is_public = COALESCE(sqlc.narg('is_public')::BOOLEAN, is_public),
|
||||
updated_at = now()
|
||||
WHERE id = @id AND deleted_at IS NULL
|
||||
RETURNING id, owner_id, name, color, is_public, public_token, created_at, updated_at;
|
||||
|
||||
-- name: SoftDeleteCalendar :exec
|
||||
UPDATE calendars SET deleted_at = now(), updated_at = now()
|
||||
WHERE id = $1 AND deleted_at IS NULL;
|
||||
|
||||
-- name: SoftDeleteCalendarsByOwner :exec
|
||||
UPDATE calendars SET deleted_at = now(), updated_at = now()
|
||||
WHERE owner_id = $1 AND deleted_at IS NULL;
|
||||
46
sqlc/queries/contacts.sql
Normal file
46
sqlc/queries/contacts.sql
Normal file
@@ -0,0 +1,46 @@
|
||||
-- name: CreateContact :one
|
||||
INSERT INTO contacts (id, owner_id, first_name, last_name, email, phone, company, notes)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetContactByID :one
|
||||
SELECT * FROM contacts
|
||||
WHERE id = $1 AND owner_id = $2 AND deleted_at IS NULL;
|
||||
|
||||
-- name: ListContacts :many
|
||||
SELECT * FROM contacts
|
||||
WHERE owner_id = @owner_id
|
||||
AND deleted_at IS NULL
|
||||
AND (
|
||||
sqlc.narg('search')::TEXT IS NULL
|
||||
OR first_name ILIKE '%' || sqlc.narg('search')::TEXT || '%'
|
||||
OR last_name ILIKE '%' || sqlc.narg('search')::TEXT || '%'
|
||||
OR email ILIKE '%' || sqlc.narg('search')::TEXT || '%'
|
||||
OR company ILIKE '%' || sqlc.narg('search')::TEXT || '%'
|
||||
)
|
||||
AND (
|
||||
sqlc.narg('cursor_time')::TIMESTAMPTZ IS NULL
|
||||
OR (created_at, id) > (sqlc.narg('cursor_time')::TIMESTAMPTZ, sqlc.narg('cursor_id')::UUID)
|
||||
)
|
||||
ORDER BY created_at ASC, id ASC
|
||||
LIMIT @lim;
|
||||
|
||||
-- name: UpdateContact :one
|
||||
UPDATE contacts
|
||||
SET first_name = COALESCE(sqlc.narg('first_name'), first_name),
|
||||
last_name = COALESCE(sqlc.narg('last_name'), last_name),
|
||||
email = COALESCE(sqlc.narg('email'), email),
|
||||
phone = COALESCE(sqlc.narg('phone'), phone),
|
||||
company = COALESCE(sqlc.narg('company'), company),
|
||||
notes = COALESCE(sqlc.narg('notes'), notes),
|
||||
updated_at = now()
|
||||
WHERE id = @id AND owner_id = @owner_id AND deleted_at IS NULL
|
||||
RETURNING *;
|
||||
|
||||
-- name: SoftDeleteContact :exec
|
||||
UPDATE contacts SET deleted_at = now(), updated_at = now()
|
||||
WHERE id = $1 AND owner_id = $2 AND deleted_at IS NULL;
|
||||
|
||||
-- name: SoftDeleteContactsByOwner :exec
|
||||
UPDATE contacts SET deleted_at = now(), updated_at = now()
|
||||
WHERE owner_id = $1 AND deleted_at IS NULL;
|
||||
10
sqlc/queries/event_exceptions.sql
Normal file
10
sqlc/queries/event_exceptions.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
-- name: ListExceptionsByEvent :many
|
||||
SELECT id, event_id, exception_date, action
|
||||
FROM event_exceptions
|
||||
WHERE event_id = $1
|
||||
ORDER BY exception_date ASC;
|
||||
|
||||
-- name: CreateEventException :one
|
||||
INSERT INTO event_exceptions (id, event_id, exception_date, action)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id, event_id, exception_date, action;
|
||||
98
sqlc/queries/events.sql
Normal file
98
sqlc/queries/events.sql
Normal file
@@ -0,0 +1,98 @@
|
||||
-- name: CreateEvent :one
|
||||
INSERT INTO events (id, calendar_id, title, description, location, start_time, end_time, timezone, all_day, recurrence_rule, tags, created_by, updated_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetEventByID :one
|
||||
SELECT * FROM events
|
||||
WHERE id = $1 AND deleted_at IS NULL;
|
||||
|
||||
-- name: ListEventsInRange :many
|
||||
SELECT e.* FROM events e
|
||||
JOIN calendar_members cm ON cm.calendar_id = e.calendar_id
|
||||
WHERE cm.user_id = @user_id
|
||||
AND e.deleted_at IS NULL
|
||||
AND e.start_time < @range_end
|
||||
AND e.end_time > @range_start
|
||||
AND (sqlc.narg('calendar_id')::UUID IS NULL OR e.calendar_id = sqlc.narg('calendar_id')::UUID)
|
||||
AND (sqlc.narg('search')::TEXT IS NULL OR (e.title ILIKE '%' || sqlc.narg('search')::TEXT || '%' OR e.description ILIKE '%' || sqlc.narg('search')::TEXT || '%'))
|
||||
AND (sqlc.narg('tag')::TEXT IS NULL OR sqlc.narg('tag')::TEXT = ANY(e.tags))
|
||||
AND (
|
||||
sqlc.narg('cursor_time')::TIMESTAMPTZ IS NULL
|
||||
OR (e.start_time, e.id) > (sqlc.narg('cursor_time')::TIMESTAMPTZ, sqlc.narg('cursor_id')::UUID)
|
||||
)
|
||||
ORDER BY e.start_time ASC, e.id ASC
|
||||
LIMIT @lim;
|
||||
|
||||
-- name: ListRecurringEventsInRange :many
|
||||
SELECT e.* FROM events e
|
||||
JOIN calendar_members cm ON cm.calendar_id = e.calendar_id
|
||||
WHERE cm.user_id = @user_id
|
||||
AND e.deleted_at IS NULL
|
||||
AND e.recurrence_rule IS NOT NULL
|
||||
AND e.start_time <= @range_end
|
||||
AND (sqlc.narg('calendar_id')::UUID IS NULL OR e.calendar_id = sqlc.narg('calendar_id')::UUID)
|
||||
ORDER BY e.start_time ASC;
|
||||
|
||||
-- name: UpdateEvent :one
|
||||
UPDATE events
|
||||
SET title = COALESCE(sqlc.narg('title'), title),
|
||||
description = COALESCE(sqlc.narg('description'), description),
|
||||
location = COALESCE(sqlc.narg('location'), location),
|
||||
start_time = COALESCE(sqlc.narg('start_time'), start_time),
|
||||
end_time = COALESCE(sqlc.narg('end_time'), end_time),
|
||||
timezone = COALESCE(sqlc.narg('timezone'), timezone),
|
||||
all_day = COALESCE(sqlc.narg('all_day'), all_day),
|
||||
recurrence_rule = sqlc.narg('recurrence_rule'),
|
||||
tags = COALESCE(sqlc.narg('tags'), tags),
|
||||
updated_by = @updated_by,
|
||||
updated_at = now()
|
||||
WHERE id = @id AND deleted_at IS NULL
|
||||
RETURNING *;
|
||||
|
||||
-- name: SoftDeleteEvent :exec
|
||||
UPDATE events SET deleted_at = now(), updated_at = now()
|
||||
WHERE id = $1 AND deleted_at IS NULL;
|
||||
|
||||
-- name: SoftDeleteEventsByCalendar :exec
|
||||
UPDATE events SET deleted_at = now(), updated_at = now()
|
||||
WHERE calendar_id = $1 AND deleted_at IS NULL;
|
||||
|
||||
-- name: SoftDeleteEventsByCreator :exec
|
||||
UPDATE events SET deleted_at = now(), updated_at = now()
|
||||
WHERE created_by = $1 AND deleted_at IS NULL;
|
||||
|
||||
-- name: CheckEventOverlap :one
|
||||
SELECT EXISTS(
|
||||
SELECT 1 FROM events
|
||||
WHERE calendar_id = $1
|
||||
AND deleted_at IS NULL
|
||||
AND start_time < $3
|
||||
AND end_time > $2
|
||||
) AS overlap;
|
||||
|
||||
-- name: CheckEventOverlapForUpdate :one
|
||||
SELECT EXISTS(
|
||||
SELECT 1 FROM events
|
||||
WHERE calendar_id = $1
|
||||
AND deleted_at IS NULL
|
||||
AND start_time < $3
|
||||
AND end_time > $2
|
||||
FOR UPDATE
|
||||
) AS overlap;
|
||||
|
||||
-- name: ListEventsByCalendarInRange :many
|
||||
SELECT * FROM events
|
||||
WHERE calendar_id = $1
|
||||
AND deleted_at IS NULL
|
||||
AND start_time < $3
|
||||
AND end_time > $2
|
||||
ORDER BY start_time ASC;
|
||||
|
||||
-- name: ListRecurringEventsByCalendar :many
|
||||
SELECT * FROM events
|
||||
WHERE calendar_id = $1
|
||||
AND deleted_at IS NULL
|
||||
AND recurrence_rule IS NOT NULL
|
||||
AND start_time <= $2
|
||||
ORDER BY start_time ASC;
|
||||
16
sqlc/queries/refresh_tokens.sql
Normal file
16
sqlc/queries/refresh_tokens.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
-- name: CreateRefreshToken :one
|
||||
INSERT INTO refresh_tokens (id, user_id, token_hash, expires_at)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id, user_id, token_hash, expires_at, revoked_at, created_at;
|
||||
|
||||
-- name: GetRefreshTokenByHash :one
|
||||
SELECT id, user_id, token_hash, expires_at, revoked_at, created_at
|
||||
FROM refresh_tokens
|
||||
WHERE token_hash = $1 AND revoked_at IS NULL;
|
||||
|
||||
-- name: RevokeRefreshToken :exec
|
||||
UPDATE refresh_tokens SET revoked_at = now() WHERE token_hash = $1;
|
||||
|
||||
-- name: RevokeAllUserRefreshTokens :exec
|
||||
UPDATE refresh_tokens SET revoked_at = now()
|
||||
WHERE user_id = $1 AND revoked_at IS NULL;
|
||||
18
sqlc/queries/reminders.sql
Normal file
18
sqlc/queries/reminders.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
-- name: CreateReminder :one
|
||||
INSERT INTO event_reminders (id, event_id, minutes_before)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id, event_id, minutes_before;
|
||||
|
||||
-- name: ListRemindersByEvent :many
|
||||
SELECT id, event_id, minutes_before
|
||||
FROM event_reminders
|
||||
WHERE event_id = $1
|
||||
ORDER BY minutes_before ASC;
|
||||
|
||||
-- name: DeleteReminder :exec
|
||||
DELETE FROM event_reminders
|
||||
WHERE id = $1 AND event_id = $2;
|
||||
|
||||
-- name: DeleteRemindersByEvent :exec
|
||||
DELETE FROM event_reminders
|
||||
WHERE event_id = $1;
|
||||
25
sqlc/queries/users.sql
Normal file
25
sqlc/queries/users.sql
Normal file
@@ -0,0 +1,25 @@
|
||||
-- name: CreateUser :one
|
||||
INSERT INTO users (id, email, password_hash, timezone)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id, email, password_hash, timezone, is_active, created_at, updated_at;
|
||||
|
||||
-- name: GetUserByID :one
|
||||
SELECT id, email, password_hash, timezone, is_active, created_at, updated_at
|
||||
FROM users
|
||||
WHERE id = $1 AND deleted_at IS NULL;
|
||||
|
||||
-- name: GetUserByEmail :one
|
||||
SELECT id, email, password_hash, timezone, is_active, created_at, updated_at
|
||||
FROM users
|
||||
WHERE email = $1 AND deleted_at IS NULL;
|
||||
|
||||
-- name: UpdateUser :one
|
||||
UPDATE users
|
||||
SET timezone = COALESCE(sqlc.narg('timezone')::TEXT, timezone),
|
||||
updated_at = now()
|
||||
WHERE id = @id AND deleted_at IS NULL
|
||||
RETURNING id, email, password_hash, timezone, is_active, created_at, updated_at;
|
||||
|
||||
-- name: SoftDeleteUser :exec
|
||||
UPDATE users SET deleted_at = now(), is_active = false, updated_at = now()
|
||||
WHERE id = $1 AND deleted_at IS NULL;
|
||||
174
sqlc/schema.sql
Normal file
174
sqlc/schema.sql
Normal file
@@ -0,0 +1,174 @@
|
||||
-- Calendar & Contacts API Schema
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
|
||||
-- Users
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
email TEXT NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
timezone TEXT NOT NULL DEFAULT 'UTC',
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
deleted_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_users_email ON users (email) WHERE deleted_at IS NULL;
|
||||
|
||||
-- Refresh Tokens
|
||||
CREATE TABLE refresh_tokens (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
token_hash TEXT NOT NULL UNIQUE,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
revoked_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_refresh_tokens_user_id ON refresh_tokens (user_id);
|
||||
|
||||
-- API Keys
|
||||
CREATE TABLE api_keys (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
name TEXT NOT NULL,
|
||||
key_hash TEXT NOT NULL UNIQUE,
|
||||
scopes JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
revoked_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX idx_api_keys_user_id ON api_keys (user_id);
|
||||
|
||||
-- Calendars
|
||||
CREATE TABLE calendars (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
owner_id UUID NOT NULL REFERENCES users(id),
|
||||
name TEXT NOT NULL,
|
||||
color TEXT NOT NULL DEFAULT '#3B82F6',
|
||||
is_public BOOLEAN NOT NULL DEFAULT false,
|
||||
public_token TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
deleted_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX idx_calendars_owner_id ON calendars (owner_id);
|
||||
|
||||
-- Calendar Members
|
||||
CREATE TABLE calendar_members (
|
||||
calendar_id UUID NOT NULL REFERENCES calendars(id),
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
role TEXT NOT NULL CHECK (role IN ('owner', 'editor', 'viewer')),
|
||||
PRIMARY KEY (calendar_id, user_id)
|
||||
);
|
||||
|
||||
-- Events
|
||||
CREATE TABLE events (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
calendar_id UUID NOT NULL REFERENCES calendars(id),
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
location TEXT,
|
||||
start_time TIMESTAMPTZ NOT NULL,
|
||||
end_time TIMESTAMPTZ NOT NULL,
|
||||
timezone TEXT NOT NULL DEFAULT 'UTC',
|
||||
all_day BOOLEAN NOT NULL DEFAULT false,
|
||||
recurrence_rule TEXT,
|
||||
tags TEXT[] NOT NULL DEFAULT '{}',
|
||||
created_by UUID NOT NULL REFERENCES users(id),
|
||||
updated_by UUID NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
deleted_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX idx_events_calendar_start ON events (calendar_id, start_time);
|
||||
CREATE INDEX idx_events_start_time ON events (start_time);
|
||||
CREATE INDEX idx_events_tags ON events USING GIN (tags);
|
||||
|
||||
-- Event Reminders
|
||||
CREATE TABLE event_reminders (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
event_id UUID NOT NULL REFERENCES events(id),
|
||||
minutes_before INTEGER NOT NULL CHECK (minutes_before >= 0 AND minutes_before <= 10080)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_event_reminders_event_id ON event_reminders (event_id);
|
||||
|
||||
-- Event Attendees
|
||||
CREATE TABLE event_attendees (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
event_id UUID NOT NULL REFERENCES events(id),
|
||||
user_id UUID REFERENCES users(id),
|
||||
email TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'accepted', 'declined', 'tentative'))
|
||||
);
|
||||
|
||||
CREATE INDEX idx_event_attendees_event_id ON event_attendees (event_id);
|
||||
|
||||
-- Event Exceptions (for recurrence)
|
||||
CREATE TABLE event_exceptions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
event_id UUID NOT NULL REFERENCES events(id),
|
||||
exception_date DATE NOT NULL,
|
||||
action TEXT NOT NULL DEFAULT 'skip' CHECK (action IN ('skip'))
|
||||
);
|
||||
|
||||
CREATE INDEX idx_event_exceptions_event_id ON event_exceptions (event_id);
|
||||
|
||||
-- Event Attachments
|
||||
CREATE TABLE event_attachments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
event_id UUID NOT NULL REFERENCES events(id),
|
||||
file_url TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_event_attachments_event_id ON event_attachments (event_id);
|
||||
|
||||
-- Contacts
|
||||
CREATE TABLE contacts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
owner_id UUID NOT NULL REFERENCES users(id),
|
||||
first_name TEXT,
|
||||
last_name TEXT,
|
||||
email TEXT,
|
||||
phone TEXT,
|
||||
company TEXT,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
deleted_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX idx_contacts_owner_id ON contacts (owner_id);
|
||||
CREATE INDEX idx_contacts_search ON contacts (owner_id, first_name, last_name, email, company);
|
||||
|
||||
-- Booking Links
|
||||
CREATE TABLE booking_links (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
calendar_id UUID NOT NULL REFERENCES calendars(id),
|
||||
token TEXT NOT NULL UNIQUE,
|
||||
duration_minutes INTEGER NOT NULL,
|
||||
buffer_minutes INTEGER NOT NULL DEFAULT 0,
|
||||
timezone TEXT NOT NULL DEFAULT 'UTC',
|
||||
working_hours JSONB NOT NULL DEFAULT '{}',
|
||||
active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_booking_links_token ON booking_links (token);
|
||||
|
||||
-- Audit Logs
|
||||
CREATE TABLE audit_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
entity_type TEXT NOT NULL,
|
||||
entity_id UUID NOT NULL,
|
||||
action TEXT NOT NULL,
|
||||
user_id UUID NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_audit_logs_entity ON audit_logs (entity_type, entity_id);
|
||||
12
sqlc/sqlc.yaml
Normal file
12
sqlc/sqlc.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
version: "2"
|
||||
sql:
|
||||
- engine: "postgresql"
|
||||
queries: "queries/"
|
||||
schema: "schema.sql"
|
||||
gen:
|
||||
go:
|
||||
package: "repository"
|
||||
out: "../internal/repository"
|
||||
sql_package: "pgx/v5"
|
||||
emit_json_tags: true
|
||||
emit_empty_slices: true
|
||||
Reference in New Issue
Block a user