Fix several bugs and race conditions.

This commit is contained in:
Oleksandr Kozachuk 2025-06-04 10:46:51 +02:00
parent eb9b29d301
commit c66772fd67
2 changed files with 40 additions and 31 deletions

View File

@ -828,11 +828,11 @@
if (idea.start_time && idea.end_time) {
const s = new Date(idea.start_time).toLocaleDateString('nb-NO', {
hour: '2-digit', minute: '2-digit', hour12: false
});
}).replace(/ /g, ' ');
const e = new Date(idea.end_time).toLocaleDateString('nb-NO', {
hour: '2-digit', minute: '2-digit', hour12: false
});
timeCell = `<td>${s}${e}</td>`;
}).replace(/ /g, '&nbsp;');
timeCell = `<td>${s}<br/>&nbsp;${e}</td>`;
} else if (idea.start_time) {
const s = new Date(idea.start_time).toLocaleDateString('nb-NO', {
hour: '2-digit', minute: '2-digit', hour12: false
@ -848,8 +848,8 @@
}
html += `<tr>`
+ `<td><strong>${idea.name}</strong><br><small>${idea.description}</small></td>`
+ timeCell;
+ `<td><strong>${idea.name}</strong><br><small>${linkify(idea.description)}</small></td>`
+ timeCell;
allVoters.forEach(voter => {
html += `<td class="vote-cell">${idea.voters.includes(voter) ? '✓' : ''}</td>`;
});

View File

@ -5,6 +5,7 @@ from typing import Optional
from datetime import datetime
from fastapi import FastAPI, HTTPException, Path
from pydantic import BaseModel, Field
from filelock import FileLock
import argparse
import uvicorn
@ -49,7 +50,11 @@ class Vote(BaseModel):
DATA_DIR = "" # will be overridden via command line
def get_tour_filepath(tour_id: str) -> str:
return os.path.join(DATA_DIR, f"{tour_id}.json")
fname = f"{tour_id}.json"
full = os.path.abspath(os.path.join(DATA_DIR, fname))
if not full.startswith(os.path.abspath(DATA_DIR) + os.sep):
raise HTTPException(status_code=400, detail="Invalid tour ID")
return full
# ─── ENDPOINT: CREATE TOUR ─────────────────────────────────────────────────────
@ -74,7 +79,7 @@ def create_tour(tour: TourCreate):
filepath = get_tour_filepath(tour_id)
with open(filepath, "w") as f:
# Using default=str so that datetime objects become ISO strings
json.dump(new_tour.dict(), f, default=str)
json.dump(new_tour.model_dump(), f, default=str)
return new_tour
@ -117,22 +122,24 @@ def add_idea(tour_id: str, idea: IdeaCreate):
if not os.path.exists(filepath):
raise HTTPException(status_code=404, detail="Tour not found")
with open(filepath) as f:
data = json.load(f)
lock = FileLock(filepath + ".lock")
with lock:
with open(filepath) as f:
data = json.load(f)
idea_id = str(uuid.uuid4())
new_idea = Idea(
id=idea_id,
name=idea.name,
description=idea.description,
voters=[],
start_time=idea.start_time,
end_time=idea.end_time,
)
idea_id = str(uuid.uuid4())
new_idea = Idea(
id=idea_id,
name=idea.name,
description=idea.description,
voters=[],
start_time=idea.start_time,
end_time=idea.end_time,
)
data["ideas"].append(new_idea.dict())
with open(filepath, "w") as f:
json.dump(data, f, default=str)
data["ideas"].append(new_idea.model_dump())
with open(filepath, "w") as f:
json.dump(data, f, default=str)
return new_idea
@ -147,17 +154,19 @@ def vote_idea(tour_id: str, idea_id: str, vote: Vote):
if not os.path.exists(filepath):
raise HTTPException(status_code=404, detail="Tour not found")
with open(filepath) as f:
data = json.load(f)
lock = FileLock(filepath + ".lock")
with lock:
with open(filepath) as f:
data = json.load(f)
for idea in data["ideas"]:
if idea["id"] == idea_id:
if vote.voterName not in idea["voters"]:
idea["voters"].append(vote.voterName)
# Persist the change
with open(filepath, "w") as f:
json.dump(data, f, default=str)
return Idea(**idea)
for idea in data["ideas"]:
if idea["id"] == idea_id:
if vote.voterName not in idea["voters"]:
idea["voters"].append(vote.voterName)
# Persist the change
with open(filepath, "w") as f:
json.dump(data, f, default=str)
return Idea(**idea)
raise HTTPException(status_code=404, detail="Idea not found")