update mariadb
This commit is contained in:
@@ -28,9 +28,6 @@ COPY --from=build /app/dist ./dist
|
|||||||
COPY server.js .
|
COPY server.js .
|
||||||
COPY favicon.png .
|
COPY favicon.png .
|
||||||
|
|
||||||
# 데이터베이스 디렉토리 생성
|
|
||||||
RUN mkdir -p db
|
|
||||||
|
|
||||||
# 환경변수 포트 노출 (Dokploy 등에서 PORT 주입 시 사용됨)
|
# 환경변수 포트 노출 (Dokploy 등에서 PORT 주입 시 사용됨)
|
||||||
ENV PORT=80
|
ENV PORT=80
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|||||||
99
package-lock.json
generated
99
package-lock.json
generated
@@ -13,6 +13,7 @@
|
|||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"leaflet": "1.9.4",
|
"leaflet": "1.9.4",
|
||||||
"lucide-react": "0.460.0",
|
"lucide-react": "0.460.0",
|
||||||
|
"mysql2": "^3.18.2",
|
||||||
"qrcode.react": "^4.2.0",
|
"qrcode.react": "^4.2.0",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
@@ -1459,6 +1460,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.9.tgz",
|
||||||
"integrity": "sha512-PD03/U8g1F9T9MI+1OBisaIARhSzeidsUjQaf51fOxrfjeiKN9bLVO06lHuHYjxdnqLWJijJHfqXPSJri2EM2A==",
|
"integrity": "sha512-PD03/U8g1F9T9MI+1OBisaIARhSzeidsUjQaf51fOxrfjeiKN9bLVO06lHuHYjxdnqLWJijJHfqXPSJri2EM2A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
}
|
}
|
||||||
@@ -1586,6 +1588,15 @@
|
|||||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
"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": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
@@ -2130,6 +2141,15 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"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": {
|
"node_modules/depd": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
@@ -2692,6 +2712,15 @@
|
|||||||
"node": ">=18"
|
"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": {
|
"node_modules/gensync": {
|
||||||
"version": "1.0.0-beta.2",
|
"version": "1.0.0-beta.2",
|
||||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||||
@@ -3062,6 +3091,12 @@
|
|||||||
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
|
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/isexe": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
@@ -3169,6 +3204,21 @@
|
|||||||
"yallist": "^3.0.2"
|
"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": {
|
"node_modules/lucide-react": {
|
||||||
"version": "0.460.0",
|
"version": "0.460.0",
|
||||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.460.0.tgz",
|
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.460.0.tgz",
|
||||||
@@ -3601,6 +3651,40 @@
|
|||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.11",
|
"version": "3.3.11",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||||
@@ -4636,6 +4720,21 @@
|
|||||||
"node": ">=0.10.0"
|
"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": {
|
"node_modules/sqlite": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/sqlite/-/sqlite-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/sqlite/-/sqlite-5.1.1.tgz",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"leaflet": "1.9.4",
|
"leaflet": "1.9.4",
|
||||||
"lucide-react": "0.460.0",
|
"lucide-react": "0.460.0",
|
||||||
|
"mysql2": "^3.18.2",
|
||||||
"qrcode.react": "^4.2.0",
|
"qrcode.react": "^4.2.0",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
|
|||||||
86
server.js
86
server.js
@@ -1,10 +1,7 @@
|
|||||||
|
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import sqlite3 from 'sqlite3';
|
import mysql from 'mysql2/promise';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
import { open } from 'sqlite';
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
@@ -19,54 +16,56 @@ app.use(cors());
|
|||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
// Database setup
|
// Database setup
|
||||||
let db;
|
let pool;
|
||||||
|
|
||||||
async function initializeDB() {
|
async function initializeDB() {
|
||||||
const dbDir = path.join(__dirname, 'db');
|
const dbConfig = {
|
||||||
if (!fs.existsSync(dbDir)) {
|
host: process.env.DB_HOST || 'truenas.site',
|
||||||
fs.mkdirSync(dbDir, { recursive: true });
|
user: process.env.DB_USER || 'wifi',
|
||||||
}
|
password: process.env.DB_PASSWORD || 'wifi12345',
|
||||||
|
database: process.env.DB_NAME || 'wifishare',
|
||||||
|
waitForConnections: true,
|
||||||
|
connectionLimit: 10,
|
||||||
|
queueLimit: 0
|
||||||
|
};
|
||||||
|
|
||||||
const dbPath = path.join(dbDir, 'wifi_markers.db');
|
try {
|
||||||
db = await open({
|
pool = mysql.createPool(dbConfig);
|
||||||
filename: dbPath,
|
|
||||||
driver: sqlite3.Database
|
|
||||||
});
|
|
||||||
|
|
||||||
// Enable WAL mode for concurrency
|
// Create table (MariaDB/MySQL syntax)
|
||||||
await db.exec('PRAGMA journal_mode = WAL;');
|
await pool.query(`
|
||||||
|
|
||||||
// Create table
|
|
||||||
await db.exec(`
|
|
||||||
CREATE TABLE IF NOT EXISTS markers (
|
CREATE TABLE IF NOT EXISTS markers (
|
||||||
id TEXT PRIMARY KEY,
|
id VARCHAR(50) PRIMARY KEY,
|
||||||
name TEXT NOT NULL,
|
name VARCHAR(255) NOT NULL,
|
||||||
ssid TEXT NOT NULL,
|
ssid VARCHAR(255) NOT NULL,
|
||||||
password TEXT,
|
password VARCHAR(255),
|
||||||
lat REAL NOT NULL,
|
lat DOUBLE NOT NULL,
|
||||||
lng REAL NOT NULL,
|
lng DOUBLE NOT NULL,
|
||||||
securityType TEXT NOT NULL,
|
securityType VARCHAR(50) NOT NULL,
|
||||||
iconType TEXT NOT NULL,
|
iconType VARCHAR(50) NOT NULL,
|
||||||
description TEXT,
|
description TEXT,
|
||||||
addedBy TEXT,
|
addedBy VARCHAR(100),
|
||||||
createdAt INTEGER,
|
createdAt BIGINT,
|
||||||
isPublic INTEGER DEFAULT 1
|
isPublic TINYINT DEFAULT 1
|
||||||
)
|
)
|
||||||
`);
|
`);
|
||||||
|
|
||||||
console.log(`[DB] Database initialized at: ${dbPath}`);
|
console.log(`[DB] MariaDB connected to ${dbConfig.host}/${dbConfig.database}`);
|
||||||
console.log('[DB] WAL mode enabled for concurrent writes.');
|
|
||||||
|
|
||||||
const count = await db.get('SELECT COUNT(*) as count FROM markers');
|
const [rows] = await pool.query('SELECT COUNT(*) as count FROM markers');
|
||||||
console.log(`[DB] Current Status: ${count.count} markers stored in database.`);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
app.get('/api/markers', async (req, res) => {
|
app.get('/api/markers', async (req, res) => {
|
||||||
const host = req.headers.host;
|
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 {
|
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`);
|
if (isDebug) console.log(`[API] Found ${markers.length} markers`);
|
||||||
// Convert isPublic from 0/1 to boolean
|
// Convert isPublic from 0/1 to boolean
|
||||||
const formattedMarkers = markers.map(m => ({
|
const formattedMarkers = markers.map(m => ({
|
||||||
@@ -82,12 +81,17 @@ app.get('/api/markers', async (req, res) => {
|
|||||||
|
|
||||||
app.post('/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;
|
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 {
|
try {
|
||||||
await db.run(`
|
await pool.query(`
|
||||||
INSERT OR REPLACE INTO markers (id, name, ssid, password, lat, lng, securityType, iconType, description, addedBy, createdAt, isPublic)
|
INSERT INTO markers (id, name, ssid, password, lat, lng, securityType, iconType, description, addedBy, createdAt, isPublic)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
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]);
|
`, [id, name, ssid, password, lat, lng, securityType, iconType, description || '', addedBy, createdAt, isPublic ? 1 : 0]);
|
||||||
|
|
||||||
if (isDebug) console.log(`[API] Successfully saved marker: ${id}`);
|
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) => {
|
app.delete('/api/markers/:id', async (req, res) => {
|
||||||
const { id } = req.params;
|
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 {
|
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}`);
|
if (isDebug) console.log(`[API] Successfully deleted marker: ${id}`);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user