forked from spacebar/SplatNet-Backend
Add telemetry support from via Judd
This commit is contained in:
parent
536eeedff5
commit
227002a9ef
19 changed files with 4110 additions and 4 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
__pycache__
|
||||
.env
|
||||
boss.yaml
|
||||
node_modules
|
||||
|
|
@ -3,6 +3,7 @@ from cryptography.fernet import Fernet
|
|||
|
||||
class Settings(BaseSettings):
|
||||
port: int = 5000
|
||||
judd_port: int = 4000
|
||||
db_url: str
|
||||
fernet_key: str
|
||||
cookie_httponly: bool = True
|
||||
|
|
|
|||
31
judd/database.js
Normal file
31
judd/database.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
const { Sequelize } = require('sequelize');
|
||||
|
||||
const connection_string = process.env.DB_URL;
|
||||
const options = {
|
||||
dialect: 'postgres',
|
||||
logging: false,
|
||||
pool: { max: 5, min: 0, acquire: 30000, idle: 10000 }
|
||||
};
|
||||
|
||||
const sequelize = new Sequelize(connection_string, options);
|
||||
|
||||
module.exports = {
|
||||
connection: sequelize,
|
||||
connect
|
||||
};
|
||||
|
||||
require('./models/result');
|
||||
require('./models/splatfest_result');
|
||||
|
||||
async function connect() {
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
console.log('PostgreSQL connected.');
|
||||
|
||||
await sequelize.sync();
|
||||
console.log('PostgreSQL synchronized');
|
||||
} catch (error) {
|
||||
console.error('PostgreSQL connection error:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
10
judd/middleware/copy-request-stream.js
Normal file
10
judd/middleware/copy-request-stream.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
const stream = require('node:stream');
|
||||
|
||||
function copyRequestStreamMiddleware(request, response, next) {
|
||||
request.copy = stream.Readable.from(request.rawBody);
|
||||
request.copy.headers = request.headers;
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
module.exports = copyRequestStreamMiddleware;
|
||||
10
judd/middleware/raw-body.js
Normal file
10
judd/middleware/raw-body.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
const express = require('express');
|
||||
|
||||
const rawBodyMiddleware = express.raw({
|
||||
type: '*/*',
|
||||
verify(request, response, buffer, encoding) {
|
||||
request.rawBody = buffer;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = rawBodyMiddleware;
|
||||
14
judd/middleware/title-code.js
Normal file
14
judd/middleware/title-code.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
const titles = require('../titles');
|
||||
|
||||
function titleCodeMiddleware(request, response, next) {
|
||||
request.titleCode = 'wup-agmj';
|
||||
|
||||
if (!titles[request.titleCode]) {
|
||||
console.error(`Titles Available: ${Object.keys(titles)}`);
|
||||
return next(`No valid title config set for title code ${request.titleCode}`);
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
module.exports = titleCodeMiddleware;
|
||||
14
judd/middleware/validate-boss-digest.js
Normal file
14
judd/middleware/validate-boss-digest.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
const crypto = require('crypto');
|
||||
|
||||
function validateBOSSDigestMiddleware(request, response, next) {
|
||||
const calculatedHash = crypto.createHash('sha1').update(request.rawBody).digest('hex');
|
||||
const expectedHash = request.headers['x-boss-digest'];
|
||||
|
||||
if (calculatedHash !== expectedHash) {
|
||||
return next('Provided BOSS digest does not match the calculated hash');
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
module.exports = validateBOSSDigestMiddleware;
|
||||
41
judd/middleware/validate-multipart.js
Normal file
41
judd/middleware/validate-multipart.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
const titles = require('../titles');
|
||||
|
||||
function validateMultipartMiddleware(request, response, next) {
|
||||
const title = titles[request.titleCode];
|
||||
|
||||
if (!title || !title.multipart_validator) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const multipartValidator = title.multipart_validator;
|
||||
const validationSchema = title.validation_schema;
|
||||
|
||||
multipartValidator(request.copy, response, error => {
|
||||
if (error) {
|
||||
return next(error);
|
||||
}
|
||||
|
||||
const resultData = {
|
||||
...request.copy.body,
|
||||
};
|
||||
|
||||
if (Array.isArray(request.copy.files)) {
|
||||
for (const file of request.copy.files) {
|
||||
resultData[file.fieldname] = file.buffer;
|
||||
}
|
||||
}
|
||||
|
||||
const validationResult = validationSchema.validate(resultData);
|
||||
|
||||
if (validationResult.error) {
|
||||
console.error('[Validation Error]', validationResult.error.details);
|
||||
return next(validationResult.error);
|
||||
}
|
||||
|
||||
request.resultData = validationResult.value;
|
||||
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = validateMultipartMiddleware;
|
||||
24
judd/models/result.js
Normal file
24
judd/models/result.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
const { DataTypes } = require('sequelize');
|
||||
const { connection } = require('../database');
|
||||
|
||||
const Result = connection.define('Result', {
|
||||
type: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
bossUniqueId: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
bossDigest: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
}
|
||||
}, {
|
||||
tableName: 'results',
|
||||
timestamps: true
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
Result
|
||||
};
|
||||
79
judd/models/splatfest_result.js
Normal file
79
judd/models/splatfest_result.js
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
const { DataTypes } = require('sequelize');
|
||||
const { connection } = require('../database');
|
||||
|
||||
const SplatfestResult = connection.define('SplatfestResult', {
|
||||
type: { type: DataTypes.STRING },
|
||||
bossUniqueId: { type: DataTypes.STRING },
|
||||
bossDigest: { type: DataTypes.STRING },
|
||||
ServerEnv: { type: DataTypes.STRING },
|
||||
PId: { type: DataTypes.BIGINT },
|
||||
MiiName: { type: DataTypes.STRING },
|
||||
Model: { type: DataTypes.INTEGER },
|
||||
Skin: { type: DataTypes.INTEGER },
|
||||
EyeColor: { type: DataTypes.INTEGER },
|
||||
Weapon: { type: DataTypes.INTEGER },
|
||||
SumPaint: { type: DataTypes.INTEGER },
|
||||
Gear_Shoes: { type: DataTypes.INTEGER },
|
||||
Gear_Shoes_Skill0: { type: DataTypes.INTEGER },
|
||||
Gear_Shoes_Skill1: { type: DataTypes.INTEGER },
|
||||
Gear_Shoes_Skill2: { type: DataTypes.INTEGER },
|
||||
Gear_Clothes: { type: DataTypes.INTEGER },
|
||||
Gear_Clothes_Skill0: { type: DataTypes.INTEGER },
|
||||
Gear_Clothes_Skill1: { type: DataTypes.INTEGER },
|
||||
Gear_Clothes_Skill2: { type: DataTypes.INTEGER },
|
||||
Gear_Head: { type: DataTypes.INTEGER },
|
||||
Gear_Head_Skill0: { type: DataTypes.INTEGER },
|
||||
Gear_Head_Skill1: { type: DataTypes.INTEGER },
|
||||
Gear_Head_Skill2: { type: DataTypes.INTEGER },
|
||||
Rank: { type: DataTypes.INTEGER },
|
||||
Udemae: { type: DataTypes.INTEGER },
|
||||
RegularKillSum: { type: DataTypes.INTEGER },
|
||||
WinSum: { type: DataTypes.INTEGER },
|
||||
LoseSum: { type: DataTypes.INTEGER },
|
||||
TodaysCondition: { type: DataTypes.INTEGER },
|
||||
Region: { type: DataTypes.STRING },
|
||||
Area: { type: DataTypes.INTEGER },
|
||||
FesID: { type: DataTypes.INTEGER },
|
||||
FesState: { type: DataTypes.INTEGER },
|
||||
FesTeam: { type: DataTypes.INTEGER },
|
||||
FesGrade: { type: DataTypes.INTEGER },
|
||||
FesPoint: { type: DataTypes.INTEGER },
|
||||
FesPower: { type: DataTypes.INTEGER },
|
||||
BestFesPower: { type: DataTypes.INTEGER },
|
||||
Money: { type: DataTypes.INTEGER },
|
||||
Shell: { type: DataTypes.INTEGER },
|
||||
TotalBonusShell: { type: DataTypes.INTEGER },
|
||||
MatchingTime: { type: DataTypes.INTEGER },
|
||||
IsRematch: { type: DataTypes.INTEGER },
|
||||
SaveDataCorrupted: { type: DataTypes.INTEGER },
|
||||
DisconnectedPId: { type: DataTypes.BIGINT },
|
||||
DisconnectedMemHash: { type: DataTypes.BIGINT },
|
||||
SessionID: { type: DataTypes.BIGINT },
|
||||
StartNetworkTime: { type: DataTypes.BIGINT },
|
||||
GameMode: { type: DataTypes.INTEGER },
|
||||
Rule: { type: DataTypes.INTEGER },
|
||||
Stage: { type: DataTypes.INTEGER },
|
||||
Team: { type: DataTypes.INTEGER },
|
||||
IsWinGame: { type: DataTypes.INTEGER },
|
||||
Kill: { type: DataTypes.INTEGER },
|
||||
Death: { type: DataTypes.INTEGER },
|
||||
Paint: { type: DataTypes.INTEGER },
|
||||
IsNetworkBurst: { type: DataTypes.INTEGER },
|
||||
BottleneckPlayerNum: { type: DataTypes.INTEGER },
|
||||
MaxSilenceFrame: { type: DataTypes.INTEGER },
|
||||
MemoryHash: { type: DataTypes.BIGINT },
|
||||
Paint_Alpha: { type: DataTypes.INTEGER },
|
||||
Paint_Bravo: { type: DataTypes.INTEGER },
|
||||
FaceImg: { type: DataTypes.BLOB }
|
||||
}, {
|
||||
tableName: 'results',
|
||||
timestamps: true
|
||||
});
|
||||
|
||||
SplatfestResult.afterCreate((result, options) => {
|
||||
console.log('---------------------');
|
||||
console.log(result.toJSON());
|
||||
console.log('');
|
||||
});
|
||||
|
||||
module.exports = { SplatfestResult };
|
||||
3577
judd/package-lock.json
generated
Normal file
3577
judd/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
24
judd/package.json
Normal file
24
judd/package.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "judd",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "src/server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node ."
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"express-subdomain": "^1.0.5",
|
||||
"joi": "^17.7.0",
|
||||
"mongoose": "^6.7.1",
|
||||
"morgan": "^1.10.0",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"pg": "^8.18.0",
|
||||
"pg-hstore": "^2.3.4",
|
||||
"sequelize": "^6.37.7"
|
||||
}
|
||||
}
|
||||
36
judd/routes/index.js
Normal file
36
judd/routes/index.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
const router = require('express').Router();
|
||||
const titles = require('../titles');
|
||||
|
||||
router.post('/post', async (request, response, next) => {
|
||||
const title = titles[request.titleCode];
|
||||
|
||||
if (!title) {
|
||||
console.warn(`[Blocked] POST request with unknown title code: ${request.titleCode}`);
|
||||
return response.status(404).send('Not Found');
|
||||
}
|
||||
|
||||
const ResultTypeModel = title.result_model;
|
||||
|
||||
try {
|
||||
const result = await ResultTypeModel.create({
|
||||
type: title.type,
|
||||
bossUniqueId: request.headers['x-boss-uniqueid'],
|
||||
bossDigest: request.headers['x-boss-digest'],
|
||||
...request.resultData
|
||||
});
|
||||
|
||||
console.log(`\n--- ${title.type.toUpperCase()} RESULT SAVED ---`);
|
||||
console.log(`User: ${result.MiiName || 'Unknown'}`);
|
||||
console.log('----------------------------------\n');
|
||||
|
||||
} catch (error) {
|
||||
console.error('[Database Error]', error.message);
|
||||
return next(error);
|
||||
}
|
||||
|
||||
return response.send('success');
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
post: router
|
||||
};
|
||||
32
judd/routes/post.js
Normal file
32
judd/routes/post.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
const router = require('express').Router();
|
||||
const titles = require('../titles');
|
||||
|
||||
router.post('/post', async (request, response, next) => {
|
||||
const title = titles[request.titleCode];
|
||||
|
||||
if (!title) {
|
||||
console.error(`Title config not found for code: ${request.titleCode}`);
|
||||
return response.status(500).send('error: title not configured');
|
||||
}
|
||||
|
||||
const resultType = title.type;
|
||||
const ResultTypeModel = title.result_model;
|
||||
|
||||
try {
|
||||
const result = await ResultTypeModel.create({
|
||||
type: resultType,
|
||||
bossUniqueId: request.headers['x-boss-uniqueid'],
|
||||
bossDigest: request.headers['x-boss-digest'],
|
||||
...request.resultData
|
||||
});
|
||||
console.log(`[Database] Saved ${resultType} result for ID: ${result.bossUniqueId}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Database Save Error:', error);
|
||||
return next(error);
|
||||
}
|
||||
|
||||
return response.send('success');
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
48
judd/server.js
Normal file
48
judd/server.js
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
const express = require('express');
|
||||
const morgan = require('morgan');
|
||||
const routes = require('./routes')
|
||||
const titleCodeMiddleware = require('./middleware/title-code');
|
||||
const rawBodyMiddleware = require('./middleware/raw-body');
|
||||
const validateBOSSDigestMiddleware = require('./middleware/validate-boss-digest');
|
||||
const copyRequestStreamMiddleware = require('./middleware/copy-request-stream');
|
||||
const validateMultipartMiddleware = require('./middleware/validate-multipart');
|
||||
const database = require('./database');
|
||||
|
||||
const app = express();
|
||||
app.set('trust proxy', true);
|
||||
const port = process.env.JUDD_PORT || 4000;
|
||||
|
||||
app.use(morgan('dev'));
|
||||
app.use(titleCodeMiddleware);
|
||||
app.use(rawBodyMiddleware);
|
||||
app.use(validateBOSSDigestMiddleware);
|
||||
app.use(copyRequestStreamMiddleware);
|
||||
app.use(validateMultipartMiddleware);
|
||||
app.use(routes.post);
|
||||
|
||||
app.use((request, response) => {
|
||||
const protocol = request.protocol;
|
||||
const hostname = request.hostname;
|
||||
const opath = request.originalUrl;
|
||||
|
||||
const fullUri = `${protocol}://${hostname}${opath}`;
|
||||
|
||||
console.warn(`HTTP 404 at ${fullUri}`);
|
||||
|
||||
response.sendStatus(404);
|
||||
});
|
||||
|
||||
app.use((error, request, response, next) => {
|
||||
console.log(error);
|
||||
return response.status(500).send('error');
|
||||
});
|
||||
|
||||
async function main() {
|
||||
await database.connect();
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server listening on http://localhost:${port}`);
|
||||
});
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
3
judd/titles/index.js
Normal file
3
judd/titles/index.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
'wup-agmj': require('./splatoon')
|
||||
};
|
||||
71
judd/titles/splatoon.js
Normal file
71
judd/titles/splatoon.js
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
const multer = require('multer');
|
||||
const { joi } = require('../util');
|
||||
const { SplatfestResult } = require('../models/splatfest_result');
|
||||
|
||||
module.exports = {
|
||||
type: 'splatfest',
|
||||
result_model: SplatfestResult,
|
||||
validation_schema: joi.object({
|
||||
ServerEnv: joi.string(),
|
||||
PId: joi.numberstring(),
|
||||
MiiName: joi.string(),
|
||||
Model: joi.numberstring(),
|
||||
Skin: joi.numberstring(),
|
||||
EyeColor: joi.numberstring(),
|
||||
Weapon: joi.numberstring(),
|
||||
SumPaint: joi.numberstring(),
|
||||
Gear_Shoes: joi.numberstring(),
|
||||
Gear_Shoes_Skill0: joi.numberstring(),
|
||||
Gear_Shoes_Skill1: joi.numberstring(),
|
||||
Gear_Shoes_Skill2: joi.numberstring(),
|
||||
Gear_Clothes: joi.numberstring(),
|
||||
Gear_Clothes_Skill0: joi.numberstring(),
|
||||
Gear_Clothes_Skill1: joi.numberstring(),
|
||||
Gear_Clothes_Skill2: joi.numberstring(),
|
||||
Gear_Head: joi.numberstring(),
|
||||
Gear_Head_Skill0: joi.numberstring(),
|
||||
Gear_Head_Skill1: joi.numberstring(),
|
||||
Gear_Head_Skill2: joi.numberstring(),
|
||||
Rank: joi.numberstring(),
|
||||
Udemae: joi.numberstring(),
|
||||
RegularKillSum: joi.numberstring(),
|
||||
WinSum: joi.numberstring(),
|
||||
LoseSum: joi.numberstring(),
|
||||
TodaysCondition: joi.numberstring(),
|
||||
Region: joi.string(),
|
||||
Area: joi.numberstring(),
|
||||
FesID: joi.numberstring(),
|
||||
FesState: joi.numberstring(),
|
||||
FesTeam: joi.numberstring(),
|
||||
FesGrade: joi.numberstring(),
|
||||
FesPoint: joi.numberstring(),
|
||||
FesPower: joi.numberstring(),
|
||||
BestFesPower: joi.numberstring(),
|
||||
Money: joi.numberstring(),
|
||||
Shell: joi.numberstring(),
|
||||
TotalBonusShell: joi.numberstring(),
|
||||
MatchingTime: joi.numberstring(),
|
||||
IsRematch: joi.numberstring(),
|
||||
SaveDataCorrupted: joi.numberstring(),
|
||||
DisconnectedPId: joi.numberstring(),
|
||||
DisconnectedMemHash: joi.numberstring(),
|
||||
SessionID: joi.numberstring(),
|
||||
StartNetworkTime: joi.numberstring(),
|
||||
GameMode: joi.numberstring(),
|
||||
Rule: joi.numberstring(),
|
||||
Stage: joi.numberstring(),
|
||||
Team: joi.numberstring(),
|
||||
IsWinGame: joi.numberstring(),
|
||||
Kill: joi.numberstring(),
|
||||
Death: joi.numberstring(),
|
||||
Paint: joi.numberstring(),
|
||||
IsNetworkBurst: joi.numberstring(),
|
||||
BottleneckPlayerNum: joi.numberstring(),
|
||||
MaxSilenceFrame: joi.numberstring(),
|
||||
MemoryHash: joi.numberstring(),
|
||||
Paint_Alpha: joi.numberstring(),
|
||||
Paint_Bravo: joi.numberstring(),
|
||||
FaceImg: joi.binary()
|
||||
}).unknown(true).options({ presence: 'optional' }).required(),
|
||||
multipart_validator: multer().any()
|
||||
};
|
||||
29
judd/util.js
Normal file
29
judd/util.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
const joi = require('joi');
|
||||
|
||||
const joiExtended = joi.extend((joi) => {
|
||||
return {
|
||||
type: 'emptystringtonum',
|
||||
base: joi.number(),
|
||||
messages: {
|
||||
'emptystringtonum.base': '{{#label}} must be an empty string',
|
||||
},
|
||||
coerce(value, helpers) {
|
||||
if (value !== '') {
|
||||
return { value: 0, errors: helpers.error('emptystringtonum.base') }
|
||||
}
|
||||
|
||||
return { value: 0 }
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
joiExtended.numberstring = () => {
|
||||
return joi.alternatives(
|
||||
joiExtended.emptystringtonum(),
|
||||
joi.number(),
|
||||
)
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
joi: joiExtended
|
||||
};
|
||||
67
main.py
67
main.py
|
|
@ -1,4 +1,4 @@
|
|||
from fastapi import FastAPI, Request
|
||||
from fastapi import FastAPI, Request, Response
|
||||
from routes import sessionid_check, sso
|
||||
from config import settings
|
||||
import uvicorn
|
||||
|
|
@ -11,6 +11,17 @@ from routes import me
|
|||
from services.boss_retrieval import process_boss_file
|
||||
from contextlib import asynccontextmanager
|
||||
import asyncio
|
||||
import subprocess
|
||||
import signal
|
||||
import sys
|
||||
import httpx
|
||||
import os
|
||||
|
||||
JUDD_CMD = ["node", "server.js"]
|
||||
JUDD_CWD = "./judd"
|
||||
|
||||
judd_process = None
|
||||
|
||||
|
||||
async def boss_worker_loop():
|
||||
print("background worker started")
|
||||
|
|
@ -24,10 +35,30 @@ async def boss_worker_loop():
|
|||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
task = asyncio.create_task(boss_worker_loop())
|
||||
global judd_process
|
||||
|
||||
print("starting judd server")
|
||||
|
||||
env_vars = os.environ.copy()
|
||||
env_vars.update({k.upper(): str(v) for k, v in settings.model_dump().items()})
|
||||
|
||||
judd_process = subprocess.Popen(
|
||||
JUDD_CMD,
|
||||
cwd=JUDD_CWD,
|
||||
stdout=sys.stdout,
|
||||
stderr=sys.stderr,
|
||||
env=env_vars,
|
||||
)
|
||||
|
||||
worker = asyncio.create_task(boss_worker_loop())
|
||||
yield
|
||||
print("shutdown: cancelling background tasks")
|
||||
task.cancel()
|
||||
worker.cancel()
|
||||
|
||||
if judd_process:
|
||||
judd_process.send_signal(signal.SIGINT)
|
||||
judd_process.wait()
|
||||
|
||||
|
||||
app = FastAPI(lifespan=lifespan)
|
||||
|
||||
|
|
@ -40,6 +71,36 @@ app.add_middleware(
|
|||
expose_headers=["Set-Cookie"],
|
||||
)
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
async def proxy_fallback(request: Request, call_next):
|
||||
response = await call_next(request)
|
||||
|
||||
if response.status_code != 404:
|
||||
return response
|
||||
|
||||
url = f"http://127.0.0.1:{settings.judd_port}{request.url.path}"
|
||||
body = await request.body()
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
try:
|
||||
judd_resp = await client.request(
|
||||
request.method,
|
||||
url,
|
||||
headers={k: v for k, v in request.headers.items() if k.lower() != "host"},
|
||||
params=request.query_params,
|
||||
content=body,
|
||||
)
|
||||
except httpx.RequestError:
|
||||
return response
|
||||
|
||||
return Response(
|
||||
content=judd_resp.content,
|
||||
status_code=judd_resp.status_code,
|
||||
headers=dict(judd_resp.headers),
|
||||
)
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
async def force_cors_on_errors(request: Request, call_next):
|
||||
response = await call_next(request)
|
||||
|
|
|
|||
Loading…
Reference in a new issue