v0.1.8 - 백엔드 초안 추가
This commit is contained in:
17
backend/src/config.js
Normal file
17
backend/src/config.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { config } from 'dotenv'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { z } from 'zod'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
|
||||
config({ path: path.join(__dirname, '..', '.env') })
|
||||
|
||||
const envSchema = z.object({
|
||||
PORT: z.coerce.number().default(3001),
|
||||
DB_FILE: z.string().default('./data/planner.sqlite'),
|
||||
CORS_ORIGIN: z.string().default('http://localhost:5173'),
|
||||
})
|
||||
|
||||
export const env = envSchema.parse(process.env)
|
||||
17
backend/src/db/client.js
Normal file
17
backend/src/db/client.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import Database from 'better-sqlite3'
|
||||
import { drizzle } from 'drizzle-orm/better-sqlite3'
|
||||
import { env } from '../config.js'
|
||||
import * as schema from './schema.js'
|
||||
|
||||
function ensureDatabaseDirectory(dbFile) {
|
||||
const absoluteDbPath = path.resolve(dbFile)
|
||||
fs.mkdirSync(path.dirname(absoluteDbPath), { recursive: true })
|
||||
return absoluteDbPath
|
||||
}
|
||||
|
||||
const sqlite = new Database(ensureDatabaseDirectory(env.DB_FILE))
|
||||
|
||||
export const db = drizzle(sqlite, { schema })
|
||||
export { sqlite }
|
||||
19
backend/src/db/schema.js
Normal file
19
backend/src/db/schema.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'
|
||||
|
||||
export const users = sqliteTable('users', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
email: text('email').notNull().unique(),
|
||||
passwordHash: text('password_hash').notNull(),
|
||||
nickname: text('nickname').notNull(),
|
||||
createdAt: integer('created_at', { mode: 'timestamp_ms' }).notNull(),
|
||||
updatedAt: integer('updated_at', { mode: 'timestamp_ms' }).notNull(),
|
||||
})
|
||||
|
||||
export const plannerEntries = sqliteTable('planner_entries', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
userId: integer('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
|
||||
entryDate: text('entry_date').notNull(),
|
||||
payload: text('payload').notNull(),
|
||||
createdAt: integer('created_at', { mode: 'timestamp_ms' }).notNull(),
|
||||
updatedAt: integer('updated_at', { mode: 'timestamp_ms' }).notNull(),
|
||||
})
|
||||
46
backend/src/server.js
Normal file
46
backend/src/server.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import Fastify from 'fastify'
|
||||
import cors from '@fastify/cors'
|
||||
import { env } from './config.js'
|
||||
import { sqlite } from './db/client.js'
|
||||
|
||||
const app = Fastify({
|
||||
logger: true,
|
||||
})
|
||||
|
||||
await app.register(cors, {
|
||||
origin: env.CORS_ORIGIN,
|
||||
credentials: true,
|
||||
})
|
||||
|
||||
app.get('/health', async () => {
|
||||
const version = sqlite.prepare('select sqlite_version() as version').get()
|
||||
|
||||
return {
|
||||
status: 'ok',
|
||||
service: 'ten-minute-planner-backend',
|
||||
database: {
|
||||
client: 'sqlite',
|
||||
version: version?.version ?? 'unknown',
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
app.get('/api/meta', async () => ({
|
||||
auth: 'planned',
|
||||
storage: 'sqlite',
|
||||
orm: 'drizzle',
|
||||
notes: [
|
||||
'회원가입 및 로그인 API는 다음 단계에서 추가 예정',
|
||||
'플래너 저장 API는 로컬 저장 레이어 분리 이후 연결 예정',
|
||||
],
|
||||
}))
|
||||
|
||||
try {
|
||||
await app.listen({
|
||||
port: env.PORT,
|
||||
host: '0.0.0.0',
|
||||
})
|
||||
} catch (error) {
|
||||
app.log.error(error)
|
||||
process.exit(1)
|
||||
}
|
||||
Reference in New Issue
Block a user