to-do first

This commit is contained in:
2026-06-15 15:11:50 +09:00
commit d15899356e
34 changed files with 3731 additions and 0 deletions

71
src/todos.repo.js Normal file
View File

@@ -0,0 +1,71 @@
// Design Ref: §2 repo — todo DB CRUD. 외부 연결은 db.js pool 경유.
import { pool } from "./db.js";
import { hasPgcrypto, newId } from "./schema.js";
// Design Ref: §4 — 응답에서 내부 S3 key 는 제외하고 hasAttachment 만 노출.
function toDto(row) {
if (!row) return null;
return {
id: row.id,
title: row.title,
done: row.done,
attachment_name: row.attachment_name,
attachment_type: row.attachment_type,
hasAttachment: row.attachment_key != null,
created_at: row.created_at,
};
}
export async function list() {
const { rows } = await pool.query(
"SELECT * FROM todo ORDER BY created_at DESC"
);
return rows.map(toDto);
}
// Plan SC: SC-02 — pgcrypto 가용 시 DB default, 아니면 app-side UUID.
export async function create({ title, attachmentKey, attachmentName, attachmentType }) {
if (hasPgcrypto()) {
const { rows } = await pool.query(
`INSERT INTO todo (title, attachment_key, attachment_name, attachment_type)
VALUES ($1, $2, $3, $4) RETURNING *`,
[title, attachmentKey ?? null, attachmentName ?? null, attachmentType ?? null]
);
return toDto(rows[0]);
}
const { rows } = await pool.query(
`INSERT INTO todo (id, title, attachment_key, attachment_name, attachment_type)
VALUES ($1, $2, $3, $4, $5) RETURNING *`,
[newId(), title, attachmentKey ?? null, attachmentName ?? null, attachmentType ?? null]
);
return toDto(rows[0]);
}
// 첨부 업로드 후 키/메타를 기존 todo row 에 반영. Plan SC: SC-02
export async function setAttachment(id, { key, name, type }) {
const { rows } = await pool.query(
`UPDATE todo SET attachment_key = $2, attachment_name = $3, attachment_type = $4
WHERE id = $1 RETURNING *`,
[id, key, name, type]
);
return toDto(rows[0]);
}
export async function setDone(id, done) {
const { rows } = await pool.query(
"UPDATE todo SET done = $2 WHERE id = $1 RETURNING *",
[id, done]
);
return toDto(rows[0]); // 없으면 null
}
// 내부용: 삭제/다운로드 시 attachment_key 가 필요하므로 raw row 반환.
export async function getRaw(id) {
const { rows } = await pool.query("SELECT * FROM todo WHERE id = $1", [id]);
return rows[0] ?? null;
}
export async function remove(id) {
const { rowCount } = await pool.query("DELETE FROM todo WHERE id = $1", [id]);
return rowCount > 0;
}