AI DEV portal 초기 커밋
Express+EJS+Postgres 사내 개발 포털. - Keycloak OIDC(사번 로그인), GitHub 감성 UI - 사이트 모음 + 프로젝트 공유/코멘트/스타 - DB: OpenEverest appdb portal schema (portal_app role) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
78
src/db.js
Normal file
78
src/db.js
Normal file
@@ -0,0 +1,78 @@
|
||||
// Postgres 풀 (OpenEverest appdb, search_path=portal). DATABASE_URL 로 연결.
|
||||
import pg from "pg";
|
||||
import { config } from "./config.js";
|
||||
|
||||
export const pool = new pg.Pool({ connectionString: config.databaseUrl });
|
||||
|
||||
export const q = (text, params) => pool.query(text, params);
|
||||
|
||||
// 로그인 시 사용자 upsert
|
||||
export async function upsertUser({ sabun, name, email }) {
|
||||
await q(
|
||||
`INSERT INTO users (sabun, name, email, avatar_seed, last_login)
|
||||
VALUES ($1,$2,$3,$1, now())
|
||||
ON CONFLICT (sabun) DO UPDATE
|
||||
SET name=EXCLUDED.name, email=EXCLUDED.email, last_login=now()`,
|
||||
[sabun, name, email || null]
|
||||
);
|
||||
}
|
||||
|
||||
// 프로젝트 목록 (스타 수 + 코멘트 수 + 소유자 이름 포함)
|
||||
export async function listProjects() {
|
||||
const { rows } = await q(
|
||||
`SELECT p.*, u.name AS owner_name,
|
||||
(SELECT count(*) FROM stars s WHERE s.project_id=p.id) AS star_count,
|
||||
(SELECT count(*) FROM comments c WHERE c.project_id=p.id) AS comment_count
|
||||
FROM projects p JOIN users u ON u.sabun=p.owner_sabun
|
||||
ORDER BY p.created_at DESC`
|
||||
);
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function getProject(id) {
|
||||
const { rows } = await q(
|
||||
`SELECT p.*, u.name AS owner_name,
|
||||
(SELECT count(*) FROM stars s WHERE s.project_id=p.id) AS star_count
|
||||
FROM projects p JOIN users u ON u.sabun=p.owner_sabun WHERE p.id=$1`,
|
||||
[id]
|
||||
);
|
||||
return rows[0];
|
||||
}
|
||||
|
||||
export async function listComments(projectId) {
|
||||
const { rows } = await q(
|
||||
`SELECT c.*, u.name AS author_name
|
||||
FROM comments c JOIN users u ON u.sabun=c.author_sabun
|
||||
WHERE c.project_id=$1 ORDER BY c.created_at ASC`,
|
||||
[projectId]
|
||||
);
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function addProject({ owner, title, description, repoUrl, appUrl, tags }) {
|
||||
const { rows } = await q(
|
||||
`INSERT INTO projects (owner_sabun, title, description, repo_url, app_url, tags)
|
||||
VALUES ($1,$2,$3,$4,$5,$6) RETURNING id`,
|
||||
[owner, title, description || "", repoUrl || "", appUrl || "", tags || ""]
|
||||
);
|
||||
return rows[0].id;
|
||||
}
|
||||
|
||||
export async function addComment({ projectId, author, body }) {
|
||||
await q(`INSERT INTO comments (project_id, author_sabun, body) VALUES ($1,$2,$3)`,
|
||||
[projectId, author, body]);
|
||||
}
|
||||
|
||||
export async function toggleStar({ projectId, sabun }) {
|
||||
const { rowCount } = await q(`DELETE FROM stars WHERE project_id=$1 AND sabun=$2`, [projectId, sabun]);
|
||||
if (rowCount === 0) {
|
||||
await q(`INSERT INTO stars (project_id, sabun) VALUES ($1,$2)`, [projectId, sabun]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function isStarred({ projectId, sabun }) {
|
||||
const { rowCount } = await q(`SELECT 1 FROM stars WHERE project_id=$1 AND sabun=$2`, [projectId, sabun]);
|
||||
return rowCount > 0;
|
||||
}
|
||||
Reference in New Issue
Block a user