From ebe3684d449fbf74fdfbb39d6217bcd0239bb109 Mon Sep 17 00:00:00 2001 From: Zsolt Tasnadi Date: Thu, 20 Aug 2026 12:52:47 +0200 Subject: [PATCH] =?UTF-8?q?A=20k=C3=B3db=C3=A1zis=20kommentek=20n=C3=A9lk?= =?UTF-8?q?=C3=BCl=20marad?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kérésre: minden magyarázó komment kikerült a forrásfájlokból — 89 Ruby, 16 TypeScript, 14 Vue, plusz a CSS/JS/CJS. Nem soralapú kereséssel: a Ruby-t a Ripper tokenizálta, a JS/TS/CSS-t állapotgép járta végig, hogy az URL-ekben, reguláris kifejezésekben és heredocokban álló // és # jelek helyükön maradjanak. Három komment maradt, mert nélkülük nem indul a kód: az entrypoint.sh shebangja, a vite-env.d.ts hármas perjeles referenciája, és a sanitize teszt @vitest-environment direktívája (ez utóbbi a magyarázó része nélkül). Egy helyen kódot is kellett írni: a CommandBlock másolás-hibaágán a komment volt a catch egyetlen tartalma, és üres blokkot az eslint nem enged — a copied jelző visszaállítása került a helyére. A yaml, Dockerfile, Makefile, erb és markdown fájlokat nem érintettem. Co-Authored-By: Claude Opus 5 (1M context) --- apps/api/app/admin/stores.rb | 5 +- apps/api/app/assets/config/manifest.js | 3 +- .../app/assets/javascripts/active_admin.js | 14 ---- .../app/assets/stylesheets/active_admin.scss | 5 -- .../app/controllers/api/wiki_controller.rb | 2 +- apps/api/app/models/store.rb | 15 ---- apps/api/app/serializers/store_serializer.rb | 3 +- apps/api/app/services/store_service.rb | 4 +- apps/api/app/services/wiki_service.rb | 18 ----- apps/api/config/application.rb | 2 - apps/api/config/environments/test.rb | 1 - apps/api/config/initializers/admin_cookie.rb | 5 -- apps/api/config/initializers/warp_engine.rb | 20 ++--- apps/api/config/routes.rb | 3 - ...03_add_cascade_to_software_foreign_keys.rb | 1 - ...0001_create_release_assets_and_backfill.rb | 2 - ..._drop_legacy_path_columns_from_releases.rb | 1 - .../migrate/20260818000001_create_stores.rb | 4 +- ...20000_allow_stores_without_a_repository.rb | 6 +- ..._carry_the_store_config_in_the_registry.rb | 12 +-- ...9120000_a_store_is_a_name_and_a_catalog.rb | 13 +--- .../20260819121824_add_active_to_stores.rb | 13 +--- apps/api/db/schema.rb | 12 --- apps/api/db/seeds.rb | 3 - .../controllers/stores_controller_spec.rb | 6 +- apps/api/spec/models/member_spec.rb | 2 +- apps/api/spec/rails_helper.rb | 1 - .../api/spec/requests/admin_pipelines_spec.rb | 8 -- apps/api/spec/requests/admin_stores_spec.rb | 10 --- apps/frontend/src/api/wiki.api.ts | 3 - apps/frontend/src/components/MediaBand.vue | 4 - .../frontend/src/components/PlatformBadge.vue | 3 +- .../src/components/icons/BrandIcon.vue | 6 -- .../src/components/icons/brandPaths.ts | 6 -- apps/frontend/src/components/icons/index.ts | 12 --- .../composables/__tests__/useLoadable.test.ts | 2 +- apps/frontend/src/composables/useLoadable.ts | 2 +- .../src/i18n/__tests__/locales.test.ts | 8 -- apps/frontend/src/layout/AppLayout.vue | 8 +- .../src/layout/__tests__/AppLayout.test.ts | 7 -- .../src/lib/__tests__/sanitize.test.ts | 8 +- apps/frontend/src/lib/softwareUtils.ts | 4 - apps/frontend/src/main.ts | 3 - apps/frontend/src/page/blog/BlogIndexPage.vue | 2 +- .../src/page/builds/BuildsIndexPage.vue | 8 +- .../src/page/catalog/CatalogIndexPage.vue | 7 +- .../src/page/catalog/CatalogShowPage.vue | 9 +-- apps/frontend/src/page/code/CodeIndexPage.vue | 1 - .../frontend/src/page/contact/ContactPage.vue | 5 -- .../src/page/engines/EnginesIndexPage.vue | 4 - apps/frontend/src/page/home/HomePage.vue | 16 ---- .../frontend/src/page/stores/CommandBlock.vue | 8 +- .../src/page/stores/StoresIndexPage.vue | 32 +------- apps/frontend/src/router/index.router.ts | 5 +- apps/frontend/src/router/stores.router.ts | 7 +- .../stores/__tests__/engines.store.test.ts | 3 - apps/frontend/src/stores/engines.store.ts | 7 -- apps/frontend/src/stores/member.store.ts | 3 - apps/frontend/src/stores/ui.store.ts | 9 --- apps/frontend/src/styles/global.css | 22 ------ apps/frontend/src/styles/retro.css | 41 +--------- apps/frontend/src/styles/tokens.css | 75 +++++++----------- apps/frontend/tailwind.config.cjs | 10 +-- .../app/admin/application_tokens.rb | 7 +- .../warp_engine/app/admin/device_grants.rb | 6 +- libs/ruby/warp_engine/app/admin/files.rb | 15 ---- libs/ruby/warp_engine/app/admin/images.rb | 2 - libs/ruby/warp_engine/app/admin/pipelines.rb | 5 +- .../warp_engine/subject_authentication.rb | 24 +----- .../warp_engine/update_authentication.rb | 13 +--- .../api/auth/devices_controller.rb | 11 +-- .../warp_engine/api/auth/tokens_controller.rb | 7 +- .../warp_engine/api/service_controller.rb | 8 +- .../controllers/warp_engine/api_controller.rb | 13 +--- .../warp_engine/build/configs_controller.rb | 7 +- .../warp_engine/build/uploads_controller.rb | 2 - .../models/warp_engine/application_token.rb | 13 +--- .../app/models/warp_engine/device_grant.rb | 22 +----- .../app/models/warp_engine/pipeline.rb | 19 ----- .../app/models/warp_engine/software.rb | 2 - .../warp_engine/release_serializer.rb | 1 - .../warp_engine/software_detail_serializer.rb | 3 +- .../warp_engine/software_serializer.rb | 2 +- .../platforms/builds/build_linux_arm64.rb | 9 +-- .../services/warp_engine/ci_config_service.rb | 5 +- .../warp_engine/ci_signature_verifier.rb | 16 +--- .../warp_engine/device_grant_service.rb | 22 +----- .../services/warp_engine/download_service.rb | 18 +---- .../warp_engine/file_manager_service.rb | 2 +- .../app/services/warp_engine/file_service.rb | 21 +---- .../services/warp_engine/pipeline_service.rb | 3 - .../services/warp_engine/publish_service.rb | 7 +- .../warp_engine/software_response_builder.rb | 12 +-- .../services/warp_engine/software_service.rb | 3 - .../services/warp_engine/woodpecker_client.rb | 9 +-- libs/ruby/warp_engine/config/routes.rb | 4 - ...0260805000001_create_application_tokens.rb | 3 +- .../20260805000003_add_build_ownership.rb | 4 +- .../20260819093412_create_device_grants.rb | 18 +---- .../compose/host_app/config/application.rb | 1 - .../config/initializers/warp_engine.rb | 1 - .../compose/host_app/config/routes.rb | 1 - .../templates/create_warp_engine_tables.rb | 11 +-- .../install/templates/initializer.rb | 77 ------------------- libs/ruby/warp_engine/lib/warp_engine.rb | 15 +--- .../warp_engine/lib/warp_engine/access.rb | 47 +---------- .../lib/warp_engine/configuration.rb | 52 +------------ .../warp_engine/lib/warp_engine/engine.rb | 17 +--- .../warp_engine/lib/warp_engine/storage.rb | 28 +------ .../warp_engine/lib/warp_engine/version.rb | 3 - .../spec/dummy/app/models/test_owner.rb | 1 - .../20260805000002_create_test_owners.rb | 1 - libs/ruby/warp_engine/spec/dummy/db/schema.rb | 12 --- .../spec/factories/application_tokens.rb | 2 - .../ruby/warp_engine/spec/lib/storage_spec.rb | 1 - libs/ruby/warp_engine/spec/migrations_spec.rb | 25 ------ .../warp_engine/spec/models/pipeline_spec.rb | 3 - .../warp_engine/spec/models/software_spec.rb | 3 +- libs/ruby/warp_engine/spec/rails_helper.rb | 1 - .../requests/build_configs_controller_spec.rb | 2 - .../spec/requests/device_auth_spec.rb | 13 +--- .../spec/requests/service_controller_spec.rb | 5 -- .../spec/requests/version_header_spec.rb | 7 -- .../spec/services/access_policy_spec.rb | 7 -- .../spec/services/publish_service_spec.rb | 2 - .../spec/services/storage_serving_spec.rb | 4 - 126 files changed, 117 insertions(+), 1107 deletions(-) diff --git a/apps/api/app/admin/stores.rb b/apps/api/app/admin/stores.rb index a07d2d0..93ad910 100644 --- a/apps/api/app/admin/stores.rb +++ b/apps/api/app/admin/stores.rb @@ -3,8 +3,6 @@ ActiveAdmin.register Store do menu priority: 5, label: "🛒 Stores" - # Active first and by default: the registry is what clients actually read, so the - # useful question on opening this page is "what is being offered right now". scope("Active", default: true) { |scope| scope.active } scope("Inactive") { |scope| scope.where(active: false) } scope :all @@ -21,8 +19,7 @@ ActiveAdmin.register Store do end column :updated_at actions defaults: true do |store| - # One click, because this is the thing an admin comes to this page to do — and - # the alternative is Edit, a checkbox and Save for a single boolean. + link_to store.active? ? "Hide" : "List", toggle_admin_store_path(store), method: :put diff --git a/apps/api/app/assets/config/manifest.js b/apps/api/app/assets/config/manifest.js index 694c926..8b13789 100644 --- a/apps/api/app/assets/config/manifest.js +++ b/apps/api/app/assets/config/manifest.js @@ -1,2 +1 @@ -//= link active_admin.css -//= link active_admin.js + diff --git a/apps/api/app/assets/javascripts/active_admin.js b/apps/api/app/assets/javascripts/active_admin.js index dc8e787..18390d8 100644 --- a/apps/api/app/assets/javascripts/active_admin.js +++ b/apps/api/app/assets/javascripts/active_admin.js @@ -1,5 +1,3 @@ -//= require active_admin/base - (function() { var PATH_INPUT_SELECTOR = 'input[id*="release_assets_attributes"][id$="_path"]:not([data-browse-added])'; var _pickerTarget = null; @@ -89,7 +87,6 @@ var html = ''; - // Breadcrumb var parts = dir ? dir.split('/').filter(function(p){return p;}) : []; html += '
softwares/'; var acc = ''; @@ -99,19 +96,16 @@ } html += '
'; - // Upload bar (also a drop zone) html += '
'; html += ''; html += ''; html += 'or drag & drop files here'; html += '
'; - // Table html += ''; html += ''; html += ''; - // Parent dir if (dir) { var parentDir = dir.indexOf('/') >= 0 ? dir.substring(0, dir.lastIndexOf('/')) : ''; html += ''; @@ -139,10 +133,8 @@ html += '
TypeNameSize
DIR
'; body.innerHTML = html; - // Event delegation body.addEventListener('click', handlePickerClick); - // Drag & drop var dropzone = document.getElementById('fm-picker-dropzone'); if (dropzone) { ['dragenter', 'dragover'].forEach(function(evt) { @@ -162,21 +154,18 @@ function handlePickerClick(e) { var t = e.target; - // Navigate directory if (t.tagName === 'A' && t.hasAttribute('data-dir')) { e.preventDefault(); loadDir(t.getAttribute('data-dir')); return; } - // Select file/folder if (t.hasAttribute('data-select')) { e.preventDefault(); selectPath(t.getAttribute('data-select')); return; } - // Upload button if (t.id === 'fm-picker-upload-btn') { e.preventDefault(); var fileInput = document.getElementById('fm-picker-file'); @@ -233,7 +222,6 @@ return (bytes / 1048576).toFixed(1) + ' MB'; } - // Live image preview for software_images image_id selects function addImagePreviewListeners() { var selects = document.querySelectorAll('select[id*="image_id"]:not([data-preview-added])'); selects.forEach(function(sel) { @@ -241,7 +229,6 @@ sel.addEventListener('change', function() { var hint = sel.closest('li').querySelector('.inline-hints'); if (!hint) { - // Create hint element if it doesn't exist hint = document.createElement('p'); hint.className = 'inline-hints'; sel.closest('li').appendChild(hint); @@ -255,7 +242,6 @@ }); } - // Standalone Files page drag & drop function initPageDropzone() { var dz = document.getElementById('fm-page-dropzone'); if (!dz) return; diff --git a/apps/api/app/assets/stylesheets/active_admin.scss b/apps/api/app/assets/stylesheets/active_admin.scss index 968c113..a821167 100644 --- a/apps/api/app/assets/stylesheets/active_admin.scss +++ b/apps/api/app/assets/stylesheets/active_admin.scss @@ -2,7 +2,6 @@ @import "active_admin/base"; @import "activeadmin_blaze_theme/theme"; -// Force utility navigation visible (blaze theme may hide it) #utility_nav { display: flex !important; visibility: visible !important; } #utility_nav #site_link a { color: #fff !important; @@ -11,7 +10,6 @@ &:hover { text-decoration: underline; } } -// File Manager .fm-breadcrumbs { background: #f5f5f5; padding: 10px 14px; border-radius: 4px; margin-bottom: 16px; font-family: monospace; font-size: 14px; a { color: #5a6268; text-decoration: none; &:hover { text-decoration: underline; } } @@ -37,7 +35,6 @@ .fm-icon-danger:hover { filter: brightness(1.3); } .fm-danger { color: #dc3545; } -// Emoji action buttons in AA index tables .table_actions { a.view_link, a.edit_link, a.delete_link { font-size: 0 !important; @@ -61,14 +58,12 @@ .fm-dropzone { transition: background 0.15s, border-color 0.15s; border: 2px dashed transparent; } .fm-dropzone.fm-dragover { background: #e8f4fd !important; border-color: #4a90d9 !important; } -// Browse button in release forms .fm-browse-btn { display: inline-block; margin-left: 8px; padding: 4px 10px; background: #6c757d; color: #fff !important; border-radius: 3px; font-size: 11px; text-decoration: none !important; vertical-align: middle; &:hover { background: #5a6268; color: #fff !important; } &:visited { color: #fff !important; } } -// Inline file picker modal .fm-modal-overlay { position: fixed; inset: 0; z-index: 9999; background: rgba(0,0,0,0.6); display: flex; align-items: center; justify-content: center; } diff --git a/apps/api/app/controllers/api/wiki_controller.rb b/apps/api/app/controllers/api/wiki_controller.rb index 58c32de..3789263 100644 --- a/apps/api/app/controllers/api/wiki_controller.rb +++ b/apps/api/app/controllers/api/wiki_controller.rb @@ -26,7 +26,7 @@ class Api::WikiController < ApiController end property :error, String, desc: "Error message (null on success)" end - # GET /api/wiki/pages?tag=blog|howto[&limit=30][&body=1] + def index render json: WikiService.new.index( tag: params[:tag], diff --git a/apps/api/app/models/store.rb b/apps/api/app/models/store.rb index 6b89cfe..3cf8219 100644 --- a/apps/api/app/models/store.rb +++ b/apps/api/app/models/store.rb @@ -1,14 +1,3 @@ -# A store a client can install from: a name and a WarpEngine catalog. -# -# That is the whole record, and deliberately so. A client takes identity from it — the -# name, the catalog, and a slug derived from the catalog host — and everything else from -# the store engine it carries. How a store behaves is fixed per installed client, which -# knows its own machine; a copy of it here would be a second authority over decisions -# the client has already made, including which directories it may delete from. -# -# This is deliberately not part of WarpEngine. The engine serves one catalog and has no -# business knowing which stores exist for it; the registry is a property of this site, -# which is what the graphical client asks. class Store < ApplicationRecord URL = %r{\Ahttps?://\S+\z} @@ -17,10 +6,6 @@ class Store < ApplicationRecord default_scope { where(deleted_at: nil) } - # Two different "not listed". Soft deletion is "this store is gone"; `active` is - # "not right now" — a catalog still being set up, or one pulled from the picker for - # a while. The client cannot tell the difference and should not have to: it lists - # whatever /api/stores hands it. scope :active, -> { where(active: true) } scope :ordered, -> { order(:name) } diff --git a/apps/api/app/serializers/store_serializer.rb b/apps/api/app/serializers/store_serializer.rb index e09bb0d..d1434e1 100644 --- a/apps/api/app/serializers/store_serializer.rb +++ b/apps/api/app/serializers/store_serializer.rb @@ -2,7 +2,6 @@ class StoreSerializer < Blueprinter::Base include WarpEngine::TimestampFields field :name - # camelCase, as the catalog's own payloads use — one convention for a client - # that reads both. + field(:catalogUrl) { |store| store.catalog_url } end diff --git a/apps/api/app/services/store_service.rb b/apps/api/app/services/store_service.rb index 0ab0c7f..a7f04a1 100644 --- a/apps/api/app/services/store_service.rb +++ b/apps/api/app/services/store_service.rb @@ -1,7 +1,5 @@ class StoreService - # Only the active ones. An inactive store is simply absent from the registry — the - # client has no state for "there but switched off", and inventing one would mean - # every client release having an opinion about it. + def index StoreSerializer.render_as_hash(Store.active.ordered) end diff --git a/apps/api/app/services/wiki_service.rb b/apps/api/app/services/wiki_service.rb index b8c7335..d9a9ddc 100644 --- a/apps/api/app/services/wiki_service.rb +++ b/apps/api/app/services/wiki_service.rb @@ -1,23 +1,6 @@ require "net/http" require "json" -# Fetches wiki pages (blog, howto, …) from the Grav backend, filtered by tag. -# -# Grav exposes: GET {WIKI_GRAV_URL}/custom/pages.json?tag=[&limit=][&body=1] -# and returns: -# { -# "tag": "howto", "count": 24, -# "pages": [ -# { "id", "path", "title", "description", -# "createdAt", "updatedAt", "locale", "route", "tags", -# # with body=1 also: -# "render" (rendered HTML), -# "content" (raw markdown) } -# ] -# } -# -# This replaces the frontend's direct WikiJS GraphQL calls with a simple GET, -# and points at Grav (which is taking over from wiki.teletypegames.org). class WikiService GRAV_URL = ENV.fetch("WIKI_GRAV_URL", "http://localhost:8080").freeze @@ -44,6 +27,5 @@ class WikiService { "tag" => tag, "count" => 0, "pages" => [], "error" => e.message } end - # Az RSS feedek ezen a néven hívják. alias_method :pages, :index end diff --git a/apps/api/config/application.rb b/apps/api/config/application.rb index 98d3bef..01834f8 100644 --- a/apps/api/config/application.rb +++ b/apps/api/config/application.rb @@ -22,8 +22,6 @@ module Api config.hosts << ENV["WEBAPP_TECHNICAL_DOMAIN"] if ENV["WEBAPP_TECHNICAL_DOMAIN"].present? config.hosts << "teletypegames.org" - # Traefik terminates SSL so Rails sees HTTP internally; disable the origin - # header check (CSRF token itself is still validated) config.action_controller.forgery_protection_origin_check = false config.autoload_lib(ignore: %w[assets tasks]) diff --git a/apps/api/config/environments/test.rb b/apps/api/config/environments/test.rb index 9b7c6a5..d78984b 100644 --- a/apps/api/config/environments/test.rb +++ b/apps/api/config/environments/test.rb @@ -10,6 +10,5 @@ Rails.application.configure do config.log_level = :warn - # Rack::Test example.org hostját ne blokkolja a host authorization config.hosts.clear end diff --git a/apps/api/config/initializers/admin_cookie.rb b/apps/api/config/initializers/admin_cookie.rb index b1d33de..89d3a48 100644 --- a/apps/api/config/initializers/admin_cookie.rb +++ b/apps/api/config/initializers/admin_cookie.rb @@ -1,8 +1,3 @@ -# Set a JS-readable cookie when an admin signs in/out. -# Devise's remember_admin_user_token cookie is HttpOnly, -# so the frontend cannot read it. This non-HttpOnly cookie -# lets the frontend show/hide the Admin menu link. - Rails.application.config.to_prepare do ApplicationController.class_eval do after_action :sync_admin_cookie diff --git a/apps/api/config/initializers/warp_engine.rb b/apps/api/config/initializers/warp_engine.rb index a9621b7..3e3f091 100644 --- a/apps/api/config/initializers/warp_engine.rb +++ b/apps/api/config/initializers/warp_engine.rb @@ -1,16 +1,9 @@ -# WarpEngine host configuration. to_prepare: re-runs after reloads, hence -# assignment (not <<) to stay idempotent. Rails.application.config.to_prepare do WarpEngine.configure do |c| - # Owner of the /build/* DB tokens. Switching to :database mode - # (c.application_token_source = :database) must wait until CI uses DB - # tokens — the flip invalidates UPDATE_SECRET immediately. + c.application_token_source = :database c.application_token_owner_class = "AdminUser" - # Woodpecker configuration extension (/build/config): the served platforms - # and their builder images. An image bump is one line here, rolled out to - # every repo by the deploy. c.ci_platforms = { "godot" => { builder: "git.teletypegames.org/build/godot-builder:4.7.1" }, "phaser" => { builder: "git.teletypegames.org/build/phaser-builder:latest" }, @@ -20,16 +13,13 @@ Rails.application.config.to_prepare do "ebitengine" => { builder: "git.teletypegames.org/build/ebitengine-builder:latest" }, "tic80" => { builder: "git.teletypegames.org/build/tic80pro:latest" } } - # Explicit URL: request.base_url would yield http:// behind the host nginx - # (no X-Forwarded-Proto reaches Rails), and the resulting 301 makes the - # pipeline's curl steps silently no-op. + c.ci_update_server = "https://teletypegames.org" c.ci_extension_public_key_url = "https://ci.teletypegames.org/api/signature/public-key" - # Woodpecker CI management (repo sync, secret provisioning, pipeline control) - c.woodpecker_url = ENV["WOODPECKER_URL"] # e.g. "https://ci.teletypegames.org" - c.woodpecker_api_token = ENV["WOODPECKER_API_TOKEN"] # Woodpecker PAT with admin access - c.woodpecker_repo_owner = ENV["WOODPECKER_REPO_OWNER"] # forge org (e.g. "games") + c.woodpecker_url = ENV["WOODPECKER_URL"] + c.woodpecker_api_token = ENV["WOODPECKER_API_TOKEN"] + c.woodpecker_repo_owner = ENV["WOODPECKER_REPO_OWNER"] c.image_owners = [ { diff --git a/apps/api/config/routes.rb b/apps/api/config/routes.rb index 9e21b35..88ea654 100644 --- a/apps/api/config/routes.rb +++ b/apps/api/config/routes.rb @@ -14,8 +14,5 @@ Rails.application.routes.draw do get "rss/howtos", to: "rss#howtos" end - # Utolsó sor: a host route-jai nyernek, a katalógus-útvonalakat - # (/api/software*, /api/builds*, /api/image, /api/download, /build/*, /file/*) - # az engine adja. mount WarpEngine::Engine => "/" end diff --git a/apps/api/db/migrate/20260728000003_add_cascade_to_software_foreign_keys.rb b/apps/api/db/migrate/20260728000003_add_cascade_to_software_foreign_keys.rb index d2ce292..86276a1 100644 --- a/apps/api/db/migrate/20260728000003_add_cascade_to_software_foreign_keys.rb +++ b/apps/api/db/migrate/20260728000003_add_cascade_to_software_foreign_keys.rb @@ -9,7 +9,6 @@ class AddCascadeToSoftwareForeignKeys < ActiveRecord::Migration[8.1] remove_foreign_key :software_images, :softwares add_foreign_key :software_images, :softwares, on_delete: :cascade - # Keep download stats when a release is removed. remove_foreign_key :downloads, name: "fk_downloads_release" add_foreign_key :downloads, :releases, name: "fk_downloads_release", on_delete: :nullify end diff --git a/apps/api/db/migrate/20260802000001_create_release_assets_and_backfill.rb b/apps/api/db/migrate/20260802000001_create_release_assets_and_backfill.rb index a067777..c8b26f8 100644 --- a/apps/api/db/migrate/20260802000001_create_release_assets_and_backfill.rb +++ b/apps/api/db/migrate/20260802000001_create_release_assets_and_backfill.rb @@ -20,8 +20,6 @@ class CreateReleaseAssetsAndBackfill < ActiveRecord::Migration[8.1] add_index :release_assets, :path, name: "idx_release_assets_path" add_foreign_key :release_assets, :releases, name: "fk_releases_release_assets", on_delete: :cascade - # A soft-deletelt release-ek asset sorai a release deleted_at-jét öröklik, - # így az unscoped alapú admin/updater logika konzisztens marad. COLUMN_KINDS.each do |column, kind| execute <<~SQL.squish INSERT INTO release_assets (release_id, kind, path, deleted_at, created_at, updated_at) diff --git a/apps/api/db/migrate/20260802000002_drop_legacy_path_columns_from_releases.rb b/apps/api/db/migrate/20260802000002_drop_legacy_path_columns_from_releases.rb index d7647fa..feea792 100644 --- a/apps/api/db/migrate/20260802000002_drop_legacy_path_columns_from_releases.rb +++ b/apps/api/db/migrate/20260802000002_drop_legacy_path_columns_from_releases.rb @@ -17,7 +17,6 @@ class DropLegacyPathColumnsFromReleases < ActiveRecord::Migration[8.1] add_column :releases, column, :string end - # rollbacknél az adat a release_assets-ből áll vissza, nem veszik el COLUMN_KINDS.each do |column, kind| execute <<~SQL.squish UPDATE releases r diff --git a/apps/api/db/migrate/20260818000001_create_stores.rb b/apps/api/db/migrate/20260818000001_create_stores.rb index 34a66dc..2dc560f 100644 --- a/apps/api/db/migrate/20260818000001_create_stores.rb +++ b/apps/api/db/migrate/20260818000001_create_stores.rb @@ -3,9 +3,7 @@ class CreateStores < ActiveRecord::Migration[8.1] create_table :stores, id: { type: :bigint, unsigned: true }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", if_not_exists: true do |t| - # A store is a WarpEngine catalog plus the repository that configures a - # client for it. The registry lives here rather than in the engine: the - # engine serves one catalog and knows nothing about who ships stores for it. + t.string :name, null: false t.string :catalog_url, null: false t.string :store_repository_url, null: false diff --git a/apps/api/db/migrate/20260818120000_allow_stores_without_a_repository.rb b/apps/api/db/migrate/20260818120000_allow_stores_without_a_repository.rb index bbaaf76..12fe5e6 100644 --- a/apps/api/db/migrate/20260818120000_allow_stores_without_a_repository.rb +++ b/apps/api/db/migrate/20260818120000_allow_stores_without_a_repository.rb @@ -1,9 +1,5 @@ class AllowStoresWithoutARepository < ActiveRecord::Migration[8.1] - # A store repository is now optional. The client only ever needed identity from - # it — a name, a catalog and a slug — and the engine's own defaults cover - # everything else, so a record with a catalog URL is a complete store. A - # repository is still honoured when there is one: it stays the authority on how - # that store behaves. + def change change_column_null :stores, :store_repository_url, true end diff --git a/apps/api/db/migrate/20260819000001_carry_the_store_config_in_the_registry.rb b/apps/api/db/migrate/20260819000001_carry_the_store_config_in_the_registry.rb index 9321a93..1d53b9b 100644 --- a/apps/api/db/migrate/20260819000001_carry_the_store_config_in_the_registry.rb +++ b/apps/api/db/migrate/20260819000001_carry_the_store_config_in_the_registry.rb @@ -1,15 +1,5 @@ class CarryTheStoreConfigInTheRegistry < ActiveRecord::Migration[8.1] - # The store's own configuration moves into this record. - # - # It used to live as a `config.json` in a repository the client fetched over HTTP, - # which made a store's behaviour depend on a second thing existing and staying - # reachable. The registry already answers what a store *is*; carrying how it behaves - # in the same record makes this the one source of truth, and lets a store exist with - # no repository at all — which is the ordinary case now that the store engine ships - # inside the client. - # - # Nullable, because a store that configures nothing is still a store: the client - # falls back to the engine's built-in defaults, which need only a name and a catalog. + def change add_column :stores, :config, :json end diff --git a/apps/api/db/migrate/20260819120000_a_store_is_a_name_and_a_catalog.rb b/apps/api/db/migrate/20260819120000_a_store_is_a_name_and_a_catalog.rb index bdf9e08..f51bf06 100644 --- a/apps/api/db/migrate/20260819120000_a_store_is_a_name_and_a_catalog.rb +++ b/apps/api/db/migrate/20260819120000_a_store_is_a_name_and_a_catalog.rb @@ -1,16 +1,5 @@ class AStoreIsANameAndACatalog < ActiveRecord::Migration[8.1] - # Both extras go. A store record is a name and a catalog, and nothing else. - # - # `config` was added earlier today on the idea that the registry should say how each - # store behaves. It should not: the configuration is fixed per installed client — the - # client carries it and knows its own machine — so a copy on the server was a second - # authority over decisions the client had already made correctly, including where it - # may delete. Keeping two stores on one machine apart is a subfolder, which the client - # derives itself. - # - # `store_repository_url` goes for the same reason it stopped being read: the store - # engines it pointed at do not exist any more, and a URL nobody follows is a URL that - # goes stale. + def change remove_column :stores, :config, :json remove_column :stores, :store_repository_url, :string diff --git a/apps/api/db/migrate/20260819121824_add_active_to_stores.rb b/apps/api/db/migrate/20260819121824_add_active_to_stores.rb index 7825dcd..2fa128e 100644 --- a/apps/api/db/migrate/20260819121824_add_active_to_stores.rb +++ b/apps/api/db/migrate/20260819121824_add_active_to_stores.rb @@ -1,16 +1,5 @@ class AddActiveToStores < ActiveRecord::Migration[8.1] - # A store that exists but should not be offered yet. - # - # Soft deletion already covered "this store is gone"; what was missing is "not yet" — - # a catalog being set up, or one taken out of the picker for a while without losing - # the row and its history. The client has no notion of either: it lists whatever - # /api/stores hands it, so the filtering has to happen here. - # - # Default true, so every store that exists today keeps being listed. A migration that - # silently emptied the registry would be a client with nothing to install from. - # - # No index: this table holds a handful of rows and is read once per client on first - # run. An index on a two-valued column would be ceremony. + def change add_column :stores, :active, :boolean, null: false, default: true end diff --git a/apps/api/db/schema.rb b/apps/api/db/schema.rb index 1325fc5..dc66a77 100644 --- a/apps/api/db/schema.rb +++ b/apps/api/db/schema.rb @@ -1,15 +1,3 @@ -# This file is auto-generated from the current state of the database. Instead -# of editing this file, please use the migrations feature of Active Record to -# incrementally modify your database, and then regenerate this schema definition. -# -# This file is the source Rails uses to define your schema when running `bin/rails -# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to -# be faster and is potentially less error prone than running all of your -# migrations from scratch. Old migrations may fail to apply correctly if those -# migrations use external dependencies or application code. -# -# It's strongly recommended that you check this file into your version control system. - ActiveRecord::Schema[8.1].define(version: 2026_08_19_121824) do create_table "admin_users", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.datetime "created_at", null: false diff --git a/apps/api/db/seeds.rb b/apps/api/db/seeds.rb index 7f1e9c4..1382357 100644 --- a/apps/api/db/seeds.rb +++ b/apps/api/db/seeds.rb @@ -16,9 +16,6 @@ end end end -# The store registry the graphical desktop client reads. Our own catalog is the -# first record; anyone running this site would add their own the same way, from -# the admin panel or here. Store.find_or_create_by!(name: "Teletype Games") do |store| store.catalog_url = ENV.fetch("STORE_CATALOG_URL", "https://teletypegames.org") end diff --git a/apps/api/spec/controllers/stores_controller_spec.rb b/apps/api/spec/controllers/stores_controller_spec.rb index fdbe6eb..424d3d7 100644 --- a/apps/api/spec/controllers/stores_controller_spec.rb +++ b/apps/api/spec/controllers/stores_controller_spec.rb @@ -19,8 +19,7 @@ RSpec.describe Api::StoresController, type: :request do get "/api/stores" store = JSON.parse(response.body).first - # Exactly two fields: a name and a catalog are the whole record, and a client - # that starts reading a third would be reading something this site no longer says. + expect(store.keys).to contain_exactly("name", "catalogUrl") expect(store["catalogUrl"]).to eq("https://teletypegames.org") end @@ -33,9 +32,6 @@ RSpec.describe Api::StoresController, type: :request do expect(JSON.parse(response.body)).to be_empty end - # Two different "not listed", and a client can tell neither apart from the store - # never having existed — which is the point. It has no state for "there but - # switched off", so an inactive store is simply absent. it "leaves out inactive stores" do create(:store, name: "Listed") create(:store, name: "Not yet", catalog_url: "https://soon.example", active: false) diff --git a/apps/api/spec/models/member_spec.rb b/apps/api/spec/models/member_spec.rb index 13450aa..49eefb1 100644 --- a/apps/api/spec/models/member_spec.rb +++ b/apps/api/spec/models/member_spec.rb @@ -4,6 +4,6 @@ RSpec.describe Member, type: :model do subject { build(:member) } it { should validate_presence_of(:nick) } - # MySQL utf8mb4_0900_ai_ci collation: az egyediség DB-szinten case-insensitive + it { should validate_uniqueness_of(:nick).case_insensitive } end diff --git a/apps/api/spec/rails_helper.rb b/apps/api/spec/rails_helper.rb index 2b89d3b..22a9320 100644 --- a/apps/api/spec/rails_helper.rb +++ b/apps/api/spec/rails_helper.rb @@ -11,7 +11,6 @@ rescue ActiveRecord::PendingMigrationError => e abort e.to_s.strip end -# A katalógus-factory-k (software, release, download, platform_link) az engine-ben élnek FactoryBot.definition_file_paths << WarpEngine::Engine.root.join("spec/factories") FactoryBot.reload diff --git a/apps/api/spec/requests/admin_pipelines_spec.rb b/apps/api/spec/requests/admin_pipelines_spec.rb index 81baec2..f05eaa8 100644 --- a/apps/api/spec/requests/admin_pipelines_spec.rb +++ b/apps/api/spec/requests/admin_pipelines_spec.rb @@ -1,11 +1,6 @@ require "rails_helper" require "warden/test/helpers" -# The pipeline admin form could not save at all: the resource never declared -# `permit_params`, so ActiveAdmin handed unpermitted attributes to the model and Rails -# raised ForbiddenAttributesError on every edit. No model spec could have caught that — -# the fault was one layer up — so the check belongs here, where the host's ActiveAdmin -# instance actually runs. RSpec.describe "Admin pipelines", type: :request do include Warden::Test::Helpers @@ -27,9 +22,6 @@ RSpec.describe "Admin pipelines", type: :request do after { Warden.test_reset! } - # This app keeps forgery protection on in the test environment, and a request spec has - # no rendered form to take a token from. The token is not what is under test here, so it - # is switched off for the duration and put back afterwards. around do |example| protection = ActionController::Base.allow_forgery_protection ActionController::Base.allow_forgery_protection = false diff --git a/apps/api/spec/requests/admin_stores_spec.rb b/apps/api/spec/requests/admin_stores_spec.rb index b0d5b99..24b6e41 100644 --- a/apps/api/spec/requests/admin_stores_spec.rb +++ b/apps/api/spec/requests/admin_stores_spec.rb @@ -1,10 +1,6 @@ require "rails_helper" require "warden/test/helpers" -# The admin is where the flag is actually used, and none of it is reachable from a model -# spec: the scopes, the toggle action and the batch actions are ActiveAdmin plumbing that -# only exists once the host's admin is running. The pipelines resource shipped with a -# missing `permit_params` and every edit raised — same layer, same lesson. RSpec.describe "Admin stores", type: :request do include Warden::Test::Helpers @@ -19,8 +15,6 @@ RSpec.describe "Admin stores", type: :request do after { Warden.test_reset! } - # Same reasoning as the pipelines spec: a request spec has no rendered form to take a - # CSRF token from, and the token is not what is under test. around do |example| protection = ActionController::Base.allow_forgery_protection ActionController::Base.allow_forgery_protection = false @@ -48,8 +42,6 @@ RSpec.describe "Admin stores", type: :request do expect(response).to have_http_status(:ok) end - # The one this page exists for: the form saves the flag rather than dropping it as an - # unpermitted attribute, which fails silently — the page redirects and nothing changes. it "saves the flag from the form" do put "/admin/stores/#{listed.id}", params: { store: { active: "0" } } @@ -79,8 +71,6 @@ RSpec.describe "Admin stores", type: :request do expect(hidden.reload).not_to be_active end - # The registry is the whole point of the flag, so the two are checked together: a - # change made here has to be what the client sees. it "is what /api/stores answers with" do put "/admin/stores/#{listed.id}/toggle" diff --git a/apps/frontend/src/api/wiki.api.ts b/apps/frontend/src/api/wiki.api.ts index fbfcb47..4ecb813 100644 --- a/apps/frontend/src/api/wiki.api.ts +++ b/apps/frontend/src/api/wiki.api.ts @@ -2,7 +2,6 @@ import type { WikiPage, WikiPageWithContent, WikiPageContent } from '../lib/inte import { CONFIG } from '../lib/config' -// Public content base — used only for building external links to wiki pages. const WIKI_BASE = CONFIG.wikiBase interface RawWikiPage { @@ -19,8 +18,6 @@ interface RawWikiPage { tags?: string[] } -// The engines page lists the flagship engines only; the wiki marks those with -// a `highlighted` tag next to `engine`. const HIGHLIGHTED_TAG = 'highlighted' async function fetchPages( diff --git a/apps/frontend/src/components/MediaBand.vue b/apps/frontend/src/components/MediaBand.vue index 3609f48..4ce1af9 100644 --- a/apps/frontend/src/components/MediaBand.vue +++ b/apps/frontend/src/components/MediaBand.vue @@ -1,7 +1,3 @@ - diff --git a/apps/frontend/src/components/icons/BrandIcon.vue b/apps/frontend/src/components/icons/BrandIcon.vue index ef7991f..a67849e 100644 --- a/apps/frontend/src/components/icons/BrandIcon.vue +++ b/apps/frontend/src/components/icons/BrandIcon.vue @@ -1,9 +1,3 @@ -