| -- ============================================================================ | |
| -- Carboany Harness System β μ‘°μ§ λ° μ¬μ©μ μ€ν€λ§ | |
| -- Migration: 002_organizations_users | |
| -- | |
| -- μ€κ³ μμΉ: | |
| -- 1. UUID PK: λΆμ° μμ€ν νμ₯μ± ν보 | |
| -- 2. μν κΈ°λ° μ κ·Ό μ μ΄: role 컬λΌμΌλ‘ Admin/Sponsor/Participant/SME κ΅¬λΆ | |
| -- 3. OAuth μμ λ‘κ·ΈμΈ: λ³λ ν μ΄λΈλ‘ λ€μ€ νλ‘λ°μ΄λ μ§μ | |
| -- 4. API ν€ ν΄μ μ μ₯: μλ³Έ ν€λ λ°κΈ μ 1νλ§ λ ΈμΆ | |
| -- ============================================================================ | |
| -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| -- 1. μ‘°μ§ (Organizations) | |
| -- μ§μ체, 곡곡기κ΄, λκΈ°μ (μ€ν°μ), μ€μκΈ°μ (SME) ν΅ν© κ΄λ¦¬ | |
| -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| CREATE TABLE organizations ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| name VARCHAR(200) NOT NULL, | |
| type VARCHAR(20) NOT NULL | |
| CHECK (type IN ('municipality', 'public_corp', 'government', 'enterprise', 'sme')), | |
| parent_id UUID REFERENCES organizations(id), -- SME β μ€ν°μ κ³μΈ΅ κ΄κ³ (nullable) | |
| created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL, | |
| updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL | |
| ); | |
| CREATE INDEX idx_org_type ON organizations(type); | |
| CREATE INDEX idx_org_parent ON organizations(parent_id) WHERE parent_id IS NOT NULL; | |
| -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| -- 2. μ¬μ©μ (Users) | |
| -- 4κ° λλ©μΈ μν : admin, sponsor_mgr, participant, sme_mgr | |
| -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| CREATE TABLE users ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| email VARCHAR(255) UNIQUE NOT NULL, | |
| password_hash VARCHAR(255), -- OAuth μ μ© μ¬μ©μλ NULL κ°λ₯ | |
| name VARCHAR(100) NOT NULL, | |
| role VARCHAR(20) NOT NULL | |
| CHECK (role IN ('admin', 'sponsor_mgr', 'participant', 'sme_mgr')), | |
| org_id UUID REFERENCES organizations(id), | |
| phone VARCHAR(20), -- λ³ΈμΈ μΈμ¦μ© (λͺ¨λ°μΌ) | |
| avatar_url VARCHAR(500), | |
| created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL, | |
| updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL | |
| ); | |
| CREATE INDEX idx_users_email ON users(email); | |
| CREATE INDEX idx_users_org ON users(org_id) WHERE org_id IS NOT NULL; | |
| CREATE INDEX idx_users_role ON users(role); | |
| -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| -- 3. OAuth μμ λ‘κ·ΈμΈ μ°κ²° (OAuth Connections) | |
| -- Google, μΉ΄μΉ΄μ€, λ€μ΄λ², μ λΆ SSO λ€μ€ νλ‘λ°μ΄λ μ§μ | |
| -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| CREATE TABLE oauth_connections ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, | |
| provider VARCHAR(30) NOT NULL | |
| CHECK (provider IN ('google', 'kakao', 'naver', 'gov_sso')), | |
| provider_id VARCHAR(255) NOT NULL, | |
| created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL, | |
| UNIQUE (provider, provider_id) | |
| ); | |
| CREATE INDEX idx_oauth_user ON oauth_connections(user_id); | |
| -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| -- 4. API ν€ (API Keys) | |
| -- μΈλΆ μμ€ν μ°λμ©, ν΄μ μ μ₯, λ μ΄νΈ λ¦¬λ° κ΄λ¦¬ | |
| -- ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| CREATE TABLE api_keys ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, | |
| key_hash VARCHAR(255) NOT NULL, -- SHA-256 ν΄μ (μλ³Έ λ―Έμ μ₯) | |
| prefix VARCHAR(20) NOT NULL, -- μλ³μ© μ λμ¬ (μ: "cbny_live_") | |
| label VARCHAR(100), -- μ©λ λΌλ²¨ (μ: "μΈμ’ μ μ°λν€") | |
| rate_limit INT DEFAULT 10000, -- μΌμΌ νΈμΆ νλ | |
| expires_at TIMESTAMPTZ, -- NULLμ΄λ©΄ 무기ν | |
| is_active BOOLEAN DEFAULT true NOT NULL, | |
| created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL | |
| ); | |
| CREATE INDEX idx_apikeys_org ON api_keys(org_id); | |
| CREATE INDEX idx_apikeys_prefix ON api_keys(prefix); | |