From fbd5f94a43c12351807ff7795bea36d9e65456b5 Mon Sep 17 00:00:00 2001 From: Cuishibing <643237029@qq.com> Date: Sun, 22 Mar 2026 22:10:41 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=87=E6=8D=A2=E4=B8=BAMariaDB?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E5=AD=98=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/auth/login/route.ts | 39 +++----- app/api/auth/me/route.ts | 34 ++----- app/api/auth/register/route.ts | 52 +++-------- app/api/districts/route.ts | 18 ++-- app/api/houses/[id]/route.ts | 163 +++++++++++++++++---------------- app/api/houses/route.ts | 115 ++++++++++------------- app/api/owner/houses/route.ts | 81 ++++++---------- lib/db.ts | 14 +++ package-lock.json | 128 +++++++++++++++++++++++++- package.json | 1 + 10 files changed, 346 insertions(+), 299 deletions(-) create mode 100644 lib/db.ts diff --git a/app/api/auth/login/route.ts b/app/api/auth/login/route.ts index 4b14788..7372fcb 100644 --- a/app/api/auth/login/route.ts +++ b/app/api/auth/login/route.ts @@ -1,26 +1,6 @@ import { NextRequest, NextResponse } from 'next/server'; -import fs from 'fs/promises'; -import path from 'path'; import crypto from 'crypto'; - -const USERS_FILE = path.join(process.cwd(), 'data/users.json'); - -interface User { - id: string; - username: string; - passwordHash: string; - token: string; - createdAt: string; -} - -async function readUsers(): Promise { - try { - const data = await fs.readFile(USERS_FILE, 'utf-8'); - return JSON.parse(data); - } catch { - return []; - } -} +import pool from '@/lib/db'; function hashPassword(password: string): string { return crypto.createHash('sha256').update(password).digest('hex'); @@ -31,6 +11,7 @@ function generateToken(): string { } export async function POST(request: NextRequest) { + let connection; try { const { username, password } = await request.json(); @@ -38,18 +19,18 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: '用户名和密码不能为空' }, { status: 400 }); } - const users = await readUsers(); - const user = users.find(u => u.username === username); + connection = await pool.getConnection(); - if (!user || user.passwordHash !== hashPassword(password)) { + const [rows] = await connection.query('SELECT * FROM users WHERE username = ?', [username]); + + if (rows.length === 0 || rows[0].password_hash !== hashPassword(password)) { return NextResponse.json({ error: '用户名或密码错误' }, { status: 401 }); } const newToken = generateToken(); - user.token = newToken; - await fs.writeFile(USERS_FILE, JSON.stringify(users, null, 2)); + await connection.query('UPDATE users SET token = ? WHERE id = ?', [newToken, rows[0].id]); - const response = NextResponse.json({ success: true, username: user.username }); + const response = NextResponse.json({ success: true, username }); response.cookies.set('auth_token', newToken, { httpOnly: true, secure: false, @@ -62,5 +43,7 @@ export async function POST(request: NextRequest) { } catch (error) { console.error('Login error:', error); return NextResponse.json({ error: '登录失败' }, { status: 500 }); + } finally { + if (connection) connection.release(); } -} +} \ No newline at end of file diff --git a/app/api/auth/me/route.ts b/app/api/auth/me/route.ts index 697f25c..16da9e5 100644 --- a/app/api/auth/me/route.ts +++ b/app/api/auth/me/route.ts @@ -1,25 +1,8 @@ import { NextRequest, NextResponse } from 'next/server'; -import fs from 'fs/promises'; -import path from 'path'; - -const USERS_FILE = path.join(process.cwd(), 'data/users.json'); - -interface User { - id: string; - username: string; - token: string; -} - -async function readUsers(): Promise { - try { - const data = await fs.readFile(USERS_FILE, 'utf-8'); - return JSON.parse(data); - } catch { - return []; - } -} +import pool from '@/lib/db'; export async function GET(request: NextRequest) { + let connection; try { const token = request.cookies.get('auth_token')?.value; @@ -27,17 +10,20 @@ export async function GET(request: NextRequest) { return NextResponse.json({ user: null }); } - const users = await readUsers(); - const user = users.find(u => u.token === token); + connection = await pool.getConnection(); - if (!user) { + const [rows] = await connection.query('SELECT username FROM users WHERE token = ?', [token]); + + if (rows.length === 0) { return NextResponse.json({ user: null }); } - return NextResponse.json({ user: { username: user.username } }); + return NextResponse.json({ user: { username: rows[0].username } }); } catch (error) { console.error('Get user error:', error); return NextResponse.json({ user: null }); + } finally { + if (connection) connection.release(); } } @@ -45,4 +31,4 @@ export async function DELETE(request: NextRequest) { const response = NextResponse.json({ success: true }); response.cookies.delete('auth_token'); return response; -} +} \ No newline at end of file diff --git a/app/api/auth/register/route.ts b/app/api/auth/register/route.ts index e31fefb..c4e1421 100644 --- a/app/api/auth/register/route.ts +++ b/app/api/auth/register/route.ts @@ -1,31 +1,6 @@ import { NextRequest, NextResponse } from 'next/server'; -import fs from 'fs/promises'; -import path from 'path'; import crypto from 'crypto'; - -const DATA_DIR = path.join(process.cwd(), 'data'); -const USERS_FILE = path.join(DATA_DIR, 'users.json'); - -interface User { - id: string; - username: string; - passwordHash: string; - token: string; - createdAt: string; -} - -async function readUsers(): Promise { - try { - const data = await fs.readFile(USERS_FILE, 'utf-8'); - return JSON.parse(data); - } catch { - return []; - } -} - -async function writeUsers(users: User[]): Promise { - await fs.writeFile(USERS_FILE, JSON.stringify(users, null, 2)); -} +import pool from '@/lib/db'; function hashPassword(password: string): string { return crypto.createHash('sha256').update(password).digest('hex'); @@ -36,6 +11,7 @@ function generateToken(): string { } export async function POST(request: NextRequest) { + let connection; try { const { username, password } = await request.json(); @@ -47,23 +23,19 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: '用户名至少3位,密码至少6位' }, { status: 400 }); } - const users = await readUsers(); + connection = await pool.getConnection(); - if (users.find(u => u.username === username)) { + const [rows] = await connection.query('SELECT id FROM users WHERE username = ?', [username]); + + if (rows.length > 0) { return NextResponse.json({ error: '用户名已存在' }, { status: 400 }); } const token = generateToken(); - const newUser: User = { - id: crypto.randomUUID(), - username, - passwordHash: hashPassword(password), - token, - createdAt: new Date().toISOString() - }; - - users.push(newUser); - await writeUsers(users); + await connection.query( + 'INSERT INTO users (id, username, password_hash, token, created_at) VALUES (?, ?, ?, ?, ?)', + [crypto.randomUUID(), username, hashPassword(password), token, new Date()] + ); const response = NextResponse.json({ success: true, username }); response.cookies.set('auth_token', token, { @@ -78,5 +50,7 @@ export async function POST(request: NextRequest) { } catch (error) { console.error('Register error:', error); return NextResponse.json({ error: '注册失败' }, { status: 500 }); + } finally { + if (connection) connection.release(); } -} +} \ No newline at end of file diff --git a/app/api/districts/route.ts b/app/api/districts/route.ts index 26bfe36..6312932 100644 --- a/app/api/districts/route.ts +++ b/app/api/districts/route.ts @@ -1,17 +1,21 @@ import { NextResponse } from 'next/server'; -import fs from 'fs/promises'; -import path from 'path'; - -const DATA_DIR = path.join(process.cwd(), 'data'); -const DISTRICTS_FILE = path.join(DATA_DIR, 'districts.json'); +import pool from '@/lib/db'; export async function GET() { + let connection; try { - const data = await fs.readFile(DISTRICTS_FILE, 'utf-8'); - const districts = JSON.parse(data); + connection = await pool.getConnection(); + + const [rows] = await connection.query('SELECT name FROM districts ORDER BY sort_order, id'); + + const districts = rows.map((row: any) => row.name); + + connection.release(); return NextResponse.json({ districts }); } catch (error) { console.error('Get districts error:', error); return NextResponse.json({ districts: [] }); + } finally { + if (connection) connection.release(); } } \ No newline at end of file diff --git a/app/api/houses/[id]/route.ts b/app/api/houses/[id]/route.ts index 587203b..57f517f 100644 --- a/app/api/houses/[id]/route.ts +++ b/app/api/houses/[id]/route.ts @@ -1,69 +1,41 @@ import { NextRequest, NextResponse } from 'next/server'; -import fs from 'fs/promises'; -import path from 'path'; - -const DATA_DIR = path.join(process.cwd(), 'data'); -const HOUSES_FILE = path.join(DATA_DIR, 'houses.json'); -const USERS_FILE = path.join(DATA_DIR, 'users.json'); - -interface House { - id: string; - owner: string; - title: string; - description: string; - price: number; - district: string; - address: string; - phone: string; - images: string[]; - createdAt: string; -} - -interface User { - id: string; - username: string; - token: string; -} - -async function readFile(filePath: string, defaultValue: T): Promise { - try { - const data = await fs.readFile(filePath, 'utf-8'); - return JSON.parse(data); - } catch { - return defaultValue; - } -} - -async function getUserFromToken(token: string): Promise { - const users = await readFile(USERS_FILE, []); - return users.find(u => u.token === token) || null; -} - -async function readHouses(): Promise { - return readFile(HOUSES_FILE, []); -} - -async function writeHouses(houses: House[]): Promise { - await fs.writeFile(HOUSES_FILE, JSON.stringify(houses, null, 2)); -} +import pool from '@/lib/db'; export async function GET( request: NextRequest, { params }: { params: Promise<{ id: string }> } ) { + let connection; try { const { id } = await params; - const houses = await readHouses(); - const house = houses.find(h => h.id === id); + connection = await pool.getConnection(); - if (!house) { + const [rows] = await connection.query('SELECT * FROM houses WHERE id = ?', [id]); + + if (rows.length === 0) { return NextResponse.json({ error: '房屋不存在' }, { status: 404 }); } + const row = rows[0]; + const house = { + id: row.id, + owner: row.owner, + title: row.title, + description: row.description, + price: row.price, + district: row.district, + address: row.address, + phone: row.phone, + images: row.images ? JSON.parse(row.images) : [], + createdAt: row.created_at + }; + return NextResponse.json({ house }); } catch (error) { console.error('Get house error:', error); return NextResponse.json({ error: '获取房屋信息失败' }, { status: 500 }); + } finally { + if (connection) connection.release(); } } @@ -71,6 +43,7 @@ export async function PUT( request: NextRequest, { params }: { params: Promise<{ id: string }> } ) { + let connection; try { const token = request.cookies.get('auth_token')?.value; @@ -78,43 +51,65 @@ export async function PUT( return NextResponse.json({ error: '请先登录' }, { status: 401 }); } - const user = await getUserFromToken(token); - if (!user) { + const { id } = await params; + connection = await pool.getConnection(); + + const [users] = await connection.query('SELECT username FROM users WHERE token = ?', [token]); + if (users.length === 0) { + connection.release(); return NextResponse.json({ error: '用户不存在' }, { status: 401 }); } - const { id } = await params; - const houses = await readHouses(); - const houseIndex = houses.findIndex(h => h.id === id); - - if (houseIndex === -1) { + const [houses] = await connection.query('SELECT * FROM houses WHERE id = ?', [id]); + if (houses.length === 0) { + connection.release(); return NextResponse.json({ error: '房屋不存在' }, { status: 404 }); } - if (houses[houseIndex].owner !== user.username) { + if (houses[0].owner !== users[0].username) { + connection.release(); return NextResponse.json({ error: '无权修改此房屋' }, { status: 403 }); } const body = await request.json(); const { title, description, price, district, address, phone, images } = body; - houses[houseIndex] = { - ...houses[houseIndex], - title: title || houses[houseIndex].title, - description: description ?? houses[houseIndex].description, - price: price !== undefined ? Number(price) : houses[houseIndex].price, - district: district || houses[houseIndex].district, - address: address || houses[houseIndex].address, - phone: phone || houses[houseIndex].phone, - images: images || houses[houseIndex].images + await connection.query( + 'UPDATE houses SET title = ?, description = ?, price = ?, district = ?, address = ?, phone = ?, images = ? WHERE id = ?', + [ + title || houses[0].title, + description ?? houses[0].description, + price !== undefined ? Number(price) : houses[0].price, + district || houses[0].district, + address || houses[0].address, + phone || houses[0].phone, + images ? JSON.stringify(images) : houses[0].images, + id + ] + ); + + const [updated] = await connection.query('SELECT * FROM houses WHERE id = ?', [id]); + const row = updated[0]; + const house = { + id: row.id, + owner: row.owner, + title: row.title, + description: row.description, + price: row.price, + district: row.district, + address: row.address, + phone: row.phone, + images: row.images ? JSON.parse(row.images) : [], + createdAt: row.created_at }; - await writeHouses(houses); - - return NextResponse.json({ success: true, house: houses[houseIndex] }); + connection.release(); + return NextResponse.json({ success: true, house }); } catch (error) { console.error('Update house error:', error); return NextResponse.json({ error: '更新房屋失败' }, { status: 500 }); + } finally { + if (connection) connection.release(); } } @@ -122,6 +117,7 @@ export async function DELETE( request: NextRequest, { params }: { params: Promise<{ id: string }> } ) { + let connection; try { const token = request.cookies.get('auth_token')?.value; @@ -129,29 +125,34 @@ export async function DELETE( return NextResponse.json({ error: '请先登录' }, { status: 401 }); } - const user = await getUserFromToken(token); - if (!user) { + const { id } = await params; + connection = await pool.getConnection(); + + const [users] = await connection.query('SELECT username FROM users WHERE token = ?', [token]); + if (users.length === 0) { + connection.release(); return NextResponse.json({ error: '用户不存在' }, { status: 401 }); } - const { id } = await params; - const houses = await readHouses(); - const house = houses.find(h => h.id === id); - - if (!house) { + const [houses] = await connection.query('SELECT * FROM houses WHERE id = ?', [id]); + if (houses.length === 0) { + connection.release(); return NextResponse.json({ error: '房屋不存在' }, { status: 404 }); } - if (house.owner !== user.username) { + if (houses[0].owner !== users[0].username) { + connection.release(); return NextResponse.json({ error: '无权删除此房屋' }, { status: 403 }); } - const newHouses = houses.filter(h => h.id !== id); - await writeHouses(newHouses); + await connection.query('DELETE FROM houses WHERE id = ?', [id]); + connection.release(); return NextResponse.json({ success: true }); } catch (error) { console.error('Delete house error:', error); return NextResponse.json({ error: '删除房屋失败' }, { status: 500 }); + } finally { + if (connection) connection.release(); } -} +} \ No newline at end of file diff --git a/app/api/houses/route.ts b/app/api/houses/route.ts index feea2b6..f9a11f2 100644 --- a/app/api/houses/route.ts +++ b/app/api/houses/route.ts @@ -1,84 +1,58 @@ import { NextRequest, NextResponse } from 'next/server'; -import fs from 'fs/promises'; -import path from 'path'; import crypto from 'crypto'; - -const DATA_DIR = path.join(process.cwd(), 'data'); -const HOUSES_FILE = path.join(DATA_DIR, 'houses.json'); -const USERS_FILE = path.join(DATA_DIR, 'users.json'); - -interface House { - id: string; - owner: string; - title: string; - description: string; - price: number; - district: string; - address: string; - phone: string; - images: string[]; - createdAt: string; -} - -interface User { - id: string; - username: string; - token: string; -} - -async function readFile(filePath: string, defaultValue: T): Promise { - try { - const data = await fs.readFile(filePath, 'utf-8'); - return JSON.parse(data); - } catch { - return defaultValue; - } -} - -async function getUserFromToken(token: string): Promise { - const users = await readFile(USERS_FILE, []); - return users.find(u => u.token === token) || null; -} - -async function readHouses(): Promise { - return readFile(HOUSES_FILE, []); -} - -async function writeHouses(houses: House[]): Promise { - await fs.writeFile(HOUSES_FILE, JSON.stringify(houses, null, 2)); -} +import pool from '@/lib/db'; export async function GET(request: NextRequest) { + let connection; try { const { searchParams } = new URL(request.url); const district = searchParams.get('district'); const keyword = searchParams.get('keyword'); - let houses = await readHouses(); + connection = await pool.getConnection(); + + let sql = 'SELECT * FROM houses WHERE 1=1'; + const params: any[] = []; if (district && district !== '全部') { - houses = houses.filter(h => h.district === district); + sql += ' AND district = ?'; + params.push(district); } if (keyword) { - const kw = keyword.toLowerCase(); - houses = houses.filter(h => - h.title.toLowerCase().includes(kw) || - h.address.toLowerCase().includes(kw) || - h.description.toLowerCase().includes(kw) - ); + sql += ' AND (title LIKE ? OR address LIKE ? OR description LIKE ?)'; + const kw = `%${keyword}%`; + params.push(kw, kw, kw); } - houses.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); + sql += ' ORDER BY created_at DESC'; + + const [rows] = await connection.query(sql, params); + + const houses = rows.map((row: any) => ({ + id: row.id, + owner: row.owner, + title: row.title, + description: row.description, + price: row.price, + district: row.district, + address: row.address, + phone: row.phone, + images: row.images ? JSON.parse(row.images) : [], + createdAt: row.created_at + })); return NextResponse.json({ houses }); } catch (error) { console.error('Get houses error:', error); return NextResponse.json({ error: '获取房屋列表失败' }, { status: 500 }); + } finally { + if (connection) connection.release(); } } export async function POST(request: NextRequest) { + let connection; try { const token = request.cookies.get('auth_token')?.value; @@ -86,8 +60,11 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: '请先登录' }, { status: 401 }); } - const user = await getUserFromToken(token); - if (!user) { + connection = await pool.getConnection(); + + const [users] = await connection.query('SELECT username FROM users WHERE token = ?', [token]); + + if (users.length === 0) { return NextResponse.json({ error: '用户不存在' }, { status: 401 }); } @@ -98,9 +75,15 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: '请填写完整信息' }, { status: 400 }); } - const house: House = { - id: crypto.randomUUID(), - owner: user.username, + const id = crypto.randomUUID(); + await connection.query( + 'INSERT INTO houses (id, owner, title, description, price, district, address, phone, images, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', + [id, users[0].username, title, description || '', Number(price), district, address, phone, JSON.stringify(images || []), new Date()] + ); + + const house = { + id, + owner: users[0].username, title, description: description || '', price: Number(price), @@ -108,16 +91,14 @@ export async function POST(request: NextRequest) { address, phone, images: images || [], - createdAt: new Date().toISOString() + createdAt: new Date() }; - const houses = await readHouses(); - houses.push(house); - await writeHouses(houses); - return NextResponse.json({ success: true, house }); } catch (error) { console.error('Create house error:', error); return NextResponse.json({ error: '创建房屋失败' }, { status: 500 }); + } finally { + if (connection) connection.release(); } -} +} \ No newline at end of file diff --git a/app/api/owner/houses/route.ts b/app/api/owner/houses/route.ts index 0275926..e8ccc09 100644 --- a/app/api/owner/houses/route.ts +++ b/app/api/owner/houses/route.ts @@ -1,49 +1,8 @@ import { NextRequest, NextResponse } from 'next/server'; -import fs from 'fs/promises'; -import path from 'path'; - -const DATA_DIR = path.join(process.cwd(), 'data'); -const HOUSES_FILE = path.join(DATA_DIR, 'houses.json'); -const USERS_FILE = path.join(DATA_DIR, 'users.json'); - -interface House { - id: string; - owner: string; - title: string; - description: string; - price: number; - district: string; - address: string; - phone: string; - images: string[]; - createdAt: string; -} - -interface User { - id: string; - username: string; - token: string; -} - -async function readFile(filePath: string, defaultValue: T): Promise { - try { - const data = await fs.readFile(filePath, 'utf-8'); - return JSON.parse(data); - } catch { - return defaultValue; - } -} - -async function getUserFromToken(token: string): Promise { - const users = await readFile(USERS_FILE, []); - return users.find(u => u.token === token) || null; -} - -async function readHouses(): Promise { - return readFile(HOUSES_FILE, []); -} +import pool from '@/lib/db'; export async function GET(request: NextRequest) { + let connection; try { const token = request.cookies.get('auth_token')?.value; @@ -51,19 +10,39 @@ export async function GET(request: NextRequest) { return NextResponse.json({ error: '请先登录' }, { status: 401 }); } - const user = await getUserFromToken(token); - if (!user) { + connection = await pool.getConnection(); + + const [users] = await connection.query('SELECT username FROM users WHERE token = ?', [token]); + + if (users.length === 0) { + connection.release(); return NextResponse.json({ error: '用户不存在' }, { status: 401 }); } - const houses = await readHouses(); - const myHouses = houses - .filter(h => h.owner === user.username) - .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); + const [rows] = await connection.query( + 'SELECT * FROM houses WHERE owner = ? ORDER BY created_at DESC', + [users[0].username] + ); - return NextResponse.json({ houses: myHouses }); + const houses = rows.map((row: any) => ({ + id: row.id, + owner: row.owner, + title: row.title, + description: row.description, + price: row.price, + district: row.district, + address: row.address, + phone: row.phone, + images: row.images ? JSON.parse(row.images) : [], + createdAt: row.created_at + })); + + connection.release(); + return NextResponse.json({ houses }); } catch (error) { console.error('Get my houses error:', error); return NextResponse.json({ error: '获取房屋列表失败' }, { status: 500 }); + } finally { + if (connection) connection.release(); } -} +} \ No newline at end of file diff --git a/lib/db.ts b/lib/db.ts new file mode 100644 index 0000000..fa29f84 --- /dev/null +++ b/lib/db.ts @@ -0,0 +1,14 @@ +import mysql from 'mysql2/promise'; + +const pool = mysql.createPool({ + host: '192.168.0.196', + port: 3306, + user: 'smalltown', + password: 'MyPassword1+', + database: 'smalltown', + waitForConnections: true, + connectionLimit: 10, + queueLimit: 0 +}); + +export default pool; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 003e699..2c9b628 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "smalltown2", "version": "0.1.0", "dependencies": { + "mysql2": "^3.20.0", "next": "16.2.1", "react": "19.2.4", "react-dom": "19.2.4" @@ -1549,7 +1550,6 @@ "version": "20.19.37", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -2405,6 +2405,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/axe-core": { "version": "4.11.1", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.1.tgz", @@ -2783,6 +2792,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -3662,6 +3680,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/generator-function": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", @@ -3926,6 +3953,22 @@ "hermes-estree": "0.25.1" } }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4248,6 +4291,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -4846,6 +4895,12 @@ "dev": true, "license": "MIT" }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4869,6 +4924,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/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -4943,6 +5013,40 @@ "dev": true, "license": "MIT" }, + "node_modules/mysql2": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.20.0.tgz", + "integrity": "sha512-eCLUs7BNbgA6nf/MZXsaBO1SfGs0LtLVrJD3WeWq+jPLDWkSufTD+aGMwykfUVPdZnblaUK1a8G/P63cl9FkKg==", + "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", @@ -5636,6 +5740,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -5867,6 +5977,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/stable-hash": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", @@ -6352,7 +6477,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/unrs-resolver": { diff --git a/package.json b/package.json index 189d4da..436932f 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "lint": "eslint" }, "dependencies": { + "mysql2": "^3.20.0", "next": "16.2.1", "react": "19.2.4", "react-dom": "19.2.4"