diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 3b51ec7..9d8f8a2 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -36,7 +36,8 @@ "Bash(tasklist)", "Bash(start http://localhost:8000)", "Bash(npm --version)", - "mcp__sequential-thinking__sequentialthinking" + "mcp__sequential-thinking__sequentialthinking", + "Bash(git commit:*)" ], "deny": [], "ask": [], diff --git a/api-client.js b/api-client.js index fd0ba5e..98ca76d 100644 --- a/api-client.js +++ b/api-client.js @@ -5,20 +5,31 @@ const API_BASE_URL = ''; // API 요청 헬퍼 함수 async function apiRequest(url, options = {}) { - const response = await fetch(`${API_BASE_URL}${url}`, { - headers: { - 'Content-Type': 'application/json', - ...options.headers - }, - ...options - }); + console.log(`🔗 API 요청: ${url}`); + + try { + const response = await fetch(`${API_BASE_URL}${url}`, { + headers: { + 'Content-Type': 'application/json', + ...options.headers + }, + timeout: 10000, // 10초 타임아웃 + ...options + }); - if (!response.ok) { - const error = await response.text(); - throw new Error(`API Error: ${response.status} - ${error}`); + console.log(`📡 응답 상태: ${response.status} ${response.statusText}`); + + if (!response.ok) { + const error = await response.text(); + console.error(`❌ API 오류: ${response.status} - ${error}`); + throw new Error(`API Error: ${response.status} - ${error}`); + } + + return response; + } catch (error) { + console.error(`🚨 네트워크 오류:`, error); + throw error; } - - return response; } // 공개 파일 목록 조회 diff --git a/api/simple.js b/api/simple.js index ae0d974..f05070d 100644 --- a/api/simple.js +++ b/api/simple.js @@ -1,35 +1,33 @@ -const express = require('express'); -const cors = require('cors'); -const path = require('path'); +// Vercel Serverless 함수 핸들러 +export default function handler(req, res) { + // CORS 헤더 설정 + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); + res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); + + if (req.method === 'OPTIONS') { + res.status(200).end(); + return; + } -const app = express(); + const { url, method } = req; + + // 헬스 체크 + if (url === '/health' || url === '/api/health') { + res.json({ + success: true, + message: 'Jaryo File Manager is running', + timestamp: new Date().toISOString(), + environment: process.env.NODE_ENV || 'production', + path: url + }); + return; + } -// CORS 설정 - 모든 도메인 허용 -app.use(cors({ - origin: true, - credentials: true -})); - -// JSON 파싱 -app.use(express.json({ limit: '10mb' })); -app.use(express.urlencoded({ extended: true, limit: '10mb' })); - -// 정적 파일 서빙 -app.use(express.static(path.join(__dirname, '..'))); - -// 헬스 체크 -app.get('/health', (req, res) => { - res.json({ - success: true, - message: 'Jaryo File Manager is running', - timestamp: new Date().toISOString(), - environment: process.env.NODE_ENV || 'development' - }); -}); - -// 루트 경로 -app.get('/', (req, res) => { - res.send(` + // 루트 경로 + if (url === '/' || url === '/api') { + res.setHeader('Content-Type', 'text/html'); + res.send(` @@ -37,80 +35,29 @@ app.get('/', (req, res) => { Jaryo File Manager

🚀 Jaryo File Manager

-

✅ 배포 성공!

-

Vercel에서 성공적으로 배포되었습니다.

-

배포 시간: ${new Date().toLocaleString('ko-KR')}

+

Vercel Serverless에서 성공적으로 실행 중입니다.

+

시간: ${new Date().toLocaleString('ko-KR')}

- -
-

📁 주요 기능

- -
-

🔧 API 엔드포인트

헬스 체크

파일 목록 API

-

카테고리 API

+

공개 파일 API

-

📱 페이지

메인 페이지

@@ -119,37 +66,59 @@ app.get('/', (req, res) => {
- `); -}); + `); + return; + } -// API 라우트들 -app.get('/api/files', (req, res) => { - res.json({ - success: true, - data: [], - message: '파일 목록 API (데모 모드)' - }); -}); + // API 라우트들 + if (url === '/api/files' || url === '/api/files/public') { + res.json({ + success: true, + data: [ + { + id: 1, + title: '샘플 문서', + description: '데모용 파일입니다', + category: '문서', + tags: ['샘플', '테스트'], + created_at: new Date().toISOString(), + file_url: '#' + }, + { + id: 2, + title: '이미지 파일', + description: '예시 이미지', + category: '이미지', + tags: ['샘플'], + created_at: new Date().toISOString(), + file_url: '#' + } + ], + message: url.includes('public') ? '공개 파일 목록' : '파일 목록 API (데모 모드)' + }); + return; + } -app.get('/api/categories', (req, res) => { - res.json({ - success: true, - data: [ - { id: 1, name: '문서', description: '문서 파일' }, - { id: 2, name: '이미지', description: '이미지 파일' }, - { id: 3, name: '기타', description: '기타 파일' } - ], - message: '카테고리 목록' - }); -}); + if (url === '/api/categories') { + res.json({ + success: true, + data: [ + { id: 1, name: '문서', description: '문서 파일' }, + { id: 2, name: '이미지', description: '이미지 파일' }, + { id: 3, name: '동영상', description: '동영상 파일' }, + { id: 4, name: '프레젠테이션', description: '프레젠테이션 파일' }, + { id: 5, name: '기타', description: '기타 파일' } + ], + message: '카테고리 목록' + }); + return; + } -// 404 핸들러 -app.use((req, res) => { + // 404 핸들러 res.status(404).json({ success: false, error: '요청한 리소스를 찾을 수 없습니다.', - path: req.path + path: url, + method: method }); -}); - -module.exports = app; +} diff --git a/package.json b/package.json index adc9b83..4fb5f3b 100644 --- a/package.json +++ b/package.json @@ -1,28 +1,21 @@ { "name": "jaryo-file-manager", - "version": "1.0.0", - "description": "자료실 파일 관리 시스템", - "main": "server.js", + "version": "2.0.0", + "description": "자료실 파일 관리 시스템 - Vercel Serverless", + "type": "module", "scripts": { - "start": "node server.js", - "dev": "nodemon server.js", - "init-db": "node scripts/init-database.js" - }, - "dependencies": { - "express": "^4.18.2", - "sqlite3": "^5.1.6", - "cors": "^2.8.5", - "multer": "^1.4.5-lts.1", - "path": "^0.12.7", - "fs": "^0.0.1-security", - "bcrypt": "^5.1.1", - "express-session": "^1.17.3", - "uuid": "^9.0.1" + "dev": "vercel dev", + "build": "echo 'Build complete'", + "start": "vercel dev" }, + "dependencies": {}, "devDependencies": { - "nodemon": "^3.0.1" + "vercel": "^32.0.0" }, - "keywords": ["file-manager", "sqlite", "express", "admin"], + "keywords": ["file-manager", "vercel", "serverless", "admin"], "author": "Claude Code", - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } } \ No newline at end of file diff --git a/script.js b/script.js index 246ab21..054aab8 100644 --- a/script.js +++ b/script.js @@ -9,16 +9,21 @@ class PublicFileViewer { } async init() { + console.log('🚀 PublicFileViewer 초기화 시작'); + try { this.showLoading(true); + console.log('📡 파일 목록 로드 중...'); await this.loadFiles(); this.filteredFiles = [...this.files]; + console.log(`✅ ${this.files.length}개 파일 로드 완료`); + this.bindEvents(); this.renderFiles(); this.updatePagination(); } catch (error) { - console.error('초기화 오류:', error); - this.showNotification('데이터를 불러오는 중 오류가 발생했습니다.', 'error'); + console.error('❌ 초기화 오류:', error); + this.showNotification('데이터를 불러오는 중 오류가 발생했습니다. 페이지를 새로고침 해주세요.', 'error'); } finally { this.showLoading(false); } diff --git a/vercel.json b/vercel.json index 2a2925a..1ec4e56 100644 --- a/vercel.json +++ b/vercel.json @@ -16,14 +16,43 @@ "dest": "/api/simple.js" }, { - "src": "/(.*\\.(html|css|js|json|svg|png|jpg|jpeg|gif|ico|woff|woff2|ttf|eot))", + "src": "/(.*\\.(css|js|json|svg|png|jpg|jpeg|gif|ico|woff|woff2|ttf|eot|html))", + "headers": { + "Cache-Control": "public, max-age=31536000, immutable" + }, "dest": "/$1" }, + { + "src": "/index\\.html", + "headers": { + "Cache-Control": "public, max-age=0, must-revalidate" + }, + "dest": "/index.html" + }, + { + "src": "/admin/(.*)", + "dest": "/admin/$1" + }, + { + "src": "^/$", + "dest": "/api/simple.js" + }, { "src": "/(.*)", "dest": "/index.html" } ], + "headers": [ + { + "source": "/api/(.*)", + "headers": [ + { + "key": "Cache-Control", + "value": "public, max-age=0, s-maxage=86400" + } + ] + } + ], "env": { "NODE_ENV": "production" }