Skip to content

Commit

Permalink
Merge branch 'hotfix/2022.2.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
Kruptein committed Jun 13, 2022
2 parents 296ed64 + bf358ae commit 4ab53e6
Show file tree
Hide file tree
Showing 16 changed files with 253 additions and 53 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,27 @@ tech changes will usually be stripped from release notes for the public

## Unreleased

## [2022.2.1] - 2022-06-13

This hides the export/import behind a server config option as I found an important underlying concurrency problem,
which is undesirable for a server with multiple active users.
In order to prevent these servers from running into issues due to upgrading to 2022.2,
I'm already publishing these changes.

Depending on the complexity of the concurrency issues,
I may release a 2022.2.2 soon-ish or it will end up being part of a later 2022.3 release.

### Added

- [server] server config option to enable import/export, defaulting to False
- given the experimental nature, it's better to not enable this by default
- it's strongly recommended to only enable this if you run a private server for the moment

### Fixed

- Note export failing
- Markers not being exported

## [2022.2] - 2022-06-12

### Added
Expand Down
2 changes: 2 additions & 0 deletions Dockerfiles/server_config_docker.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ max_log_backups = 5

allow_signups = true

enable_export = false

[APIserver]
# The API server is an administration server on which some API calls can be made.
# It should use a different port or socket than the main webserver.
Expand Down
25 changes: 21 additions & 4 deletions client/src/dashboard/Dashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface DashboardState {
joined: RoomInfo[];
error: string;
activeNavigation: Navigation;
exportEnabled: boolean;
}
const state: DashboardState = reactive({
Expand All @@ -34,6 +35,7 @@ const state: DashboardState = reactive({
joined: [],
error: "",
activeNavigation: Navigation.Play,
exportEnabled: false,
});
const modals = useModal();
Expand All @@ -45,6 +47,10 @@ socket.on("Campaign.Import.Done", async () => {
state.activeNavigation = Navigation.Run;
});
socket.on("Export.Enabled", () => {
state.exportEnabled = true;
});
onMounted(async () => {
if (route.params?.error === "join_game") {
await modals.confirm(
Expand Down Expand Up @@ -73,7 +79,6 @@ const mainNavigation: NavigationEntry[] = [
{ type: "action", navigation: Navigation.Play, fn: setActiveNavigation },
{ type: "action", navigation: Navigation.Run, fn: setActiveNavigation },
{ type: "action", navigation: Navigation.Create, fn: setActiveNavigation },
{ type: "action", navigation: Navigation.Import, fn: setActiveNavigation },
{ type: "separator" },
{ text: "assets", type: "header" },
{ type: "action", navigation: Navigation.AssetManage, fn: openAssetManager },
Expand All @@ -92,9 +97,19 @@ const settingsNavigation: NavigationEntry[] = [
];
const activeNavigation = ref(NavigationMode.Main);
const navigation = computed(() =>
activeNavigation.value === NavigationMode.Main ? mainNavigation : settingsNavigation,
);
const navigation = computed(() => {
if (activeNavigation.value === NavigationMode.Main) {
if (state.exportEnabled) {
return [
...mainNavigation.slice(0, 4),
{ type: "action", navigation: Navigation.Import, fn: setActiveNavigation },
...mainNavigation.slice(4),
] as NavigationEntry[];
}
return mainNavigation;
}
return settingsNavigation;
});
const navigationTranslation: Record<Navigation, string> = {
[Navigation.Play]: "play",
Expand Down Expand Up @@ -154,12 +169,14 @@ function updateLogo(index: number, logo: string): void {
v-if="state.activeNavigation === Navigation.Play"
:sessions="state.joined"
:dmMode="false"
:exportEnabled="state.exportEnabled"
@remove-room="leaveRoom"
/>
<SessionList
v-else-if="state.activeNavigation === Navigation.Run"
:sessions="state.owned"
:dmMode="true"
:exportEnabled="state.exportEnabled"
@rename="rename"
@remove-room="removeRoom"
@update-logo="updateLogo"
Expand Down
3 changes: 2 additions & 1 deletion client/src/dashboard/SessionList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { RoomInfo } from "./types";
const props = defineProps<{
dmMode: boolean;
sessions: RoomInfo[];
exportEnabled: boolean;
}>();
const emit = defineEmits(["remove-room", "rename", "update-logo"]);
Expand Down Expand Up @@ -158,7 +159,7 @@ function exportCampaign(): void {
<div class="header">Notes</div>
<textarea :style="{ flexGrow: 1 }" :value="notes" @change="setNotes(getValue($event))"></textarea>
<div :style="{ flexGrow: 2 }"></div>
<div class="leave" v-if="dmMode" @click="exportCampaign">EXPORT</div>
<div class="leave" v-if="dmMode && exportEnabled" @click="exportCampaign">EXPORT</div>
<div class="leave" @click="leaveOrDelete">{{ dmMode ? "DELETE" : "LEAVE" }}</div>
</div>
</div>
Expand Down
1 change: 0 additions & 1 deletion client/src/dashboard/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { baseAdjust } from "../core/http";
import { socketManager } from "../core/socket";

export const socket = socketManager.socket("/dashboard");
if (!socket.connected) socket.connect();

socket.on("Campaign.Export.Done", (filename: string) => {
window.open(baseAdjust(`/static/temp/${filename}.pac`));
Expand Down
3 changes: 3 additions & 0 deletions client/src/store/core.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Store } from "../core/store";
import { socket } from "../dashboard/socket";

interface CoreState {
authenticated: boolean;
Expand Down Expand Up @@ -28,6 +29,8 @@ class CoreStore extends Store<CoreState> {

setAuthenticated(authenticated: boolean): void {
this._state.authenticated = authenticated;
if (authenticated && !socket.connected) socket.connect();
else if (!authenticated && socket.connected) socket.disconnect();
}

setLoading(loading: boolean): void {
Expand Down
2 changes: 2 additions & 0 deletions server/server_config.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ max_log_backups = 5

allow_signups = true

enable_export = false

[APIserver]
# The API server is an administration server on which some API calls can be made.
# It should use a different port or socket than the main webserver.
Expand Down
32 changes: 23 additions & 9 deletions server/src/api/http/rooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@

from aiohttp import web
from aiohttp_security import check_authorized
from api.socket.constants import DASHBOARD_NS

from api.socket.constants import DASHBOARD_NS
from app import sio
from config import config
from export.campaign import export_campaign, import_campaign
from models import Location, LocationOptions, PlayerRoom, Room, User
from models.db import db
Expand Down Expand Up @@ -179,6 +180,9 @@ async def delete(request: web.Request):


async def export(request: web.Request):
if not config.getboolean("General", "enable_export"):
return web.HTTPForbidden()

user: User = await check_authorized(request)

creator = request.match_info["creator"]
Expand All @@ -189,7 +193,7 @@ async def export(request: web.Request):
if room is None:
return web.HTTPBadRequest()

asyncio.create_task(export_campaign(f"{roomname}-{creator}", [room]))
await asyncio.create_task(export_campaign(f"{roomname}-{creator}", [room]))

return web.HTTPAccepted(
text=f"Processing started. Check /static/temp/{room.name}-{room.creator.name}.pac soon."
Expand All @@ -198,6 +202,9 @@ async def export(request: web.Request):


async def export_all(request: web.Request):
if not config.getboolean("General", "enable_export"):
return web.HTTPForbidden()

user: User = await check_authorized(request)

creator = request.match_info["creator"]
Expand All @@ -224,6 +231,9 @@ class ImportData(TypedDict):


async def import_info(request: web.Request):
if not config.getboolean("General", "enable_export"):
return web.HTTPForbidden()

await check_authorized(request)

name = request.match_info["name"]
Expand All @@ -247,6 +257,9 @@ async def import_info(request: web.Request):


async def import_chunk(request: web.Request):
if not config.getboolean("General", "enable_export"):
return web.HTTPForbidden()

user: User = await check_authorized(request)

name = request.match_info["name"]
Expand All @@ -269,14 +282,15 @@ async def import_chunk(request: web.Request):
chunks = import_mapping[name]["chunks"]
if all(chunks):
print(f"Got all chunks for {name}")
asyncio.create_task(
import_campaign(user, io.BytesIO(b"".join(cast(List[bytes], chunks))))
await asyncio.create_task(
handle_import(user, name, io.BytesIO(b"".join(cast(List[bytes], chunks))))
)
del import_mapping[name]

for sid in dashboard_state.get_sids(id=user.id):
await sio.emit(
"Campaign.Import.Done", name, room=sid, namespace=DASHBOARD_NS
)

return web.HTTPOk()


async def handle_import(user: User, name: str, pac: io.BytesIO):
await import_campaign(user, pac)
for sid in dashboard_state.get_sids(id=user.id):
await sio.emit("Campaign.Import.Done", name, room=sid, namespace=DASHBOARD_NS)
3 changes: 3 additions & 0 deletions server/src/api/socket/dashboard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from . import campaign
from api.socket.constants import DASHBOARD_NS
from app import sio
from config import config
from state.dashboard import dashboard_state


Expand All @@ -11,6 +12,8 @@ async def dashboard_connect(sid: str, environ):
user = await authorized_userid(environ["aiohttp.request"])
if user is not None:
await dashboard_state.add_sid(sid, user)
if config.getboolean("General", "enable_export"):
await sio.emit("Export.Enabled", True, sid=sid, namespace=DASHBOARD_NS)


@sio.on("disconnect", namespace=DASHBOARD_NS)
Expand Down
10 changes: 7 additions & 3 deletions server/src/api/socket/dashboard/campaign.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
from asyncio.log import logger
import asyncio
from typing import Optional

import auth
from api.socket.constants import DASHBOARD_NS
from app import app, sio
from config import config
from export.campaign import export_campaign
from logs import logger
from models.campaign import Room
from state.dashboard import dashboard_state


@sio.on("Campaign.Export", namespace=DASHBOARD_NS)
@auth.login_required(app, sio, "dashboard")
async def _export_campaign(sid: str, campaign_name: str):
if not config.getboolean("General", "enable_export"):
return

user = dashboard_state.get_user(sid)

room: Optional[Room] = Room.get_or_none(name=campaign_name, creator=user)
Expand All @@ -22,5 +27,4 @@ async def _export_campaign(sid: str, campaign_name: str):
return

filename = f"{campaign_name}-{user.name}"
await export_campaign(filename, [room])
await sio.emit("Campaign.Export.Done", filename, room=sid, namespace=DASHBOARD_NS)
await asyncio.create_task(export_campaign(filename, [room], sid=sid))
2 changes: 2 additions & 0 deletions server/src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import aiohttp_security
import aiohttp_session
import aiojobs.aiohttp
from aiohttp import web
from aiohttp_security import SessionIdentityPolicy
from aiohttp_session.cookie_storage import EncryptedCookieStorage
Expand All @@ -22,6 +23,7 @@ def setup_app(middlewares: Iterable[Callable] = ()) -> web.Application:
app["AuthzPolicy"] = auth.AuthPolicy()
aiohttp_security.setup(app, SessionIdentityPolicy(), app["AuthzPolicy"])
aiohttp_session.setup(app, EncryptedCookieStorage(auth.get_secret_token()))
aiojobs.aiohttp.setup(app)
return app


Expand Down
Loading

0 comments on commit 4ab53e6

Please sign in to comment.