import express, { Request, Response } from 'express' import { PrismaClient } from '@prisma/client' import fs from 'fs' import path from 'path' import axios from 'axios' import multer from 'multer' import bodyParser from 'body-parser' import cors from 'cors' import { Coordinate } from './interfaces/map' import { generateTilesForZoomLevel } from './utils/tiles' import { query, validationResult } from 'express-validator' import { Connection, Request as TediousRequest } from 'tedious' const tedious = new Connection({ server: 'localhost', options: { trustServerCertificate: true, port: 1433, database: 'nGeneral' }, authentication: { type: 'default', options: { userName: 'SA', password: 'oMhthmsvbYHc' } } }) tedious.on('connect', function (err) { if (err) { console.log('Error: ', err) } console.log('Connected to General') }) tedious.connect() const prisma = new PrismaClient() const app = express() const PORT = process.env.EMS_PORT || 5000 const tileFolder = path.join(__dirname, '..', 'public', 'tile_data') const uploadDir = path.join(__dirname, '..', 'public', 'temp') app.use(cors()) app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: true })) const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, path.join(__dirname, '..', 'public', 'temp')) }, filename: function (req, file, cb) { cb(null, Date.now() + path.extname(file.originalname)) } }) const upload = multer({ storage: storage }) app.get('/nodes/all', async (req: Request, res: Response) => { try { const nodes = await prisma.nodes.findMany() res.json(nodes) } catch (error) { console.error('Error getting node:', error); res.status(500).json({ error: 'Failed to get node' }); } }) app.get('/nodes', query('id').isString().isUUID(), async (req: Request, res: Response) => { try { const result = validationResult(req) if (!result.isEmpty()) { return res.send({ errors: result.array() }) } const { id } = req.params const node = await prisma.nodes.findFirst({ where: { id: id } }) res.json(node) } catch (error) { console.error('Error getting node:', error); res.status(500).json({ error: 'Failed to get node' }); } }) app.post('/nodes', async (req: Request, res: Response) => { try { const { coordinates, object_id, type } = req.body; // Convert the incoming array of coordinates into the shape structure const shape = coordinates.map((point: number[]) => ({ object_id: object_id || null, x: point[0], y: point[1] })); console.log(shape) // Create a new node in the database const node = await prisma.nodes.create({ data: { object_id: object_id || null, // Nullable if object_id is not provided shape_type: type, // You can adjust this dynamically shape: shape, // Store the shape array as Json[] label: 'Default' } }); res.status(201).json(node); } catch (error) { console.error('Error creating node:', error); res.status(500).json({ error: 'Failed to create node' }); } }) app.post('/upload', upload.single('file'), async (req: Request, res: Response) => { const { extentMinX, extentMinY, extentMaxX, extentMaxY, blX, blY, tlX, tlY, trX, trY, brX, brY } = req.body const bottomLeft: Coordinate = { x: blX, y: blY } const topLeft: Coordinate = { x: tlX, y: tlY } const topRight: Coordinate = { x: trX, y: trY } const bottomRight: Coordinate = { x: brX, y: brY } if (req.file) { for (let z = 0; z <= 21; z++) { await generateTilesForZoomLevel(uploadDir, tileFolder, req.file, [extentMinX, extentMinY, extentMaxX, extentMaxY], bottomLeft, topLeft, topRight, bottomRight, z) } } return res.status(200) }) const fetchTileFromAPI = async (provider: string, z: string, x: string, y: string): Promise => { const url = provider === 'google' ? `https://khms2.google.com/kh/v=984?x=${x}&y=${y}&z=${z}` : `https://core-sat.maps.yandex.net/tiles?l=sat&x=${x}&y=${y}&z=${z}&scale=1&lang=ru_RU` const response = await axios.get(url, { responseType: 'arraybuffer' }) return response.data } app.get('/tile/:provider/:z/:x/:y', async (req: Request, res: Response) => { const { provider, z, x, y } = req.params if (!['google', 'yandex', 'custom'].includes(provider)) { return res.status(400).send('Invalid provider') } const tilePath = provider === 'custom' ? path.join(tileFolder, provider, z, x, `${y}.png`) : path.join(tileFolder, provider, z, x, `${y}.jpg`) if (fs.existsSync(tilePath)) { return res.sendFile(tilePath) } else { if (provider !== 'custom') { try { const tileData = await fetchTileFromAPI(provider, z, x, y) fs.mkdirSync(path.dirname(tilePath), { recursive: true }) fs.writeFileSync(tilePath, tileData) res.contentType('image/jpeg') res.send(tileData) } catch (error) { console.error('Error fetching tile from API:', error) res.status(500).send('Error fetching tile from API') } } else { res.status(404).send('Tile is not generated or not provided') } } }) function tediousQuery(query: string) { // Read all rows from table const request = new TediousRequest( query, (err, rowCount) => { if (err) { console.log(`Executing ${query}, ${rowCount} rows.`); console.error(err.message); } else { console.log(`Executing ${query}, ${rowCount} rows.`); } } ); return new Promise((resolve, reject) => { const result: any = []; request.on("row", (columns) => { const entry: any = {}; columns.forEach((column: any) => { entry[column.metadata.colName] = column.value; }); result.push(entry); }); request.on('error', error => reject(error));// some error happened, reject the promise request.on('requestCompleted', () => resolve(result)); // resolve the promise with the result rows. tedious.execSql(request); }); } app.get('/general/cities/all', async (req: Request, res: Response) => { try { const { offset, limit, search, id } = req.query const result = await tediousQuery( ` SELECT * FROM nGeneral..Cities ${id ? `WHERE id = '${id}'` : ''} ${search ? `WHERE name LIKE '%${search || ''}%'` : ''} ORDER BY id OFFSET ${Number(offset) || 0} ROWS FETCH NEXT ${Number(limit) || 10} ROWS ONLY; ` ) res.status(200).json(result) } catch (err) { res.status(500) } }) app.get('/general/objects/all', async (req: Request, res: Response) => { try { const { offset, limit, city_id, id } = req.query const result = await tediousQuery( ` SELECT * FROM nGeneral..tObjects ${city_id ? `WHERE id_city = ${city_id}` : ''} ${id ? `WHERE id = '${id}'` : ''} ORDER BY id OFFSET ${Number(offset) || 0} ROWS FETCH NEXT ${Number(limit) || 10} ROWS ONLY; ` ) res.status(200).json(result) } catch (err) { res.status(500) } }) app.get('/gis/images/all', async (req: Request, res: Response) => { try { const { offset, limit, city_id } = req.query const result = await tediousQuery( ` SELECT * FROM New_Gis..images ${city_id ? `WHERE city_id = ${city_id}` : ''} ORDER BY city_id OFFSET ${Number(offset) || 0} ROWS FETCH NEXT ${Number(limit) || 10} ROWS ONLY; ` ) res.status(200).json(result) } catch (err) { res.status(500) } }) // Get figures by year and city id app.get('/gis/figures/all', async (req: Request, res: Response) => { try { const { offset, limit, object_id, year, city_id } = req.query const result = await tediousQuery( ` SELECT * FROM New_Gis..figures f JOIN nGeneral..tObjects o ON f.object_id = o.id WHERE o.id_city = ${city_id} AND f.year = ${year} ORDER BY f.year OFFSET ${Number(offset) || 0} ROWS FETCH NEXT ${Number(limit) || 10} ROWS ONLY; ` ) res.status(200).json(result) } catch (err) { res.status(500) } }) // Get lines by year and city id app.get('/gis/lines/all', async (req: Request, res: Response) => { try { const { offset, limit, object_id, year, city_id } = req.query const result = await tediousQuery( ` SELECT * FROM New_Gis..lines l JOIN nGeneral..tObjects o ON l.object_id = o.id WHERE o.id_city = ${city_id} AND l.year = ${year} ORDER BY l.year OFFSET ${Number(offset) || 0} ROWS FETCH NEXT ${Number(limit) || 10} ROWS ONLY; ` ) res.status(200).json(result) } catch (err) { res.status(500) } }) app.listen(PORT, () => console.log(`Server running on port ${PORT}`));