fix: harden API, add rate limiting, disable debug by default

- Add input validation and error handling across all API endpoints
  (malformed JSON, missing fields, invalid types return proper 4xx)
- Add per-IP rate limiting (10/min new games, 60/min other requests)
- Add session rollback on unhandled exceptions
- Default debug=False, add --debug CLI flag to opt in
- Frontend: add fetch error handling, fix feedback timeout stacking,
  disable buttons during requests, allow clicking found cells for
  overlapping word selection
- Expand seed data to 7 categories with 25-35 words each

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mathew Sir Guest the best
2026-05-07 00:59:47 -06:00
co-authored by Claude Opus 4.6
parent 4d681a92ca
commit cd1dc9200b
11 changed files with 708 additions and 110 deletions
+169 -7
View File
@@ -23,6 +23,21 @@ SEED_DATA = {
"CRANE",
"MOOSE",
"RAVEN",
"JAGUAR",
"PARROT",
"TURTLE",
"SALMON",
"FERRET",
"COYOTE",
"OSPREY",
"LIZARD",
"BADGER",
"PELICAN",
"MONKEY",
"RABBIT",
"IGUANA",
"CONDOR",
"JACKAL",
],
"colors": [
"CRIMSON",
@@ -45,6 +60,21 @@ SEED_DATA = {
"JADE",
"CYAN",
"MAUVE",
"COBALT",
"COPPER",
"EBONY",
"PEACH",
"CREAM",
"LEMON",
"ORCHID",
"SALMON",
"SIENNA",
"KHAKI",
"LILAC",
"PEARL",
"CHARCOAL",
"MAGENTA",
"EMERALD",
],
"food": [
"PIZZA",
@@ -67,6 +97,129 @@ SEED_DATA = {
"CURRY",
"BAGEL",
"FUDGE",
"SHRIMP",
"CHEESE",
"BUTTER",
"CHERRY",
"PEPPER",
"GINGER",
"PRETZEL",
"BRISKET",
"NOODLE",
"OYSTER",
"TURNIP",
"RADISH",
"CELERY",
"COOKIE",
"BISQUE",
],
"sports": [
"SOCCER",
"TENNIS",
"BOXING",
"RUGBY",
"GOLF",
"DIVING",
"ROWING",
"HOCKEY",
"FENCING",
"ARCHER",
"SPRINT",
"DISCUS",
"SQUASH",
"KARATE",
"JUDO",
"RELAY",
"SLALOM",
"CRAWL",
"VAULT",
"SERVE",
"VOLLEY",
"TACKLE",
"PADDLE",
"JAVELIN",
"HURDLE",
],
"space": [
"PLANET",
"GALAXY",
"NEBULA",
"COMET",
"ORBIT",
"QUASAR",
"PULSAR",
"LUNAR",
"SOLAR",
"METEOR",
"AURORA",
"ECLIPSE",
"ZENITH",
"CRATER",
"COSMOS",
"PHOTON",
"PLASMA",
"ROCKET",
"SATURN",
"VENUS",
"MARS",
"PLUTO",
"TITAN",
"VORTEX",
"FLARE",
],
"nature": [
"RIVER",
"OCEAN",
"CANYON",
"FOREST",
"DESERT",
"ISLAND",
"GLACIER",
"MEADOW",
"VALLEY",
"SUMMIT",
"LAGOON",
"TUNDRA",
"MARSH",
"CLIFF",
"DELTA",
"FJORD",
"GROVE",
"RIDGE",
"CORAL",
"BROOK",
"DUNE",
"GEYSER",
"CAVERN",
"RAPIDS",
"SAVANNA",
],
"music": [
"GUITAR",
"PIANO",
"DRUMS",
"VIOLIN",
"FLUTE",
"CELLO",
"BANJO",
"ORGAN",
"HARP",
"TRUMPET",
"RHYTHM",
"MELODY",
"CHORD",
"TEMPO",
"FORTE",
"TREBLE",
"OCTAVE",
"SONATA",
"BALLAD",
"OPERA",
"HYMN",
"LYRIC",
"CHORUS",
"BRIDGE",
"DUET",
],
}
@@ -80,14 +233,23 @@ def seed():
for cat_name, words in SEED_DATA.items():
existing = session.query(Category).filter_by(name=cat_name).first()
if existing:
print(f"Category '{cat_name}' already exists, skipping.")
continue
cat = Category(name=cat_name)
session.add(cat)
session.flush()
cat = existing
else:
cat = Category(name=cat_name)
session.add(cat)
session.flush()
added = 0
for w in words:
session.add(CategoryWord(category_id=cat.id, word=w))
print(f"Seeded category '{cat_name}' with {len(words)} words.")
exists = (
session.query(CategoryWord)
.filter_by(category_id=cat.id, word=w)
.first()
)
if not exists:
session.add(CategoryWord(category_id=cat.id, word=w))
added += 1
print(f"Category '{cat_name}': added {added} new words.")
session.commit()
session.close()