forked from VinokurovVE/tests
Map caching in Redis
This commit is contained in:
@ -17,6 +17,8 @@ ENV REDIS_PORT=$REDIS_PORT
|
||||
ENV REDIS_PASSWORD=$REDIS_PASSWORD
|
||||
ENV EMS_PORT=$EMS_PORT
|
||||
|
||||
ENV DATABASE_URL=$DATABASE_URL
|
||||
|
||||
EXPOSE $EMS_PORT
|
||||
|
||||
CMD ["npm", "run", "start"]
|
148
ems/package-lock.json
generated
148
ems/package-lock.json
generated
@ -9,11 +9,14 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@prisma/client": "^5.18.0",
|
||||
"axios": "^1.7.4",
|
||||
"body-parser": "^1.20.2",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
"ioredis": "^5.4.1"
|
||||
"ioredis": "^5.4.1",
|
||||
"prisma": "^5.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.21",
|
||||
@ -66,6 +69,63 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/client": {
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.18.0.tgz",
|
||||
"integrity": "sha512-BWivkLh+af1kqC89zCJYkHsRcyWsM8/JHpsDMM76DjP3ZdEquJhXa4IeX+HkWPnwJ5FanxEJFZZDTWiDs/Kvyw==",
|
||||
"hasInstallScript": true,
|
||||
"engines": {
|
||||
"node": ">=16.13"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prisma": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"prisma": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/debug": {
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.18.0.tgz",
|
||||
"integrity": "sha512-f+ZvpTLidSo3LMJxQPVgAxdAjzv5OpzAo/eF8qZqbwvgi2F5cTOI9XCpdRzJYA0iGfajjwjOKKrVq64vkxEfUw=="
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.18.0.tgz",
|
||||
"integrity": "sha512-ofmpGLeJ2q2P0wa/XaEgTnX/IsLnvSp/gZts0zjgLNdBhfuj2lowOOPmDcfKljLQUXMvAek3lw5T01kHmCG8rg==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/debug": "5.18.0",
|
||||
"@prisma/engines-version": "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169",
|
||||
"@prisma/fetch-engine": "5.18.0",
|
||||
"@prisma/get-platform": "5.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engines-version": {
|
||||
"version": "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169.tgz",
|
||||
"integrity": "sha512-a/+LpJj8vYU3nmtkg+N3X51ddbt35yYrRe8wqHTJtYQt7l1f8kjIBcCs6sHJvodW/EK5XGvboOiwm47fmNrbgg=="
|
||||
},
|
||||
"node_modules/@prisma/fetch-engine": {
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.18.0.tgz",
|
||||
"integrity": "sha512-I/3u0x2n31rGaAuBRx2YK4eB7R/1zCuayo2DGwSpGyrJWsZesrV7QVw7ND0/Suxeo/vLkJ5OwuBqHoCxvTHpOg==",
|
||||
"dependencies": {
|
||||
"@prisma/debug": "5.18.0",
|
||||
"@prisma/engines-version": "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169",
|
||||
"@prisma/get-platform": "5.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/get-platform": {
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.18.0.tgz",
|
||||
"integrity": "sha512-Tk+m7+uhqcKDgnMnFN0lRiH7Ewea0OEsZZs9pqXa7i3+7svS3FSCqDBCaM9x5fmhhkufiG0BtunJVDka+46DlA==",
|
||||
"dependencies": {
|
||||
"@prisma/debug": "5.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/bloom": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
|
||||
@ -316,6 +376,21 @@
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
|
||||
"integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@ -437,6 +512,17 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
@ -517,6 +603,14 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/denque": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
||||
@ -677,6 +771,38 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
@ -1156,6 +1282,21 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/prisma": {
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.18.0.tgz",
|
||||
"integrity": "sha512-+TrSIxZsh64OPOmaSgVPH7ALL9dfU0jceYaMJXsNrTkFHO7/3RANi5K2ZiPB1De9+KDxCWn7jvRq8y8pvk+o9g==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines": "5.18.0"
|
||||
},
|
||||
"bin": {
|
||||
"prisma": "build/index.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.13"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
@ -1168,6 +1309,11 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"node_modules/pstree.remy": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
|
||||
|
@ -13,11 +13,14 @@
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@prisma/client": "^5.18.0",
|
||||
"axios": "^1.7.4",
|
||||
"body-parser": "^1.20.2",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
"ioredis": "^5.4.1"
|
||||
"ioredis": "^5.4.1",
|
||||
"prisma": "^5.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.21",
|
||||
|
40
ems/prisma/schema.prisma
Normal file
40
ems/prisma/schema.prisma
Normal file
@ -0,0 +1,40 @@
|
||||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
|
||||
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model Post {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
title String @db.VarChar(255)
|
||||
content String?
|
||||
published Boolean @default(false)
|
||||
author User @relation(fields: [authorId], references: [id])
|
||||
authorId Int
|
||||
}
|
||||
|
||||
model Profile {
|
||||
id Int @id @default(autoincrement())
|
||||
bio String?
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId Int @unique
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
email String @unique
|
||||
name String?
|
||||
posts Post[]
|
||||
profile Profile?
|
||||
}
|
@ -2,6 +2,8 @@ import express, { Request, Response } from 'express'
|
||||
import { Redis } from 'ioredis'
|
||||
import dotenv from 'dotenv'
|
||||
import bodyParser from 'body-parser'
|
||||
import { SatelliteMapsProvider } from './interfaces/map'
|
||||
const axios = require('axios');
|
||||
|
||||
const cors = require('cors')
|
||||
|
||||
@ -19,6 +21,61 @@ const port = process.env.EMS_PORT
|
||||
// Middleware to parse JSON requests
|
||||
app.use(bodyParser.json())
|
||||
|
||||
const getTileUrl = (provider: string, x: string, y: string, z: string) => {
|
||||
if (provider === 'google') {
|
||||
return `https://khms2.google.com/kh/v=984?x=${x}&y=${y}&z=${z}`;
|
||||
} else if (provider === 'yandex') {
|
||||
return `https://core-sat.maps.yandex.net/tiles?l=sat&x=${x}&y=${y}&z=${z}&scale=1&lang=ru_RU`;
|
||||
}
|
||||
throw new Error('Invalid provider');
|
||||
}
|
||||
|
||||
app.get('/tile/:provider/:z/:x/:y', async (req, res) => {
|
||||
const { provider, x, y, z } = req.params;
|
||||
const cacheKey = `${provider}:${z}:${x}:${y}`;
|
||||
|
||||
try {
|
||||
// Check if tile is in cache
|
||||
redis.get(cacheKey, async (err, cachedTile) => {
|
||||
if (err) {
|
||||
console.error('Redis GET error:', err);
|
||||
return res.status(500).send('Server error');
|
||||
}
|
||||
|
||||
if (cachedTile) {
|
||||
// If cached, return tile
|
||||
console.log('Tile served from cache');
|
||||
const imgBuffer = Buffer.from(cachedTile, 'base64');
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'image/png',
|
||||
'Content-Length': imgBuffer.length,
|
||||
});
|
||||
return res.end(imgBuffer);
|
||||
} else {
|
||||
// Fetch tile from provider
|
||||
const tileUrl = getTileUrl(provider, x, y, z);
|
||||
const response = await axios.get(tileUrl, {
|
||||
responseType: 'arraybuffer',
|
||||
});
|
||||
|
||||
// Cache the tile in Redis
|
||||
const base64Tile = Buffer.from(response.data).toString('base64');
|
||||
redis.setex(cacheKey, 3600 * 24 * 30, base64Tile); // Cache for 1 hour
|
||||
|
||||
// Return the tile to the client
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'image/png',
|
||||
'Content-Length': response.data.length,
|
||||
});
|
||||
return res.end(response.data);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching tile:', error);
|
||||
res.status(500).send('Error fetching tile');
|
||||
}
|
||||
})
|
||||
|
||||
app.get('/hello', cors(), (req: Request, res: Response) => {
|
||||
res.send('Hello, World!')
|
||||
})
|
||||
|
5
ems/src/interfaces/map.ts
Normal file
5
ems/src/interfaces/map.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface SatelliteMapsProviders {
|
||||
google: 'google';
|
||||
yandex: 'yandex';
|
||||
}
|
||||
export type SatelliteMapsProvider = SatelliteMapsProviders[keyof SatelliteMapsProviders]
|
Reference in New Issue
Block a user