mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
refactor(api): 🔥 Remove Glitch-FE explicit support
This commit is contained in:
parent
df466ecaa0
commit
bec60fbf96
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -179,10 +179,6 @@ log.txt
|
||||||
*.log
|
*.log
|
||||||
build
|
build
|
||||||
config/extended_description_test.md
|
config/extended_description_test.md
|
||||||
glitch-old
|
|
||||||
glitch
|
|
||||||
glitch.tar.gz
|
|
||||||
glitch-dev
|
|
||||||
*.pem
|
*.pem
|
||||||
oclif.manifest.json
|
oclif.manifest.json
|
||||||
.direnv/
|
.direnv/
|
||||||
|
|
|
||||||
9
app.ts
9
app.ts
|
|
@ -11,7 +11,6 @@ import { bait } from "./middlewares/bait";
|
||||||
import { boundaryCheck } from "./middlewares/boundary-check";
|
import { boundaryCheck } from "./middlewares/boundary-check";
|
||||||
import { ipBans } from "./middlewares/ip-bans";
|
import { ipBans } from "./middlewares/ip-bans";
|
||||||
import { logger } from "./middlewares/logger";
|
import { logger } from "./middlewares/logger";
|
||||||
import { handleGlitchRequest } from "./packages/glitch-server/main";
|
|
||||||
import { routes } from "./routes";
|
import { routes } from "./routes";
|
||||||
import type { ApiRouteExports } from "./types/api";
|
import type { ApiRouteExports } from "./types/api";
|
||||||
|
|
||||||
|
|
@ -102,14 +101,6 @@ export const appFactory = async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
app.all("*", async (context) => {
|
app.all("*", async (context) => {
|
||||||
if (config.frontend.glitch.enabled) {
|
|
||||||
const glitch = await handleGlitchRequest(context.req.raw);
|
|
||||||
|
|
||||||
if (glitch) {
|
|
||||||
return glitch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const replacedUrl = new URL(
|
const replacedUrl = new URL(
|
||||||
new URL(context.req.url).pathname,
|
new URL(context.req.url).pathname,
|
||||||
config.frontend.url,
|
config.frontend.url,
|
||||||
|
|
|
||||||
12
biome.json
12
biome.json
|
|
@ -1,8 +1,7 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
|
"$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
|
||||||
"organizeImports": {
|
"organizeImports": {
|
||||||
"enabled": true,
|
"enabled": true
|
||||||
"ignore": ["node_modules", "dist", "glitch", "glitch-dev"]
|
|
||||||
},
|
},
|
||||||
"linter": {
|
"linter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -62,16 +61,17 @@
|
||||||
"useImportExtensions": "off",
|
"useImportExtensions": "off",
|
||||||
"useThrowNewError": "warn"
|
"useThrowNewError": "warn"
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"ignore": ["node_modules", "dist", "glitch", "glitch-dev"]
|
|
||||||
},
|
},
|
||||||
"formatter": {
|
"formatter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"indentStyle": "space",
|
"indentStyle": "space",
|
||||||
"indentWidth": 4,
|
"indentWidth": 4
|
||||||
"ignore": ["node_modules", "dist", "glitch", "glitch-dev"]
|
|
||||||
},
|
},
|
||||||
"javascript": {
|
"javascript": {
|
||||||
"globals": ["Bun", "HTMLRewriter", "BufferEncoding"]
|
"globals": ["Bun", "HTMLRewriter", "BufferEncoding"]
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"ignore": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ 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 Versia Server only accessible via the Mastodon API)
|
# Enable custom frontends (warning: not enabling this will make Versia Server only accessible via the Mastodon API)
|
||||||
# Frontends also control the OAuth flow, so if you disable this, you will need to use the Mastodon frontend
|
# Frontends also control the OAuth flow, so if you disable this, you will need to use the Mastodon frontend
|
||||||
enabled = true
|
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)
|
||||||
|
|
@ -141,14 +141,6 @@ url = "http://localhost:3000"
|
||||||
# This can be used to set up custom themes, etc on supported frontends.
|
# This can be used to set up custom themes, etc on supported frontends.
|
||||||
# theme = "dark"
|
# theme = "dark"
|
||||||
|
|
||||||
[frontend.glitch]
|
|
||||||
# Enable the Glitch frontend integration
|
|
||||||
enabled = false
|
|
||||||
# Glitch assets folder
|
|
||||||
assets = "glitch"
|
|
||||||
# Server the assets were ripped from (and any eventual CDNs)
|
|
||||||
server = ["https://tech.lgbt"]
|
|
||||||
|
|
||||||
[smtp]
|
[smtp]
|
||||||
# SMTP server to use for sending emails
|
# SMTP server to use for sending emails
|
||||||
server = "smtp.example.com"
|
server = "smtp.example.com"
|
||||||
|
|
|
||||||
|
|
@ -392,33 +392,6 @@
|
||||||
"$ref": "#/properties/http/properties/proxy/properties/address/anyOf/0",
|
"$ref": "#/properties/http/properties/proxy/properties/address/anyOf/0",
|
||||||
"default": "http://localhost:3000"
|
"default": "http://localhost:3000"
|
||||||
},
|
},
|
||||||
"glitch": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"enabled": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"assets": {
|
|
||||||
"type": "string",
|
|
||||||
"minLength": 1,
|
|
||||||
"default": "glitch"
|
|
||||||
},
|
|
||||||
"server": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/properties/http/properties/proxy/properties/address/anyOf/0"
|
|
||||||
},
|
|
||||||
"default": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false,
|
|
||||||
"default": {
|
|
||||||
"enabled": false,
|
|
||||||
"assets": "glitch",
|
|
||||||
"server": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"routes": {
|
"routes": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -463,11 +436,6 @@
|
||||||
"default": {
|
"default": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"url": "http://localhost:3000",
|
"url": "http://localhost:3000",
|
||||||
"glitch": {
|
|
||||||
"enabled": false,
|
|
||||||
"assets": "glitch",
|
|
||||||
"server": []
|
|
||||||
},
|
|
||||||
"settings": {}
|
"settings": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ services:
|
||||||
- ./logs:/app/dist/logs
|
- ./logs:/app/dist/logs
|
||||||
- ./config:/app/dist/config
|
- ./config:/app/dist/config
|
||||||
- ./uploads:/app/dist/uploads
|
- ./uploads:/app/dist/uploads
|
||||||
- ./glitch:/app/dist/glitch
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
container_name: versia
|
container_name: versia
|
||||||
command: "cli start --all-threads"
|
command: "cli start --all-threads"
|
||||||
|
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
# Enabling Glitch-Soc
|
|
||||||
|
|
||||||
Versia Server supports the use of the Glitch-Soc fork of Mastodon's frontend. Here's how to do it:
|
|
||||||
|
|
||||||
1. Download the latest Glitch FE package from [the releases page](https://github.com/lysand-org/server/releases) (it should be named `glitch.tar.gz` and be inside the assets of a normal Versia Server release).
|
|
||||||
2. Extract the contents of the package into a folder, which you can name `glitch` for simplicity. (if using Docker, now is the time to mount that folder into the container, for example with `-v /path/to/glitch:/app/dist/glitch`)
|
|
||||||
3. Change the config to enable Glitch-FE:
|
|
||||||
```toml
|
|
||||||
[frontend]
|
|
||||||
# Enable custom frontends (warning: not enabling this or Glitch will make Versia Server only accessible via the Mastodon API)
|
|
||||||
# Frontends also control the OAuth flow, so if you disable this, you will need to use the Mastodon frontend
|
|
||||||
enabled = true
|
|
||||||
# The URL to reach the frontend at (should be on a local network)
|
|
||||||
url = "http://localhost:3000"
|
|
||||||
|
|
||||||
[frontend.glitch]
|
|
||||||
# Enable the Glitch frontend integration
|
|
||||||
enabled = true
|
|
||||||
# Glitch assets folder
|
|
||||||
assets = "glitch"
|
|
||||||
# Server the assets were ripped from (and any eventual CDNs)
|
|
||||||
server = ["https://tech.lgbt"]
|
|
||||||
```
|
|
||||||
The `server` option can be left as-is, unless you have downloaded your own `index.html` file from a different Glitch instance.
|
|
||||||
4. Start Versia Server and navigate to `/` to see the Glitch frontend in action.
|
|
||||||
|
|
||||||
## How is this package created?
|
|
||||||
|
|
||||||
Glitch-FE is a React single-page app, which dynamically creates an `index.html` file on every request and builds all the other assets at build time.
|
|
||||||
|
|
||||||
The package static files were taken from a build of Glitch-Soc, while the index.html file was taken from [the tech.lgbt instance](https://tech.lgbt) with `cURL`.
|
|
||||||
|
|
||||||
Then, the paths in the `index.html` file were replaced with the correct paths for the static files (as they have different hashes in their names from the ones in the Glitch-Soc build).
|
|
||||||
|
|
||||||
At runtime, Versia Server dynamically edits the index.html file to replace the content with correct data, as well as disabling `integrity` checks on the script/link tags.
|
|
||||||
|
|
||||||
In the future, I'll find a way to make this less hacky and more user-friendly, but for now, this is the best I can do.
|
|
||||||
|
|
@ -92,11 +92,6 @@ You may use the environment variables `NO_COLORS=true` and `NO_FANCY_DATES=true`
|
||||||
|
|
||||||
Please see the [CLI documentation](cli.md) for more information on how to use the CLI.
|
Please see the [CLI documentation](cli.md) for more information on how to use the CLI.
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> You might be interested in running the [Glitch-Soc](glitch-soc.md) frontend, which is a fork of Mastodon's frontend with additional features.
|
|
||||||
>
|
|
||||||
> This is possible by following the instructions in [this file](glitch-soc.md).
|
|
||||||
|
|
||||||
## Updating the server
|
## Updating the server
|
||||||
|
|
||||||
Updating the server is as simple as pulling the latest changes from the repository and running `bun prod-build` again. You may need to run `bun install` again if there are new dependencies.
|
Updating the server is as simple as pulling the latest changes from the repository and running `bun prod-build` again. You may need to run `bun install` again if there are new dependencies.
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
"start": "NODE_ENV=production bun run dist/index.js --prod",
|
"start": "NODE_ENV=production bun run dist/index.js --prod",
|
||||||
"lint": "bunx @biomejs/biome check .",
|
"lint": "bunx @biomejs/biome check .",
|
||||||
"build": "bun run build.ts",
|
"build": "bun run build.ts",
|
||||||
"cloc": "cloc . --exclude-dir node_modules,dist,.output,.nuxt,meta,logs,glitch,glitch-dev --exclude-ext sql,log,pem",
|
"cloc": "cloc . --exclude-dir node_modules,dist,.output,.nuxt,meta,logs --exclude-ext sql,log,pem",
|
||||||
"wc": "find server database *.ts docs packages types utils drizzle tests -type f -print0 | wc -m --files0-from=-",
|
"wc": "find server database *.ts docs packages types utils drizzle tests -type f -print0 | wc -m --files0-from=-",
|
||||||
"cli": "bun run cli/index.ts",
|
"cli": "bun run cli/index.ts",
|
||||||
"prune": "ts-prune | grep -v server/ | grep -v dist/ | grep -v '(used in module)'",
|
"prune": "ts-prune | grep -v server/ | grep -v dist/ | grep -v '(used in module)'",
|
||||||
|
|
|
||||||
|
|
@ -182,17 +182,6 @@ export const configValidator = z.object({
|
||||||
.object({
|
.object({
|
||||||
enabled: z.boolean().default(true),
|
enabled: z.boolean().default(true),
|
||||||
url: zUrl.default("http://localhost:3000"),
|
url: zUrl.default("http://localhost:3000"),
|
||||||
glitch: z
|
|
||||||
.object({
|
|
||||||
enabled: z.boolean().default(false),
|
|
||||||
assets: z.string().min(1).default("glitch"),
|
|
||||||
server: z.array(zUrl).default([]),
|
|
||||||
})
|
|
||||||
.default({
|
|
||||||
enabled: false,
|
|
||||||
assets: "glitch",
|
|
||||||
server: [],
|
|
||||||
}),
|
|
||||||
routes: z
|
routes: z
|
||||||
.object({
|
.object({
|
||||||
home: zUrlPath.default("/"),
|
home: zUrlPath.default("/"),
|
||||||
|
|
@ -213,11 +202,6 @@ export const configValidator = z.object({
|
||||||
.default({
|
.default({
|
||||||
enabled: true,
|
enabled: true,
|
||||||
url: "http://localhost:3000",
|
url: "http://localhost:3000",
|
||||||
glitch: {
|
|
||||||
enabled: false,
|
|
||||||
assets: "glitch",
|
|
||||||
server: [],
|
|
||||||
},
|
|
||||||
settings: {},
|
settings: {},
|
||||||
}),
|
}),
|
||||||
smtp: z
|
smtp: z
|
||||||
|
|
|
||||||
|
|
@ -1,210 +0,0 @@
|
||||||
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", "ⵜⴰⵎⴰⵣⵉⵖⵜ"],
|
|
||||||
];
|
|
||||||
|
|
@ -1,361 +0,0 @@
|
||||||
import { join } from "node:path";
|
|
||||||
import { redirect } from "@/response";
|
|
||||||
import type { BunFile } from "bun";
|
|
||||||
import { retrieveUserFromToken } from "~/classes/functions/user";
|
|
||||||
import { config } from "~/packages/config-manager/index";
|
|
||||||
import type { User } from "~/packages/database-interface/user";
|
|
||||||
import { languages } from "./glitch-languages";
|
|
||||||
|
|
||||||
const handleManifestRequest = () => {
|
|
||||||
const manifest = {
|
|
||||||
id: "/home",
|
|
||||||
name: config.instance.name,
|
|
||||||
short_name: config.instance.name,
|
|
||||||
icons: [
|
|
||||||
{
|
|
||||||
src: "/packs/media/icons/android-chrome-36x36-e67f2bc645cc669c04ffcbc17203aeac.png",
|
|
||||||
sizes: "36x36",
|
|
||||||
type: "image/png",
|
|
||||||
purpose: "any maskable",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: "/packs/media/icons/android-chrome-48x48-d3afc36e9388913fb6add2476a556f67.png",
|
|
||||||
sizes: "48x48",
|
|
||||||
type: "image/png",
|
|
||||||
purpose: "any maskable",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: "/packs/media/icons/android-chrome-72x72-23ee104da45dc5388d59b8b0fad866f2.png",
|
|
||||||
sizes: "72x72",
|
|
||||||
type: "image/png",
|
|
||||||
purpose: "any maskable",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: "/packs/media/icons/android-chrome-96x96-fb2abfd885ab5de94025e09f6f9408b5.png",
|
|
||||||
sizes: "96x96",
|
|
||||||
type: "image/png",
|
|
||||||
purpose: "any maskable",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: "/packs/media/icons/android-chrome-144x144-99b386f89a3a2a22440964eba3b9f242.png",
|
|
||||||
sizes: "144x144",
|
|
||||||
type: "image/png",
|
|
||||||
purpose: "any maskable",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: "/packs/media/icons/android-chrome-192x192-8b4d35fdd9b5fa4592056ce687c9d0ba.png",
|
|
||||||
sizes: "192x192",
|
|
||||||
type: "image/png",
|
|
||||||
purpose: "any maskable",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: "/packs/media/icons/android-chrome-256x256-fecf6504157e3b195dd0e604cd711730.png",
|
|
||||||
sizes: "256x256",
|
|
||||||
type: "image/png",
|
|
||||||
purpose: "any maskable",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: "/packs/media/icons/android-chrome-384x384-dc559d916be51de4965dd7b8abf9c7c8.png",
|
|
||||||
sizes: "384x384",
|
|
||||||
type: "image/png",
|
|
||||||
purpose: "any maskable",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: "/packs/media/icons/android-chrome-512x512-85515d059c83f47d8e77e0703ebb7ff5.png",
|
|
||||||
sizes: "512x512",
|
|
||||||
type: "image/png",
|
|
||||||
purpose: "any maskable",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
theme_color: "#191b22",
|
|
||||||
background_color: "#191b22",
|
|
||||||
display: "standalone",
|
|
||||||
start_url: "/",
|
|
||||||
scope: "/",
|
|
||||||
share_target: {
|
|
||||||
url_template: "share?title={title}\u0026text={text}\u0026url={url}",
|
|
||||||
action: "share",
|
|
||||||
method: "GET",
|
|
||||||
enctype: "application/x-www-form-urlencoded",
|
|
||||||
params: { title: "title", text: "text", url: "url" },
|
|
||||||
},
|
|
||||||
shortcuts: [
|
|
||||||
{ name: "Compose new post", url: "/publish" },
|
|
||||||
{ name: "Notifications", url: "/notifications" },
|
|
||||||
{ name: "Explore", url: "/explore" },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
return new Response(JSON.stringify(manifest), {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json; charset=utf-8",
|
|
||||||
"Content-Length": String(JSON.stringify(manifest).length),
|
|
||||||
"Cache-Control": "max-age=31536000",
|
|
||||||
Date: new Date().toUTCString(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSignInRequest = async (
|
|
||||||
req: Request,
|
|
||||||
_path: string,
|
|
||||||
url: URL,
|
|
||||||
user: User | null,
|
|
||||||
accessToken: string,
|
|
||||||
) => {
|
|
||||||
if (req.method === "POST") {
|
|
||||||
if (url.searchParams.get("error")) {
|
|
||||||
const fileContents = await Bun.file(
|
|
||||||
join(config.frontend.glitch.assets, "/auth/sign_in.html"),
|
|
||||||
).text();
|
|
||||||
|
|
||||||
// Insert error message as first child of form.form_container
|
|
||||||
const rewriter = new HTMLRewriter()
|
|
||||||
.on("div.form-container", {
|
|
||||||
element(element) {
|
|
||||||
element.prepend(
|
|
||||||
` <div class='flash-message alert'>
|
|
||||||
<strong>${decodeURIComponent(
|
|
||||||
url.searchParams.get("error") ?? "",
|
|
||||||
)}</strong>
|
|
||||||
</div>`,
|
|
||||||
{
|
|
||||||
html: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.transform(new Response(fileContents));
|
|
||||||
|
|
||||||
return returnFile(
|
|
||||||
Bun.file(
|
|
||||||
join(config.frontend.glitch.assets, "/auth/sign_in.html"),
|
|
||||||
),
|
|
||||||
await brandingTransforms(
|
|
||||||
await rewriter.text(),
|
|
||||||
accessToken,
|
|
||||||
user,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return redirect("/api/auth/mastodon-login", 307);
|
|
||||||
}
|
|
||||||
|
|
||||||
const file = Bun.file(
|
|
||||||
join(config.frontend.glitch.assets, "/auth/sign_in.html"),
|
|
||||||
);
|
|
||||||
|
|
||||||
return returnFile(
|
|
||||||
file,
|
|
||||||
await htmlTransforms(
|
|
||||||
await brandingTransforms(await file.text(), accessToken, user),
|
|
||||||
accessToken,
|
|
||||||
user,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSignOutRequest = (req: Request) => {
|
|
||||||
if (req.method === "POST") {
|
|
||||||
return redirect("/api/auth/mastodon-logout", 307);
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect("/", 307);
|
|
||||||
};
|
|
||||||
|
|
||||||
const returnFile = async (file: BunFile, content?: string) => {
|
|
||||||
return new Response(content ?? (await file.text()), {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": `${file.type}; charset=utf-8`,
|
|
||||||
"Content-Length": String(file.size),
|
|
||||||
"Cache-Control": "max-age=31536000",
|
|
||||||
Date: new Date().toUTCString(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDefaultRequest = async (
|
|
||||||
_req: Request,
|
|
||||||
path: string,
|
|
||||||
user: User | null,
|
|
||||||
accessToken: string,
|
|
||||||
) => {
|
|
||||||
const file = Bun.file(join(config.frontend.glitch.assets, path));
|
|
||||||
|
|
||||||
if (await file.exists()) {
|
|
||||||
const transformedText = await brandingTransforms(
|
|
||||||
path.endsWith(".html")
|
|
||||||
? await htmlTransforms(await file.text(), accessToken, user)
|
|
||||||
: await file.text(),
|
|
||||||
accessToken,
|
|
||||||
user,
|
|
||||||
);
|
|
||||||
|
|
||||||
return returnFile(file, transformedText);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const brandingTransforms = (
|
|
||||||
fileContents: string,
|
|
||||||
_accessToken: string,
|
|
||||||
_user: User | null,
|
|
||||||
) => {
|
|
||||||
let newFileContents = fileContents;
|
|
||||||
for (const server of config.frontend.glitch.server) {
|
|
||||||
newFileContents = newFileContents.replaceAll(
|
|
||||||
`${new URL(server).origin}/`,
|
|
||||||
"/",
|
|
||||||
);
|
|
||||||
newFileContents = newFileContents.replaceAll(
|
|
||||||
new URL(server).host,
|
|
||||||
new URL(config.http.base_url).host,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
newFileContents = newFileContents.replaceAll(
|
|
||||||
"Glitch-soc is free open source software forked from Mastodon.",
|
|
||||||
"Versia Server is free and open-source software using the Glitch-Soc frontend.",
|
|
||||||
);
|
|
||||||
newFileContents = newFileContents.replaceAll("Mastodon", "Versia Server");
|
|
||||||
newFileContents = newFileContents.replaceAll(
|
|
||||||
"Versia is free, open-source software, and a trademark of Versia gGmbH.",
|
|
||||||
"This is not a Mastodon instance.",
|
|
||||||
);
|
|
||||||
|
|
||||||
newFileContents = newFileContents.replaceAll(
|
|
||||||
"joinmastodon.org",
|
|
||||||
"versia.pub",
|
|
||||||
);
|
|
||||||
|
|
||||||
return newFileContents;
|
|
||||||
};
|
|
||||||
|
|
||||||
const htmlTransforms = async (
|
|
||||||
fileContents: string,
|
|
||||||
accessToken: string,
|
|
||||||
user: User | null,
|
|
||||||
) => {
|
|
||||||
// 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: accessToken || 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/server",
|
|
||||||
search_enabled: true,
|
|
||||||
single_user_mode: false,
|
|
||||||
source_url: "https://github.com/lysand-org/server",
|
|
||||||
sso_redirect: null,
|
|
||||||
status_page_url: null,
|
|
||||||
streaming_api_base_url: `wss://${
|
|
||||||
new URL(config.http.base_url).host
|
|
||||||
}`,
|
|
||||||
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,
|
|
||||||
me: user ? user.id : undefined,
|
|
||||||
},
|
|
||||||
compose: user
|
|
||||||
? {
|
|
||||||
text: "",
|
|
||||||
me: user.id,
|
|
||||||
default_privacy: "public",
|
|
||||||
default_sensitive: false,
|
|
||||||
default_language: "en",
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
text: "",
|
|
||||||
},
|
|
||||||
accounts: user
|
|
||||||
? {
|
|
||||||
[user.id]: user.toApi(true),
|
|
||||||
}
|
|
||||||
: {},
|
|
||||||
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,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.on("script", {
|
|
||||||
element(element) {
|
|
||||||
element.removeAttribute("integrity");
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.on("link", {
|
|
||||||
element(element) {
|
|
||||||
element.removeAttribute("integrity");
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.transform(new Response(fileContents));
|
|
||||||
|
|
||||||
return rewriter.text();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const handleGlitchRequest = async (
|
|
||||||
req: Request,
|
|
||||||
): Promise<Response | null> => {
|
|
||||||
const url = new URL(req.url);
|
|
||||||
let path = url.pathname;
|
|
||||||
const accessToken =
|
|
||||||
req.headers.get("Cookie")?.match(/_session_id=(.*?)(;|$)/)?.[1] ?? "";
|
|
||||||
const user = await retrieveUserFromToken(accessToken ?? "");
|
|
||||||
|
|
||||||
// Strip leading /web from path
|
|
||||||
if (path.startsWith("/web")) {
|
|
||||||
path = path.slice(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path === "/manifest") {
|
|
||||||
return handleManifestRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path === "/auth/sign_in") {
|
|
||||||
return handleSignInRequest(req, path, url, user, accessToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path === "/auth/sign_out") {
|
|
||||||
return handleSignOutRequest(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
req.headers.get("Accept")?.includes("text/html") &&
|
|
||||||
!path.includes(".")
|
|
||||||
) {
|
|
||||||
path = "/index.html";
|
|
||||||
}
|
|
||||||
|
|
||||||
return handleDefaultRequest(req, path, user, accessToken);
|
|
||||||
};
|
|
||||||
Loading…
Reference in a new issue