Skip to content

Commit a0a4e60

Browse files
committed
CHANGES:
- Disabled security enhancements in autoexec - RestAPI: /squadrons returns members now - RestAPI: Additional parameter offset for pagination in /squadrons, /topkills, /topkdr, /trueskill, /traps
1 parent 4fc5fa5 commit a0a4e60

File tree

3 files changed

+46
-45
lines changed

3 files changed

+46
-45
lines changed

core/data/impl/instanceimpl.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,16 @@ def __post_init__(self):
4646
if use_upnp != net.get('use_upnp', True):
4747
net['use_upnp'] = use_upnp
4848
dirty |= True
49-
# set new security settings (as of DCS 2.9.18)
50-
allow_unsafe_api = dcs_config.get('allow_unsafe_api', ["userhooks"])
51-
allow_dostring_in = dcs_config.get('allow_dostring_in', ["server", "mission"])
52-
if set(allow_unsafe_api) != set(net.get('allow_unsafe_api', set())):
53-
net['allow_unsafe_api'] = allow_unsafe_api
54-
dirty |= True
55-
if set(allow_dostring_in) != net.get('allow_dostring_in', set()):
56-
net['allow_dostring_in'] = allow_dostring_in
57-
dirty |= True
49+
# removed as of DCS 2.9.19
50+
# # set new security settings (as of DCS 2.9.18)
51+
# allow_unsafe_api = dcs_config.get('allow_unsafe_api', ["userhooks"])
52+
# allow_dostring_in = dcs_config.get('allow_dostring_in', ["server", "mission"])
53+
# if set(allow_unsafe_api) != set(net.get('allow_unsafe_api', set())):
54+
# net['allow_unsafe_api'] = allow_unsafe_api
55+
# dirty |= True
56+
# if set(allow_dostring_in) != net.get('allow_dostring_in', set()):
57+
# net['allow_dostring_in'] = allow_dostring_in
58+
# dirty |= True
5859
if dirty:
5960
autoexec.net = net
6061

plugins/restapi/commands.py

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
from typing import Optional, Any, Union, Literal
1616

1717
from .models import (TopKill, ServerInfo, SquadronInfo, TopKDR, Trueskill, Highscore, UserEntry, WeaponPK, PlayerStats,
18-
CampaignCredits, TrapEntry, SquadronMember, SquadronCampaignCredit, LinkMeResponse, ServerStats,
19-
PlayerInfo, PlayerSquadron)
18+
CampaignCredits, TrapEntry, SquadronCampaignCredit, LinkMeResponse, ServerStats, PlayerInfo,
19+
PlayerSquadron)
2020

2121
app: Optional[FastAPI] = None
2222

@@ -68,7 +68,7 @@ def register_routes(self):
6868
router.add_api_route(
6969
"/squadron_members", self.squadron_members,
7070
methods = ["POST"],
71-
response_model = list[SquadronMember],
71+
response_model = list[UserEntry],
7272
description = "List squadron members",
7373
summary = "Squadron Members",
7474
tags = ["Info"]
@@ -317,29 +317,37 @@ def filter_servers(servers: list[Server]):
317317

318318
return servers
319319

320-
async def squadrons(self):
321-
self.log.debug('Calling /squadrons')
320+
async def squadrons(self, limit: int = Query(default=None), offset: int = Query(default=0)):
321+
self.log.debug(f'Calling /squadrons with limit={limit}, offset={offset}')
322+
if limit:
323+
sql_part = f"LIMIT {limit} OFFSET {offset}"
324+
else:
325+
sql_part = ""
322326
async with self.apool.connection() as conn:
323327
async with conn.cursor(row_factory=dict_row) as cursor:
324328
squadrons: list[SquadronInfo] = []
325-
async for row in await cursor.execute("""
326-
SELECT * FROM squadrons ORDER BY name
329+
async for row in await cursor.execute(f"""
330+
SELECT * FROM squadrons
331+
ORDER BY name
332+
{sql_part}
327333
"""):
334+
members = await self.squadron_members(row['name'])
328335
squadrons.append(SquadronInfo.model_validate({
329336
"name": row['name'],
330337
"description": row['description'],
331338
"image_url": row['image_url'],
332339
"locked": row['locked'],
333-
"role": self.bot.get_role(row['role']).name if row['role'] else None
340+
"role": self.bot.get_role(row['role']).name if row['role'] else None,
341+
"members": members
334342
}))
335343
return squadrons
336344

337-
async def top_players(self, what: Literal['kills', 'kdr'], limit: Optional[int] = 10,
345+
async def top_players(self, what: Literal['kills', 'kdr'], limit: Optional[int] = 10, offset: Optional[int] = 0,
338346
server_name: Optional[str] = None):
339347
if what == 'kills':
340348
order_column = 3
341349
else:
342-
order_column = 5
350+
order_column = 6
343351

344352
async with self.apool.connection() as conn:
345353
async with conn.cursor(row_factory=dict_row) as cursor:
@@ -362,19 +370,23 @@ async def top_players(self, what: Literal['kills', 'kdr'], limit: Optional[int]
362370
FROM statistics s
363371
JOIN players p ON s.player_ucid = p.ucid
364372
{join}
365-
GROUP BY 1, 2 ORDER BY {order_column} DESC LIMIT {limit}
373+
GROUP BY 1, 2 ORDER BY {order_column} DESC
374+
LIMIT {limit} OFFSET {offset}
366375
""", {"server_name": server_name})
367376
return [TopKill.model_validate(result) for result in await cursor.fetchall()]
368377

369-
async def topkills(self, limit: int = Query(default=10), server_name: str = Query(default=None)):
378+
async def topkills(self, limit: int = Query(default=10), offset: int = Query(default=0),
379+
server_name: str = Query(default=None)):
370380
self.log.debug(f'Calling /topkills with limit={limit}, server_name={server_name}')
371-
return await self.top_players('kills', limit, server_name)
381+
return await self.top_players('kills', limit, offset, server_name)
372382

373-
async def topkdr(self, limit: int = Query(default=10), server_name: str = Query(default=None)):
383+
async def topkdr(self, limit: int = Query(default=10), offset: int = Query(default=0),
384+
server_name: str = Query(default=None)):
374385
self.log.debug(f'Calling /topkdr with limit={limit}, server_bane={server_name}')
375-
return await self.top_players('kdr', limit, server_name)
386+
return await self.top_players('kdr', limit, offset, server_name)
376387

377-
async def trueskill(self, limit: int = Query(default=10), server_name: str = Query(default=None)):
388+
async def trueskill(self, limit: int = Query(default=10), offset: int = Query(default=0),
389+
server_name: str = Query(default=None)):
378390
self.log.debug(f'Calling /trueskill with limit={limit}, server_name={server_name}')
379391
if server_name:
380392
join = "JOIN missions m ON s.mission_id = m.id AND m.server_name = %(server_name)s"
@@ -389,7 +401,8 @@ async def trueskill(self, limit: int = Query(default=10), server_name: str = Que
389401
JOIN trueskill t ON t.player_ucid = p.ucid
390402
{join}
391403
WHERE s.hop_on > (now() AT TIME ZONE 'utc') - interval '1 month'
392-
GROUP BY 1, 2, 5 ORDER BY 5 DESC LIMIT {limit}
404+
GROUP BY 1, 2, 5 ORDER BY 5 DESC
405+
LIMIT {limit} OFFSET {offset}
393406
""", {"server_name": server_name})
394407
return [Trueskill.model_validate(result) for result in await cursor.fetchall()]
395408

@@ -603,7 +616,8 @@ async def credits(self, nick: str = Form(...), date: Optional[str] = Form(None),
603616
return CampaignCredits.model_validate(row)
604617

605618
async def traps(self, nick: str = Form(None), date: Optional[str] = Form(None),
606-
limit: int = Form(10), server_name: Optional[str] = Form(None)):
619+
limit: Optional[int] = Form(10), offset: Optional[int] = Form(0),
620+
server_name: Optional[str] = Form(None)):
607621
self.log.debug(f'Calling /traps with nick="{nick}", date="{date}", server_name="{server_name}"')
608622
if server_name:
609623
join = "JOIN missions m ON t.mission_id = m.id AND m.server_name = %(server_name)s"
@@ -617,7 +631,8 @@ async def traps(self, nick: str = Form(None), date: Optional[str] = Form(None),
617631
FROM traps t
618632
{join}
619633
WHERE t.player_ucid = %(ucid)s
620-
ORDER BY time DESC LIMIT {limit}
634+
ORDER BY time DESC
635+
LIMIT {limit} OFFSET {offset}
621636
""", {"ucid": ucid, "server_name": server_name})
622637
return [TrapEntry.model_validate(result) for result in await cursor.fetchall()]
623638

@@ -631,7 +646,7 @@ async def squadron_members(self, name: str = Form(...)):
631646
JOIN squadrons s ON sm.squadron_id = s.id
632647
WHERE s.name = %s
633648
""", (name, ))
634-
return [SquadronMember.model_validate(result) for result in await cursor.fetchall()]
649+
return [UserEntry.model_validate(result) for result in await cursor.fetchall()]
635650

636651
async def squadron_credits(self, name: str = Form(...), campaign: str = Form(default=None)):
637652
self.log.debug(f'Calling /squadron_credits with name="{name}"')

plugins/restapi/models.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ class SquadronInfo(BaseModel):
150150
image_url: str = Field(..., description="URL to the squadron's image")
151151
locked: bool = Field(..., description="Whether the squadron is locked")
152152
role: Optional[str] = Field(None, description="Discord role name associated with the squadron")
153+
members: list[UserEntry] = Field(default_factory=list)
153154

154155
model_config = {
155156
"json_schema_extra": {
@@ -424,22 +425,6 @@ class TrapEntry(BaseModel):
424425
}
425426

426427

427-
class SquadronMember(BaseModel):
428-
nick: str = Field(..., description="Player name")
429-
date: datetime = Field(..., description="Last seen timestamp")
430-
431-
model_config = {
432-
"json_encoders": {
433-
datetime: lambda v: v.isoformat()
434-
},
435-
"json_schema_extra": {
436-
"example": {
437-
"nick": "Player1",
438-
"date": "2025-08-07T12:00:00"
439-
}
440-
}
441-
}
442-
443428
class SquadronCampaignCredit(BaseModel):
444429
campaign: Optional[str] = Field(None, description="Campaign name")
445430
credits: Optional[float] = Field(None, description="Squadron's credits in the campaign")

0 commit comments

Comments
 (0)