Bump versions to 1.0.18 and remove unused plugins
This commit is contained in:
68
backend/package-lock.json
generated
68
backend/package-lock.json
generated
@@ -1,24 +1,21 @@
|
||||
{
|
||||
"name": "edh-stats-backend",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.18",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "edh-stats-backend",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.18",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.4.0",
|
||||
"@fastify/jwt": "^7.2.0",
|
||||
"@fastify/rate-limit": "^9.0.1",
|
||||
"@fastify/under-pressure": "^8.3.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"better-sqlite3": "^9.2.2",
|
||||
"close-with-grace": "^1.2.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"fastify": "^4.24.3",
|
||||
"fastify-metrics": "^10.0.1",
|
||||
"pino-pretty": "^13.1.3",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
@@ -187,27 +184,6 @@
|
||||
"fast-deep-equal": "^3.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/rate-limit": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/rate-limit/-/rate-limit-9.1.0.tgz",
|
||||
"integrity": "sha512-h5dZWCkuZXN0PxwqaFQLxeln8/LNwQwH9popywmDCFdKfgpi4b/HoMH1lluy6P+30CG9yzzpSpwTCIPNB9T1JA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@lukeed/ms": "^2.0.1",
|
||||
"fastify-plugin": "^4.0.0",
|
||||
"toad-cache": "^3.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/under-pressure": {
|
||||
"version": "8.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/under-pressure/-/under-pressure-8.5.2.tgz",
|
||||
"integrity": "sha512-aFLFN2Vt1UPlt4b1m1wEr8eOzwcPUKNhda6g9OFbQ+pHLE6aStm2nGrd7DujqHFJBYEf1j6mPh0ZpsZIqQQz2Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/error": "^3.0.0",
|
||||
"fastify-plugin": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
||||
@@ -686,12 +662,6 @@
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bintrees": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz",
|
||||
"integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bl": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||
@@ -1908,19 +1878,6 @@
|
||||
"toad-cache": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fastify-metrics": {
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/fastify-metrics/-/fastify-metrics-10.6.0.tgz",
|
||||
"integrity": "sha512-QIPncCnwBOEObMn+VaRhsBC1ox8qEsaiYF2sV/A1UbXj7ic70W8/HNn/hlEC2W8JQbBeZMx++o1um2fPfhsFDQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fastify-plugin": "^4.3.0",
|
||||
"prom-client": "^14.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"fastify": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/fastify-plugin": {
|
||||
"version": "4.5.1",
|
||||
"resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.5.1.tgz",
|
||||
@@ -3559,18 +3516,6 @@
|
||||
"integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/prom-client": {
|
||||
"version": "14.2.0",
|
||||
"resolved": "https://registry.npmjs.org/prom-client/-/prom-client-14.2.0.tgz",
|
||||
"integrity": "sha512-sF308EhTenb/pDRPakm+WgiN+VdM/T1RaHj1x+MvAuT8UiQP8JmOEbxVqtkbfR4LrvOg5n7ic01kRBDGXjYikA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tdigest": "^0.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
@@ -4356,15 +4301,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/tdigest": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz",
|
||||
"integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bintrees": "1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/text-table": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "edh-stats-backend",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.18",
|
||||
"description": "Backend API for EDH/Commander stats tracking application",
|
||||
"main": "src/server.js",
|
||||
"type": "module",
|
||||
@@ -17,14 +17,11 @@
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.4.0",
|
||||
"@fastify/jwt": "^7.2.0",
|
||||
"@fastify/rate-limit": "^9.0.1",
|
||||
"@fastify/under-pressure": "^8.3.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"better-sqlite3": "^9.2.2",
|
||||
"close-with-grace": "^1.2.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"fastify": "^4.24.3",
|
||||
"fastify-metrics": "^10.0.1",
|
||||
"pino-pretty": "^13.1.3",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
// JWT Authentication Middleware
|
||||
import { jwtConfig } from '../config/jwt.js'
|
||||
|
||||
// Verify JWT token
|
||||
export const verifyJWT = async (request, reply) => {
|
||||
try {
|
||||
await request.jwtVerify()
|
||||
} catch (err) {
|
||||
reply.code(401).send({
|
||||
reply.code(401).send({
|
||||
error: 'Unauthorized',
|
||||
message: 'Invalid or expired token'
|
||||
message: 'Invalid or expired token'
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -28,71 +25,72 @@ export const validateUser = async (request, reply) => {
|
||||
try {
|
||||
const user = request.user
|
||||
if (!user) {
|
||||
return reply.code(401).send({
|
||||
return reply.code(401).send({
|
||||
error: 'Unauthorized',
|
||||
message: 'User not authenticated'
|
||||
message: 'User not authenticated'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// You could add additional user validation here
|
||||
// e.g., check if user is active, banned, etc.
|
||||
|
||||
} catch (err) {
|
||||
reply.code(500).send({
|
||||
reply.code(500).send({
|
||||
error: 'Internal Server Error',
|
||||
message: 'Failed to validate user'
|
||||
message: 'Failed to validate user'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Extract user ID from token and validate resource ownership
|
||||
export const validateOwnership = (resourceParam = 'id', resourceTable = 'commanders') => {
|
||||
export const validateOwnership = (
|
||||
resourceParam = 'id',
|
||||
resourceTable = 'commanders'
|
||||
) => {
|
||||
return async (request, reply) => {
|
||||
try {
|
||||
const user = request.user
|
||||
if (!user) {
|
||||
return reply.code(401).send({
|
||||
return reply.code(401).send({
|
||||
error: 'Unauthorized',
|
||||
message: 'User not authenticated'
|
||||
message: 'User not authenticated'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const resourceId = request.params[resourceParam]
|
||||
if (!resourceId) {
|
||||
return reply.code(400).send({
|
||||
return reply.code(400).send({
|
||||
error: 'Bad Request',
|
||||
message: 'Resource ID not provided'
|
||||
message: 'Resource ID not provided'
|
||||
})
|
||||
}
|
||||
|
||||
const db = await import('../config/database.js').then(m => m.default)
|
||||
|
||||
const db = await import('../config/database.js').then((m) => m.default)
|
||||
const database = await db.initialize()
|
||||
|
||||
|
||||
// Check if user owns the resource
|
||||
const query = `SELECT user_id FROM ${resourceTable} WHERE id = ?`
|
||||
const resource = database.prepare(query).get([resourceId])
|
||||
|
||||
|
||||
if (!resource) {
|
||||
return reply.code(404).send({
|
||||
return reply.code(404).send({
|
||||
error: 'Not Found',
|
||||
message: 'Resource not found'
|
||||
message: 'Resource not found'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
if (resource.user_id !== user.id) {
|
||||
return reply.code(403).send({
|
||||
return reply.code(403).send({
|
||||
error: 'Forbidden',
|
||||
message: 'Access denied to this resource'
|
||||
message: 'Access denied to this resource'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// Add resource to request object for later use
|
||||
request.resourceId = resourceId
|
||||
|
||||
} catch (err) {
|
||||
reply.code(500).send({
|
||||
reply.code(500).send({
|
||||
error: 'Internal Server Error',
|
||||
message: 'Failed to validate ownership'
|
||||
message: 'Failed to validate ownership'
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -118,4 +116,4 @@ export const rateLimitGeneral = {
|
||||
skipOnError: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { z } from 'zod'
|
||||
import dbManager from '../config/database.js'
|
||||
|
||||
export default async function statsRoutes(fastify, options) {
|
||||
@@ -80,20 +79,20 @@ export default async function statsRoutes(fastify, options) {
|
||||
]
|
||||
},
|
||||
async (request, reply) => {
|
||||
try {
|
||||
const db = await dbManager.initialize()
|
||||
const userId = request.user.id
|
||||
try {
|
||||
const db = await dbManager.initialize()
|
||||
const userId = request.user.id
|
||||
|
||||
// Get detailed commander stats with at least 5 games, sorted by total games then win rate
|
||||
const rawStats = db
|
||||
.prepare(
|
||||
`
|
||||
// Get detailed commander stats with at least 5 games, sorted by total games then win rate
|
||||
const rawStats = db
|
||||
.prepare(
|
||||
`
|
||||
SELECT * FROM commander_stats
|
||||
WHERE user_id = ? AND total_games >= 5
|
||||
ORDER BY total_games DESC, win_rate DESC
|
||||
`
|
||||
)
|
||||
.all([userId])
|
||||
)
|
||||
.all([userId])
|
||||
|
||||
// Convert snake_case to camelCase
|
||||
const stats = rawStats.map((stat) => ({
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import fastify from 'fastify'
|
||||
import cors from '@fastify/cors'
|
||||
import jwt from '@fastify/jwt'
|
||||
import underPressure from '@fastify/under-pressure'
|
||||
import rateLimit from '@fastify/rate-limit'
|
||||
import closeWithGrace from 'close-with-grace'
|
||||
|
||||
// Import configurations
|
||||
import { jwtConfig, corsConfig, rateLimitConfig, serverConfig } from './config/jwt.js'
|
||||
import { jwtConfig, corsConfig, serverConfig } from './config/jwt.js'
|
||||
import dbManager from './config/database.js'
|
||||
|
||||
// Import routes
|
||||
@@ -20,7 +18,7 @@ export default async function build(opts = {}) {
|
||||
|
||||
// Register plugins
|
||||
await app.register(cors, corsConfig)
|
||||
|
||||
|
||||
await app.register(jwt, {
|
||||
secret: jwtConfig.secret
|
||||
})
|
||||
@@ -30,9 +28,9 @@ export default async function build(opts = {}) {
|
||||
try {
|
||||
await request.jwtVerify()
|
||||
} catch (err) {
|
||||
reply.code(401).send({
|
||||
reply.code(401).send({
|
||||
error: 'Unauthorized',
|
||||
message: 'Invalid or expired token'
|
||||
message: 'Invalid or expired token'
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -42,7 +40,7 @@ export default async function build(opts = {}) {
|
||||
try {
|
||||
await dbManager.initialize()
|
||||
const dbHealthy = await dbManager.healthCheck()
|
||||
|
||||
|
||||
const status = {
|
||||
status: 'healthy',
|
||||
timestamp: new Date().toISOString(),
|
||||
@@ -50,7 +48,7 @@ export default async function build(opts = {}) {
|
||||
memory: process.memoryUsage(),
|
||||
database: dbHealthy ? 'connected' : 'disconnected'
|
||||
}
|
||||
|
||||
|
||||
if (dbHealthy) {
|
||||
return status
|
||||
} else {
|
||||
@@ -86,7 +84,7 @@ export default async function build(opts = {}) {
|
||||
|
||||
// Root endpoint
|
||||
app.get('/', async (request, reply) => {
|
||||
return {
|
||||
return {
|
||||
message: 'EDH/Commander Stats API',
|
||||
version: '1.0.0',
|
||||
status: 'running'
|
||||
@@ -104,7 +102,7 @@ export default async function build(opts = {}) {
|
||||
// Error handler
|
||||
app.setErrorHandler(async (error, request, reply) => {
|
||||
app.log.error(error)
|
||||
|
||||
|
||||
// Handle validation errors
|
||||
if (error.validation) {
|
||||
reply.code(400).send({
|
||||
@@ -163,7 +161,7 @@ export default async function build(opts = {}) {
|
||||
} else {
|
||||
app.log.info({ signal }, 'Received signal')
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
await dbManager.close()
|
||||
await app.close()
|
||||
@@ -188,18 +186,18 @@ async function start() {
|
||||
// }
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// Initialize database
|
||||
await dbManager.initialize()
|
||||
|
||||
|
||||
const port = serverConfig.port
|
||||
const host = serverConfig.host
|
||||
|
||||
|
||||
await app.listen({ port, host })
|
||||
|
||||
|
||||
app.log.info(`Server listening on http://${host}:${port}`)
|
||||
app.log.info(`Health check available at http://${host}:${port}/api/health`)
|
||||
|
||||
|
||||
return app
|
||||
} catch (error) {
|
||||
console.error('Failed to start server:', error)
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.0.17
|
||||
1.0.18
|
||||
|
||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "edh-stats-frontend",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.18",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "edh-stats-frontend",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.18",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"alpinejs": "^3.13.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "edh-stats-frontend",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.18",
|
||||
"description": "Frontend for EDH/Commander stats tracking application",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -26,4 +26,4 @@
|
||||
],
|
||||
"author": "EDH Stats App",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.0.17
|
||||
1.0.18
|
||||
|
||||
Reference in New Issue
Block a user