Enable the sort-keys eslint rule (#1746)

## Description:

Enable the `sort-keys` eslint rule.

Fixes #1629

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [ ] I have read and accepted the CLA agreement (only required once).
This commit is contained in:
Scott Anderson
2025-08-07 19:13:42 -04:00
committed by GitHub
parent 63cb51a4f9
commit b56e380107
48 changed files with 320 additions and 254 deletions
+2
View File
@@ -11,11 +11,13 @@ const log = logger.child({ component: "Archive" });
// R2 client configuration
const r2 = new S3({
region: "auto", // R2 ignores region, but it's required by the SDK
/* eslint-disable sort-keys */
endpoint: config.r2Endpoint(),
credentials: {
accessKeyId: config.r2AccessKey(),
secretAccessKey: config.r2SecretKey(),
},
/* eslint-disable sort-keys */
});
const bucket = config.r2Bucket();
+7 -7
View File
@@ -60,12 +60,12 @@ export class Cloudflare {
data?: any,
): Promise<T> {
const response = await fetch(url, {
method,
body: data ? JSON.stringify(data) : undefined,
headers: {
Authorization: `Bearer ${this.apiToken}`,
"Content-Type": "application/json",
},
body: data ? JSON.stringify(data) : undefined,
method,
});
if (!response.ok) {
@@ -178,7 +178,6 @@ export class Cloudflare {
log.info(`Created credentials file at: ${this.credsPath}`);
const tunnelConfig: CloudflaredConfig = {
tunnel: tunnelId,
"credentials-file": this.credsPath,
ingress: [
...Array.from(subdomainToService.entries()).map(
@@ -191,6 +190,7 @@ export class Cloudflare {
service: "http_status:404",
},
],
tunnel: tunnelId,
};
// Write config file
@@ -210,11 +210,11 @@ export class Cloudflare {
const recordId = existingRecords.result[0]?.id;
const dnsData = {
type: "CNAME",
name: subdomain,
content: `${tunnelId}.cfargotunnel.com`,
ttl: 1,
name: subdomain,
proxied: true,
ttl: 1,
type: "CNAME",
};
if (recordId) {
@@ -240,12 +240,12 @@ export class Cloudflare {
["tunnel", "--config", this.configPath, "--loglevel", "error", "run"],
{
detached: true,
stdio: ["ignore", "pipe", "pipe"],
env: {
...process.env,
// Set this to bypass origin cert requirement for named tunnels
TUNNEL_ORIGIN_CERT: "/dev/null",
},
stdio: ["ignore", "pipe", "pipe"],
},
);
+5 -5
View File
@@ -39,16 +39,16 @@ export class GameManager {
Date.now(),
this.config,
{
gameMap: GameMapType.World,
gameType: GameType.Private,
bots: 400,
difficulty: Difficulty.Medium,
disableNPCs: false,
disabledUnits: [],
gameMap: GameMapType.World,
gameMode: GameMode.FFA,
gameType: GameType.Private,
infiniteGold: false,
infiniteTroops: false,
instantBuild: false,
gameMode: GameMode.FFA,
bots: 400,
disabledUnits: [],
...gameConfig,
},
creatorClientID,
+26 -26
View File
@@ -123,15 +123,15 @@ export class GameServer {
// Log when lobby creator joins private game
if (client.clientID === this.LobbyCreatorID) {
this.log.info("Lobby creator joined", {
gameID: this.id,
creatorID: this.LobbyCreatorID,
gameID: this.id,
});
}
this.log.info("client (re)joining game", {
clientID: client.clientID,
persistentID: client.persistentID,
clientIP: ipAnonymize(client.ip),
isRejoin: lastTurn > 0,
persistentID: client.persistentID,
});
if (
@@ -210,9 +210,9 @@ export class GameServer {
});
client.ws.send(
JSON.stringify({
type: "error",
error,
message,
type: "error",
} satisfies ServerErrorMessage),
);
client.ws.close(1002, "ClientMessageSchema");
@@ -244,8 +244,8 @@ export class GameServer {
this.log.warn(`Only lobby creator can kick players`, {
clientID: authenticatedClientID,
creatorID: this.LobbyCreatorID,
target: clientMsg.intent.target,
gameID: this.id,
target: clientMsg.intent.target,
});
return;
}
@@ -261,9 +261,9 @@ export class GameServer {
// Log and execute the kick
this.log.info(`Lobby creator initiated kick of player`, {
creatorID: authenticatedClientID,
target: clientMsg.intent.target,
gameID: this.id,
kickMethod: "websocket",
target: clientMsg.intent.target,
});
this.kickClient(clientMsg.intent.target);
@@ -358,8 +358,8 @@ export class GameServer {
this._hasPrestarted = true;
const prestartMsg = ServerPrestartMessageSchema.safeParse({
type: "prestart",
gameMap: this.gameConfig.gameMap,
type: "prestart",
});
if (!prestartMsg.success) {
@@ -393,13 +393,13 @@ export class GameServer {
this.lastPingUpdate = Date.now();
const result = GameStartInfoSchema.safeParse({
gameID: this.id,
config: this.gameConfig,
gameID: this.id,
players: this.activeClients.map((c) => ({
username: c.username,
clientID: c.clientID,
pattern: c.pattern,
flag: c.flag,
pattern: c.pattern,
username: c.username,
})),
});
if (!result.success) {
@@ -430,9 +430,9 @@ export class GameServer {
try {
ws.send(
JSON.stringify({
type: "start",
turns: this.turns.slice(lastTurn),
gameStartInfo: this.gameStartInfo,
turns: this.turns.slice(lastTurn),
type: "start",
} satisfies ServerStartGameMessage),
);
} catch (error) {
@@ -447,8 +447,8 @@ export class GameServer {
private endTurn() {
const pastTurn: Turn = {
turnNumber: this.turns.length,
intents: this.intents,
turnNumber: this.turns.length,
};
this.turns.push(pastTurn);
this.intents = [];
@@ -457,8 +457,8 @@ export class GameServer {
this.checkDisconnectedStatus();
const msg = JSON.stringify({
type: "turn",
turn: pastTurn,
type: "turn",
} satisfies ServerTurnMessage);
this.activeClients.forEach((c) => {
c.ws.send(msg);
@@ -510,9 +510,9 @@ export class GameServer {
}
this.log.error("Error archiving game record details:", {
gameId: this.id,
errorType: typeof error,
error: errorDetails,
errorType: typeof error,
gameId: this.id,
});
}
}
@@ -587,12 +587,12 @@ export class GameServer {
public gameInfo(): GameInfo {
return {
gameID: this.id,
clients: this.activeClients.map((c) => ({
username: c.username,
clientID: c.clientID,
username: c.username,
})),
gameConfig: this.gameConfig,
gameID: this.id,
msUntilStart: this.isPublic()
? this.createdAt + this.config.gameCreationRate()
: undefined,
@@ -618,8 +618,8 @@ export class GameServer {
});
client.ws.send(
JSON.stringify({
type: "error",
error: "Kicked from game (you may have been playing on another tab)",
type: "error",
} satisfies ServerErrorMessage),
);
client.ws.close(1000, "Kicked from game");
@@ -660,9 +660,9 @@ export class GameServer {
private markClientDisconnected(clientID: string, isDisconnected: boolean) {
this.clientsDisconnectedStatus.set(clientID, isDisconnected);
this.addIntent({
type: "mark_disconnected",
clientID: clientID,
clientID,
isDisconnected: isDisconnected,
type: "mark_disconnected",
});
}
@@ -681,10 +681,10 @@ export class GameServer {
}
return {
clientID: player.clientID,
username: player.username,
persistentID:
this.allClients.get(player.clientID)?.persistentID ?? "",
stats,
username: player.username,
} satisfies PlayerRecord;
},
);
@@ -722,17 +722,17 @@ export class GameServer {
}
const serverDesync = ServerDesyncSchema.safeParse({
type: "desync",
turn: lastHashTurn,
correctHash: mostCommonHash,
clientsWithCorrectHash:
this.activeClients.length - outOfSyncClients.length,
correctHash: mostCommonHash,
totalActiveClients: this.activeClients.length,
turn: lastHashTurn,
type: "desync",
});
if (!serverDesync.success) {
this.log.warn("failed to create desync message", {
gameID: this.id,
error: serverDesync.error,
gameID: this.id,
});
return;
}
@@ -745,8 +745,8 @@ export class GameServer {
}
this.sentDesyncMessageClients.add(c.clientID);
this.log.info("sending desync to client", {
gameID: this.id,
clientID: c.clientID,
gameID: this.id,
persistentID: c.persistentID,
});
c.ws.send(desyncMsg);
+3
View File
@@ -29,6 +29,7 @@ if (config.otelEnabled()) {
// Add OTLP exporter for logs
const logExporter = new OTLPLogExporter({
url: `${config.otelEndpoint()}/v1/logs`,
// eslint-disable-next-line sort-keys
headers,
});
@@ -56,6 +57,7 @@ const addSeverityFormat = winston.format((info) => {
// Define your base/parent logger
const logger = winston.createLogger({
level: "info",
/* eslint-disable sort-keys */
format: winston.format.combine(
winston.format.timestamp(),
addSeverityFormat(),
@@ -65,6 +67,7 @@ const logger = winston.createLogger({
service: "openfront",
environment: process.env.GAME_ENV ?? "prod",
},
/* eslint-enable sort-keys */
transports: [
new winston.transports.Console(),
new OpenTelemetryTransportV3(),
+29 -29
View File
@@ -20,33 +20,33 @@ const config = getServerConfigFromServer();
// How many times each map should appear in the playlist.
// Note: The Partial should eventually be removed for better type safety.
const frequency: Partial<Record<GameMapName, number>> = {
World: 3,
Europe: 2,
Africa: 2,
Baikal: 2,
Australia: 1,
NorthAmerica: 1,
Britannia: 1,
GatewayToTheAtlantic: 1,
Iceland: 1,
SouthAmerica: 1,
DeglaciatedAntarctica: 1,
EuropeClassic: 1,
Mena: 1,
Pangaea: 1,
Asia: 1,
Australia: 1,
Baikal: 2,
BetweenTwoSeas: 1,
BlackSea: 1,
Britannia: 1,
DeglaciatedAntarctica: 1,
EastAsia: 1,
Europe: 2,
EuropeClassic: 1,
FalklandIslands: 1,
FaroeIslands: 1,
GatewayToTheAtlantic: 1,
Halkidiki: 1,
Iceland: 1,
Italia: 1,
Mars: 1,
MarsRevised: 1,
BetweenTwoSeas: 1,
EastAsia: 1,
BlackSea: 1,
FaroeIslands: 1,
FalklandIslands: 1,
Halkidiki: 1,
StraitOfGibraltar: 1,
Italia: 1,
Yenisei: 1,
Mena: 1,
NorthAmerica: 1,
Pangaea: 1,
Pluto: 1,
SouthAmerica: 1,
StraitOfGibraltar: 1,
World: 3,
Yenisei: 1,
};
interface MapWithMode {
@@ -77,18 +77,18 @@ export class MapPlaylist {
// Create the default public game config (from your GameManager)
return {
gameMap: map,
maxPlayers: config.lobbyMaxPlayers(map, mode, playerTeams),
gameType: GameType.Public,
bots: 400,
difficulty: Difficulty.Medium,
disableNPCs: mode === GameMode.Team,
disabledUnits: [],
gameMap: map,
gameMode: mode,
gameType: GameType.Public,
infiniteGold: false,
infiniteTroops: false,
instantBuild: false,
disableNPCs: mode === GameMode.Team,
gameMode: mode,
maxPlayers: config.lobbyMaxPlayers(map, mode, playerTeams),
playerTeams,
bots: 400,
disabledUnits: [],
} satisfies GameConfig;
}
+6 -6
View File
@@ -54,8 +54,8 @@ app.use(express.json());
app.set("trust proxy", 3);
app.use(
rateLimit({
windowMs: 1000, // 1 second
max: 20, // 20 requests per IP per second
windowMs: 1000, // 1 second
}),
);
@@ -180,10 +180,10 @@ app.post(
const response = await fetch(
`http://localhost:${config.workerPort(gameID)}/api/kick_player/${gameID}/${clientID}`,
{
method: "POST",
headers: {
[config.adminHeader()]: config.adminToken(),
},
method: "POST",
},
);
@@ -232,10 +232,10 @@ async function fetchLobbies(): Promise<number> {
.filter((result) => result !== null)
.map((gi: GameInfo) => {
return {
gameID: gi.gameID,
numClients: gi?.clients?.length ?? 0,
gameConfig: gi.gameConfig,
gameID: gi.gameID,
msUntilStart: (gi.msUntilStart ?? Date.now()) - Date.now(),
numClients: gi?.clients?.length ?? 0,
} as GameInfo;
});
@@ -283,12 +283,12 @@ async function schedulePublicGame(playlist: MapPlaylist) {
const response = await fetch(
`http://localhost:${config.workerPort(gameID)}/api/create_game/${gameID}`,
{
method: "POST",
body: JSON.stringify(playlist.gameConfig()),
headers: {
"Content-Type": "application/json",
[config.adminHeader()]: config.adminToken(),
},
body: JSON.stringify(playlist.gameConfig()),
method: "POST",
},
);
+2
View File
@@ -18,6 +18,7 @@ export function getOtelResource() {
export function getPromLabels() {
return {
"service.instance.id": process.env.HOSTNAME,
/* eslint-disable sort-keys */
"openfront.environment": config.env(),
"openfront.host": process.env.HOST,
"openfront.domain": process.env.DOMAIN,
@@ -25,5 +26,6 @@ export function getPromLabels() {
"openfront.component": process.env.WORKER_ID
? "Worker " + process.env.WORKER_ID
: "Master",
/* eslint-enable sort-keys */
};
}
+1 -1
View File
@@ -55,8 +55,8 @@ async function setupTunnels() {
if (!(await cloudflare.configAlreadyExists())) {
await cloudflare.createTunnel({
subdomain: config.subdomain(),
domain: config.domain(),
subdomain: config.subdomain(),
subdomainToService: domainToService,
} as TunnelConfig);
} else {
+9 -9
View File
@@ -85,8 +85,8 @@ export async function startWorker() {
app.use(express.static(path.join(__dirname, "../../out")));
app.use(
rateLimit({
windowMs: 1000, // 1 second
max: 20, // 20 requests per IP per second
windowMs: 1000, // 1 second
}),
);
@@ -232,9 +232,9 @@ export async function startWorker() {
if (!gameRecord) {
return res.status(404).json({
success: false,
error: "Game not found",
exists: false,
success: false,
});
}
@@ -246,20 +246,20 @@ export async function startWorker() {
`git commit mismatch for game ${req.params.id}, expected ${config.gitCommit()}, got ${gameRecord.gitCommit}`,
);
return res.status(409).json({
success: false,
details: {
actualCommit: gameRecord.gitCommit,
expectedCommit: config.gitCommit(),
},
error: "Version mismatch",
exists: true,
details: {
expectedCommit: config.gitCommit(),
actualCommit: gameRecord.gitCommit,
},
success: false,
});
}
return res.status(200).json({
success: true,
exists: true,
gameRecord: gameRecord,
success: true,
});
}),
);
@@ -324,8 +324,8 @@ export async function startWorker() {
log.warn("Error parsing client message", error);
ws.send(
JSON.stringify({
type: "error",
error: error.toString(),
type: "error",
} satisfies ServerErrorMessage),
);
ws.close(1002, "ClientJoinMessageSchema");
+3 -3
View File
@@ -25,20 +25,20 @@ export function initWorkerMetrics(gameManager: GameManager): void {
// Create metrics exporter
const metricExporter = new OTLPMetricExporter({
url: `${config.otelEndpoint()}/v1/metrics`,
headers,
url: `${config.otelEndpoint()}/v1/metrics`,
});
// Configure the metric reader
const metricReader = new PeriodicExportingMetricReader({
exporter: metricExporter,
exportIntervalMillis: 15000, // Export metrics every 15 seconds
exporter: metricExporter,
});
// Create a meter provider
const meterProvider = new MeterProvider({
resource,
readers: [metricReader],
resource,
});
// Get meter for creating metrics
+3 -2
View File
@@ -21,6 +21,7 @@ export async function verifyClientToken(
config: ServerConfig,
): Promise<TokenVerificationResult> {
if (PersistentIdSchema.safeParse(token).success) {
// eslint-disable-next-line sort-keys
return { persistentId: token, claims: null };
}
try {
@@ -29,8 +30,8 @@ export async function verifyClientToken(
const key = await config.jwkPublicKey();
const { payload, protectedHeader } = await jwtVerify(token, key, {
algorithms: ["EdDSA"],
issuer,
audience,
issuer,
});
const result = TokenPayloadSchema.safeParse(payload);
if (!result.success) {
@@ -40,7 +41,7 @@ export async function verifyClientToken(
}
const claims = result.data;
const persistentId = claims.sub;
return { persistentId, claims };
return { claims, persistentId };
} catch (e) {
return false;
}