No application code reads ENV, and the wiki has one address again
The API read its environment wherever it happened to need it: `Image` in the model, `RssService` and `WikiService` in class-level constants, the softwares admin page in a sidebar. Two of those wanted the same wiki and disagreed about its name — `RssService::WIKI_URL` against `WikiService::GRAV_URL` — and only the second one is set in docker-compose, so the howtos feed had been linking to the hard-coded default all along. Every `ENV` read is in `config/application.rb` now, as `config.x.site_url`, `config.x.wiki_url` and `config.x.images.container_path`, and the code asks `Rails.configuration.x`. One place says what this app needs from its environment, and a test can override it. `RssService` was three copies of the same twenty-line `RSS::Maker` block. It is `Rss::Feed` plus `Rss::BlogFeed`, `Rss::ReleasesFeed` and `Rss::HowtosFeed`, each of which now only answers what its title, its link and its items are — and the `Time.parse(...) rescue Time.current` modifier, which swallowed everything, is a rescue of ArgumentError and TypeError. `WikiService` becomes `Wiki::Pages` and loses `alias_method :pages, :index`: two names for one method meant the controller and the feeds each called it something different. The admin cookie was a monkey patch — `ApplicationController.class_eval` in an initializer, adding an `after_action` that the three-line controller file gave no hint of. It is a `SyncsAdminCookie` concern the controller includes. And the engine stops reading the host's config keys: the "View on site" link in the softwares admin asked for `Rails.configuration.x.site_url`, which is ours, not its. `c.site_url` is an engine setting now, nil by default, and the link is left out when the host does not set it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,21 +7,21 @@ class Api::RssController < ApiController
|
||||
api :GET, "/api/rss/blog", "Blog RSS feed"
|
||||
returns code: 200, desc: "RSS XML feed of blog posts"
|
||||
def blog
|
||||
xml = RssService.new.blog_feed
|
||||
xml = Rss::BlogFeed.new.xml
|
||||
render xml: xml, content_type: "application/rss+xml"
|
||||
end
|
||||
|
||||
api :GET, "/api/rss/releases", "Software releases RSS feed"
|
||||
returns code: 200, desc: "RSS XML feed of software releases"
|
||||
def releases
|
||||
xml = RssService.new.releases_feed
|
||||
xml = Rss::ReleasesFeed.new.xml
|
||||
render xml: xml, content_type: "application/rss+xml"
|
||||
end
|
||||
|
||||
api :GET, "/api/rss/howtos", "HowTos RSS feed"
|
||||
returns code: 200, desc: "RSS XML feed of tech howtos"
|
||||
def howtos
|
||||
xml = RssService.new.howtos_feed
|
||||
xml = Rss::HowtosFeed.new.xml
|
||||
render xml: xml, content_type: "application/rss+xml"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -28,7 +28,7 @@ class Api::WikiController < ApiController
|
||||
end
|
||||
|
||||
def index
|
||||
render json: WikiService.new.index(
|
||||
render json: Wiki::Pages.new.fetch(
|
||||
tag: params[:tag],
|
||||
limit: params[:limit],
|
||||
body: params[:body]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
class ApplicationController < ActionController::Base
|
||||
include SyncsAdminCookie
|
||||
|
||||
protect_from_forgery with: :exception
|
||||
end
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
module SyncsAdminCookie
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
after_action :sync_admin_cookie
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sync_admin_cookie
|
||||
if current_admin_user
|
||||
cookies[:is_admin] = { value: "1", httponly: false, same_site: :lax, path: "/" }
|
||||
elsif cookies[:is_admin]
|
||||
cookies.delete(:is_admin, path: "/")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
module Rss
|
||||
class BlogFeed < Feed
|
||||
private
|
||||
|
||||
def title = "Teletype Games Blog"
|
||||
def link = "#{site_url}/blog"
|
||||
def description = "Latest blog posts from Teletype Games"
|
||||
|
||||
def items
|
||||
Wiki::Pages.new.all(tag: "blog", limit: 30).map do |page|
|
||||
{
|
||||
title: page["title"],
|
||||
link: "#{site_url}/blog/#{page['route']}",
|
||||
description: page["description"],
|
||||
published_at: parse_time(page["createdAt"])
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,35 @@
|
||||
require "rss"
|
||||
|
||||
module Rss
|
||||
class Feed
|
||||
def xml
|
||||
RSS::Maker.make("2.0") do |maker|
|
||||
maker.channel.title = title
|
||||
maker.channel.link = link
|
||||
maker.channel.description = description
|
||||
maker.channel.language = "hu"
|
||||
|
||||
items.each do |item|
|
||||
maker.items.new_item do |rss_item|
|
||||
rss_item.title = item[:title]
|
||||
rss_item.link = item[:link]
|
||||
rss_item.description = item[:description].to_s
|
||||
rss_item.pubDate = item[:published_at]
|
||||
end
|
||||
end
|
||||
end.to_s
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def site_url = Rails.configuration.x.site_url
|
||||
|
||||
def wiki_url = Rails.configuration.x.wiki_url
|
||||
|
||||
def parse_time(value)
|
||||
Time.parse(value.to_s)
|
||||
rescue ArgumentError, TypeError
|
||||
Time.current
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
module Rss
|
||||
class HowtosFeed < Feed
|
||||
private
|
||||
|
||||
def title = "Teletype Games HowTos"
|
||||
def link = "#{site_url}/howtos"
|
||||
def description = "Latest tech howtos from Teletype Games"
|
||||
|
||||
def items
|
||||
Wiki::Pages.new.all(tag: "howto", limit: 30).map do |page|
|
||||
{
|
||||
title: page["title"],
|
||||
link: "#{wiki_url}/#{page['path']}",
|
||||
description: page["description"],
|
||||
published_at: parse_time(page["createdAt"])
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,23 @@
|
||||
module Rss
|
||||
class ReleasesFeed < Feed
|
||||
private
|
||||
|
||||
def title = "Teletype Games Releases"
|
||||
def link = "#{site_url}/catalog"
|
||||
def description = "Latest game releases from Teletype Games"
|
||||
|
||||
def items
|
||||
WarpEngine::Release.includes(:software).order(created_at: :desc).limit(50).filter_map do |release|
|
||||
software = release.software
|
||||
next if software.nil?
|
||||
|
||||
{
|
||||
title: "#{software.title} v#{release.version}",
|
||||
link: "#{site_url}/catalog/#{software.name}",
|
||||
description: "#{software.title} #{release.version} released – #{software.desc}",
|
||||
published_at: release.created_at.to_time
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,73 +0,0 @@
|
||||
require "rss"
|
||||
|
||||
class RssService
|
||||
SITE_URL = ENV.fetch("SITE_URL", "https://teletypegames.org").freeze
|
||||
WIKI_URL = ENV.fetch("WIKI_URL", "https://wiki.teletypegames.org").freeze
|
||||
|
||||
def blog_feed
|
||||
pages = WikiService.new.pages(tag: "blog", limit: 30)
|
||||
items = pages.fetch("pages", [])
|
||||
|
||||
RSS::Maker.make("2.0") do |maker|
|
||||
maker.channel.title = "Teletype Games Blog"
|
||||
maker.channel.link = "#{SITE_URL}/blog"
|
||||
maker.channel.description = "Latest blog posts from Teletype Games"
|
||||
maker.channel.language = "hu"
|
||||
|
||||
items.each do |page|
|
||||
maker.items.new_item do |item|
|
||||
item.title = page["title"]
|
||||
item.link = "#{SITE_URL}/blog/#{page["route"]}"
|
||||
item.description = page["description"].to_s
|
||||
item.pubDate = Time.parse(page["createdAt"]) rescue Time.current
|
||||
end
|
||||
end
|
||||
end.to_s
|
||||
end
|
||||
|
||||
def releases_feed
|
||||
releases = WarpEngine::Release.includes(:software)
|
||||
.order(created_at: :desc)
|
||||
.limit(50)
|
||||
|
||||
RSS::Maker.make("2.0") do |maker|
|
||||
maker.channel.title = "Teletype Games Releases"
|
||||
maker.channel.link = "#{SITE_URL}/catalog"
|
||||
maker.channel.description = "Latest game releases from Teletype Games"
|
||||
maker.channel.language = "hu"
|
||||
|
||||
releases.each do |release|
|
||||
sw = release.software
|
||||
next unless sw
|
||||
|
||||
maker.items.new_item do |item|
|
||||
item.title = "#{sw.title} v#{release.version}"
|
||||
item.link = "#{SITE_URL}/catalog/#{sw.name}"
|
||||
item.description = "#{sw.title} #{release.version} released – #{sw.desc}"
|
||||
item.pubDate = release.created_at.to_time
|
||||
end
|
||||
end
|
||||
end.to_s
|
||||
end
|
||||
|
||||
def howtos_feed
|
||||
pages = WikiService.new.pages(tag: "howto", limit: 30)
|
||||
items = pages.fetch("pages", [])
|
||||
|
||||
RSS::Maker.make("2.0") do |maker|
|
||||
maker.channel.title = "Teletype Games HowTos"
|
||||
maker.channel.link = "#{SITE_URL}/howtos"
|
||||
maker.channel.description = "Latest tech howtos from Teletype Games"
|
||||
maker.channel.language = "hu"
|
||||
|
||||
items.each do |page|
|
||||
maker.items.new_item do |item|
|
||||
item.title = page["title"]
|
||||
item.link = "#{WIKI_URL}/#{page["path"]}"
|
||||
item.description = page["description"].to_s
|
||||
item.pubDate = Time.parse(page["createdAt"]) rescue Time.current
|
||||
end
|
||||
end
|
||||
end.to_s
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,37 @@
|
||||
require "net/http"
|
||||
require "json"
|
||||
|
||||
module Wiki
|
||||
class Pages
|
||||
def fetch(tag:, limit: nil, body: nil)
|
||||
query = { tag: tag }
|
||||
query[:limit] = limit if limit.present?
|
||||
query[:body] = body if body.present?
|
||||
|
||||
uri = URI.parse("#{Rails.configuration.x.wiki_url}/custom/pages.json")
|
||||
uri.query = URI.encode_www_form(query)
|
||||
|
||||
response = Net::HTTP.start(
|
||||
uri.host, uri.port,
|
||||
use_ssl: uri.scheme == "https",
|
||||
open_timeout: 5, read_timeout: 10
|
||||
) { |http| http.get(uri.request_uri) }
|
||||
|
||||
return empty(tag, "grav responded #{response.code}") unless response.is_a?(Net::HTTPSuccess)
|
||||
|
||||
JSON.parse(response.body)
|
||||
rescue StandardError => e
|
||||
empty(tag, e.message)
|
||||
end
|
||||
|
||||
def all(tag:, limit: nil)
|
||||
fetch(tag: tag, limit: limit).fetch("pages", [])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def empty(tag, error)
|
||||
{ "tag" => tag, "count" => 0, "pages" => [], "error" => error }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,31 +0,0 @@
|
||||
require "net/http"
|
||||
require "json"
|
||||
|
||||
class WikiService
|
||||
GRAV_URL = ENV.fetch("WIKI_GRAV_URL", "http://localhost:8080").freeze
|
||||
|
||||
def index(tag:, limit: nil, body: nil)
|
||||
query = { tag: tag }
|
||||
query[:limit] = limit if limit.present?
|
||||
query[:body] = body if body.present?
|
||||
|
||||
uri = URI.parse("#{GRAV_URL}/custom/pages.json")
|
||||
uri.query = URI.encode_www_form(query)
|
||||
|
||||
response = Net::HTTP.start(
|
||||
uri.host, uri.port,
|
||||
use_ssl: uri.scheme == "https",
|
||||
open_timeout: 5, read_timeout: 10
|
||||
) { |http| http.get(uri.request_uri) }
|
||||
|
||||
unless response.is_a?(Net::HTTPSuccess)
|
||||
return { "tag" => tag, "count" => 0, "pages" => [], "error" => "grav responded #{response.code}" }
|
||||
end
|
||||
|
||||
JSON.parse(response.body)
|
||||
rescue StandardError => e
|
||||
{ "tag" => tag, "count" => 0, "pages" => [], "error" => e.message }
|
||||
end
|
||||
|
||||
alias_method :pages, :index
|
||||
end
|
||||
@@ -25,5 +25,9 @@ module Api
|
||||
config.action_controller.forgery_protection_origin_check = false
|
||||
|
||||
config.autoload_lib(ignore: %w[assets tasks])
|
||||
|
||||
config.x.site_url = ENV.fetch("SITE_URL", "https://teletypegames.org")
|
||||
config.x.wiki_url = ENV.fetch("WIKI_GRAV_URL", "https://wiki.teletypegames.org")
|
||||
config.x.images.container_path = ENV.fetch("IMAGE_CONTAINER_PATH", "/images")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
Rails.application.config.to_prepare do
|
||||
ApplicationController.class_eval do
|
||||
after_action :sync_admin_cookie
|
||||
|
||||
private
|
||||
|
||||
def sync_admin_cookie
|
||||
if current_admin_user
|
||||
cookies[:is_admin] = { value: "1", httponly: false, same_site: :lax, path: "/" }
|
||||
elsif cookies[:is_admin]
|
||||
cookies.delete(:is_admin, path: "/")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -6,6 +6,8 @@ Rails.application.config.to_prepare do
|
||||
|
||||
c.image_class_name = "Image"
|
||||
|
||||
c.site_url = Rails.configuration.x.site_url
|
||||
|
||||
c.ci_adapter = WarpEngine::CI::Woodpecker::Adapter.new(
|
||||
url: ENV["WOODPECKER_URL"],
|
||||
api_token: ENV["WOODPECKER_API_TOKEN"],
|
||||
|
||||
Reference in New Issue
Block a user