From c0b3598f88c27682081c0c1a001dfa0f9e110fea Mon Sep 17 00:00:00 2001 From: LGram16 Date: Sun, 1 Mar 2026 14:42:49 +0900 Subject: [PATCH] update mariadb --- Dockerfile | 3 -- package-lock.json | 99 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + server.js | 100 ++++++++++++++++++++++++---------------------- 4 files changed, 152 insertions(+), 51 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5a295cb..5da3065 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,9 +28,6 @@ COPY --from=build /app/dist ./dist COPY server.js . COPY favicon.png . -# 데이터베이스 디렉토리 생성 -RUN mkdir -p db - # 환경변수 포트 노출 (Dokploy 등에서 PORT 주입 시 사용됨) ENV PORT=80 EXPOSE 80 diff --git a/package-lock.json b/package-lock.json index 48a62d0..1f6769c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "express": "^5.2.1", "leaflet": "1.9.4", "lucide-react": "0.460.0", + "mysql2": "^3.18.2", "qrcode.react": "^4.2.0", "react": "^19.2.4", "react-dom": "^19.2.4", @@ -1459,6 +1460,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.9.tgz", "integrity": "sha512-PD03/U8g1F9T9MI+1OBisaIARhSzeidsUjQaf51fOxrfjeiKN9bLVO06lHuHYjxdnqLWJijJHfqXPSJri2EM2A==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -1586,6 +1588,15 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2130,6 +2141,15 @@ "license": "MIT", "optional": true }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -2692,6 +2712,15 @@ "node": ">=18" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3062,6 +3091,12 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3169,6 +3204,21 @@ "yallist": "^3.0.2" } }, + "node_modules/lru.min": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz", + "integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, "node_modules/lucide-react": { "version": "0.460.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.460.0.tgz", @@ -3601,6 +3651,40 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/mysql2": { + "version": "3.18.2", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.18.2.tgz", + "integrity": "sha512-UfEShBFAZZEAKjySnTUuE7BgqkYT4mx+RjoJ5aqtmwSSvNcJ/QxQPXz/y3jSxNiVRedPfgccmuBtiPCSiEEytw==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.2", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.2", + "long": "^5.3.2", + "lru.min": "^1.1.4", + "named-placeholders": "^1.1.6", + "sql-escaper": "^1.3.3" + }, + "engines": { + "node": ">= 8.0" + }, + "peerDependencies": { + "@types/node": ">= 8" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", + "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", + "license": "MIT", + "dependencies": { + "lru.min": "^1.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -4636,6 +4720,21 @@ "node": ">=0.10.0" } }, + "node_modules/sql-escaper": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/sql-escaper/-/sql-escaper-1.3.3.tgz", + "integrity": "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=2.0.0", + "node": ">=12.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/mysqljs/sql-escaper?sponsor=1" + } + }, "node_modules/sqlite": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/sqlite/-/sqlite-5.1.1.tgz", diff --git a/package.json b/package.json index 3bf2301..ed2cd9c 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "express": "^5.2.1", "leaflet": "1.9.4", "lucide-react": "0.460.0", + "mysql2": "^3.18.2", "qrcode.react": "^4.2.0", "react": "^19.2.4", "react-dom": "^19.2.4", diff --git a/server.js b/server.js index 38ceda1..d8515ee 100644 --- a/server.js +++ b/server.js @@ -1,10 +1,7 @@ - import express from 'express'; -import sqlite3 from 'sqlite3'; +import mysql from 'mysql2/promise'; import cors from 'cors'; -import { open } from 'sqlite'; import path from 'path'; -import fs from 'fs'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); @@ -19,54 +16,56 @@ app.use(cors()); app.use(express.json()); // Database setup -let db; +let pool; async function initializeDB() { - const dbDir = path.join(__dirname, 'db'); - if (!fs.existsSync(dbDir)) { - fs.mkdirSync(dbDir, { recursive: true }); + const dbConfig = { + host: process.env.DB_HOST || 'truenas.site', + user: process.env.DB_USER || 'wifi', + password: process.env.DB_PASSWORD || 'wifi12345', + database: process.env.DB_NAME || 'wifishare', + waitForConnections: true, + connectionLimit: 10, + queueLimit: 0 + }; + + try { + pool = mysql.createPool(dbConfig); + + // Create table (MariaDB/MySQL syntax) + await pool.query(` + CREATE TABLE IF NOT EXISTS markers ( + id VARCHAR(50) PRIMARY KEY, + name VARCHAR(255) NOT NULL, + ssid VARCHAR(255) NOT NULL, + password VARCHAR(255), + lat DOUBLE NOT NULL, + lng DOUBLE NOT NULL, + securityType VARCHAR(50) NOT NULL, + iconType VARCHAR(50) NOT NULL, + description TEXT, + addedBy VARCHAR(100), + createdAt BIGINT, + isPublic TINYINT DEFAULT 1 + ) + `); + + console.log(`[DB] MariaDB connected to ${dbConfig.host}/${dbConfig.database}`); + + const [rows] = await pool.query('SELECT COUNT(*) as count FROM markers'); + console.log(`[DB] Current Status: ${rows[0].count} markers stored in database.`); + } catch (error) { + console.error('[DB] MariaDB connection failed:', error.message); + process.exit(1); } - - const dbPath = path.join(dbDir, 'wifi_markers.db'); - db = await open({ - filename: dbPath, - driver: sqlite3.Database - }); - - // Enable WAL mode for concurrency - await db.exec('PRAGMA journal_mode = WAL;'); - - // Create table - await db.exec(` - CREATE TABLE IF NOT EXISTS markers ( - id TEXT PRIMARY KEY, - name TEXT NOT NULL, - ssid TEXT NOT NULL, - password TEXT, - lat REAL NOT NULL, - lng REAL NOT NULL, - securityType TEXT NOT NULL, - iconType TEXT NOT NULL, - description TEXT, - addedBy TEXT, - createdAt INTEGER, - isPublic INTEGER DEFAULT 1 - ) - `); - - console.log(`[DB] Database initialized at: ${dbPath}`); - console.log('[DB] WAL mode enabled for concurrent writes.'); - - const count = await db.get('SELECT COUNT(*) as count FROM markers'); - console.log(`[DB] Current Status: ${count.count} markers stored in database.`); } // Routes app.get('/api/markers', async (req, res) => { const host = req.headers.host; - if (isDebug) console.log(`[API] GET /api/markers - Host: ${host} - Fetching from SQLite`); + if (isDebug) console.log(`[API] GET /api/markers - Host: ${host} - Fetching from MariaDB`); try { - const markers = await db.all('SELECT * FROM markers'); + const [markers] = await pool.query('SELECT * FROM markers'); if (isDebug) console.log(`[API] Found ${markers.length} markers`); // Convert isPublic from 0/1 to boolean const formattedMarkers = markers.map(m => ({ @@ -82,12 +81,17 @@ app.get('/api/markers', async (req, res) => { app.post('/api/markers', async (req, res) => { const { id, name, ssid, password, lat, lng, securityType, iconType, description, addedBy, createdAt, isPublic } = req.body; - if (isDebug) console.log(`[API] POST /api/markers - Saving marker: ${name} (${ssid}) to SQLite`); + if (isDebug) console.log(`[API] POST /api/markers - Saving marker: ${name} (${ssid}) to MariaDB`); try { - await db.run(` - INSERT OR REPLACE INTO markers (id, name, ssid, password, lat, lng, securityType, iconType, description, addedBy, createdAt, isPublic) + await pool.query(` + INSERT INTO markers (id, name, ssid, password, lat, lng, securityType, iconType, description, addedBy, createdAt, isPublic) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + name=VALUES(name), ssid=VALUES(ssid), password=VALUES(password), + lat=VALUES(lat), lng=VALUES(lng), securityType=VALUES(securityType), + iconType=VALUES(iconType), description=VALUES(description), + addedBy=VALUES(addedBy), createdAt=VALUES(createdAt), isPublic=VALUES(isPublic) `, [id, name, ssid, password, lat, lng, securityType, iconType, description || '', addedBy, createdAt, isPublic ? 1 : 0]); if (isDebug) console.log(`[API] Successfully saved marker: ${id}`); @@ -100,9 +104,9 @@ app.post('/api/markers', async (req, res) => { app.delete('/api/markers/:id', async (req, res) => { const { id } = req.params; - if (isDebug) console.log(`[API] DELETE /api/markers/${id} - Deleting marker from SQLite`); + if (isDebug) console.log(`[API] DELETE /api/markers/${id} - Deleting marker from MariaDB`); try { - await db.run('DELETE FROM markers WHERE id = ?', [id]); + await pool.query('DELETE FROM markers WHERE id = ?', [id]); if (isDebug) console.log(`[API] Successfully deleted marker: ${id}`); res.json({ success: true }); } catch (error) {