Skip to content

Latest commit

 

History

History
412 lines (295 loc) · 28.6 KB

File metadata and controls

412 lines (295 loc) · 28.6 KB

Changelog

v1.10.0

Added

  • Home Assistant Integration - Per-user webhook credentials for pushing trade and wishlist-price alerts to Home Assistant, and pulling live collection stats.
    • New Webhooks page where any user can generate credentials, each pairing a Home Assistant target URL with a bearer secret shown once at creation.
    • Push events for trade proposals, updates, engagement, acceptance, and rejection/cancellation, plus a wishlist target-price-met alert.
    • A new authenticated pull endpoint for Home Assistant to fetch a user's collection stats on its own schedule.
    • Per-credential TLS verification toggle for Home Assistant instances behind a self-signed certificate or internal CA, with a visible "Insecure" indicator when disabled.
    • Admin controls in Settings to enable/disable the integration and cap credentials per user (default 3, off by default).
    • See the wiki for setup and Home Assistant configuration details.

Changed

  • Version bumped from 1.9.2 to 1.10.0 within constants.py.

v1.9.2

Fixed

  • added_at, created_at, recorded_at, and last_fetched columns (cards, users, collection_entries, decks, price_history, wishlist_entries, converted_currencies) used server_default=sa.text('now()'), valid Postgres SQL but not a SQLite function, causing sqlite3.OperationalError: unknown function: now() on any insert that didn't explicitly set the timestamp (e.g. importing a card) when running on SQLite
  • Migrations now use sa.func.now(), which SQLAlchemy compiles per dialect (CURRENT_TIMESTAMP on SQLite, now() on Postgres); new migration c3d4e5f6a7b8 repairs existing SQLite databases in place via batch_alter_table, applied automatically on next alembic upgrade head

Changed

  • Version bumped from 1.9.1 to 1.9.2 within constants.py.

v1.9.1

Added

  • Deck Usage Tracking - Shows how many copies of a card printing are currently allocated to decks.

    • GET /collection returns a new in_decks field per entry: the sum of DeckCard.quantity across all of the user's decks (mainboard, sideboard, and commander) for that card's printing.
    • Counted per printing (card_id), not per foil/condition/language entry. DeckCard has no foil field, so a foil and non-foil row of the same printing report the same count.
    • Collection page: "N in decks" shown below the Qty value.
    • TradeAddCardModal - "N in deck(s)" badge shown per card in the browse list, and a note next to the quantity picker once a card is selected.
  • SQLite Database Support - PostgreSQL remains the default; docker-compose.sqlite.yml runs the app against a local SQLite file instead. Permanent, per-instance choice - there is no tool to convert an existing instance between backends.

    • backend/database.py - exports DB_BACKEND; registers PRAGMA journal_mode=WAL and PRAGMA foreign_keys=ON on every SQLite connection.
    • GET /auth/setup-required returns db_backend; the Setup page banner names the active backend.
    • POST /auth/setup writes $CONFIG_PATH/db_backend.lock after the first admin account is created. entrypoint.sh compares this against the current DATABASE_URL on every boot and refuses to start on a mismatch.
    • docker-compose.sqlite.yml - standalone compose file (docker compose -f docker-compose.sqlite.yml up) with its own SQLITE_PATH volume. docker-compose.yml itself is unchanged.
    • app and db in docker-compose.yml, and app in docker-compose.sqlite.yml, now set container_name (openmtg, openmtg-db) instead of Compose's default <project>-app-1 naming.
  • Migration portability test suite - backend/tests/test_migration_dialect_safety.py statically scans every migration for op.alter_column/op.drop_column calls outside batch_alter_table, which break on SQLite. backend/tests/test_migration_schema_parity.py replays the real Alembic chain against a fresh SQLite database and a fresh PostgreSQL database and diffs the resulting schemas.

    • ci.yml - added a postgres:16-alpine service container so the parity check runs on every push/PR.
    • Downgrading (alembic downgrade) is not supported on SQLite - only the forward upgrade head path is exercised in production.

Changed

  • Version bumped from 1.9.0 to 1.9.1 within constants.py.

v1.9.0

Added

  • Loan Tracking - Track cards loaned out to other players from the Collection.

    • on_loan, loaned_to, and loan_date fields added to CollectionEntry model and schemas.
    • Loan section added to the Edit Card modal with an on-loan toggle, borrower name field, and loan date picker.
    • "On Loan" badge shown on loaned entries in the Collection view.
    • Alembic migration f0a1b2c3d4e5 adds the three new columns to collection_entries.
  • Card Photos - Upload and view front and back condition photos for individual collection entries.

    • Photos stored on disk at /data/uploads/card_photos/. One photo per side per entry; re-uploading replaces the existing photo. Served through FastAPI (not nginx) so authentication is enforced on every request.
    • card_photos table with cascade delete tied to the parent entry. Alembic migration f1a2b3c4d5e6.
    • POST/DELETE/GET /collection/{entry_id}/photos/{side} - upload, remove, and serve photos (owner only).
    • PhotoUploadModal component - drag-and-drop or file browse with live preview. Loads any existing photo for the selected side on open, functioning as both viewer and uploader.
    • CardPhotoViewer component - Front/Back tab switcher that fetches photos as authenticated blob URLs via URL.createObjectURL().
    • Updated CSP img-src in nginx.conf to include blob: for URL.createObjectURL() to render in the browser.
  • Trade System - Formal trade proposals, negotiation, and automatic card transfers between accounts.

    • Trades stored in a dedicated SQLite database (/data/trades/trades.db) fully independent of the main PostgreSQL inventory. Deleting it removes all trade history without affecting card data.
    • Trade and TradeItem models in backend/models/trade.py using a separate SQLAlchemy engine and session (database_trades.py).
    • Separate Alembic environment (alembic_trades.ini, migrations_trades/) for the trades database.
    • Trade state machine: proposed to active to accepted / rejected / cancelled. On double-confirmation, cards transfer automatically, validated against live quantities before execution.
    • GET /trades/pending-count, GET /trades, POST /trades, GET /trades/{id}, PUT /trades/{id}/items, POST /trades/{id}/confirm, POST /trades/{id}/unconfirm, POST /trades/{id}/reject, GET /trades/{id}/photos/{entry_id}/{side}.
    • Trades.jsx - trade list page with Active and History sections, status color badges, "Your Turn" indicator, and a Propose Trade modal.
    • TradeDetail.jsx - split-screen view of both offers with live totals (condition multipliers applied from local state), and a Confirm / Un-submit / Reject action bar.
    • TradeAddCardModal - pick cards from your collection to add to a trade, with a gold-outline selected state.
    • ! badge on the Trades nav link (desktop and mobile) when any trade is awaiting your action.
    • services/webhooks.py stub included as the foundation for v1.10 Home Assistant integration.
  • Trades Feature Toggle - Admins can enable or disable Trades from the Feature Toggles section of the Settings page.

    • GET /trades/status public endpoint fetched by AuthContext on init; exposes tradesEnabled to all components via context.
    • When disabled: the nav link is hidden, trade pages redirect to /collection, and pending count returns zero.
    • trades_enabled added to services/settings.py DEFAULTS, SettingsUpdate schema, and the PATCH /admin/settings handler.
  • Scryfall batch price refresh - Price updates now use POST /cards/collection to fetch up to 75 cards per request, replacing individual GET /cards/{id} calls.

    • scryfall_queue.post() method added with a configurable per-request HTTP timeout (30 s for batch vs 10 s for single-card GETs).
    • price_refresh.py rewritten to batch all cards in groups of 75, mapping Scryfall responses back to database records by scryfall_id and committing per batch.
  • Multi-platform Docker image builds targeting linux/amd64 and linux/arm64 via docker buildx.

Changed

  • docker-compose.yml volume paths for uploads and trades use inline shell defaults (${UPLOADS_PATH:-./uploads}, ${TRADES_PATH:-./trades}) so the stack starts without those variables set in .env.
  • README.md Credits section updated with a Scryfall attribution line.

v1.8.2

Added

  • Added per-account login throttling in backend/login_throttle.py. After 5 failed attempts within 10 minutes, the account enters a 5-minute cooldown. Returns the same generic 401 as a wrong-password response.
  • Added security response headers to nginx.conf: X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN, Referrer-Policy: strict-origin-when-cross-origin, and a Content-Security-Policy locking scripts to 'self', images to self/data:/Scryfall CDN, and frames to none. Users running TLS should add HSTS on their own reverse proxy.
  • Added minimum password length of 8 characters to RegisterRequest, CreateUserRequest, and UpdateUserRequest in backend/schemas.py via Pydantic Field(min_length=8).
  • Added test coverage for login throttle.

Changed

  • Fixed a broken rate-limit key, uvicorn now starts with --proxy-headers --forwarded-allow-ips=127.0.0.1 in supervisord.conf, so slowapi's get_remote_address reads the real client IP from nginx's X-Real-IP/X-Forwarded-For headers instead of always seeing 127.0.0.1.
  • Fixed a TOCTOU race on the first-run /auth/setup endpoint: the count-then-insert now catches IntegrityError and rolls back, so two simultaneous requests cannot both create an admin account.
  • Renamed the Card Search feature from scanner to card-search throughout the codebase. The API endpoint GET /scanner/status is now GET /card-search/status. The settings key scanner_enabled is now card_search_enabled in backend/services/settings.py, backend/routers/settings.py, backend/schemas.py (SettingsUpdate), and the frontend Settings page. Frontend routes and nav links updated from /scanner to /card-search. A compatibility shim in services/settings.py translates the old DB key on read for existing instances. Alembic migration a9b8c7d6e5f4 renames the row in the settings table.
  • Split backend/models/__init__.py into one file per model group: user.py, card.py, collection.py, deck.py, setting.py, wishlist.py, currency.py, price_history.py. __init__.py re-exports all classes, so all existing import models call-sites are unchanged.
  • Updated README.md Python badge and tech-stack table from 3.12+ to 3.14 to match the Dockerfile.
  • Updated backend/tests/test_admin and backend/tests/test_auth to new 8-character passwords for tests.
  • Fixed slowapi storage isolation between tests.
  • Bumped anyio dependency from >=4.14.0 to >=4.14.1.
  • Fixed Alembic migration ordering.
  • Clarified Deck Import Modal language and word formatting.

v1.8.1

Added

Added a module-level in-memory cache for application settings in backend/services/settings.py to reduce calls to the settings DB. Added a limit(10000) cap to the collection GET endpoint in backend/routers/collections.py to reduce the maximum number of cards loaded at a time. Added staleTime: Infinity to frontend/src/pages/Collection.jsx to reduce redundant full-collection calls on an HTML mutation unuless an actual change occurs.

Changed

Replaced PyJWT[crypto] with PyJWT backend/requirements.txt which did not use any RSA or elliptic-curve JWT algorithms. Changed the price refresh cycle in backend/services/price_refresh.py to load only one card at a time into memory instead of the full collection.

Removed

Removed the tesseract.js dependency from frontend/package.json. Removed asyncpg from backend/requirements.txt which was never imported or used. Deleted frontend/nginx.conf, leftover and unused file. Deleted nginx/nginx.conf, leftover and unused file.

v1.8.0

Added

  • Showroom - A public, unauthenticated display page for each user's collection highlights.

    • GET /showroom/display/{username} - Public endpoint returning the user's public decks and showroom-flagged collection cards. Username matching is case-insensitive.
    • GET /showroom/display/{username}/deck/{deck_id} - Public endpoint returning the full card list for a shared deck.
    • /showroom/display/:username - Public Showroom display page showing decks with card preview strips and a card grid.
    • /showroom/display/:username/deck/:deckId - Public read-only deck viewer page with Commander, Main Deck, and Sideboard sections. All cards are clickable to enlarge.
    • /showroom/edit/:username - Owner-facing Showroom management page. Shows everything currently on display with per-item remove controls and a "View Display" link.
    • Per-deck showroom toggle on the Decks page (Eye button) marks a deck is_public and surfaces it on the owner's Showroom display.
    • Per-card showroom toggle on the Collection page (Eye button, both desktop action row and mobile action menu) sets in_showroom on a collection entry.
    • Showroom navigation link (Eye icon) added to sidebar and mobile menu, conditionally shown based on showroomEnabled from AuthContext.
    • S/M/L card size selector on both Showroom pages, persisted per-browser via usePersistedView and shared between display and edit views.
    • Alembic migration b2c3d4e5f6a7 - adds in_showroom boolean column (default false) to collection_entries.
    • New in_showroom field added to CollectionEntry model, CollectionEntryOut schema, UpdateCardRequest schema, and the PATCH /collection/{id} handler.
  • Deck Import - Paste a Moxfield, MTGO, or Arena deck list and create a populated deck in one step.

    • POST /decks/import - Streaming SSE endpoint. Creates the deck, then processes each card line and yields start, progress (with card name), and done events so the frontend can show real-time progress. Handles Moxfield set+number lookup with a name-based fallback. Recognises Commander, Sideboard, Mainboard, Maindeck, Main, and Deck section headers. Commander entries are forced to quantity 1.
    • DeckImportModal component - Modal with deck name, format, and description fields plus a card list textarea. During import, the hint text is replaced by a real-time progress bar showing N of total - Card Name. Returns an imported/skipped summary with a per-line error list and a "View Deck" link on completion. Cancel is disabled while streaming.
    • Import button added to the Decks page header alongside the existing "New Deck" button.
  • Deck card preview strips - Horizontal scrolling strip of card images shown on every deck row.

    • DeckPreviewRow component - Shared across the Showroom display, Showroom edit, and Decks list pages. Uses a ResizeObserver to calculate exactly how many cards fit in the available width and slices preview_cards accordingly. Commander cards are highlighted with an accent-color outline. Accepts an optional actions slot rendered after the strip.
    • GET /decks now eager-loads all DeckCard → Card relationships and manually builds preview_cards (commanders first, then mainboard) and card_count per deck. DeckOut schema extended with card_count: int = 0 and preview_cards: list[DeckPreviewCard] = [] (defaulted so PATCH responses remain valid).
  • Feature Toggles - Admin-controllable on/off switches for optional features, replacing the previous per-feature hardcoded visibility.

    • Showroom toggle: disabling hides the nav link, the public display and deck-viewer pages return 404, and all per-card/per-deck eye buttons disappear.
    • Card Search toggle: disabling hides the Card Search nav link and redirects any direct navigation to /collection.
    • GET /scanner/status - New public endpoint (mirrors /showroom/status) reporting whether Card Search is enabled.
    • scanner_enabled and showroom_enabled added to services/settings.py DEFAULTS (both "true"), SettingsUpdate schema, and the PATCH /admin/settings handler.
    • AuthContext fetches both /showroom/status and /scanner/status on init and exposes showroomEnabled and scannerEnabled via context. Failures are non-fatal.
  • New Pydantic schemas: DeckPreviewCard, DeckImportRequest, DeckImportResult, ShowroomPreviewCard, ShowroomDeckOut, ShowroomCardOut, ShowroomOut.

  • New CSS: Showroom page layout, deck preview strip, card grid clickable state, deck viewer header, import progress bar, showroom card placeholder, and commander highlight styles.

  • New README.md badges for Architecture, Scryfall, Last Commit + Release, and CI Pass/Fail status.

  • Updated nginx.conf to proxy /openapi.json for future API-based tooling.

Changed

  • Settings page: removed the "Save Settings" button. All settings now auto-apply when changed, feature toggles fire immediately on toggle, and the price refresh slider saves on mouseup/touchend (not on every drag tick).
  • Settings page: "Showroom" settings section renamed to "Feature Toggles" to accommodate multiple toggleable features.
  • Decks list: rows now use DeckPreviewRow (card image strip + info) instead of the previous plain name/format text layout.
  • Version bumped from 1.7.0 to 1.8.0 within constants.py.
  • Redirected dependabot.yml to dev branch instead of main.
  • Corrected Mobile view of the User Management page, content now split between multiple rows for each user.

v1.7.0

Added

  • Multi-currency support - Admins can now add custom currencies (e.g. CAD, AUD, GBP) via the Admin panel. Rates are fetched automatically from Frankfurter and refreshed after each Scryfall price cycle. Admins can select any configured currency from the User Account settings for any user.
  • backend/markets.py - Central currency registry allowing each market to define its symbol, display name, Scryfall adapter, and capabilities in one place.
  • backend/services/market_scryfall.py - Scryfall price adapter which maps Scryfall API price keys to database column names.
  • backend/services/exchange_rates.py - Frankfurter integration for validating and batch-refreshing stored exchange rates.
  • GET /currencies - Public endpoint; frontend fetches all currency metadata (symbol, rate, conversion base) at runtime instead of hardcoding.
  • GET|POST|PATCH|DELETE /admin/currencies - Admin CRUD for custom currencies. New codes are validated against Frankfurter before being accepted.
  • useCurrency() hook - Replaces scattered user?.preferred_currency reads across all pages; provides currency, market, and markets to any component that needs them.
  • ConvertedCurrency database model and Alembic migration.
  • New 'SM', 'MD', and 'LG' buttons to Grid views for Decks and Wishlist which changes the visible card size.

Changed

  • Price extraction in scryfall.py and price_refresh.py now delegates to the market adapter (ScryfallMarket.extract_prices()), eliminating all hardcoded if prices.get("usd") chains.
  • currency.js - formatPrice and resolvePrice are now market-aware. Custom currencies apply a stored exchange rate against USD automatically on the frontend.
  • Collection stats endpoint now supports custom currencies via DB rate lookup. All price expressions are multiplied by the exchange rate server-side.
  • Wishlist price history response is now dynamic across all markets rather than hardcoded to USD/EUR fields.
  • set_currency in auth now validates the chosen code against MARKETS and the database before accepting it.
  • PRICE_FIELDS constant removed from constants.py. All callers now derive field names from MARKETS.
  • All JSX inline styles with more than one property moved to named CSS classes in index.css. Dynamic values are passed via CSS custom properties (--bar-w, --bar-bg, --tile-accent).

Fixed

  • add_to_wishlist endpoint was calling _serialize(entry, currency) after _serialize signature was updated to take one argument, causing a 500 on all POST /wishlist requests.

Removed

  • Server-side price_met computation removed from wishlist serializer, field is now computed on the frontend where currency context is available.
  • Removed CSS class .wishlist-page limiting Wishlist width to a specific pixel count; Wishlist is now adopts full screen width.

v1.6.2

Fixed

  • Deleting a user now correctly removes their collection, deck, and wishlist entries; previously caused a 500 error due to missing cascade delete on the User-CollectionEntry, User-Deck, and User-WishlistEntry relationships

Changed

  • Mobile navigation replaced with a hamburger menu (☰) in the top-right; tapping it opens a full-width dropdown with page names and Logout at the bottom.
  • Long usernames no longer push nav items off-screen, as usernames are no longer rendered in Mobile view.
  • User Management table unified for mobile and desktop; Less useful Email and Created columns are hidden on narrow screens rather than switching to a separate card layout

v1.6.1

Added

  • Collections: page GOTO input replaces static page indicator - type a page number and press Enter to jump directly

Changed

  • Wishlist list view on mobile: two-row card layout (name + price on top, set code + action buttons below); set name abbreviated to 3-letter code; History button restored
  • User Management on mobile: per-user cards with a status row (name, role, status) and an action row (currency, admin toggle, reset password, disable, delete); desktop table unchanged
  • Stats Top 10 Most Valuable Cards on mobile: two-row card list instead of the overflowing table
  • List/Grid toggle order standardized to List first across all pages (Decks and Wishlist)
  • Deck and Wishlist List/Grid view preference is now persisted per-browser; each deck remembers its own setting independently

Fixed

  • Deck view total and per-card prices now respect the user's preferred currency instead of always showing USD
  • Stats page loading spinner used incorrect CSS class (isLoadingloading)

Removed

  • Stats: removed local duplicate of formatPrice and CURRENCY_SYMBOLS in favour of the shared currency.js utility

v1.6.0

  • Added grid view to Deck Viewer (default), with card images, quantity badges, and hover actions
  • Added card image viewer to Deck Viewer (both grid and list views)
  • Overhauled "Add Card to Deck"; card image thumbnails in search results, owned/non-owned toggle, zone dropdown (Mainboard/Sideboard/Commander), and set picker for non-owned cards
  • Added "Edit Card" modal to Deck Viewer
  • Added Deck Analysis including Mana Curve, Color Distribution, Card Types, Avg. CMC, and Rarity breakdown
  • Two-step delete confirmation on card removal in Decks to prevent accidental deletes
  • Updated API/Application version handling in all background files involving API calls
  • Added Wishlist page
  • Added List and Grid views to Wishlist page
  • Pushed all API calls to a single rate-limited caller function to enforce Scryfall's 2 req/sec rate cap
  • Set up API call prioritizer to push frontend user activity through API caller function first
  • Deck Edit modal now supports changing a card's printing via Set Picker
  • Wishlist cards are prioritized in background price cache refresh
  • Unified Add Card button sizes across Wishlist, Decks, and Deck Detail pages
  • Fixed deck card update endpoint returning a 500 instead of 404 on an invalid Scryfall ID
  • Fixed Admin page delete confirmation using browser native dialog instead of the app's modal
  • Set Picker dropdown now renders over modals using fixed positioning rather than being clipped

v1.5.1

  • Bump eslint from 9.39.4 to 10.3.0 in /frontend
  • Update pytest-mock requirement from >=3.14 to >=3.15.1 in /backend
  • Update pytest requirement from >=8.0 to >=9.0.3 in /backend
  • Update anyio requirement from >=4.0 to >=4.13.0 in /backend
  • Update httpx requirement from >=0.27 to >=0.28.1 in /backend
  • Bump @eslint/js from 9.39.4 to 10.0.1 in /frontend

v1.5.0

  • Corrected use of _HEARTBEAT_JITTER to the correct _HEARTBEAT_INTERVAL for telemetry timing.
  • Admin panel now shows a per-user currency dropdown that takes effect immediately without a page reload.
  • Scryfall service now fetches and stores all four price fields: price_usd, price_usd_foil, price_eur, price_eur_foil.
  • Currency selection is driven by a PRICE_FIELDS registry in constants.py, making future currencies (e.g. CAD) a one-line addition.

v1.4.2

  • Added a check to see date of creation for current UUID, and re-generate UUID if >60 days.
  • Added a check for timestamp of last message compated to current message, and delay heartbeat by an hour if within 23 hours of previous heartbeat.
  • Added a dropdown in the Settings menu next to the Telemetry toggle to see the last-sent telemetry packet in its entirety.
  • Added a data retention statement in the README.md and Wiki.
  • Lowered timestamp accuracy to round to the nearest minute.
  • Replaced invisible Telemetry tab with disabled message when NOTEL=true is set.

v1.4.1

  • Corrected duplicated 'Uvicorn' processes in 'supervisord.conf'

v1.4.0

  • Added optional usage telemetry to Settings page (Opt-in only, see README.md)
  • Corrected missing icons from mobile web view

v1.3.4

  • Updated eslint/js from 9.39.4 to 10.0.1
  • Updated lucide-react from 0.577.0 to 1.7.0

v1.3.3

  • Modified httpx usage in price_refresh.py and scryfall.py to use existing HTTP handshake instead of creating a new one for every card requests. DNS requests for api.scryfall.com should fall dramatically now.
  • Updated all utcnow() calls to proper now(timezone.utc) calls.
  • Fixed SQLite thread safety and suppressed test scheduler startup noise in conftest.py and database.py.

v1.3.2

  • Removed known remainder of AI code. Repository has been cleaned and is now 100% human-developed. Summary of major changes below

Collection.jsx

  • Removed unused Search icon and SetPicker import.
  • Properly split components AddCardModal, EditModal, and CardImageModal into imported components.
  • Replaced complicated const onMobile = /Mobile/i.test(navigator.userAgent) with simpler const isMobile = useIsMobile() hook.
  • All onMobile references changed to isMobile.
  • Replaced all outdated window.confirm() calls with proper setConfirmAction({ message, onConfirm }) calls.
  • Replaced color filter logic getCardCastingColors(entry.card) with (entry.card.colors || '').split('') to use the colors string already stored from Scryfall API cache instead of parsing mana_cost in frontend every time.

Layout.jsx

  • Replaced const isMobile = /Mobile/i.test(navigator.userAgent) with import { useIsMobile } from '../hooks/useIsMobile'.
  • Added const isMobile = useIsMobile() inside the component body, for dynamic pointer type changes.

v1.3.1

  • Placeholder UI template has been removed. Dev-intended UI is now in place.
  • Changed ruling link from Gatherer to Scryfall.
  • Added backend tests.

v1.3.0

  • Added clickable card images in Collection which blows the card image to full size, and provides a link to the gatherer.wizards.com ruling for that card.
  • Added multi-card selection for batch deleting from Collection.
  • Improved the mobile webpage rendering.
  • Re-ordered and improved Collection filters.
  • Implemented adding and sorting cards by 'Favorite'.
  • Combined Docker images openmtg-backend, openmtg-frontend, and nginx into a single Docker image openmtg.
  • Modified how Stats page shows pie charts to help with rendering small percentages.

v1.2.0

  • Added CHANGELOG.md.
  • Added CREDITS.md.
  • Edited Collection page to use pagination through a drop-down menu.
  • Corrected CSV and JSON export functions.
  • Fixed Deck building page occasionally not working.
  • Added Deck Moxfield and JSON export buttons.
  • Added Sorting and Filtering features to Collection page.
  • Corrected tab names to reflect which tab the user is on, as well as the project name.
  • Added new Favicon, credited to Faithtoken and licensed under CC BY 3.0.
  • Updated 'Database Cache Freshness Bar' to make it a live updating element instead of a static one.

v1.1.0

  • Edited frontend/Dockerfile to add RUN apk upgrade --no-cache, clearing known libexpat and zlib CVE's.
  • Edited backend/Dockerfile to add RUN apt-get update && apt-get upgrade -y && apt-get clean && rm -rf /var/lib/apt/lists/* && pip install --upgrade pip, clearing CVE-2025-8869.
  • Created nginx/Dockerfile to build nginx instead of pulling image.
  • Replaced ecdsa with PyJWT in security.py, clearing ecdsa CVE-2024-23342.