new
This commit is contained in:
+3
-6
@@ -1,8 +1,5 @@
|
|||||||
MYSQL_HOST=127.0.0.1
|
|
||||||
MYSQL_USER=root
|
|
||||||
MYSQL_PASSWORD=12345678
|
|
||||||
MYSQL_DATABASE=laravel9
|
|
||||||
MYSQL_PORT=3306
|
|
||||||
|
|
||||||
ADMIN_HASHED_PASSWORD="$2b$10$JQm79m.C64TWTwc7qmbJLOW2UnDLCUsL5e34eFb1MhRPqGBBxw0QK"
|
ADMIN_HASHED_PASSWORD="$2b$10$JQm79m.C64TWTwc7qmbJLOW2UnDLCUsL5e34eFb1MhRPqGBBxw0QK"
|
||||||
JWT_SECRET=!^AqCAhL^XZP$f5cEtKhuo7@!96c%8gChuyJvUH
|
JWT_SECRET=!^AqCAhL^XZP$f5cEtKhuo7@!96c%8gChuyJvUH
|
||||||
|
|
||||||
|
TELEGRAM_BOT_TOKEN=
|
||||||
|
TELEGRAM_CHAT_ID=
|
||||||
|
|||||||
@@ -40,3 +40,8 @@ yarn-error.log*
|
|||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|
||||||
|
# SQLite database
|
||||||
|
/data/*.db
|
||||||
|
/data/*.db-shm
|
||||||
|
/data/*.db-wal
|
||||||
|
|||||||
Binary file not shown.
+2
-25
@@ -13,9 +13,7 @@ services:
|
|||||||
- "3000"
|
- "3000"
|
||||||
volumes:
|
volumes:
|
||||||
- uploads_data:/app/uploads
|
- uploads_data:/app/uploads
|
||||||
depends_on:
|
- sqlite_data:/app/data
|
||||||
db:
|
|
||||||
condition: service_healthy
|
|
||||||
networks:
|
networks:
|
||||||
- backend
|
- backend
|
||||||
healthcheck:
|
healthcheck:
|
||||||
@@ -45,30 +43,9 @@ services:
|
|||||||
retries: 5
|
retries: 5
|
||||||
start_period: 10s
|
start_period: 10s
|
||||||
|
|
||||||
db:
|
|
||||||
image: mysql:8.4
|
|
||||||
container_name: strike_arena_db
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
MYSQL_ROOT_PASSWORD: ${MYSQL_PASSWORD}
|
|
||||||
MYSQL_DATABASE: ${MYSQL_DATABASE}
|
|
||||||
expose:
|
|
||||||
- "3306"
|
|
||||||
volumes:
|
|
||||||
- db_data:/var/lib/mysql
|
|
||||||
- ./init/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
|
|
||||||
networks:
|
|
||||||
- backend
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-u", "root", "--password=${MYSQL_PASSWORD}"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 10
|
|
||||||
start_period: 30s
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
db_data:
|
|
||||||
uploads_data:
|
uploads_data:
|
||||||
|
sqlite_data:
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
backend:
|
backend:
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
PRAGMA journal_mode = WAL;
|
||||||
|
PRAGMA foreign_keys = ON;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS rooms (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
cpu TEXT,
|
||||||
|
ram TEXT,
|
||||||
|
gpu TEXT,
|
||||||
|
mouse TEXT,
|
||||||
|
keyboard TEXT,
|
||||||
|
headphones TEXT,
|
||||||
|
monitor TEXT,
|
||||||
|
bracket TEXT,
|
||||||
|
chair TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO rooms VALUES
|
||||||
|
(1,'solo','5 отдельных комнат с мощным железом для игры соло','intel i7-13700kf','kingston 16gX2=32G/3200','Asus dual4070 012g','Logitech superlight 2','Hyper X origins core','Hyper X Cloud II','ASUS ROG 380 Hz','NB Display stand','DX Racer Gladiator'),
|
||||||
|
(2,'DUO / TRIO','4 комнаты на двоих и 3 на троих','intel i5-14600kf','kingston 16gX2=32G/3200','MSI 4060 Ventus 2X','Logitech superlight 1','Hyper X origins core','Hyper X Cloud II','ASUS ROG 310 Hz','NB Display stand','DX Racer Gladiator'),
|
||||||
|
(3,'BOOTCAMP 5/6','2 буткэмпа на пять и 1 на шесть игроков','intel i5-12400f','kingston 16gX2=32G/3200','MSI 4060 Ventus 2X','Logitech G403','Hyper X origins core','Hyper X Cloud II','ASUS ROG 310 Hz','NB Display stand','DX Racer Gladiator'),
|
||||||
|
(4,'ps','Консольный зал','PlayStation 5','2x DualShock','LG 55','','','','','','');
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS faq (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
description TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO faq VALUES
|
||||||
|
(1,'Можно ли загрузить свою игру?','Да, вы можете установить свою игру самостоятельно, однако после перезагрузки компьютера игра удалится. Если вам нужно долгосрочное использование игры, сообщите нам заранее, и мы поможем всё настроить.'),
|
||||||
|
(2,'Какая у вас версия игр?','Мы всегда используем только самые свежие и актуальные версии игр. Наши операторы регулярно следят за обновлениями.'),
|
||||||
|
(3,'Установлены ли CS-2, CS:GO, CS 1.6?','Да, у нас установлены все популярные версии Counter-Strike: CS-2, CS:GO и CS 1.6.'),
|
||||||
|
(5,'Сколько стоит комп в общем зале?','Общего зала у нас нет. Наш клуб является VIP-форматом и предлагает только приватные кабинки. Вы можете забронировать место в BootCamp 5/6, если вам комфортно играть рядом с другими игроками.'),
|
||||||
|
(6,'Можно ли распечатать документы?','Да, можно распечатать документы в чёрно-белом формате. Стоимость распечатки — 100 тг за одну сторону листа А4.'),
|
||||||
|
(7,'Указанная цена за 1 компьютер или за весь буткэмп?','Все цены указаны за одно игровое место (1 компьютер).'),
|
||||||
|
(8,'Почему такие высокие цены?','Наш клуб ориентирован на тех, кто ценит приватность, комфорт и тишину. Наши гости часто отмечают, что наши цены полностью оправданы высоким уровнем комфорта и качества обслуживания.'),
|
||||||
|
(9,'Как можно забронировать место?','Есть два способа бронирования:
|
||||||
|
Через администратора с предварительной оплатой 1000 тг за каждый компьютер.
|
||||||
|
Онлайн через регистрацию с дополнительной скидкой 10%.');
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS contacts (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
value TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO contacts VALUES
|
||||||
|
(1,'phone','+7 700 062 8559'),
|
||||||
|
(2,'whatsapp','https://wa.me/77752608559'),
|
||||||
|
(3,'tg','https://t.me/StrikeArenaAstana'),
|
||||||
|
(4,'gis2','https://2gis.kz/astana/firm/70000001097382279'),
|
||||||
|
(5,'inst','https://www.instagram.com/strikearena_astana');
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS games (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
image_url TEXT,
|
||||||
|
platform TEXT DEFAULT 'pc'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO games VALUES
|
||||||
|
(14,'Мир танков','image.png','pc'),
|
||||||
|
(15,'Sims4','image-1.png','pc'),
|
||||||
|
(16,'Pubg','image-2.png','pc'),
|
||||||
|
(17,'Generals zerohour','image-3.png','pc'),
|
||||||
|
(18,'Dota2','image-4.png','pc'),
|
||||||
|
(19,'Valorant','image-5.png','pc'),
|
||||||
|
(20,'CS2','image-6.png','pc'),
|
||||||
|
(21,'League of legends','image-7.png','pc'),
|
||||||
|
(22,'CS1.6','image-8.png','pc'),
|
||||||
|
(23,'Genshin Impact','image-9.png','pc'),
|
||||||
|
(24,'Call of duty','image-10.png','pc'),
|
||||||
|
(25,'War thunder','image-11.png','pc'),
|
||||||
|
(26,'Gta 5','image-12.png','pc'),
|
||||||
|
(27,'Warface','image-13.png','pc'),
|
||||||
|
(28,'InZoi','image-14.png','pc'),
|
||||||
|
(29,'Gta SA','image-15.png','pc'),
|
||||||
|
(30,'It takes two','image-16.png','pc'),
|
||||||
|
(31,'Heroes of might and magic','image-17.png','pc'),
|
||||||
|
(32,'Left4dead 2','image-18.png','pc'),
|
||||||
|
(33,'Frozen throne','image-19.png','pc'),
|
||||||
|
(34,'Overwatch 2','image-20.png','pc'),
|
||||||
|
(35,'Nfs carbon','image-21.png','pc'),
|
||||||
|
(36,'Fortnite','image-22.png','pc'),
|
||||||
|
(37,'Red dead redemption 2','image-23.png','pc');
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS prices (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
game_type TEXT NOT NULL,
|
||||||
|
room_type TEXT NOT NULL,
|
||||||
|
day_type TEXT,
|
||||||
|
hour_1 REAL,
|
||||||
|
hour_3 REAL,
|
||||||
|
hour_5 REAL,
|
||||||
|
night REAL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO prices VALUES
|
||||||
|
(1,'ПК','solo','ПН-ЧТ',2800,5600,8400,10000),
|
||||||
|
(2,'ПК','solo','ПТ-ВС',3000,6000,9000,11000),
|
||||||
|
(3,'ПК','duo','ПН-ЧТ',2500,5000,7500,9500),
|
||||||
|
(4,'ПК','duo','ПТ-ВС',2700,5400,8100,10500),
|
||||||
|
(5,'ПК','trio','ПН-ЧТ',2300,4600,6900,9000),
|
||||||
|
(6,'ПК','trio','ПТ-ВС',2500,5000,7500,9800),
|
||||||
|
(7,'ПК','five','ПН-ЧТ',2100,4200,6300,8500),
|
||||||
|
(8,'ПК','five','ПТ-ВС',2300,4600,6900,9200),
|
||||||
|
(9,'ПК','six','ПН-ЧТ',2000,4000,6000,8200),
|
||||||
|
(10,'ПК','six','ПТ-ВС',2200,4400,6600,9000),
|
||||||
|
(11,'PS','lounge','2000',4000,9000,9000,14000),
|
||||||
|
(13,'PS','vip_normal','2500',5000,10000,10000,15000),
|
||||||
|
(15,'PS','vip_big','3000',6000,11000,12000,17000);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS bookings (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
room TEXT NOT NULL,
|
||||||
|
date_time TEXT NOT NULL,
|
||||||
|
time_slot TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
game TEXT,
|
||||||
|
contact TEXT NOT NULL,
|
||||||
|
contact_method TEXT,
|
||||||
|
status TEXT DEFAULT 'pending',
|
||||||
|
created_at TEXT DEFAULT (datetime('now'))
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_date_room_time ON bookings (date_time, room, time_slot);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_status ON bookings (status);
|
||||||
Generated
+772
-1094
File diff suppressed because it is too large
Load Diff
+10
-5
@@ -3,21 +3,21 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "NODE_OPTIONS='--localstorage-file=/tmp/next-dev-storage' next dev",
|
"dev": "next dev",
|
||||||
"build": "NODE_OPTIONS='--localstorage-file=/tmp/next-build-storage' next build",
|
"build": "next build",
|
||||||
"start": "NODE_OPTIONS='--localstorage-file=/tmp/next-start-storage' next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"postbuild": "next-sitemap"
|
"postbuild": "next-sitemap"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcrypt": "^6.0.0",
|
"bcrypt": "^6.0.0",
|
||||||
|
"better-sqlite3": "^12.10.0",
|
||||||
"bootstrap": "^5.3.6",
|
"bootstrap": "^5.3.6",
|
||||||
"formidable": "^3.5.4",
|
"formidable": "^3.5.4",
|
||||||
"framer-motion": "^12.10.1",
|
"framer-motion": "^12.10.1",
|
||||||
"jose": "^6.0.11",
|
"jose": "^6.0.11",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"mysql2": "^3.14.1",
|
"next": "^15.5.19",
|
||||||
"next": "15.3.2",
|
|
||||||
"next-sitemap": "^4.2.3",
|
"next-sitemap": "^4.2.3",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
@@ -25,15 +25,20 @@
|
|||||||
"styled-components": "^6.1.17",
|
"styled-components": "^6.1.17",
|
||||||
"zustand": "^5.0.4"
|
"zustand": "^5.0.4"
|
||||||
},
|
},
|
||||||
|
"overrides": {
|
||||||
|
"postcss": "^8.5.10"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
"@types/bcrypt": "^5.0.2",
|
"@types/bcrypt": "^5.0.2",
|
||||||
|
"@types/better-sqlite3": "^7.6.13",
|
||||||
"@types/formidable": "^3.4.5",
|
"@types/formidable": "^3.4.5",
|
||||||
"@types/jsonwebtoken": "^9.0.9",
|
"@types/jsonwebtoken": "^9.0.9",
|
||||||
"@types/node": "^22.15.17",
|
"@types/node": "^22.15.17",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
"@types/react-input-mask": "^3.0.6",
|
"@types/react-input-mask": "^3.0.6",
|
||||||
|
"cross-env": "^10.1.0",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.3.2",
|
"eslint-config-next": "15.3.2",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ export async function POST(req: NextRequest) {
|
|||||||
// Создаем бронирование
|
// Создаем бронирование
|
||||||
const [result] = await bd.query(
|
const [result] = await bd.query(
|
||||||
`INSERT INTO bookings (type, room, date_time, time_slot, name, game, contact, contact_method, status, created_at)
|
`INSERT INTO bookings (type, room, date_time, time_slot, name, game, contact, contact_method, status, created_at)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())`,
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`,
|
||||||
[type, room, dateTime, time, name, game || null, contact, contactMethod || null, status || 'pending']
|
[type, room, dateTime, time, name, game || null, contact, contactMethod || null, status || 'pending']
|
||||||
) as any[];
|
) as any[];
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server'
|
import { NextRequest, NextResponse } from 'next/server'
|
||||||
import { bd } from '@/lib/bd'
|
import { bd } from '@/lib/bd'
|
||||||
import { verifyAdminToken } from '@/lib/verifyAdmin'
|
import { verifyAdminToken } from '@/lib/verifyAdmin'
|
||||||
import type { ResultSetHeader, FieldPacket } from 'mysql2'
|
|
||||||
|
|
||||||
// GET — получить все записи
|
// GET — получить все записи
|
||||||
export async function GET(req: NextRequest) {
|
export async function GET(req: NextRequest) {
|
||||||
@@ -22,7 +21,7 @@ export async function POST(req: NextRequest) {
|
|||||||
return NextResponse.json({ error: 'Заполните все поля' }, { status: 400 })
|
return NextResponse.json({ error: 'Заполните все поля' }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const [result]: [ResultSetHeader, FieldPacket[]] = await bd.query(
|
const [result] = await bd.query(
|
||||||
'INSERT INTO faq (title, description) VALUES (?, ?)',
|
'INSERT INTO faq (title, description) VALUES (?, ?)',
|
||||||
[title, description]
|
[title, description]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
import { verifyAdminToken } from '@/lib/verifyAdmin';
|
import { verifyAdminToken } from '@/lib/verifyAdmin';
|
||||||
import { bd } from '@/lib/bd';
|
import { bd } from '@/lib/bd';
|
||||||
import type { ResultSetHeader } from 'mysql2';
|
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
@@ -39,7 +38,7 @@ export async function POST(req: NextRequest) {
|
|||||||
// сохраняем файл
|
// сохраняем файл
|
||||||
await fs.writeFile(filePath, buffer);
|
await fs.writeFile(filePath, buffer);
|
||||||
|
|
||||||
const [result] = await bd.query<ResultSetHeader>(
|
const [result] = await bd.query(
|
||||||
'INSERT INTO games (title, image_url, platform) VALUES (?, ?, ?)',
|
'INSERT INTO games (title, image_url, platform) VALUES (?, ?, ?)',
|
||||||
[title, fileName, platform]
|
[title, fileName, platform]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export async function POST(req: NextRequest) {
|
|||||||
// Создаем бронирование
|
// Создаем бронирование
|
||||||
const [result] = await bd.query(
|
const [result] = await bd.query(
|
||||||
`INSERT INTO bookings (type, room, date_time, time_slot, name, game, contact, contact_method, status, created_at)
|
`INSERT INTO bookings (type, room, date_time, time_slot, name, game, contact, contact_method, status, created_at)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pending', NOW())`,
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pending', datetime('now'))`,
|
||||||
[type, room, dateTime, time, name, game || null, contact, contactMethod || null]
|
[type, room, dateTime, time, name, game || null, contact, contactMethod || null]
|
||||||
) as any[];
|
) as any[];
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import "./globals.css";
|
|||||||
import "../styles/reset.css";
|
import "../styles/reset.css";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import StartupLoader from "@/components/StartupLoader";
|
import StartupLoader from "@/components/StartupLoader";
|
||||||
import Script from "next/script";
|
|
||||||
|
|
||||||
export const viewport = {
|
export const viewport = {
|
||||||
themeColor: "#00ff90",
|
themeColor: "#00ff90",
|
||||||
@@ -61,20 +60,6 @@ export default function RootLayout({
|
|||||||
<body>
|
<body>
|
||||||
{children}
|
{children}
|
||||||
<StartupLoader />
|
<StartupLoader />
|
||||||
<Script
|
|
||||||
id="binotel-getcall"
|
|
||||||
strategy="afterInteractive"
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: `
|
|
||||||
(function(d, w, s) {
|
|
||||||
var widgetHash = 'j2he8ic2huirt42fsxdj', gcw = d.createElement(s);
|
|
||||||
gcw.type = 'text/javascript'; gcw.async = true;
|
|
||||||
gcw.src = '//widgets.binotel.com/getcall/widgets/' + widgetHash + '.js';
|
|
||||||
var sn = d.getElementsByTagName(s)[0]; sn.parentNode.insertBefore(gcw, sn);
|
|
||||||
})(document, window, 'script');
|
|
||||||
`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
+49
-8
@@ -1,9 +1,50 @@
|
|||||||
import mysql from 'mysql2/promise'
|
import Database from 'better-sqlite3';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
export const bd = mysql.createPool({
|
const DB_PATH = path.join(process.cwd(), 'data', 'strike-arena.db');
|
||||||
host: process.env.MYSQL_HOST,
|
const INIT_SQL = path.join(process.cwd(), 'init', 'init.sqlite.sql');
|
||||||
user: process.env.MYSQL_USER,
|
|
||||||
password: process.env.MYSQL_PASSWORD,
|
function openDb(): Database.Database {
|
||||||
database: process.env.MYSQL_DATABASE,
|
const dir = path.dirname(DB_PATH);
|
||||||
charset: 'utf8mb4',
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
||||||
})
|
|
||||||
|
const db = new Database(DB_PATH);
|
||||||
|
db.pragma('journal_mode = WAL');
|
||||||
|
db.pragma('foreign_keys = ON');
|
||||||
|
|
||||||
|
// Seed on first run (no tables yet)
|
||||||
|
const tables = db.prepare("SELECT count(*) as n FROM sqlite_master WHERE type='table'").get() as { n: number };
|
||||||
|
if (tables.n === 0 && fs.existsSync(INIT_SQL)) {
|
||||||
|
const sql = fs.readFileSync(INIT_SQL, 'utf-8');
|
||||||
|
db.exec(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Singleton: reuse across hot-reload in dev
|
||||||
|
declare global {
|
||||||
|
// eslint-disable-next-line no-var
|
||||||
|
var __sqlite_db: Database.Database | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const db: Database.Database = global.__sqlite_db ?? (global.__sqlite_db = openDb());
|
||||||
|
|
||||||
|
function isSelect(sql: string) {
|
||||||
|
return /^\s*(SELECT|PRAGMA|WITH)/i.test(sql.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
export const bd = {
|
||||||
|
query: async (sql: string, params: unknown[] = []): Promise<any[]> => {
|
||||||
|
const stmt = db.prepare(sql);
|
||||||
|
if (isSelect(sql)) {
|
||||||
|
return [stmt.all(...(params as any[]))];
|
||||||
|
}
|
||||||
|
const info = stmt.run(...(params as any[]));
|
||||||
|
return [{ insertId: info.lastInsertRowid, affectedRows: info.changes }];
|
||||||
|
},
|
||||||
|
execute: async (sql: string, params: unknown[] = []): Promise<any[]> => {
|
||||||
|
return bd.query(sql, params);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user