grav interation
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
class Api::WikiController < ApiController
|
||||
# GET /api/wiki/pages?tag=blog|howto[&limit=30][&body=1]
|
||||
def index
|
||||
render json: WikiService.new.pages(
|
||||
tag: params[:tag],
|
||||
limit: params[:limit],
|
||||
body: params[:body]
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,46 @@
|
||||
require "net/http"
|
||||
require "json"
|
||||
|
||||
# Fetches wiki pages (blog, howto, …) from the Grav backend, filtered by tag.
|
||||
#
|
||||
# Grav exposes: GET {WIKI_GRAV_URL}/api/pages.json?tag=<tag>[&limit=<n>][&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
|
||||
|
||||
def pages(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}/api/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
|
||||
end
|
||||
@@ -8,6 +8,7 @@ Rails.application.routes.draw do
|
||||
get "events", to: "events#index"
|
||||
get "members", to: "members#index"
|
||||
get "image/:id", to: "images#show"
|
||||
get "wiki/pages", to: "wiki#index"
|
||||
end
|
||||
|
||||
get "update", to: "update#update"
|
||||
|
||||
@@ -1,45 +1,28 @@
|
||||
import type { WikiPage, WikiPageWithContent, WikiPageContent } from '../lib/interfaces/wiki.interface'
|
||||
|
||||
const BASE = import.meta.env.VITE_WIKI_BASE || 'https://wiki.teletypegames.org'
|
||||
const TOKEN = import.meta.env.WEBAPP_WIKIJS_TOKEN
|
||||
// Public content base — used only for building external links to wiki pages.
|
||||
// Points at Grav (which is taking over from wiki.teletypegames.org).
|
||||
const WIKI_BASE = import.meta.env.VITE_WIKI_BASE || 'https://wiki.teletypegames.org'
|
||||
|
||||
function buildHeaders(): Record<string, string> {
|
||||
const h: Record<string, string> = { 'Content-Type': 'application/json', 'Accept': 'application/json' }
|
||||
if (TOKEN) h['Authorization'] = `Bearer ${TOKEN}`
|
||||
return h
|
||||
}
|
||||
// Pages come from our own Rails API (same-origin), which proxies the Grav
|
||||
// content backend: GET /api/wiki/pages?tag=<tag>[&limit=<n>][&body=1]
|
||||
async function fetchPages(
|
||||
tag: string,
|
||||
opts: { body?: boolean; limit?: number } = {},
|
||||
): Promise<any[]> {
|
||||
const params = new URLSearchParams({ tag })
|
||||
if (opts.body) params.set('body', '1')
|
||||
if (opts.limit) params.set('limit', String(opts.limit))
|
||||
|
||||
async function gql(query: string): Promise<any> {
|
||||
const res = await fetch(`${BASE}/graphql`, {
|
||||
method: 'POST',
|
||||
headers: buildHeaders(),
|
||||
body: JSON.stringify({ query }),
|
||||
})
|
||||
const res = await fetch(`/api/wiki/pages?${params.toString()}`)
|
||||
if (!res.ok) return []
|
||||
const json = await res.json()
|
||||
if (json.errors) throw new Error(json.errors.map((e: any) => e.message).join(', '))
|
||||
return json.data
|
||||
return json?.pages ?? []
|
||||
}
|
||||
|
||||
const listBlogPages = async (): Promise<WikiPageWithContent[]> => {
|
||||
const data = await gql(`{
|
||||
pages {
|
||||
list(orderBy: CREATED, orderByDirection: DESC, tags: ["blog"]) {
|
||||
id path title description updatedAt createdAt locale
|
||||
}
|
||||
}
|
||||
}`)
|
||||
const pages: any[] = data?.pages?.list ?? []
|
||||
|
||||
const pagesWithContent = await Promise.all(pages.map(async (p: any) => {
|
||||
try {
|
||||
const contentData = await gql(`{ pages { single(id: ${p.id}) { content } } }`)
|
||||
return { ...p, content: contentData?.pages?.single?.content ?? '' }
|
||||
} catch {
|
||||
return { ...p, content: '' }
|
||||
}
|
||||
}))
|
||||
|
||||
return pagesWithContent.map((p: any): WikiPageWithContent => ({
|
||||
const pages = await fetchPages('blog', { body: true })
|
||||
return pages.map((p: any): WikiPageWithContent => ({
|
||||
id: p.id,
|
||||
path: p.path,
|
||||
title: p.title || p.path,
|
||||
@@ -52,14 +35,7 @@ const listBlogPages = async (): Promise<WikiPageWithContent[]> => {
|
||||
}
|
||||
|
||||
const getBlogPage = async (slug: string): Promise<WikiPageContent | null> => {
|
||||
const data = await gql(`{
|
||||
pages {
|
||||
list(orderBy: CREATED, orderByDirection: DESC, tags: ["blog"]) {
|
||||
id path
|
||||
}
|
||||
}
|
||||
}`)
|
||||
const pages: any[] = data?.pages?.list ?? []
|
||||
const pages = await fetchPages('blog', { body: true })
|
||||
|
||||
const matched = pages.find((p: any) => {
|
||||
const pageSlug = p.path.startsWith('blog/') ? p.path.replace('blog/', '') : p.path
|
||||
@@ -68,25 +44,20 @@ const getBlogPage = async (slug: string): Promise<WikiPageContent | null> => {
|
||||
|
||||
if (!matched) return null
|
||||
|
||||
const singleData = await gql(`{
|
||||
pages {
|
||||
single(id: ${matched.id}) {
|
||||
id path title description render updatedAt createdAt locale
|
||||
}
|
||||
}
|
||||
}`)
|
||||
return singleData?.pages?.single ?? null
|
||||
return {
|
||||
id: matched.id,
|
||||
path: matched.path,
|
||||
title: matched.title || matched.path,
|
||||
description: matched.description ?? '',
|
||||
render: matched.render ?? '',
|
||||
updatedAt: matched.updatedAt,
|
||||
createdAt: matched.createdAt,
|
||||
locale: matched.locale,
|
||||
}
|
||||
}
|
||||
|
||||
const listHowtoPages = async (): Promise<WikiPage[]> => {
|
||||
const data = await gql(`{
|
||||
pages {
|
||||
list(orderBy: UPDATED, orderByDirection: DESC, tags: ["howto"]) {
|
||||
id path title description updatedAt createdAt locale
|
||||
}
|
||||
}
|
||||
}`)
|
||||
const pages: any[] = data?.pages?.list ?? []
|
||||
const pages = await fetchPages('howto', { limit: 30 })
|
||||
return pages.map((p: any): WikiPage => ({
|
||||
id: p.id,
|
||||
path: p.path,
|
||||
@@ -95,8 +66,8 @@ const listHowtoPages = async (): Promise<WikiPage[]> => {
|
||||
updatedAt: p.updatedAt,
|
||||
createdAt: p.createdAt,
|
||||
locale: p.locale,
|
||||
})).slice(0, 30)
|
||||
}))
|
||||
}
|
||||
|
||||
export { BASE as WIKI_BASE }
|
||||
export { WIKI_BASE }
|
||||
export default { listBlogPages, getBlogPage, listHowtoPages }
|
||||
|
||||
@@ -13,14 +13,6 @@ export default defineConfig({
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/proxy\/wiki/, '')
|
||||
},
|
||||
'/_assets': {
|
||||
target: 'https://wiki.teletypegames.org',
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/_error': {
|
||||
target: 'https://wiki.teletypegames.org',
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/proxy/git': {
|
||||
target: 'https://git.teletypegames.org',
|
||||
changeOrigin: true,
|
||||
|
||||
+1
-1
@@ -125,7 +125,6 @@ services:
|
||||
- NODE_ENV=development
|
||||
- HOST=0.0.0.0
|
||||
- WEBAPP_GITEA_TOKEN=${WEBAPP_GITEA_TOKEN}
|
||||
- WEBAPP_WIKIJS_TOKEN=${WEBAPP_WIKIJS_TOKEN}
|
||||
- DISCORD_INVITE_LINK=${DISCORD_INVITE_LINK}
|
||||
- YOUTUBE_API_KEY=${YOUTUBE_API_KEY}
|
||||
- YOUTUBE_CHANNEL_ID=${YOUTUBE_CHANNEL_ID}
|
||||
@@ -161,6 +160,7 @@ services:
|
||||
- SECRET_KEY_BASE=${SECRET_KEY_BASE:-changeme_in_production_use_long_random_string}
|
||||
- ADMIN_EMAIL=${ADMIN_EMAIL:-admin@teletype.hu}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-password123}
|
||||
- WIKI_GRAV_URL=${WIKI_GRAV_URL:-https://wiki.teletypegames.org}
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
|
||||
Reference in New Issue
Block a user