feat: xavis ai_platform 기능 이전 및 ncue 환경 전환

xavis 소스·DB 스키마·활용사례/F-Scan/프롬프트 라이브러리 등 기능 반영.
@xavis.co.kr → @ncue.net, 관리자 토큰 ncue-admin, 런타임 data/ Git 추적 제외.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dsyoon
2026-05-26 22:27:48 +09:00
parent 7bee72f287
commit 073a8343dd
84 changed files with 10883 additions and 1043 deletions

View File

@@ -106,11 +106,11 @@ BEFORE UPDATE ON ax_assignments
FOR EACH ROW
EXECUTE FUNCTION set_ax_assignments_updated_at();
-- OPS 이메일(@xavis.co.kr) 매직 링크 인증 — 이벤트 감사 로그
-- OPS 이메일(@ncue.net) 매직 링크 인증 — 이벤트 감사 로그
CREATE TABLE IF NOT EXISTS ops_email_auth_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(320) NOT NULL,
event_type VARCHAR(40) NOT NULL CHECK (event_type IN ('magic_link_requested', 'login_success', 'logout')),
event_type VARCHAR(40) NOT NULL CHECK (event_type IN ('magic_link_requested', 'login_success', 'logout', 'sessions_revoked')),
ip_address VARCHAR(45),
user_agent TEXT,
return_to TEXT,
@@ -126,11 +126,18 @@ CREATE TABLE IF NOT EXISTS ops_email_users (
email VARCHAR(320) PRIMARY KEY,
first_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
last_login_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
login_count INTEGER NOT NULL DEFAULT 0
login_count INTEGER NOT NULL DEFAULT 0,
sessions_revoked_at TIMESTAMPTZ
);
CREATE INDEX IF NOT EXISTS idx_ops_email_users_last_login ON ops_email_users (last_login_at DESC);
ALTER TABLE ops_email_users ADD COLUMN IF NOT EXISTS sessions_revoked_at TIMESTAMPTZ;
ALTER TABLE ops_email_auth_events DROP CONSTRAINT IF EXISTS ops_email_auth_events_event_type_check;
ALTER TABLE ops_email_auth_events ADD CONSTRAINT ops_email_auth_events_event_type_check
CHECK (event_type IN ('magic_link_requested', 'login_success', 'logout', 'sessions_revoked'));
-- 회의록 AI: 이메일(OPS 세션) 기반 사용자·프롬프트·회의 저장
CREATE TABLE IF NOT EXISTS meeting_ai_users (
email VARCHAR(320) PRIMARY KEY,
@@ -295,3 +302,124 @@ CREATE TABLE IF NOT EXISTS mgmt_perf_snapshots (
);
CREATE INDEX IF NOT EXISTS idx_mgmt_perf_snapshots_upload ON mgmt_perf_snapshots (upload_id);
-- AI 활용 사례: 일반 사용자 제출(글쓰기) — 본문 4섹션·썸네일·첨부, 제출자 이메일·시각
CREATE TABLE IF NOT EXISTS ai_use_case_submissions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
submitter_email VARCHAR(320) NOT NULL,
title TEXT NOT NULL,
situation TEXT NOT NULL DEFAULT '',
task_goal TEXT NOT NULL DEFAULT '',
action_taken TEXT NOT NULL DEFAULT '',
result_outcome TEXT NOT NULL DEFAULT '',
tags TEXT[] NOT NULL DEFAULT '{}',
thumbnail_relative_path TEXT,
thumbnail_files JSONB NOT NULL DEFAULT '[]',
attachment_files JSONB NOT NULL DEFAULT '[]',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_ai_use_case_submissions_created ON ai_use_case_submissions (created_at DESC);
CREATE INDEX IF NOT EXISTS idx_ai_use_case_submissions_submitter ON ai_use_case_submissions (submitter_email, created_at DESC);
CREATE OR REPLACE FUNCTION set_ai_use_case_submissions_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_ai_use_case_submissions_updated_at ON ai_use_case_submissions;
CREATE TRIGGER trg_ai_use_case_submissions_updated_at
BEFORE UPDATE ON ai_use_case_submissions
FOR EACH ROW
EXECUTE FUNCTION set_ai_use_case_submissions_updated_at();
-- 썸네일 다중 업로드(최대 5): 기존 DB 마이그레이션
ALTER TABLE ai_use_case_submissions
ADD COLUMN IF NOT EXISTS thumbnail_files JSONB NOT NULL DEFAULT '[]';
UPDATE ai_use_case_submissions
SET thumbnail_files = jsonb_build_array(
jsonb_build_object(
'originalName', COALESCE(NULLIF(regexp_replace(thumbnail_relative_path, '^.+/', ''), ''), 'thumbnail'),
'relativePath', thumbnail_relative_path
)
)
WHERE thumbnail_relative_path IS NOT NULL
AND btrim(thumbnail_relative_path) <> ''
AND (thumbnail_files IS NULL OR thumbnail_files = '[]'::jsonb);
-- AI 활용 사례 제출: 페이지뷰 조회수(제출자 본인 로그인 조회 제외) · 좋아요
ALTER TABLE ai_use_case_submissions
ADD COLUMN IF NOT EXISTS view_count INTEGER NOT NULL DEFAULT 0;
-- (legacy) 초기 unique 조회 추적용 — 현재 집계는 view_count 페이지뷰만 사용
CREATE TABLE IF NOT EXISTS ai_use_case_views (
submission_id UUID NOT NULL REFERENCES ai_use_case_submissions(id) ON DELETE CASCADE,
viewer_email VARCHAR(320) NOT NULL,
viewed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (submission_id, viewer_email)
);
CREATE INDEX IF NOT EXISTS idx_ai_use_case_views_submission ON ai_use_case_views (submission_id);
CREATE TABLE IF NOT EXISTS ai_use_case_submission_likes (
submission_id UUID NOT NULL REFERENCES ai_use_case_submissions(id) ON DELETE CASCADE,
user_email VARCHAR(320) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (submission_id, user_email)
);
CREATE INDEX IF NOT EXISTS idx_ai_use_case_submission_likes_submission
ON ai_use_case_submission_likes (submission_id);
-- 프롬프트 라이브러리: 공식 템플릿(company-prompts.json id) + 커뮤니티 공유 글에 대한 좋아요
CREATE TABLE IF NOT EXISTS prompt_likes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_email VARCHAR(320) NOT NULL,
target_kind VARCHAR(20) NOT NULL CHECK (target_kind IN ('official', 'community')),
target_id VARCHAR(200) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT uq_prompt_likes_user_target UNIQUE (user_email, target_kind, target_id)
);
CREATE INDEX IF NOT EXISTS idx_prompt_likes_target ON prompt_likes (target_kind, target_id);
CREATE INDEX IF NOT EXISTS idx_prompt_likes_user ON prompt_likes (user_email, created_at DESC);
-- 임직원이 공유한 프롬프트(라이브러리 커뮤니티) — author_email: POST 시 세션(OPS) 이메일, UI는 @ 앞 로컬부로 표시
CREATE TABLE IF NOT EXISTS prompt_community_entries (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
author_email VARCHAR(320) NOT NULL,
title VARCHAR(500) NOT NULL,
description TEXT NOT NULL DEFAULT '',
body TEXT NOT NULL,
tag VARCHAR(100) NOT NULL DEFAULT '기타',
is_published BOOLEAN NOT NULL DEFAULT true,
is_deleted BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_prompt_community_author ON prompt_community_entries (author_email, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_prompt_community_created ON prompt_community_entries (created_at DESC) WHERE is_deleted = false AND is_published = true;
CREATE OR REPLACE FUNCTION set_prompt_community_entries_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_prompt_community_entries_updated_at ON prompt_community_entries;
CREATE TRIGGER trg_prompt_community_entries_updated_at
BEFORE UPDATE ON prompt_community_entries
FOR EACH ROW
EXECUTE FUNCTION set_prompt_community_entries_updated_at();
-- 팀 공유 프롬프트: 프롬프트 관련·결과(샘플) 첨부(JSON 배열, 경로는 /uploads/…)
ALTER TABLE prompt_community_entries ADD COLUMN IF NOT EXISTS prompt_attachments JSONB NOT NULL DEFAULT '[]';
ALTER TABLE prompt_community_entries ADD COLUMN IF NOT EXISTS result_sample_attachments JSONB NOT NULL DEFAULT '[]';