feat(frontend): Finish glitch frontend implementation

This commit is contained in:
Jesse Wierzbinski 2024-04-15 08:50:06 -10:00
parent e5e8099883
commit 972ced1f10
No known key found for this signature in database
6 changed files with 348 additions and 26 deletions

View file

@ -87,6 +87,8 @@ bait_ips = ["127.0.0.1", "::1"]
bait_user_agents = ["curl", "wget"] bait_user_agents = ["curl", "wget"]
[frontend] [frontend]
# Enable custom frontends (warning: not enabling this or Glitch will make Lysand only accessible via the Mastodon API)
enabled = true
# The URL to reach the frontend at (should be on a local network) # The URL to reach the frontend at (should be on a local network)
url = "http://localhost:3000" url = "http://localhost:3000"

View file

@ -59,12 +59,22 @@ await dualServerLogger.log(
`Database is online, now serving ${postCount} posts`, `Database is online, now serving ${postCount} posts`,
); );
// Check if frontend is reachable if (config.frontend.enabled) {
const response = await fetch(new URL("/", config.frontend.url)) if (!URL.canParse(config.frontend.url)) {
await dualServerLogger.log(
LogLevel.ERROR,
"Server",
`Frontend URL is not a valid URL: ${config.frontend.url}`,
);
process.exit(1);
}
// Check if frontend is reachable
const response = await fetch(new URL("/", config.frontend.url))
.then((res) => res.ok) .then((res) => res.ok)
.catch(() => false); .catch(() => false);
if (!response) { if (!response) {
await dualServerLogger.log( await dualServerLogger.log(
LogLevel.ERROR, LogLevel.ERROR,
"Server", "Server",
@ -75,6 +85,13 @@ if (!response) {
"Server", "Server",
"Please ensure the frontend is online and reachable", "Please ensure the frontend is online and reachable",
); );
}
} else {
await dualServerLogger.log(
LogLevel.WARNING,
"Server",
"Frontend is disabled, skipping check",
);
} }
export { config, server }; export { config, server };

View file

@ -121,6 +121,9 @@ export interface Config {
}; };
frontend: { frontend: {
/** @default true */
enabled: boolean;
/** @default "http://localhost:3000" */ /** @default "http://localhost:3000" */
url: string; url: string;
@ -442,6 +445,7 @@ export const defaultConfig: Config = {
}, },
}, },
frontend: { frontend: {
enabled: true,
url: "http://localhost:3000", url: "http://localhost:3000",
glitch: { glitch: {
enabled: false, enabled: false,

View file

@ -0,0 +1,210 @@
export const languages = [
["aa", "Afar", "Afaraf"],
["ab", "Abkhaz", "аҧсуа бызшәа"],
["ae", "Avestan", "avesta"],
["af", "Afrikaans", "Afrikaans"],
["ak", "Akan", "Akan"],
["am", "Amharic", "አማርኛ"],
["an", "Aragonese", "aragonés"],
["ar", "Arabic", "اللغة العربية"],
["as", "Assamese", "অসমীয়া"],
["av", "Avaric", "авар мацӀ"],
["ay", "Aymara", "aymar aru"],
["az", "Azerbaijani", "azərbaycan dili"],
["ba", "Bashkir", "башҡорт теле"],
["be", "Belarusian", "беларуская мова"],
["bg", "Bulgarian", "български език"],
["bh", "Bihari", "भोजपुरी"],
["bi", "Bislama", "Bislama"],
["bm", "Bambara", "bamanankan"],
["bn", "Bengali", "বাংলা"],
["bo", "Tibetan", "བོད་ཡིག"],
["br", "Breton", "brezhoneg"],
["bs", "Bosnian", "bosanski jezik"],
["ca", "Catalan", "Català"],
["ce", "Chechen", "нохчийн мотт"],
["ch", "Chamorro", "Chamoru"],
["co", "Corsican", "corsu"],
["cr", "Cree", "ᓀᐦᐃᔭᐍᐏᐣ"],
["cs", "Czech", "čeština"],
["cu", "Old Church Slavonic", "ѩзыкъ словѣньскъ"],
["cv", "Chuvash", "чӑваш чӗлхи"],
["cy", "Welsh", "Cymraeg"],
["da", "Danish", "dansk"],
["de", "German", "Deutsch"],
["dv", "Divehi", "Dhivehi"],
["dz", "Dzongkha", "རྫོང་ཁ"],
["ee", "Ewe", "Eʋegbe"],
["el", "Greek", "Ελληνικά"],
["en", "English", "English"],
["eo", "Esperanto", "Esperanto"],
["es", "Spanish", "Español"],
["et", "Estonian", "eesti"],
["eu", "Basque", "euskara"],
["fa", "Persian", "فارسی"],
["ff", "Fula", "Fulfulde"],
["fi", "Finnish", "suomi"],
["fj", "Fijian", "Vakaviti"],
["fo", "Faroese", "føroyskt"],
["fr", "French", "Français"],
["fy", "Western Frisian", "Frysk"],
["ga", "Irish", "Gaeilge"],
["gd", "Scottish Gaelic", "Gàidhlig"],
["gl", "Galician", "galego"],
["gu", "Gujarati", "ગુજરાતી"],
["gv", "Manx", "Gaelg"],
["ha", "Hausa", "هَوُسَ"],
["he", "Hebrew", "עברית"],
["hi", "Hindi", "हिन्दी"],
["ho", "Hiri Motu", "Hiri Motu"],
["hr", "Croatian", "Hrvatski"],
["ht", "Haitian", "Kreyòl ayisyen"],
["hu", "Hungarian", "magyar"],
["hy", "Armenian", "Հայերեն"],
["hz", "Herero", "Otjiherero"],
["ia", "Interlingua", "Interlingua"],
["id", "Indonesian", "Bahasa Indonesia"],
["ie", "Interlingue", "Interlingue"],
["ig", "Igbo", "Asụsụ Igbo"],
["ii", "Nuosu", "ꆈꌠ꒿ Nuosuhxop"],
["ik", "Inupiaq", "Iñupiaq"],
["io", "Ido", "Ido"],
["is", "Icelandic", "Íslenska"],
["it", "Italian", "Italiano"],
["iu", "Inuktitut", "ᐃᓄᒃᑎᑐᑦ"],
["ja", "Japanese", "日本語"],
["jv", "Javanese", "basa Jawa"],
["ka", "Georgian", "ქართული"],
["kg", "Kongo", "Kikongo"],
["ki", "Kikuyu", "Gĩkũyũ"],
["kj", "Kwanyama", "Kuanyama"],
["kk", "Kazakh", "қазақ тілі"],
["kl", "Kalaallisut", "kalaallisut"],
["km", "Khmer", "ខេមរភាសា"],
["kn", "Kannada", "ಕನ್ನಡ"],
["ko", "Korean", "한국어"],
["kr", "Kanuri", "Kanuri"],
["ks", "Kashmiri", "कश्मीरी"],
["ku", "Kurmanji (Kurdish)", "Kurmancî"],
["kv", "Komi", "коми кыв"],
["kw", "Cornish", "Kernewek"],
["ky", "Kyrgyz", "Кыргызча"],
["la", "Latin", "latine"],
["lb", "Luxembourgish", "Lëtzebuergesch"],
["lg", "Ganda", "Luganda"],
["li", "Limburgish", "Limburgs"],
["ln", "Lingala", "Lingála"],
["lo", "Lao", "ລາວ"],
["lt", "Lithuanian", "lietuvių kalba"],
["lu", "Luba-Katanga", "Tshiluba"],
["lv", "Latvian", "latviešu valoda"],
["mg", "Malagasy", "fiteny malagasy"],
["mh", "Marshallese", "Kajin M̧ajeļ"],
["mi", "Māori", "te reo Māori"],
["mk", "Macedonian", "македонски јазик"],
["ml", "Malayalam", "മലയാളം"],
["mn", "Mongolian", "Монгол хэл"],
["mr", "Marathi", "मराठी"],
["ms", "Malay", "Bahasa Melayu"],
["ms-Arab", "Jawi Malay", "بهاس ملايو"],
["mt", "Maltese", "Malti"],
["my", "Burmese", "ဗမာစာ"],
["na", "Nauru", "Ekakairũ Naoero"],
["nb", "Norwegian Bokmål", "Norsk bokmål"],
["nd", "Northern Ndebele", "isiNdebele"],
["ne", "Nepali", "नेपाली"],
["ng", "Ndonga", "Owambo"],
["nl", "Dutch", "Nederlands"],
["nn", "Norwegian Nynorsk", "Norsk Nynorsk"],
["no", "Norwegian", "Norsk"],
["nr", "Southern Ndebele", "isiNdebele"],
["nv", "Navajo", "Diné bizaad"],
["ny", "Chichewa", "chiCheŵa"],
["oc", "Occitan", "occitan"],
["oj", "Ojibwe", "ᐊᓂᔑᓈᐯᒧᐎᓐ"],
["om", "Oromo", "Afaan Oromoo"],
["or", "Oriya", "ଓଡ଼ିଆ"],
["os", "Ossetian", "ирон æвзаг"],
["pa", "Punjabi", "ਪੰਜਾਬੀ"],
["pi", "Pāli", "पाऴि"],
["pl", "Polish", "Polski"],
["ps", "Pashto", "پښتو"],
["pt", "Portuguese", "Português"],
["qu", "Quechua", "Runa Simi"],
["rm", "Romansh", "rumantsch grischun"],
["rn", "Kirundi", "Ikirundi"],
["ro", "Romanian", "Română"],
["ru", "Russian", "Русский"],
["rw", "Kinyarwanda", "Ikinyarwanda"],
["sa", "Sanskrit", "संस्कृतम्"],
["sc", "Sardinian", "sardu"],
["sd", "Sindhi", "सिन्धी"],
["se", "Northern Sami", "Davvisámegiella"],
["sg", "Sango", "yângâ tî sängö"],
["si", "Sinhala", "සිංහල"],
["sk", "Slovak", "slovenčina"],
["sl", "Slovenian", "slovenščina"],
["sn", "Shona", "chiShona"],
["so", "Somali", "Soomaaliga"],
["sq", "Albanian", "Shqip"],
["sr", "Serbian", "српски језик"],
["ss", "Swati", "SiSwati"],
["st", "Southern Sotho", "Sesotho"],
["su", "Sundanese", "Basa Sunda"],
["sv", "Swedish", "Svenska"],
["sw", "Swahili", "Kiswahili"],
["ta", "Tamil", "தமிழ்"],
["te", "Telugu", "తెలుగు"],
["tg", "Tajik", "тоҷикӣ"],
["th", "Thai", "ไทย"],
["ti", "Tigrinya", "ትግርኛ"],
["tk", "Turkmen", "Türkmen"],
["tl", "Tagalog", "Wikang Tagalog"],
["tn", "Tswana", "Setswana"],
["to", "Tonga", "faka Tonga"],
["tr", "Turkish", "Türkçe"],
["ts", "Tsonga", "Xitsonga"],
["tt", "Tatar", "татар теле"],
["tw", "Twi", "Twi"],
["ty", "Tahitian", "Reo Tahiti"],
["ug", "Uyghur", "ئۇيغۇرچە‎"],
["uk", "Ukrainian", "Українська"],
["ur", "Urdu", "اردو"],
["uz", "Uzbek", "Ўзбек"],
["ve", "Venda", "Tshivenḓa"],
["vi", "Vietnamese", "Tiếng Việt"],
["vo", "Volapük", "Volapük"],
["wa", "Walloon", "walon"],
["wo", "Wolof", "Wollof"],
["xh", "Xhosa", "isiXhosa"],
["yi", "Yiddish", "ייִדיש"],
["yo", "Yoruba", "Yorùbá"],
["za", "Zhuang", "Saɯ cueŋƅ"],
["zh", "Chinese", "中文"],
["zu", "Zulu", "isiZulu"],
["zh-CN", "Chinese (China)", "简体中文"],
["zh-HK", "Chinese (Hong Kong)", "繁體中文(香港)"],
["zh-TW", "Chinese (Taiwan)", "繁體中文(臺灣)"],
["zh-YUE", "Cantonese", "廣東話"],
["ast", "Asturian", "Asturianu"],
["chr", "Cherokee", "ᏣᎳᎩ ᎦᏬᏂᎯᏍᏗ"],
["ckb", "Sorani (Kurdish)", "سۆرانی"],
["cnr", "Montenegrin", "crnogorski"],
["csb", "Kashubian", "Kaszëbsczi"],
["jbo", "Lojban", "la .lojban."],
["kab", "Kabyle", "Taqbaylit"],
["ldn", "Láadan", "Láadan"],
["lfn", "Lingua Franca Nova", "lingua franca nova"],
["moh", "Mohawk", "Kanienʼkéha"],
["nds", "Low German", "Plattdüütsch"],
["pdc", "Pennsylvania Dutch", "Pennsilfaani-Deitsch"],
["sco", "Scots", "Scots"],
["sma", "Southern Sami", "Åarjelsaemien Gïele"],
["smj", "Lule Sami", "Julevsámegiella"],
["szl", "Silesian", "ślůnsko godka"],
["tok", "Toki Pona", "toki pona"],
["vai", "Vai", "ꕙꔤ"],
["xal", "Kalmyk", "Хальмг келн"],
["zba", "Balaibalan", "باليبلن"],
["zgh", "Standard Moroccan Tamazight", "ⵜⴰⵎⴰⵣⵉⵖⵜ"],
];

View file

@ -1,17 +1,12 @@
import { join } from "node:path"; import { join } from "node:path";
import { dualLogger } from "@loggers";
import { errorResponse } from "@response";
import { config } from "config-manager"; import { config } from "config-manager";
import { import type { LogManager, MultiLogManager } from "~packages/log-manager";
LogLevel, import { languages } from "./glitch-languages";
type LogManager,
type MultiLogManager,
} from "~packages/log-manager";
export const handleGlitchRequest = async ( export const handleGlitchRequest = async (
req: Request, req: Request,
logger: LogManager | MultiLogManager, logger: LogManager | MultiLogManager,
): Promise<Response> => { ): Promise<Response | null> => {
const url = new URL(req.url); const url = new URL(req.url);
let path = url.pathname; let path = url.pathname;
@ -28,10 +23,102 @@ export const handleGlitchRequest = async (
const file = Bun.file(join(config.frontend.glitch.assets, path)); const file = Bun.file(join(config.frontend.glitch.assets, path));
if (await file.exists()) { if (await file.exists()) {
return new Response(file); let fileContents = await file.text();
for (const server of config.frontend.glitch.server) {
fileContents = fileContents.replace(
`${new URL(server).origin}/`,
"/",
);
} }
dualLogger.log(LogLevel.WARNING, "Glitch-Soc", `Asset not found: ${path}`); fileContents = fileContents.replaceAll(
"Glitch-soc is free open source software forked from Mastodon.",
"Lysand is free and open-source software using the Glitch-Soc frontend.",
);
fileContents = fileContents.replaceAll("Mastodon", "Lysand");
fileContents = fileContents.replaceAll(
"Lysand is free, open-source software, and a trademark of Mastodon gGmbH.",
"This is not a Mastodon instance.",
);
fileContents = fileContents.replaceAll(
"joinmastodon.org",
"lysand.org",
);
return errorResponse("Glitch-Soc route not found", 404); // Check if file is index
if (path === "/index.html") {
// Find script id="initial-state" and replace its contents with custom json
const rewriter = new HTMLRewriter()
.on("script#initial-state", {
element(element) {
element.setInnerContent(
JSON.stringify({
meta: {
access_token: null,
activity_api_enabled: true,
admin: null,
domain: new URL(config.http.base_url).host,
limited_federation_mode: false,
locale: "en",
mascot: "https://media.tech.lgbt/site_uploads/files/000/000/004/original/1a16a73feb5c2463.png",
profile_directory: true,
registrations_open: true,
repository: "lysand-org/lysand",
search_enabled: true,
single_user_mode: false,
source_url:
"https://github.com/lysand-org/lysand",
sso_redirect: null,
status_page_url: null,
streaming_api_base_url: null,
timeline_preview: true,
title: config.instance.name,
trends_as_landing_page: false,
trends_enabled: true,
version: "4.3.0-alpha.3+glitch",
auto_play_gif: null,
display_media: null,
reduce_motion: null,
use_blurhash: null,
},
compose: { text: "" },
accounts: {},
media_attachments: {
accept_content_types:
config.validation.allowed_mime_types,
},
settings: {},
max_feed_hashtags: 4,
poll_limits: {
max_options:
config.validation.max_poll_options,
max_option_chars:
config.validation.max_poll_option_size,
min_expiration:
config.validation.min_poll_duration,
max_expiration:
config.validation.max_poll_duration,
},
languages: languages,
push_subscription: null,
role: null,
}),
);
},
})
.transform(new Response(fileContents));
fileContents = await rewriter.text();
}
return new Response(fileContents, {
headers: {
"Content-Type": `${file.type}; charset=utf-8`,
"Content-Length": String(file.size),
Date: new Date().toUTCString(),
},
});
}
return null;
}; };

View file

@ -126,8 +126,10 @@ export const createServer = (
} }
if (config.frontend.glitch.enabled) { if (config.frontend.glitch.enabled) {
// Proxy all /web requests to Glitch-Soc return (
return await handleGlitchRequest(req, dualLogger); (await handleGlitchRequest(req, dualLogger)) ??
errorResponse("Route not found", 404)
);
} }
const base_url_with_http = config.http.base_url.replace( const base_url_with_http = config.http.base_url.replace(