Merge branch 'master' of https://github.com/DEVTomatoCake/spacebar-server into feat/webhooks-3
3
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Nix stuff is owned by Rory& - we& want to be notified if these are changed.
|
||||
/flake.nix root@rory.gay
|
||||
/nix-update.sh root@rory.gay
|
||||
18
.github/workflows/nix-build.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
name: Nix build
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build-nix:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v25
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@v2
|
||||
- run: nix build -L
|
||||
- run: nix develop --command echo OK
|
||||
3
.gitignore
vendored
@ -18,4 +18,5 @@ build
|
||||
*.log.ansi
|
||||
*.tmp
|
||||
tmp/
|
||||
dump/
|
||||
dump/
|
||||
result
|
||||
|
||||
@ -1,4 +1,28 @@
|
||||
#!/usr/bin/env sh
|
||||
#!nix-shell -i "bash" -p bash prefetch-npm-deps jq nodejs nix-output-monitor
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx -y lint-staged
|
||||
# Check if nix is available
|
||||
if [ -x "$(/usr/bin/env which nix-shell 2>/dev/null)" ]; then
|
||||
# Check if we haven't re-executed ourselves yet
|
||||
if [ ! "$HOOK_NIX_SHELL" ]; then
|
||||
echo "Nix is available, updating nix flake..."
|
||||
export HOOK_NIX_SHELL=1
|
||||
nix-shell $0
|
||||
exit $?
|
||||
else
|
||||
nix flake update
|
||||
# run ./nix-update.sh if package lock has changed and has no unstaged changes
|
||||
if [ -n "$(git status --porcelain=v1 2>/dev/null | grep -E '^(MM| M) package-lock.json')" ]; then
|
||||
echo "package-lock.json has unstaged changes. Skipping update of nix dependencies."
|
||||
elif [ ! -n "$(git status --porcelain=v1 2>/dev/null | grep -E '^M package-lock.json')" ]; then
|
||||
echo "package-lock.json has no changes. Skipping update of nix dependencies."
|
||||
else
|
||||
./nix-update.sh || exit $?
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "You do not appear to have nix installed. Skipping update of nix dependencies."
|
||||
fi
|
||||
|
||||
npx -y lint-staged
|
||||
|
||||
@ -1,102 +1,120 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
|
||||
<title>Verify {instanceName} Login from New Location</title>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
|
||||
<title>Verify {instanceName} Login from New Location</title>
|
||||
<style>
|
||||
* {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
<style>
|
||||
* {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
p {
|
||||
color: white;
|
||||
}
|
||||
|
||||
p {
|
||||
color: white;
|
||||
}
|
||||
.ExternalClass {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
.ExternalClass {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="background-color: #202225;">
|
||||
<img src="https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/branding/svg/Spacebar__Logo-Blue.svg"
|
||||
alt="Branding" style="
|
||||
<body>
|
||||
<div style="background-color: #202225">
|
||||
<img
|
||||
src="https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/branding/svg/Spacebar__Logo-Blue.svg"
|
||||
alt="Branding"
|
||||
style="
|
||||
width: 100%;
|
||||
max-width: 200px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
padding: 20px;
|
||||
" />
|
||||
<div style="
|
||||
"
|
||||
/>
|
||||
<div
|
||||
style="
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
padding: 40px 50px;
|
||||
background-color: #32353b;
|
||||
border-radius: 5px;
|
||||
">
|
||||
<p style="
|
||||
"
|
||||
>
|
||||
<p
|
||||
style="
|
||||
font-weight: 600;
|
||||
font-size: 20px;
|
||||
letter-spacing: 0.27px;
|
||||
line-height: 24px;
|
||||
">
|
||||
Hey {userUsername},
|
||||
</p>
|
||||
<p>
|
||||
It looks like someone tried to log into your {instanceName}
|
||||
account from a new location. If this is you, follow the link
|
||||
below to authorize logging in from this location on your
|
||||
account. If this isn't you, we suggest changing your
|
||||
password as soon as possible.
|
||||
</p>
|
||||
<p>
|
||||
<strong>IP Address:</strong> {ipAddress}
|
||||
<br />
|
||||
<strong>Location:</strong> {locationCity}, {locationRegion},
|
||||
{locationCountryName}
|
||||
</p>
|
||||
<div>
|
||||
<div style="
|
||||
"
|
||||
>
|
||||
Hey {userUsername},
|
||||
</p>
|
||||
<p>
|
||||
It looks like someone tried to log into your {instanceName}
|
||||
account from a new location. If this is you, follow the link
|
||||
below to authorize logging in from this location on your
|
||||
account. If this isn't you, we suggest changing your
|
||||
password as soon as possible.
|
||||
</p>
|
||||
<p>
|
||||
<strong>IP Address:</strong> {ipAddress}
|
||||
<br />
|
||||
<strong>Location:</strong> {locationCity}, {locationRegion},
|
||||
{locationCountryName}
|
||||
</p>
|
||||
<div>
|
||||
<div
|
||||
style="
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
padding-bottom: 10px;
|
||||
">
|
||||
<a href="{actionUrl}" target="_blank" style="
|
||||
"
|
||||
>
|
||||
<a
|
||||
href="{actionUrl}"
|
||||
target="_blank"
|
||||
style="
|
||||
font-size: 15px;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
padding: 15px 19px;
|
||||
background-color: #0185ff;
|
||||
border-radius: 5px;
|
||||
">Verify Login</a>
|
||||
</div>
|
||||
<hr />
|
||||
<div style="
|
||||
"
|
||||
>Verify Login</a
|
||||
>
|
||||
</div>
|
||||
<hr />
|
||||
<div
|
||||
style="
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
padding-bottom: 10px;
|
||||
">
|
||||
<p>
|
||||
Alternatively, you can directly paste this link into
|
||||
your browser:
|
||||
</p>
|
||||
<a href="{actionUrl}" target="_blank" style="word-wrap: break-word;">{actionUrl}</a>
|
||||
"
|
||||
>
|
||||
<p>
|
||||
Alternatively, you can directly paste this link into
|
||||
your browser:
|
||||
</p>
|
||||
<a
|
||||
href="{actionUrl}"
|
||||
target="_blank"
|
||||
style="word-wrap: break-word"
|
||||
>{actionUrl}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
@ -22,7 +22,7 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="background-color: #202225;">
|
||||
<div style="background-color: #202225">
|
||||
<img
|
||||
src="https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/branding/svg/Spacebar__Logo-Blue.svg"
|
||||
alt="Branding"
|
||||
|
||||
@ -1,90 +1,106 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
|
||||
<title>Password Reset Request for {instanceName}</title>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
|
||||
<title>Password Reset Request for {instanceName}</title>
|
||||
<style>
|
||||
* {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
<style>
|
||||
* {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
p {
|
||||
color: white;
|
||||
}
|
||||
|
||||
p {
|
||||
color: white;
|
||||
}
|
||||
.ExternalClass {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
.ExternalClass {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="background-color: #202225;">
|
||||
<img src="https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/branding/svg/Spacebar__Logo-Blue.svg"
|
||||
alt="Branding" style="
|
||||
<body>
|
||||
<div style="background-color: #202225">
|
||||
<img
|
||||
src="https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/branding/svg/Spacebar__Logo-Blue.svg"
|
||||
alt="Branding"
|
||||
style="
|
||||
width: 100%;
|
||||
max-width: 200px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
padding: 20px;
|
||||
" />
|
||||
<div style="
|
||||
"
|
||||
/>
|
||||
<div
|
||||
style="
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
padding: 40px 50px;
|
||||
background-color: #32353b;
|
||||
border-radius: 5px;
|
||||
">
|
||||
<p style="
|
||||
"
|
||||
>
|
||||
<p
|
||||
style="
|
||||
font-weight: 600;
|
||||
font-size: 20px;
|
||||
letter-spacing: 0.27px;
|
||||
line-height: 24px;
|
||||
">
|
||||
Hey {userUsername},
|
||||
</p>
|
||||
<p>
|
||||
Your {instanceName} password can be reset by clicking the
|
||||
button below. If you did not request a new password, please
|
||||
ignore this email.
|
||||
</p>
|
||||
<div>
|
||||
<div style="
|
||||
"
|
||||
>
|
||||
Hey {userUsername},
|
||||
</p>
|
||||
<p>
|
||||
Your {instanceName} password can be reset by clicking the
|
||||
button below. If you did not request a new password, please
|
||||
ignore this email.
|
||||
</p>
|
||||
<div>
|
||||
<div
|
||||
style="
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
padding-bottom: 10px;
|
||||
">
|
||||
<a href="{actionUrl}" target="_blank" style="
|
||||
"
|
||||
>
|
||||
<a
|
||||
href="{actionUrl}"
|
||||
target="_blank"
|
||||
style="
|
||||
font-size: 15px;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
padding: 15px 19px;
|
||||
background-color: #ff5f00;
|
||||
border-radius: 5px;
|
||||
">Reset Password</a>
|
||||
</div>
|
||||
<hr />
|
||||
<div style="text-align: center">
|
||||
<p>
|
||||
Alternatively, you can directly paste this link into
|
||||
your browser:
|
||||
</p>
|
||||
<a href="{actionUrl}" target="_blank" style="word-wrap: break-word;">{actionUrl}</a>
|
||||
"
|
||||
>Reset Password</a
|
||||
>
|
||||
</div>
|
||||
<hr />
|
||||
<div style="text-align: center">
|
||||
<p>
|
||||
Alternatively, you can directly paste this link into
|
||||
your browser:
|
||||
</p>
|
||||
<a
|
||||
href="{actionUrl}"
|
||||
target="_blank"
|
||||
style="word-wrap: break-word"
|
||||
>{actionUrl}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
@ -22,7 +22,7 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="background-color: #202225;">
|
||||
<div style="background-color: #202225">
|
||||
<img
|
||||
src="https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/branding/svg/Spacebar__Logo-Blue.svg"
|
||||
alt="Branding"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
@ -22,7 +22,7 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="background-color: #202225;">
|
||||
<div style="background-color: #202225">
|
||||
<img
|
||||
src="https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/branding/svg/Spacebar__Logo-Blue.svg"
|
||||
alt="Branding"
|
||||
@ -69,12 +69,11 @@
|
||||
>
|
||||
<a
|
||||
class="btn"
|
||||
href="{emailVerificationUrl}"
|
||||
href="{actionUrl}"
|
||||
target="_blank"
|
||||
style="
|
||||
font-size: 15px;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
@ -91,8 +90,11 @@
|
||||
Alternatively, you can directly paste this link into
|
||||
your browser:
|
||||
</p>
|
||||
<a href="{emailVerificationUrl}" target="_blank" style="word-wrap: break-word;"
|
||||
>{emailVerificationUrl}</a
|
||||
<a
|
||||
href="{actionUrl}"
|
||||
target="_blank"
|
||||
style="word-wrap: break-word"
|
||||
>{actionUrl}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,16 +1,24 @@
|
||||
{
|
||||
"login": {
|
||||
"INVALID_LOGIN": "E-Mail or Phone not found",
|
||||
"INVALID_PASSWORD": "Invalid Password",
|
||||
"ACCOUNT_DISABLED": "This account is disabled"
|
||||
"INVALID_LOGIN": "Ungültiger Benutzername/E-Mail oder Passwort.",
|
||||
"INVALID_PASSWORD": "Ungültiges Passwort",
|
||||
"ACCOUNT_DISABLED": "Dieser Account wurde deaktiviert",
|
||||
"INVALID_TOTP_CODE": "Ungültiger Zwei-Faktor-Authentifierungs-Code.",
|
||||
"INVALID_TOTP_SECRET": "Ungültiges Zwei-Faktor-Authentifierungs-Secret"
|
||||
},
|
||||
"register": {
|
||||
"REGISTRATION_DISABLED": "New user registration is disabled",
|
||||
"INVITE_ONLY": "You must be invited to register",
|
||||
"EMAIL_INVALID": "Invalid Email",
|
||||
"EMAIL_ALREADY_REGISTERED": "Email is already registered",
|
||||
"DATE_OF_BIRTH_UNDERAGE": "You need to be {{years}} years or older",
|
||||
"CONSENT_REQUIRED": "You must agree to the Terms of Service and Privacy Policy.",
|
||||
"USERNAME_TOO_MANY_USERS": "Too many users have this username, please try another"
|
||||
"REGISTRATION_DISABLED": "Die Registrierung von neuen Benutzern ist deaktiviert",
|
||||
"INVITE_ONLY": "Du musst eingeladen werden, um dich registrieren zu können",
|
||||
"EMAIL_INVALID": "Ungültige E-Mail-Adresse",
|
||||
"EMAIL_ALREADY_REGISTERED": "E-Mail-Adresse ist bereits registriert",
|
||||
"DATE_OF_BIRTH_UNDERAGE": "Du musst mindestens {{years}} Jahre oder älter sein",
|
||||
"PASSWORD_REQUIREMENTS_MIN_LENGTH": "Das Passwort muss mindestens {{min}} Zeichen lang sein.",
|
||||
"CONSENT_REQUIRED": "Du musst den Nutzungsbedingungen und der Datenschutzerklärung zustimmen.",
|
||||
"USERNAME_TOO_MANY_USERS": "Zu viele Nutzer haben diesen Benutzernamen, bitte probiere einen anderen",
|
||||
"TOO_MANY_REGISTRATIONS": "Zu viele Registrierungen, bitte versuche es später erneut"
|
||||
},
|
||||
"password_reset": {
|
||||
"EMAIL_DOES_NOT_EXIST": "E-Mail-Adresse existiert nicht.",
|
||||
"INVALID_TOKEN": "Ungültiger Token."
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"login": {
|
||||
"INVALID_LOGIN": "E-Mail or Phone not found",
|
||||
"INVALID_LOGIN": "Invalid login or password.",
|
||||
"INVALID_PASSWORD": "Invalid Password",
|
||||
"ACCOUNT_DISABLED": "This account is disabled",
|
||||
"INVALID_TOTP_CODE": "Invalid two-factor code.",
|
||||
@ -12,9 +12,9 @@
|
||||
"EMAIL_INVALID": "Invalid Email",
|
||||
"EMAIL_ALREADY_REGISTERED": "Email is already registered",
|
||||
"DATE_OF_BIRTH_UNDERAGE": "You need to be {{years}} years or older",
|
||||
"PASSWORD_REQUIREMENTS_MIN_LENGTH": "The password must be at least {{min}} characters long.",
|
||||
"CONSENT_REQUIRED": "You must agree to the Terms of Service and Privacy Policy.",
|
||||
"USERNAME_TOO_MANY_USERS": "Too many users have this username, please try another",
|
||||
"GUESTS_DISABLED": "Guest users are disabled",
|
||||
"TOO_MANY_REGISTRATIONS": "Too many registrations, please try again later"
|
||||
},
|
||||
"password_reset": {
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"EMAIL_INVALID": "Invalid Email",
|
||||
"EMAIL_ALREADY_REGISTERED": "Email is already registered",
|
||||
"DATE_OF_BIRTH_UNDERAGE": "You need to be {{years}} years or older",
|
||||
"PASSWORD_REQUIREMENTS_MIN_LENGTH": "Must be at least {{min}} characters long.",
|
||||
"CONSENT_REQUIRED": "You must agree to the Terms of Service and Privacy Policy.",
|
||||
"PASSWORD_REQUIREMENTS_MIN_LENGTH": "The password must be at least {{min}} characters long.",
|
||||
"CONSENT_REQUIRED": "You must agree to the Terms of Service and Privacy Policy.",
|
||||
"USERNAME_TOO_MANY_USERS": "Too many users have this username, please try another"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
const supportedLocales = [
|
||||
"bg",
|
||||
"cs",
|
||||
"da",
|
||||
"de",
|
||||
"el",
|
||||
"en-GB",
|
||||
"es-ES",
|
||||
"fi",
|
||||
"fr",
|
||||
"hi",
|
||||
"hr",
|
||||
"hu",
|
||||
"it",
|
||||
"ja",
|
||||
"ko",
|
||||
"lt",
|
||||
"nl",
|
||||
"no",
|
||||
"pl",
|
||||
"pt-BR",
|
||||
"ro",
|
||||
"ru",
|
||||
"sv-SE",
|
||||
"th",
|
||||
"tr",
|
||||
"uk",
|
||||
"vi",
|
||||
"zh-CN",
|
||||
"zh-TW"
|
||||
];
|
||||
|
||||
const settings = JSON.parse(window.localStorage.getItem("UserSettingsStore"));
|
||||
if (settings && !supportedLocales.includes(settings.locale)) {
|
||||
// fix client locale wrong and client not loading at all
|
||||
settings.locale = "en-US";
|
||||
window.localStorage.setItem("UserSettingsStore", JSON.stringify(settings));
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
// Fixes /oauth2 endpoints not requesting a CSS file
|
||||
|
||||
if (location.pathname.startsWith("/oauth2/")) {
|
||||
const link = document.createElement("link");
|
||||
link.rel = "stylesheet"
|
||||
link.type = "text/css"
|
||||
link.href = "/assets/40532.f7b1e10347ef10e790ac.css"
|
||||
document.head.appendChild(link)
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.5 KiB |
BIN
assets/public/22341bdb500c7b63a93bbce957d1601e.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/public/4a8562cf00887030c416d3ec2d46385a.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
BIN
assets/public/7213ab6677377974697dfdfbaf5f6a6f.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
BIN
assets/public/9b0bb198936784c45c72833cc426cc55.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
assets/public/9d6ddb4e4d899a533a8cc617011351c9.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
BIN
assets/public/d9977836b82058bf2f74eebd50edc095.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
@ -1,68 +0,0 @@
|
||||
/* replace tos acceptance popup */
|
||||
#app-mount > div:nth-child(7) > div > div > div.tooltipContent-bqVLWK {
|
||||
visibility: hidden;
|
||||
}
|
||||
#app-mount > div:nth-child(7) > div > div > div.tooltipContent-bqVLWK::after {
|
||||
visibility: visible;
|
||||
display: block;
|
||||
content: "You need to agree to this instance's rules to continue";
|
||||
margin-top: -32px;
|
||||
}
|
||||
/* replace login header */
|
||||
#app-mount > div.app-1q1i1E > div > div > div > div > form > div > div > div.mainLoginContainer-1ddwnR > h3 {
|
||||
visibility: hidden;
|
||||
}
|
||||
h3.title-jXR8lp.marginBottom8-AtZOdT.base-1x0h_U.size24-RIRrxO::after {
|
||||
margin-top: -32px;
|
||||
content: "Welcome to Spacebar!";
|
||||
visibility: visible;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Logo in top left when bg removed */
|
||||
#app-mount > div.app-1q1i1E > div > a {
|
||||
/* replace me: original dimensions: 130x36 */
|
||||
background: url(https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/branding/svg/Spacebar__Logo-Blue.svg);
|
||||
width: 130px;
|
||||
height: 23px;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
/* replace TOS text */
|
||||
|
||||
#app-mount
|
||||
> div.app-1q1i1E
|
||||
> div
|
||||
> div
|
||||
> div
|
||||
> form
|
||||
> div
|
||||
> div
|
||||
> div.flex-1xMQg5.flex-1O1GKY.horizontal-1ae9ci.horizontal-2EEEnY.flex-1O1GKY.directionRow-3v3tfG.justifyStart-2NDFzi.alignCenter-1dQNNs.noWrap-3jynv6.marginTop20-3TxNs6
|
||||
> label
|
||||
> div.label-cywgfr.labelClickable-11AuB8.labelForward-1wfipV
|
||||
> * {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#app-mount
|
||||
> div.app-1q1i1E
|
||||
> div
|
||||
> div
|
||||
> div
|
||||
> form
|
||||
> div
|
||||
> div
|
||||
> div.flex-1xMQg5.flex-1O1GKY.horizontal-1ae9ci.horizontal-2EEEnY.flex-1O1GKY.directionRow-3v3tfG.justifyStart-2NDFzi.alignCenter-1dQNNs.noWrap-3jynv6.marginTop20-3TxNs6
|
||||
> label
|
||||
> div.label-cywgfr.labelClickable-11AuB8.labelForward-1wfipV::after {
|
||||
visibility: visible;
|
||||
content: "I have read and agree with the rules set by this instance.";
|
||||
display: block;
|
||||
margin-top: -16px;
|
||||
}
|
||||
|
||||
/* shrink login box to same size as register */
|
||||
.authBoxExpanded-2jqaBe {
|
||||
width: 480px !important;
|
||||
}
|
||||
@ -1,92 +0,0 @@
|
||||
/* loading spinner */
|
||||
#app-mount > div.app-1q1i1E > div.container-16j22k.fixClipping-3qAKRb > div.content-1-zrf2 > video {
|
||||
filter: opacity(1);
|
||||
background: url("http://www.clipartbest.com/cliparts/7ca/6Rr/7ca6RrLAi.gif");
|
||||
background-size: contain;
|
||||
/* width: 64px;
|
||||
height: 64px; */
|
||||
padding-bottom: 64px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
/* home button icon */
|
||||
#app-mount
|
||||
> div.app-1q1i1E
|
||||
> div
|
||||
> div.layers-3iHuyZ.layers-3q14ss
|
||||
> div
|
||||
> div
|
||||
> nav
|
||||
> ul
|
||||
> div.scroller-1Bvpku.none-2Eo-qx.scrollerBase-289Jih
|
||||
> div.tutorialContainer-2sGCg9
|
||||
> div
|
||||
> div.listItemWrapper-KhRmzM
|
||||
> div
|
||||
> svg
|
||||
> foreignObject
|
||||
> div
|
||||
> div {
|
||||
background-image: url(https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/branding/svg/Spacebar__Icon-Rounded-Subtract.svg);
|
||||
background-size: contain;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
#app-mount
|
||||
> div.app-1q1i1E
|
||||
> div
|
||||
> div.layers-3iHuyZ.layers-3q14ss
|
||||
> div
|
||||
> div
|
||||
> nav
|
||||
> ul
|
||||
> div.scroller-1Bvpku.none-2Eo-qx.scrollerBase-289Jih
|
||||
> div.tutorialContainer-2sGCg9
|
||||
> div
|
||||
> div.listItemWrapper-KhRmzM
|
||||
> div
|
||||
> svg
|
||||
> foreignObject
|
||||
> div
|
||||
> div,
|
||||
#app-mount
|
||||
> div.app-1q1i1E
|
||||
> div
|
||||
> div.layers-3iHuyZ.layers-3q14ss
|
||||
> div
|
||||
> div
|
||||
> nav
|
||||
> ul
|
||||
> div.scroller-1Bvpku.none-2Eo-qx.scrollerBase-289Jih
|
||||
> div.tutorialContainer-2sGCg9
|
||||
> div
|
||||
> div.listItemWrapper-KhRmzM
|
||||
> div
|
||||
> svg
|
||||
> foreignObject
|
||||
> div
|
||||
> div:hover {
|
||||
background-color: white;
|
||||
}
|
||||
/* Login QR */
|
||||
#app-mount > div.app-1q1i1E > div > div > div > div > form > div > div > div.transitionGroup-aR7y1d.qrLogin-1AOZMt,
|
||||
#app-mount > div.app-1q1i1E > div > div > div > div > form > div > div > div.verticalSeparator-3huAjp,
|
||||
/* Remove login bg */
|
||||
#app-mount > div.app-1q1i1E > div > svg,
|
||||
/* Download bar */
|
||||
#app-mount > div.app-1q1i1E > div > div.layers-3iHuyZ.layers-3q14ss > div > div > div > div.notice-3bPHh-.colorDefault-22HBa0,
|
||||
/* Connection problem links */
|
||||
#app-mount > div.app-1q1i1E > div.container-16j22k.fixClipping-3qAKRb > div.problems-3mgf6w.slideIn-sCvzGz > div:nth-child(2),
|
||||
/* Downloads button */
|
||||
#app-mount > div.app-1q1i1E > div > div.layers-3iHuyZ.layers-3q14ss > div > div > nav > ul > div.scroller-1Bvpku.none-2Eo-qx.scrollerBase-289Jih > div:nth-child(7) > div.listItemWrapper-KhRmzM > div > svg > foreignObject > div,
|
||||
#app-mount > div.app-1q1i1E > div > div.layers-3iHuyZ.layers-3q14ss > div > div > nav > ul > div.scroller-1Bvpku.none-2Eo-qx.scrollerBase-289Jih > div:nth-child(6) > div,
|
||||
/* help button */
|
||||
#app-mount > div.app-1q1i1E > div > div.layers-3iHuyZ.layers-3q14ss > div > div > div > div.content-98HsJk > div.chat-3bRxxu > section > div.toolbar-1t6TWx > a,
|
||||
/* download button start of guild */
|
||||
#chat-messages-899316648933185083 > div > div > div:nth-child(5),
|
||||
/* Thread permissions etc popups */
|
||||
#app-mount > div.app-1q1i1E > div > div.layers-3iHuyZ.layers-3q14ss > div > div > div > div.content-98HsJk > div.sidebar-2K8pFh.hasNotice-1XRy4h > nav > div.container-3O_wAf,
|
||||
/* home button icon */
|
||||
#app-mount > div.app-1q1i1E > div > div.layers-3iHuyZ.layers-3q14ss > div > div > nav > ul > div.scroller-1Bvpku.none-2Eo-qx.scrollerBase-289Jih > div.tutorialContainer-2sGCg9 > div > div.listItemWrapper-KhRmzM > div > svg > foreignObject > div > div > svg {
|
||||
display: none;
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
/* Your custom CSS goes here, enjoy! */
|
||||
147
assets/public/verify.html
Normal file
@ -0,0 +1,147 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Spacebar Server</title>
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: "Montserrat", sans-serif;
|
||||
background-color: rgb(10, 10, 10);
|
||||
color: white;
|
||||
font-size: 1.1rem;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#wordmark {
|
||||
width: min(200px, 50%);
|
||||
margin: 20px;
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.box {
|
||||
width: 22vw;
|
||||
padding: 32px;
|
||||
border-radius: 8px;
|
||||
background-color: rgb(32, 32, 32);
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<img
|
||||
alt="Spacebar Logo"
|
||||
id="wordmark"
|
||||
src="https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/branding/svg/Spacebar__Logo-Blue.svg"
|
||||
/>
|
||||
|
||||
<div class="box">
|
||||
<p id="title" class="title">Verifying your email</p>
|
||||
<p id="subtitle" class="subtitle">Please wait...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.onload = verify;
|
||||
|
||||
function verify() {
|
||||
const title = document.getElementById("title");
|
||||
const subtitle = document.getElementById("subtitle");
|
||||
|
||||
// if no fragment identifier in URL, error
|
||||
if (!window.location.hash) {
|
||||
title.innerText = "Invalid Link";
|
||||
subtitle.innerText = "Please check the link and try again.";
|
||||
return;
|
||||
}
|
||||
|
||||
// convert fragment to a key-value pair
|
||||
const fragment = window.location.hash.substring(1);
|
||||
const pairs = fragment.split("&");
|
||||
const values = {};
|
||||
pairs.forEach((pair) => {
|
||||
const [key, value] = pair.split("=");
|
||||
values[key] = value;
|
||||
});
|
||||
|
||||
// ensure token key is present
|
||||
if (!values.token) {
|
||||
title.innerText = "Invalid Link";
|
||||
subtitle.innerText = "Please check the link and try again.";
|
||||
return;
|
||||
}
|
||||
|
||||
// make request to server
|
||||
const token = values.token;
|
||||
fetch("/api/auth/verify", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
token,
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// check for an error response
|
||||
if ("message" in data) {
|
||||
title.innerText = "Email Verification Link Expired";
|
||||
subtitle.innerText =
|
||||
"Please request a new verification link.";
|
||||
return;
|
||||
}
|
||||
|
||||
title.innerText = "Email Verified";
|
||||
subtitle.innerText = "You can now login.";
|
||||
})
|
||||
.catch((error) => {
|
||||
title.innerText = "Email Verification Failed";
|
||||
subtitle.innerText = error;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,82 +0,0 @@
|
||||
/*
|
||||
This file is used to patch client version 134842 ( and probably a lot more ) to send additional info when using webrtc.
|
||||
If you want to use it, throw it into the `preload-plugins` folder.
|
||||
TODO: Make it so this file is not required for webrtc.
|
||||
|
||||
Do note that webrtc, as of 17/12/2022, is not implemented yet in spacebarchat/server.
|
||||
*/
|
||||
|
||||
(this.webpackChunkdiscord_app = this.webpackChunkdiscord_app || []).push([
|
||||
[[228974]],
|
||||
{
|
||||
632540: (module, exports, req) => {
|
||||
window.find = (filter, options = {}) => {
|
||||
const { cacheOnly = false } = options;
|
||||
for (let i in req.c) {
|
||||
if (req.c.hasOwnProperty(i)) {
|
||||
let m = req.c[i].exports;
|
||||
if (m && m.__esModule && m.default && filter(m.default)) return m.default;
|
||||
if (m && filter(m)) return m;
|
||||
}
|
||||
}
|
||||
if (cacheOnly) {
|
||||
console.warn("Cannot find loaded module in cache");
|
||||
return null;
|
||||
}
|
||||
console.warn("Cannot find loaded module in cache. Loading all modules may have unexpected side effects");
|
||||
for (let i = 0; i < req.m.length; ++i) {
|
||||
let m = req(i);
|
||||
if (m && m.__esModule && m.default && filter(m.default)) return m.default;
|
||||
if (m && filter(m)) return m;
|
||||
}
|
||||
console.warn("Cannot find module");
|
||||
return null;
|
||||
};
|
||||
window.findByUniqueProperties = (propNames, options) =>
|
||||
find((module) => propNames.every((prop) => module[prop] !== undefined), options);
|
||||
window.findByDisplayName = (displayName, options) => find((module) => module.displayName === displayName, options);
|
||||
window.req = req;
|
||||
|
||||
init();
|
||||
}
|
||||
},
|
||||
(t) => t(632540)
|
||||
]);
|
||||
|
||||
function retry(callback) {
|
||||
return new Promise((resolve) => {
|
||||
const interval = setInterval(() => {
|
||||
const mod = callback();
|
||||
if (!mod) return;
|
||||
|
||||
clearInterval(interval);
|
||||
resolve(mod);
|
||||
}, 50);
|
||||
});
|
||||
}
|
||||
|
||||
async function init() {
|
||||
const SDP = await retry(() => findByUniqueProperties(["truncateSDP"]));
|
||||
const StringManipulator = findByUniqueProperties(["uniq"]);
|
||||
|
||||
const truncateSDP = SDP.truncateSDP;
|
||||
SDP.truncateSDP = (e) => {
|
||||
const result = truncateSDP(e);
|
||||
const i = result.codecs.find((x) => x.name === "VP8");
|
||||
const a = new RegExp("^a=ice|a=extmap|opus|VP8|fingerprint|" + i?.rtxPayloadType + " rtx", "i");
|
||||
return {
|
||||
sdp: StringManipulator(e)
|
||||
.split(/\r\n/)
|
||||
.filter(function (e) {
|
||||
return a.test(e);
|
||||
})
|
||||
.uniq()
|
||||
.join("\n"),
|
||||
codecs: result.codecs
|
||||
};
|
||||
};
|
||||
// SDP.generateUnifiedSessionDescription = (e) => {
|
||||
// console.log(e);
|
||||
// return new RTCSessionDescription({ sdp: e.baseSDP.replace(/sendonly/g, "recvonly"), type: "answer" });
|
||||
// };
|
||||
}
|
||||
BIN
flake.lock
generated
Normal file
70
flake.nix
Normal file
@ -0,0 +1,70 @@
|
||||
{
|
||||
description = "Spacebar server, written in Typescript.";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachSystem flake-utils.lib.allSystems (system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
};
|
||||
hashesFile = builtins.fromJSON (builtins.readFile ./hashes.json);
|
||||
in rec {
|
||||
packages.default = pkgs.buildNpmPackage {
|
||||
pname = "spacebar-server-ts";
|
||||
src = ./.;
|
||||
name = "spacebar-server-ts";
|
||||
nativeBuildInputs = with pkgs; [ python3 ];
|
||||
npmDepsHash = hashesFile.npmDepsHash;
|
||||
makeCacheWritable = true;
|
||||
postPatch = ''
|
||||
substituteInPlace package.json --replace 'npx patch-package' '${pkgs.nodePackages.patch-package}/bin/patch-package'
|
||||
'';
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
set -x
|
||||
#remove packages not needed for production, or at least try to...
|
||||
npm prune --omit dev --no-save $npmInstallFlags "''${npmInstallFlagsArray[@]}" $npmFlags "''${npmFlagsArray[@]}"
|
||||
find node_modules -maxdepth 1 -type d -empty -delete
|
||||
|
||||
mkdir -p $out/node_modules/
|
||||
cp -r node_modules/* $out/node_modules/
|
||||
cp -r dist/ $out/node_modules/@spacebar
|
||||
for i in dist/**/start.js
|
||||
do
|
||||
makeWrapper ${pkgs.nodejs-slim}/bin/node $out/bin/start-`dirname ''${i/dist\//}` --prefix NODE_PATH : $out/node_modules --add-flags $out/node_modules/@spacebar`dirname ''${i/dist/}`/start.js
|
||||
done
|
||||
set +x
|
||||
substituteInPlace package.json --replace 'dist/' 'node_modules/@spacebar/'
|
||||
find $out/node_modules/@spacebar/ -type f -name "*.js" | while read srcFile; do
|
||||
echo Patching imports in ''${srcFile/$out\/node_modules\/@spacebar//}...
|
||||
substituteInPlace $srcFile --replace 'require("./' 'require(__dirname + "/'
|
||||
substituteInPlace $srcFile --replace 'require("../' 'require(__dirname + "/../'
|
||||
substituteInPlace $srcFile --replace ', "assets"' ', "..", "assets"'
|
||||
#substituteInPlace $srcFile --replace 'require("@spacebar/' 'require("
|
||||
done
|
||||
set -x
|
||||
cp -r assets/ $out/
|
||||
cp package.json $out/
|
||||
rm -v $out/assets/openapi.json
|
||||
#rm -v $out/assets/schemas.json
|
||||
|
||||
#debug utils:
|
||||
#cp $out/node_modules/@spacebar/ $out/build_output -r
|
||||
set +x
|
||||
runHook postInstall
|
||||
'';
|
||||
};
|
||||
devShell = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
nodejs
|
||||
nodePackages.typescript
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
3
hashes.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"npmDepsHash": "sha256-fZNDN2/fNy6Nu7tbr0RhQ8j4BP7X1Yhrh/fSTH7hbJc="
|
||||
}
|
||||
10
nix-update.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i "bash -x" -p bash prefetch-npm-deps jq git nix-output-monitor
|
||||
nix flake update
|
||||
DEPS_HASH=`prefetch-npm-deps package-lock.json`
|
||||
TMPFILE=$(mktemp)
|
||||
jq '.npm_deps_hash = "'$DEPS_HASH'"' hashes.json > $TMPFILE
|
||||
mv -- "$TMPFILE" hashes.json
|
||||
|
||||
nom build .# || exit $?
|
||||
git add hashes.json flake.lock flake.nix
|
||||
BIN
package-lock.json
generated
@ -10,6 +10,7 @@
|
||||
"start:cdn": "node dist/cdn/start.js",
|
||||
"start:gateway": "node dist/gateway/start.js",
|
||||
"build": "tsc -p .",
|
||||
"watch": "tsc -w -p .",
|
||||
"test": "node scripts/test.js",
|
||||
"lint": "eslint .",
|
||||
"setup": "npm run build && npm run generate:schema",
|
||||
@ -55,9 +56,9 @@
|
||||
"@types/probe-image-size": "^7.2.0",
|
||||
"@types/sharp": "^0.31.1",
|
||||
"@types/ws": "^8.5.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"eslint": "^8.46.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"eslint": "^8.56.0",
|
||||
"express": "^4.18.2",
|
||||
"husky": "^8.0.3",
|
||||
"prettier": "^2.8.8",
|
||||
@ -116,9 +117,11 @@
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"erlpack": "^0.1.4",
|
||||
"mysql": "^2.18.1",
|
||||
"nodemailer-mailgun-transport": "^2.1.5",
|
||||
"nodemailer-mailjet-transport": "github:n0script22/nodemailer-mailjet-transport",
|
||||
"nodemailer-sendgrid-transport": "github:Maria-Golomb/nodemailer-sendgrid-transport",
|
||||
"pg": "^8.11.3",
|
||||
"sqlite3": "^5.1.6"
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,15 +18,15 @@
|
||||
|
||||
import {
|
||||
Config,
|
||||
Email,
|
||||
initDatabase,
|
||||
initEvent,
|
||||
JSONReplacer,
|
||||
registerRoutes,
|
||||
Sentry,
|
||||
WebAuthn,
|
||||
ConnectionConfig,
|
||||
ConnectionLoader,
|
||||
Email,
|
||||
JSONReplacer,
|
||||
Sentry,
|
||||
WebAuthn,
|
||||
initDatabase,
|
||||
initEvent,
|
||||
registerRoutes,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { Server, ServerOptions } from "lambert-server";
|
||||
@ -141,6 +141,10 @@ export class SpacebarServer extends Server {
|
||||
res.sendFile(path.join(PUBLIC_ASSETS_FOLDER, "index.html")),
|
||||
);
|
||||
|
||||
app.get("/verify", (req, res) =>
|
||||
res.sendFile(path.join(PUBLIC_ASSETS_FOLDER, "verify.html")),
|
||||
);
|
||||
|
||||
this.app.use(ErrorHandler);
|
||||
|
||||
Sentry.errorHandler(this.app);
|
||||
|
||||
@ -21,6 +21,7 @@ import {
|
||||
Application,
|
||||
ApplicationModifySchema,
|
||||
DiscordApiErrors,
|
||||
handleFile,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
@ -83,6 +84,13 @@ router.patch(
|
||||
)
|
||||
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
|
||||
|
||||
if (body.icon) {
|
||||
body.icon = await handleFile(
|
||||
`/app-icons/${app.id}`,
|
||||
body.icon as string,
|
||||
);
|
||||
}
|
||||
|
||||
if (app.bot) {
|
||||
app.bot.assign({ bio: body.description });
|
||||
await app.bot.save();
|
||||
|
||||
@ -38,7 +38,7 @@ router.get(
|
||||
"The length of each registration token. Defaults to 255.",
|
||||
},
|
||||
},
|
||||
right: "OPERATOR",
|
||||
right: "CREATE_REGISTRATION_TOKENS",
|
||||
responses: { 200: { body: "GenerateRegistrationTokensResponse" } },
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
|
||||
@ -85,7 +85,7 @@ router.post(
|
||||
user = userTokenData.user;
|
||||
} catch {
|
||||
throw FieldErrors({
|
||||
password: {
|
||||
token: {
|
||||
message: req.t("auth:password_reset.INVALID_TOKEN"),
|
||||
code: "INVALID_TOKEN",
|
||||
},
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -50,7 +50,13 @@ router.get(
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: { id: channel_id },
|
||||
});
|
||||
if (!channel.guild_id) return res.send(channel);
|
||||
|
||||
channel.position = await Channel.calculatePosition(
|
||||
channel_id,
|
||||
channel.guild_id,
|
||||
channel.guild,
|
||||
);
|
||||
return res.send(channel);
|
||||
},
|
||||
);
|
||||
@ -90,6 +96,24 @@ router.delete(
|
||||
} else if (channel.type === ChannelType.GROUP_DM) {
|
||||
await Channel.removeRecipientFromChannel(channel, req.user_id);
|
||||
} else {
|
||||
if (channel.type == ChannelType.GUILD_CATEGORY) {
|
||||
const channels = await Channel.find({
|
||||
where: { parent_id: channel_id },
|
||||
});
|
||||
for await (const c of channels) {
|
||||
c.parent_id = null;
|
||||
|
||||
await Promise.all([
|
||||
c.save(),
|
||||
emitEvent({
|
||||
event: "CHANNEL_UPDATE",
|
||||
data: c,
|
||||
channel_id: c.id,
|
||||
} as ChannelUpdateEvent),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
Channel.delete({ id: channel_id }),
|
||||
emitEvent({
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -56,6 +56,7 @@ router.post(
|
||||
edited_timestamp: null,
|
||||
flags: 1,
|
||||
components: [],
|
||||
poll: {},
|
||||
}).status(200);
|
||||
},
|
||||
);
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -91,11 +91,10 @@ router.patch(
|
||||
}
|
||||
} else rights.hasThrow("SELF_EDIT_MESSAGES");
|
||||
|
||||
// @ts-expect-error Something is wrong with message_reference here, TS complains since "channel_id" is optional in MessageCreateSchema
|
||||
const new_message = await handleMessage({
|
||||
...message,
|
||||
// TODO: should message_reference be overridable?
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
message_reference: message.message_reference,
|
||||
...body,
|
||||
author_id: message.author_id,
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -183,9 +183,17 @@ router.get(
|
||||
const uri = y.proxy_url.startsWith("http")
|
||||
? y.proxy_url
|
||||
: `https://example.org${y.proxy_url}`;
|
||||
y.proxy_url = `${endpoint == null ? "" : endpoint}${
|
||||
new URL(uri).pathname
|
||||
}`;
|
||||
|
||||
let pathname = new URL(uri).pathname;
|
||||
while (
|
||||
pathname.split("/")[0] != "attachments" &&
|
||||
pathname.length > 30
|
||||
) {
|
||||
pathname = pathname.split("/").slice(1).join("/");
|
||||
}
|
||||
if (!endpoint?.endsWith("/")) pathname = "/" + pathname;
|
||||
|
||||
y.proxy_url = `${endpoint == null ? "" : endpoint}${pathname}`;
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -53,6 +53,11 @@ router.put(
|
||||
where: { id: channel_id },
|
||||
});
|
||||
if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
|
||||
channel.position = await Channel.calculatePosition(
|
||||
channel_id,
|
||||
channel.guild_id,
|
||||
channel.guild,
|
||||
);
|
||||
|
||||
if (body.type === 0) {
|
||||
if (!(await Role.count({ where: { id: overwrite_id } })))
|
||||
|
||||
45
src/api/routes/connections/index.ts
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { route } from "@spacebar/api";
|
||||
import { ConnectionConfig } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIConnectionsConfiguration",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const config = ConnectionConfig.get();
|
||||
|
||||
Object.keys(config).forEach((key) => {
|
||||
delete config[key].clientId;
|
||||
delete config[key].clientSecret;
|
||||
});
|
||||
|
||||
res.json(config);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -19,7 +19,6 @@
|
||||
import { getIpAdress, route } from "@spacebar/api";
|
||||
import {
|
||||
Ban,
|
||||
BanModeratorSchema,
|
||||
BanRegistrySchema,
|
||||
DiscordApiErrors,
|
||||
GuildBanAddEvent,
|
||||
@ -82,7 +81,7 @@ router.get(
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/:user",
|
||||
"/:user_id",
|
||||
route({
|
||||
permission: "BAN_MEMBERS",
|
||||
responses: {
|
||||
@ -98,23 +97,21 @@ router.get(
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
const user_id = req.params.ban;
|
||||
const { guild_id, user_id } = req.params;
|
||||
|
||||
let ban = (await Ban.findOneOrFail({
|
||||
const ban = (await Ban.findOneOrFail({
|
||||
where: { guild_id: guild_id, user_id: user_id },
|
||||
})) as BanRegistrySchema;
|
||||
|
||||
if (ban.user_id === ban.executor_id) throw DiscordApiErrors.UNKNOWN_BAN;
|
||||
// pretend self-bans don't exist to prevent victim chasing
|
||||
|
||||
/* Filter secret from registry. */
|
||||
const banInfo = {
|
||||
user: await User.getPublicUser(ban.user_id),
|
||||
reason: ban.reason,
|
||||
};
|
||||
|
||||
ban = ban as BanModeratorSchema;
|
||||
|
||||
delete ban.ip;
|
||||
|
||||
return res.json(ban);
|
||||
return res.json(banInfo);
|
||||
},
|
||||
);
|
||||
|
||||
@ -151,6 +148,12 @@ router.put(
|
||||
if (req.permission?.cache.guild?.owner_id === banned_user_id)
|
||||
throw new HTTPError("You can't ban the owner", 400);
|
||||
|
||||
const existingBan = await Ban.findOne({
|
||||
where: { guild_id: guild_id, user_id: banned_user_id },
|
||||
});
|
||||
// Bans on already banned users are silently ignored
|
||||
if (existingBan) return res.status(204).send();
|
||||
|
||||
const banned_user = await User.getPublicUser(banned_user_id);
|
||||
|
||||
const ban = Ban.create({
|
||||
@ -174,59 +177,7 @@ router.put(
|
||||
} as GuildBanAddEvent),
|
||||
]);
|
||||
|
||||
return res.json(ban);
|
||||
},
|
||||
);
|
||||
|
||||
router.put(
|
||||
"/@me",
|
||||
route({
|
||||
requestBody: "BanCreateSchema",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Ban",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
|
||||
const banned_user = await User.getPublicUser(req.params.user_id);
|
||||
|
||||
if (req.permission?.cache.guild?.owner_id === req.params.user_id)
|
||||
throw new HTTPError(
|
||||
"You are the guild owner, hence can't ban yourself",
|
||||
403,
|
||||
);
|
||||
|
||||
const ban = Ban.create({
|
||||
user_id: req.params.user_id,
|
||||
guild_id: guild_id,
|
||||
ip: getIpAdress(req),
|
||||
executor_id: req.params.user_id,
|
||||
reason: req.body.reason, // || otherwise empty
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
Member.removeFromGuild(req.user_id, guild_id),
|
||||
ban.save(),
|
||||
emitEvent({
|
||||
event: "GUILD_BAN_ADD",
|
||||
data: {
|
||||
guild_id: guild_id,
|
||||
user: banned_user,
|
||||
},
|
||||
guild_id: guild_id,
|
||||
} as GuildBanAddEvent),
|
||||
]);
|
||||
|
||||
return res.json(ban);
|
||||
return res.status(204).send();
|
||||
},
|
||||
);
|
||||
|
||||
@ -247,13 +198,10 @@ router.delete(
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id, user_id } = req.params;
|
||||
|
||||
const ban = await Ban.findOneOrFail({
|
||||
await Ban.findOneOrFail({
|
||||
where: { guild_id: guild_id, user_id: user_id },
|
||||
});
|
||||
|
||||
if (ban.user_id === ban.executor_id) throw DiscordApiErrors.UNKNOWN_BAN;
|
||||
// make self-bans irreversible and hide them from view to avoid victim chasing
|
||||
|
||||
const banned_user = await User.getPublicUser(user_id);
|
||||
|
||||
await Promise.all([
|
||||
|
||||
130
src/api/routes/guilds/#guild_id/bulk-ban.ts
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { getIpAdress, route } from "@spacebar/api";
|
||||
import {
|
||||
Ban,
|
||||
DiscordApiErrors,
|
||||
GuildBanAddEvent,
|
||||
Member,
|
||||
User,
|
||||
emitEvent,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({
|
||||
requestBody: "BulkBanSchema",
|
||||
permission: ["BAN_MEMBERS", "MANAGE_GUILD"],
|
||||
responses: {
|
||||
200: {
|
||||
body: "Ban",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
|
||||
const userIds: Array<string> = req.body.user_ids;
|
||||
if (!userIds) throw new HTTPError("The user_ids array is missing", 400);
|
||||
if (userIds.length > 200)
|
||||
throw new HTTPError(
|
||||
"The user_ids array must be between 1 and 200 in length",
|
||||
400,
|
||||
);
|
||||
|
||||
const banned_users = [];
|
||||
const failed_users = [];
|
||||
for await (const banned_user_id of userIds) {
|
||||
if (
|
||||
req.user_id === banned_user_id &&
|
||||
banned_user_id === req.permission?.cache.guild?.owner_id
|
||||
) {
|
||||
failed_users.push(banned_user_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (req.permission?.cache.guild?.owner_id === banned_user_id) {
|
||||
failed_users.push(banned_user_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
const existingBan = await Ban.findOne({
|
||||
where: { guild_id: guild_id, user_id: banned_user_id },
|
||||
});
|
||||
if (existingBan) {
|
||||
failed_users.push(banned_user_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
let banned_user;
|
||||
try {
|
||||
banned_user = await User.getPublicUser(banned_user_id);
|
||||
} catch {
|
||||
failed_users.push(banned_user_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
const ban = Ban.create({
|
||||
user_id: banned_user_id,
|
||||
guild_id: guild_id,
|
||||
ip: getIpAdress(req),
|
||||
executor_id: req.user_id,
|
||||
reason: req.body.reason, // || otherwise empty
|
||||
});
|
||||
|
||||
try {
|
||||
await Promise.all([
|
||||
Member.removeFromGuild(banned_user_id, guild_id),
|
||||
ban.save(),
|
||||
emitEvent({
|
||||
event: "GUILD_BAN_ADD",
|
||||
data: {
|
||||
guild_id: guild_id,
|
||||
user: banned_user,
|
||||
},
|
||||
guild_id: guild_id,
|
||||
} as GuildBanAddEvent),
|
||||
]);
|
||||
banned_users.push(banned_user_id);
|
||||
} catch {
|
||||
failed_users.push(banned_user_id);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (banned_users.length === 0 && failed_users.length > 0)
|
||||
throw DiscordApiErrors.BULK_BAN_FAILED;
|
||||
return res.json({
|
||||
banned_users: banned_users,
|
||||
failed_users: failed_users,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -41,6 +41,15 @@ router.get(
|
||||
const { guild_id } = req.params;
|
||||
const channels = await Channel.find({ where: { guild_id } });
|
||||
|
||||
for await (const channel of channels) {
|
||||
channel.position = await Channel.calculatePosition(
|
||||
channel.id,
|
||||
guild_id,
|
||||
channel.guild,
|
||||
);
|
||||
}
|
||||
channels.sort((a, b) => a.position - b.position);
|
||||
|
||||
res.json(channels);
|
||||
},
|
||||
);
|
||||
@ -71,6 +80,11 @@ router.post(
|
||||
{ ...body, guild_id },
|
||||
req.user_id,
|
||||
);
|
||||
channel.position = await Channel.calculatePosition(
|
||||
channel.id,
|
||||
guild_id,
|
||||
channel.guild,
|
||||
);
|
||||
|
||||
res.status(201).json(channel);
|
||||
},
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -125,6 +125,7 @@ router.post(
|
||||
const user = await User.findOneOrFail({ where: { id: req.user_id } });
|
||||
body.image = (await handleFile(`/emojis/${id}`, body.image)) as string;
|
||||
|
||||
const mimeType = body.image.split(":")[1].split(";")[0];
|
||||
const emoji = await Emoji.create({
|
||||
id: id,
|
||||
guild_id: guild_id,
|
||||
@ -132,7 +133,10 @@ router.post(
|
||||
require_colons: body.require_colons ?? undefined, // schema allows nulls, db does not
|
||||
user: user,
|
||||
managed: false,
|
||||
animated: false, // TODO: Add support animated emojis
|
||||
animated:
|
||||
mimeType == "image/gif" ||
|
||||
mimeType == "image/apng" ||
|
||||
mimeType == "video/webm",
|
||||
available: true,
|
||||
roles: [],
|
||||
}).save();
|
||||
|
||||
@ -113,9 +113,6 @@ router.patch(
|
||||
relations: ["roles", "user"],
|
||||
});
|
||||
const permission = await getPermission(req.user_id, guild_id);
|
||||
const everyone = await Role.findOneOrFail({
|
||||
where: { guild_id: guild_id, name: "@everyone", position: 0 },
|
||||
});
|
||||
|
||||
if ("nick" in body) {
|
||||
permission.hasThrow("MANAGE_NICKNAMES");
|
||||
@ -152,15 +149,14 @@ router.patch(
|
||||
body.roles = body.roles || [];
|
||||
body.roles.filter((x) => !!x);
|
||||
|
||||
if (body.roles.indexOf(everyone.id) === -1)
|
||||
body.roles.push(everyone.id);
|
||||
if (body.roles.indexOf(guild_id) === -1) body.roles.push(guild_id);
|
||||
// foreign key constraint will fail if role doesn't exist
|
||||
member.roles = body.roles.map((x) => Role.create({ id: x }));
|
||||
}
|
||||
|
||||
await member.save();
|
||||
|
||||
member.roles = member.roles.filter((x) => x.id !== everyone.id);
|
||||
member.roles = member.roles.filter((x) => x.id !== guild_id);
|
||||
|
||||
// do not use promise.all as we have to first write to db before emitting the event to catch errors
|
||||
await emitEvent({
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -162,6 +162,7 @@ router.get(
|
||||
edited_timestamp: x.edited_timestamp,
|
||||
flags: x.flags,
|
||||
components: x.components,
|
||||
poll: x.poll,
|
||||
hit: true,
|
||||
},
|
||||
]);
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Badge,
|
||||
Member,
|
||||
PrivateUserProjection,
|
||||
User,
|
||||
@ -98,6 +99,9 @@ router.get(
|
||||
bio: guild_member?.bio || "",
|
||||
guild_id,
|
||||
};
|
||||
|
||||
const badges = await Badge.find();
|
||||
|
||||
res.json({
|
||||
connected_accounts: user.connected_accounts.filter(
|
||||
(x) => x.visibility != 0,
|
||||
@ -111,6 +115,7 @@ router.get(
|
||||
user_profile: userProfile,
|
||||
guild_member: guild_member?.toPublicMember(),
|
||||
guild_member_profile: guild_id && guildMemberProfile,
|
||||
badges: badges.filter((x) => user.badge_ids?.includes(x.id)),
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@ -16,45 +16,49 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import * as Sentry from "@sentry/node";
|
||||
import { EmbedHandlers } from "@spacebar/api";
|
||||
import {
|
||||
Application,
|
||||
Attachment,
|
||||
Channel,
|
||||
Config,
|
||||
Embed,
|
||||
EmbedCache,
|
||||
emitEvent,
|
||||
Guild,
|
||||
Message,
|
||||
MessageCreateEvent,
|
||||
MessageUpdateEvent,
|
||||
EVERYONE_MENTION,
|
||||
getPermission,
|
||||
getRights,
|
||||
Guild,
|
||||
HERE_MENTION,
|
||||
Message,
|
||||
MessageCreateEvent,
|
||||
MessageCreateSchema,
|
||||
MessageType,
|
||||
MessageUpdateEvent,
|
||||
Role,
|
||||
ROLE_MENTION,
|
||||
Sticker,
|
||||
User,
|
||||
//CHANNEL_MENTION,
|
||||
USER_MENTION,
|
||||
ROLE_MENTION,
|
||||
Role,
|
||||
EVERYONE_MENTION,
|
||||
HERE_MENTION,
|
||||
MessageType,
|
||||
User,
|
||||
Application,
|
||||
Webhook,
|
||||
Attachment,
|
||||
Config,
|
||||
Sticker,
|
||||
MessageCreateSchema,
|
||||
EmbedCache,
|
||||
handleFile,
|
||||
Permissions,
|
||||
} from "@spacebar/util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { In } from "typeorm";
|
||||
import { EmbedHandlers } from "@spacebar/api";
|
||||
import * as Sentry from "@sentry/node";
|
||||
import fetch from "node-fetch";
|
||||
const allow_empty = false;
|
||||
// TODO: check webhook, application, system author, stickers
|
||||
// TODO: embed gifs/videos/images
|
||||
|
||||
const LINK_REGEX =
|
||||
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/g;
|
||||
/<?https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)>?/g;
|
||||
|
||||
export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
const channel = await Channel.findOneOrFail({
|
||||
@ -69,6 +73,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
: undefined;
|
||||
const message = Message.create({
|
||||
...opts,
|
||||
poll: opts.poll,
|
||||
sticker_items: stickers,
|
||||
guild_id: channel.guild_id,
|
||||
channel_id: opts.channel_id,
|
||||
@ -162,29 +167,28 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
message.member = permission.cache.member;
|
||||
}
|
||||
|
||||
if (opts.tts) permission.hasThrow("SEND_TTS_MESSAGES");
|
||||
if (opts.message_reference) {
|
||||
permission.hasThrow("READ_MESSAGE_HISTORY");
|
||||
// code below has to be redone when we add custom message routing
|
||||
if (message.guild_id !== null) {
|
||||
const guild = await Guild.findOneOrFail({
|
||||
where: { id: channel.guild_id },
|
||||
});
|
||||
if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) {
|
||||
if (opts.message_reference.guild_id !== channel.guild_id)
|
||||
throw new HTTPError(
|
||||
"You can only reference messages from this guild",
|
||||
);
|
||||
if (opts.message_reference.channel_id !== opts.channel_id)
|
||||
throw new HTTPError(
|
||||
"You can only reference messages from this channel",
|
||||
);
|
||||
}
|
||||
if (opts.tts) permission.hasThrow("SEND_TTS_MESSAGES");
|
||||
if (opts.message_reference) {
|
||||
permission.hasThrow("READ_MESSAGE_HISTORY");
|
||||
// code below has to be redone when we add custom message routing
|
||||
if (message.guild_id !== null) {
|
||||
const guild = await Guild.findOneOrFail({
|
||||
where: { id: channel.guild_id },
|
||||
});
|
||||
if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) {
|
||||
if (opts.message_reference.guild_id !== channel.guild_id)
|
||||
throw new HTTPError(
|
||||
"You can only reference messages from this guild",
|
||||
);
|
||||
if (opts.message_reference.channel_id !== opts.channel_id)
|
||||
throw new HTTPError(
|
||||
"You can only reference messages from this channel",
|
||||
);
|
||||
}
|
||||
/** Q: should be checked if the referenced message exists? ANSWER: NO
|
||||
otherwise backfilling won't work **/
|
||||
message.type = MessageType.REPLY;
|
||||
}
|
||||
/** Q: should be checked if the referenced message exists? ANSWER: NO
|
||||
otherwise backfilling won't work **/
|
||||
message.type = MessageType.REPLY;
|
||||
}
|
||||
|
||||
// TODO: stickers/activity
|
||||
@ -193,7 +197,9 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
!opts.content &&
|
||||
!opts.embeds?.length &&
|
||||
!opts.attachments?.length &&
|
||||
!opts.sticker_ids?.length
|
||||
!opts.sticker_ids?.length &&
|
||||
!opts.poll &&
|
||||
!opts.components?.length
|
||||
) {
|
||||
throw new HTTPError("Empty messages are not allowed", 50006);
|
||||
}
|
||||
@ -272,6 +278,9 @@ export async function postHandleMessage(message: Message) {
|
||||
const cachePromises = [];
|
||||
|
||||
for (const link of links) {
|
||||
// Don't embed links in <>
|
||||
if (link.startsWith("<") && link.endsWith(">")) continue;
|
||||
|
||||
const url = new URL(link);
|
||||
|
||||
const cached = await EmbedCache.findOne({ where: { url: link } });
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -90,19 +90,23 @@ export function route(opts: RouteOptions) {
|
||||
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
if (opts.permission) {
|
||||
const required = new Permissions(opts.permission);
|
||||
req.permission = await getPermission(
|
||||
req.user_id,
|
||||
req.params.guild_id,
|
||||
req.params.channel_id,
|
||||
);
|
||||
|
||||
// bitfield comparison: check if user lacks certain permission
|
||||
if (!req.permission.has(required)) {
|
||||
throw DiscordApiErrors.MISSING_PERMISSIONS.withParams(
|
||||
opts.permission as string,
|
||||
);
|
||||
}
|
||||
const requiredPerms = Array.isArray(opts.permission)
|
||||
? opts.permission
|
||||
: [opts.permission];
|
||||
requiredPerms.forEach((perm) => {
|
||||
// bitfield comparison: check if user lacks certain permission
|
||||
if (!req.permission!.has(new Permissions(perm))) {
|
||||
throw DiscordApiErrors.MISSING_PERMISSIONS.withParams(
|
||||
perm as string,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (opts.right) {
|
||||
|
||||
40
src/cdn/routes/badge-icons.ts
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { storage } from "../util/Storage";
|
||||
import FileType from "file-type";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/:badge_id", async (req: Request, res: Response) => {
|
||||
const { badge_id } = req.params;
|
||||
const path = `badge-icons/${badge_id}`;
|
||||
|
||||
const file = await storage.get(path);
|
||||
if (!file) throw new HTTPError("not found", 404);
|
||||
const type = await FileType.fromBuffer(file);
|
||||
|
||||
res.set("Content-Type", type?.mime);
|
||||
res.set("Cache-Control", "public, max-age=31536000, must-revalidate");
|
||||
|
||||
return res.send(file);
|
||||
});
|
||||
|
||||
export default router;
|
||||
@ -23,12 +23,12 @@ import { HTTPError } from "lambert-server";
|
||||
import { join } from "path";
|
||||
|
||||
const defaultAvatarHashMap = new Map([
|
||||
["0", "823a3de61c4dc2415cc4dbc38fca4299"],
|
||||
["1", "e56a89224be0b2b1f7c04eca975be468"],
|
||||
["2", "0c8138dcc0dfe2689cdd73f7952c2475"],
|
||||
["3", "5ac2728593bb455250d11b848a0c36c6"],
|
||||
["4", "addd2f3268df46459e1d6012ad8e75bd"],
|
||||
["5", "c4e0c8300fa491d94acfd2a1fb26cea8"],
|
||||
["0", "4a8562cf00887030c416d3ec2d46385a"],
|
||||
["1", "9b0bb198936784c45c72833cc426cc55"],
|
||||
["2", "22341bdb500c7b63a93bbce957d1601e"],
|
||||
["3", "d9977836b82058bf2f74eebd50edc095"],
|
||||
["4", "9d6ddb4e4d899a533a8cc617011351c9"],
|
||||
["5", "7213ab6677377974697dfdfbaf5f6a6f"],
|
||||
]);
|
||||
|
||||
const defaultGroupDMAvatarHashMap = new Map([
|
||||
@ -63,7 +63,15 @@ router.get("/avatars/:id", async (req: Request, res: Response) => {
|
||||
id = id.split(".")[0]; // remove .file extension
|
||||
const hash = defaultAvatarHashMap.get(id);
|
||||
if (!hash) throw new HTTPError("not found", 404);
|
||||
const path = join(process.cwd(), "assets", "public", `${hash}.png`);
|
||||
const path = join(
|
||||
__dirname,
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"assets",
|
||||
"public",
|
||||
`${hash}.png`,
|
||||
);
|
||||
|
||||
const file = await getFile(path);
|
||||
if (!file) throw new HTTPError("not found", 404);
|
||||
@ -80,7 +88,15 @@ router.get("/group-avatars/:id", async (req: Request, res: Response) => {
|
||||
id = id.split(".")[0]; // remove .file extension
|
||||
const hash = defaultGroupDMAvatarHashMap.get(id);
|
||||
if (!hash) throw new HTTPError("not found", 404);
|
||||
const path = join(process.cwd(), "assets", "public", `${hash}.png`);
|
||||
const path = join(
|
||||
__dirname,
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"assets",
|
||||
"public",
|
||||
`${hash}.png`,
|
||||
);
|
||||
|
||||
const file = await getFile(path);
|
||||
if (!file) throw new HTTPError("not found", 404);
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -47,13 +47,15 @@ export default class BattleNetConnection extends Connection {
|
||||
settings: BattleNetSettings = new BattleNetSettings();
|
||||
|
||||
init(): void {
|
||||
const settings =
|
||||
ConnectionLoader.getConnectionConfig<BattleNetSettings>(
|
||||
this.id,
|
||||
this.settings,
|
||||
);
|
||||
this.settings = ConnectionLoader.getConnectionConfig<BattleNetSettings>(
|
||||
this.id,
|
||||
this.settings,
|
||||
);
|
||||
|
||||
if (settings.enabled && (!settings.clientId || !settings.clientSecret))
|
||||
if (
|
||||
this.settings.enabled &&
|
||||
(!this.settings.clientId || !this.settings.clientSecret)
|
||||
)
|
||||
throw new Error(`Invalid settings for connection ${this.id}`);
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -43,12 +43,15 @@ export default class DiscordConnection extends Connection {
|
||||
settings: DiscordSettings = new DiscordSettings();
|
||||
|
||||
init(): void {
|
||||
const settings = ConnectionLoader.getConnectionConfig<DiscordSettings>(
|
||||
this.settings = ConnectionLoader.getConnectionConfig<DiscordSettings>(
|
||||
this.id,
|
||||
this.settings,
|
||||
);
|
||||
|
||||
if (settings.enabled && (!settings.clientId || !settings.clientSecret))
|
||||
if (
|
||||
this.settings.enabled &&
|
||||
(!this.settings.clientId || !this.settings.clientSecret)
|
||||
)
|
||||
throw new Error(`Invalid settings for connection ${this.id}`);
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -53,13 +53,15 @@ export default class EpicGamesConnection extends Connection {
|
||||
settings: EpicGamesSettings = new EpicGamesSettings();
|
||||
|
||||
init(): void {
|
||||
const settings =
|
||||
ConnectionLoader.getConnectionConfig<EpicGamesSettings>(
|
||||
this.id,
|
||||
this.settings,
|
||||
);
|
||||
this.settings = ConnectionLoader.getConnectionConfig<EpicGamesSettings>(
|
||||
this.id,
|
||||
this.settings,
|
||||
);
|
||||
|
||||
if (settings.enabled && (!settings.clientId || !settings.clientSecret))
|
||||
if (
|
||||
this.settings.enabled &&
|
||||
(!this.settings.clientId || !this.settings.clientSecret)
|
||||
)
|
||||
throw new Error(`Invalid settings for connection ${this.id}`);
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -52,12 +52,15 @@ export default class FacebookConnection extends Connection {
|
||||
settings: FacebookSettings = new FacebookSettings();
|
||||
|
||||
init(): void {
|
||||
const settings = ConnectionLoader.getConnectionConfig<FacebookSettings>(
|
||||
this.settings = ConnectionLoader.getConnectionConfig<FacebookSettings>(
|
||||
this.id,
|
||||
this.settings,
|
||||
);
|
||||
|
||||
if (settings.enabled && (!settings.clientId || !settings.clientSecret))
|
||||
if (
|
||||
this.settings.enabled &&
|
||||
(!this.settings.clientId || !this.settings.clientSecret)
|
||||
)
|
||||
throw new Error(`Invalid settings for connection ${this.id}`);
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -42,12 +42,15 @@ export default class GitHubConnection extends Connection {
|
||||
settings: GitHubSettings = new GitHubSettings();
|
||||
|
||||
init(): void {
|
||||
const settings = ConnectionLoader.getConnectionConfig<GitHubSettings>(
|
||||
this.settings = ConnectionLoader.getConnectionConfig<GitHubSettings>(
|
||||
this.id,
|
||||
this.settings,
|
||||
);
|
||||
|
||||
if (settings.enabled && (!settings.clientId || !settings.clientSecret))
|
||||
if (
|
||||
this.settings.enabled &&
|
||||
(!this.settings.clientId || !this.settings.clientSecret)
|
||||
)
|
||||
throw new Error(`Invalid settings for connection ${this.id}`);
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -54,12 +54,15 @@ export default class RedditConnection extends Connection {
|
||||
settings: RedditSettings = new RedditSettings();
|
||||
|
||||
init(): void {
|
||||
const settings = ConnectionLoader.getConnectionConfig<RedditSettings>(
|
||||
this.settings = ConnectionLoader.getConnectionConfig<RedditSettings>(
|
||||
this.id,
|
||||
this.settings,
|
||||
);
|
||||
|
||||
if (settings.enabled && (!settings.clientId || !settings.clientSecret))
|
||||
if (
|
||||
this.settings.enabled &&
|
||||
(!this.settings.clientId || !this.settings.clientSecret)
|
||||
)
|
||||
throw new Error(`Invalid settings for connection ${this.id}`);
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -63,12 +63,16 @@ export default class SpotifyConnection extends RefreshableConnection {
|
||||
* So to prevent spamming the spotify api we disable the ability to refresh.
|
||||
*/
|
||||
this.refreshEnabled = false;
|
||||
const settings = ConnectionLoader.getConnectionConfig<SpotifySettings>(
|
||||
|
||||
this.settings = ConnectionLoader.getConnectionConfig<SpotifySettings>(
|
||||
this.id,
|
||||
this.settings,
|
||||
);
|
||||
|
||||
if (settings.enabled && (!settings.clientId || !settings.clientSecret))
|
||||
if (
|
||||
this.settings.enabled &&
|
||||
(!this.settings.clientId || !this.settings.clientSecret)
|
||||
)
|
||||
throw new Error(`Invalid settings for connection ${this.id}`);
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -55,12 +55,15 @@ export default class TwitchConnection extends RefreshableConnection {
|
||||
settings: TwitchSettings = new TwitchSettings();
|
||||
|
||||
init(): void {
|
||||
const settings = ConnectionLoader.getConnectionConfig<TwitchSettings>(
|
||||
this.settings = ConnectionLoader.getConnectionConfig<TwitchSettings>(
|
||||
this.id,
|
||||
this.settings,
|
||||
);
|
||||
|
||||
if (settings.enabled && (!settings.clientId || !settings.clientSecret))
|
||||
if (
|
||||
this.settings.enabled &&
|
||||
(!this.settings.clientId || !this.settings.clientSecret)
|
||||
)
|
||||
throw new Error(`Invalid settings for connection ${this.id}`);
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -55,12 +55,15 @@ export default class TwitterConnection extends RefreshableConnection {
|
||||
settings: TwitterSettings = new TwitterSettings();
|
||||
|
||||
init(): void {
|
||||
const settings = ConnectionLoader.getConnectionConfig<TwitterSettings>(
|
||||
this.settings = ConnectionLoader.getConnectionConfig<TwitterSettings>(
|
||||
this.id,
|
||||
this.settings,
|
||||
);
|
||||
|
||||
if (settings.enabled && (!settings.clientId || !settings.clientSecret))
|
||||
if (
|
||||
this.settings.enabled &&
|
||||
(!this.settings.clientId || !this.settings.clientSecret)
|
||||
)
|
||||
throw new Error(`Invalid settings for connection ${this.id}`);
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -62,12 +62,15 @@ export default class XboxConnection extends Connection {
|
||||
settings: XboxSettings = new XboxSettings();
|
||||
|
||||
init(): void {
|
||||
const settings = ConnectionLoader.getConnectionConfig<XboxSettings>(
|
||||
this.settings = ConnectionLoader.getConnectionConfig<XboxSettings>(
|
||||
this.id,
|
||||
this.settings,
|
||||
);
|
||||
|
||||
if (settings.enabled && (!settings.clientId || !settings.clientSecret))
|
||||
if (
|
||||
this.settings.enabled &&
|
||||
(!this.settings.clientId || !this.settings.clientSecret)
|
||||
)
|
||||
throw new Error(`Invalid settings for connection ${this.id}`);
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -62,12 +62,15 @@ export default class YoutubeConnection extends Connection {
|
||||
settings: YoutubeSettings = new YoutubeSettings();
|
||||
|
||||
init(): void {
|
||||
const settings = ConnectionLoader.getConnectionConfig<YoutubeSettings>(
|
||||
this.settings = ConnectionLoader.getConnectionConfig<YoutubeSettings>(
|
||||
this.id,
|
||||
this.settings,
|
||||
);
|
||||
|
||||
if (settings.enabled && (!settings.clientId || !settings.clientSecret))
|
||||
if (
|
||||
this.settings.enabled &&
|
||||
(!this.settings.clientId || !this.settings.clientSecret)
|
||||
)
|
||||
throw new Error(`Invalid settings for connection ${this.id}`);
|
||||
}
|
||||
|
||||
|
||||
@ -439,6 +439,10 @@ export async function onIdentify(this: WebSocket, data: Payload) {
|
||||
tutorial: null,
|
||||
session_type: "normal", // TODO
|
||||
auth_session_id_hash: "", // TODO
|
||||
notification_settings: {
|
||||
// ????
|
||||
flags: 0,
|
||||
},
|
||||
};
|
||||
|
||||
// Send READY
|
||||
|
||||
@ -25,6 +25,7 @@ import { SendGridConfiguration } from "./subconfigurations/email/SendGrid";
|
||||
|
||||
export class EmailConfiguration {
|
||||
provider: string | null = null;
|
||||
senderAddress: string | null = null;
|
||||
smtp: SMTPConfiguration = new SMTPConfiguration();
|
||||
mailgun: MailGunConfiguration = new MailGunConfiguration();
|
||||
mailjet: MailJetConfiguration = new MailJetConfiguration();
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -43,7 +43,7 @@ export abstract class Connection {
|
||||
*/
|
||||
getRedirectUri() {
|
||||
const endpointPublic =
|
||||
Config.get().api.endpointPublic ?? "http://localhost:3001";
|
||||
Config.get().general.frontPage ?? "http://localhost:3001";
|
||||
return `${endpointPublic}/connections/${this.id}/callback`;
|
||||
}
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ import path from "path";
|
||||
import { ConnectionConfig } from "./ConnectionConfig";
|
||||
import { ConnectionStore } from "./ConnectionStore";
|
||||
|
||||
const root = "dist/connections";
|
||||
const root = path.join(__dirname, "..", "..", "connections");
|
||||
const connectionsLoaded = false;
|
||||
|
||||
export class ConnectionLoader {
|
||||
|
||||
@ -24,6 +24,7 @@ export class MinimalPublicUserDTO {
|
||||
id: string;
|
||||
public_flags: number;
|
||||
username: string;
|
||||
badge_ids?: string[] | null;
|
||||
|
||||
constructor(user: User) {
|
||||
this.avatar = user.avatar;
|
||||
@ -31,5 +32,6 @@ export class MinimalPublicUserDTO {
|
||||
this.id = user.id;
|
||||
this.public_flags = user.public_flags;
|
||||
this.username = user.username;
|
||||
this.badge_ids = user.badge_ids;
|
||||
}
|
||||
}
|
||||
|
||||
35
src/util/entities/Badge.ts
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Column, Entity } from "typeorm";
|
||||
import { BaseClassWithoutId } from "./BaseClass";
|
||||
|
||||
@Entity("badges")
|
||||
export class Badge extends BaseClassWithoutId {
|
||||
@Column({ primary: true })
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
description: string;
|
||||
|
||||
@Column()
|
||||
icon: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
link?: string;
|
||||
}
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -105,7 +105,7 @@ export class Channel extends BaseClass {
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((channel: Channel) => channel.parent)
|
||||
parent_id: string;
|
||||
parent_id: string | null;
|
||||
|
||||
@JoinColumn({ name: "parent_id" })
|
||||
@ManyToOne(() => Channel)
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -31,7 +31,7 @@ import {
|
||||
PrimaryGeneratedColumn,
|
||||
RelationId,
|
||||
} from "typeorm";
|
||||
import { Ban, PublicGuildRelations } from ".";
|
||||
import { Ban, Channel, PublicGuildRelations } from ".";
|
||||
import { ReadyGuildDTO } from "../dtos";
|
||||
import {
|
||||
GuildCreateEvent,
|
||||
@ -330,6 +330,13 @@ export class Member extends BaseClassWithoutId {
|
||||
relationLoadStrategy: "query",
|
||||
});
|
||||
|
||||
for await (const channel of guild.channels) {
|
||||
channel.position = await Channel.calculatePosition(
|
||||
channel.id,
|
||||
guild_id,
|
||||
);
|
||||
}
|
||||
|
||||
const memberCount = await Member.count({ where: { guild_id } });
|
||||
|
||||
const memberPreview = (
|
||||
|
||||
@ -192,8 +192,8 @@ export class Message extends BaseClass {
|
||||
party_id: string;
|
||||
};
|
||||
|
||||
@Column({ nullable: true })
|
||||
flags?: number;
|
||||
@Column({ default: 0 })
|
||||
flags: number;
|
||||
|
||||
@Column({ type: "simple-json", nullable: true })
|
||||
message_reference?: {
|
||||
@ -252,6 +252,7 @@ export class Message extends BaseClass {
|
||||
activity: this.activity ?? undefined,
|
||||
application: this.application ?? undefined,
|
||||
components: this.components ?? undefined,
|
||||
poll: this.poll ?? undefined,
|
||||
content: this.content ?? "",
|
||||
};
|
||||
}
|
||||
@ -263,6 +264,7 @@ export interface MessageComponent {
|
||||
label?: string;
|
||||
emoji?: PartialEmoji;
|
||||
custom_id?: string;
|
||||
sku_id?: string;
|
||||
url?: string;
|
||||
disabled?: boolean;
|
||||
components: MessageComponent[];
|
||||
@ -341,3 +343,32 @@ export interface AllowedMentions {
|
||||
users?: string[];
|
||||
replied_user?: boolean;
|
||||
}
|
||||
|
||||
export interface Poll {
|
||||
question: PollMedia;
|
||||
answers: PollAnswer[];
|
||||
expiry: Date;
|
||||
allow_multiselect: boolean;
|
||||
results?: PollResult;
|
||||
}
|
||||
|
||||
export interface PollMedia {
|
||||
text?: string;
|
||||
emoji?: PartialEmoji;
|
||||
}
|
||||
|
||||
export interface PollAnswer {
|
||||
answer_id?: string;
|
||||
poll_media: PollMedia;
|
||||
}
|
||||
|
||||
export interface PollResult {
|
||||
is_finalized: boolean;
|
||||
answer_counts: PollAnswerCount[];
|
||||
}
|
||||
|
||||
export interface PollAnswerCount {
|
||||
id: string;
|
||||
count: number;
|
||||
me_voted: boolean;
|
||||
}
|
||||
|
||||
@ -49,6 +49,7 @@ export enum PublicUserEnum {
|
||||
premium_type,
|
||||
theme_colors,
|
||||
pronouns,
|
||||
badge_ids,
|
||||
}
|
||||
export type PublicUserKeys = keyof typeof PublicUserEnum;
|
||||
|
||||
@ -231,6 +232,9 @@ export class User extends BaseClass {
|
||||
@OneToMany(() => SecurityKey, (key: SecurityKey) => key.user)
|
||||
security_keys: SecurityKey[];
|
||||
|
||||
@Column({ type: "simple-array", nullable: true })
|
||||
badge_ids?: string[];
|
||||
|
||||
// TODO: I don't like this method?
|
||||
validate() {
|
||||
if (this.discriminator) {
|
||||
|
||||
@ -63,6 +63,9 @@ export class UserSettings extends BaseClassWithoutId {
|
||||
@Column({ nullable: true })
|
||||
explicit_content_filter: number = 0;
|
||||
|
||||
@Column({ nullable: true })
|
||||
friend_discovery_flags: number = 0;
|
||||
|
||||
@Column({ nullable: true, type: "simple-json" })
|
||||
friend_source_flags: FriendSourceFlags = { all: true };
|
||||
|
||||
@ -116,6 +119,9 @@ export class UserSettings extends BaseClassWithoutId {
|
||||
|
||||
@Column({ nullable: true })
|
||||
timezone_offset: number = 0; // e.g -60
|
||||
|
||||
@Column({ nullable: true })
|
||||
view_nsfw_guilds: boolean = true;
|
||||
}
|
||||
|
||||
interface CustomStatus {
|
||||
|
||||
@ -20,6 +20,7 @@ export * from "./Application";
|
||||
export * from "./Attachment";
|
||||
export * from "./AuditLog";
|
||||
export * from "./BackupCodes";
|
||||
export * from "./Badge";
|
||||
export * from "./Ban";
|
||||
export * from "./BaseClass";
|
||||
export * from "./Categories";
|
||||
|
||||
@ -129,6 +129,9 @@ export interface ReadyEventData {
|
||||
| "REQUIRE_CAPTCHA" // TODO: allow these to be triggered
|
||||
| "TOS_UPDATE_ACKNOWLEDGMENT"
|
||||
| "AGREEMENTS";
|
||||
notification_settings: {
|
||||
flags: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ReadyEvent extends Event {
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MessageFlagsNotNull1713116476900 implements MigrationInterface {
|
||||
name = "MessageFlagsNotNull1713116476900";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `messages` CHANGE flags flags_old integer;",
|
||||
);
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `messages` ADD flags integer NOT NULL DEFAULT 0;",
|
||||
);
|
||||
await queryRunner.query(
|
||||
"UPDATE `messages` SET flags = IFNULL(flags_old, 0);",
|
||||
);
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `messages` DROP COLUMN flags_old;",
|
||||
);
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {
|
||||
// dont care
|
||||
throw new Error("Migration down is not implemented.");
|
||||
}
|
||||
}
|
||||
23
src/util/migration/mariadb/1719776735000-newUserSettings.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class NewUserSettings1719776735000 implements MigrationInterface {
|
||||
name = "NewUserSettings1719776735000";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `user_settings` ADD friend_discovery_flags integer NULL DEFAULT 0;",
|
||||
);
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `user_settings` ADD view_nsfw_guilds tinyint NULL DEFAULT 1;",
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `user_settings` DROP COLUMN friend_discovery_flags;",
|
||||
);
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `user_settings` DROP COLUMN view_nsfw_guilds;",
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MessagePollObject1720157926878 implements MigrationInterface {
|
||||
name = "MessagePollObject1720157926878";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query("ALTER TABLE `messages` ADD `poll` text NULL");
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `poll`");
|
||||
}
|
||||
}
|
||||
21
src/util/migration/mariadb/1720628601997-badges.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class Badges1720628601997 implements MigrationInterface {
|
||||
name = "Badges1720628601997";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE \`badges\` (\`id\` varchar(255) NOT NULL, \`description\` varchar(255) NOT NULL, \`icon\` varchar(255) NOT NULL, \`link\` varchar(255) NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE \`users\` ADD \`badge_ids\` text NULL`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE \`users\` DROP COLUMN \`badge_ids\``,
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE \`badges\``);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MessageFlagsNotNull1713116476900 implements MigrationInterface {
|
||||
name = "MessageFlagsNotNull1713116476900";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `messages` CHANGE flags flags_old integer;",
|
||||
);
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `messages` ADD flags integer NOT NULL DEFAULT 0;",
|
||||
);
|
||||
await queryRunner.query(
|
||||
"UPDATE `messages` SET flags = IFNULL(flags_old, 0);",
|
||||
);
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `messages` DROP COLUMN flags_old;",
|
||||
);
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {
|
||||
// dont care
|
||||
throw new Error("Migration down is not implemented.");
|
||||
}
|
||||
}
|
||||
23
src/util/migration/mysql/1719776735000-newUserSettings.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class NewUserSettings1719776735000 implements MigrationInterface {
|
||||
name = "NewUserSettings1719776735000";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `user_settings` ADD friend_discovery_flags integer NULL DEFAULT 0;",
|
||||
);
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `user_settings` ADD view_nsfw_guilds tinyint NULL DEFAULT 1;",
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `user_settings` DROP COLUMN friend_discovery_flags;",
|
||||
);
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `user_settings` DROP COLUMN view_nsfw_guilds;",
|
||||
);
|
||||
}
|
||||
}
|
||||
13
src/util/migration/mysql/1720157926878-messagePollObject.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MessagePollObject1720157926878 implements MigrationInterface {
|
||||
name = "MessagePollObject1720157926878";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query("ALTER TABLE `messages` ADD `poll` text NULL");
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `poll`");
|
||||
}
|
||||
}
|
||||
21
src/util/migration/mysql/1720628601997-badges.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class Badges1720628601997 implements MigrationInterface {
|
||||
name = "Badges1720628601997";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE \`badges\` (\`id\` varchar(255) NOT NULL, \`description\` varchar(255) NOT NULL, \`icon\` varchar(255) NOT NULL, \`link\` varchar(255) NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE \`users\` ADD \`badge_ids\` text NULL`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE \`users\` DROP COLUMN \`badge_ids\``,
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE \`badges\``);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MessageFlagsNotNull1713116476900 implements MigrationInterface {
|
||||
name = "MessageFlagsNotNull1713116476900";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE messages RENAME COLUMN flags TO flags_old;",
|
||||
);
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE messages ADD COLUMN flags integer NOT NULL DEFAULT 0;",
|
||||
);
|
||||
await queryRunner.query(
|
||||
"UPDATE messages SET flags = COALESCE(flags_old, 0);",
|
||||
);
|
||||
await queryRunner.query("ALTER TABLE messages DROP COLUMN flags_old;");
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {
|
||||
// dont care
|
||||
throw new Error("Migration down is not implemented.");
|
||||
}
|
||||
}
|
||||
23
src/util/migration/postgres/1719776735000-newUserSettings.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class NewUserSettings1719776735000 implements MigrationInterface {
|
||||
name = "NewUserSettings1719776735000";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE user_settings ADD COLUMN friend_discovery_flags integer DEFAULT 0;",
|
||||
);
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE user_settings ADD COLUMN view_nsfw_guilds boolean DEFAULT true;",
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE user_settings DROP COLUMN friend_discovery_flags;",
|
||||
);
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE user_settings DROP COLUMN view_nsfw_guilds;",
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MessagePollObject1720157926878 implements MigrationInterface {
|
||||
name = "MessagePollObject1720157926878";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query("ALTER TABLE messages ADD poll text NULL");
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query("ALTER TABLE messages DROP COLUMN poll");
|
||||
}
|
||||
}
|
||||
16
src/util/migration/postgres/1720628601997-badges.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class Badges1720628601997 implements MigrationInterface {
|
||||
name = "Badges1720628601997";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "badges" ("id" character varying NOT NULL, "description" character varying NOT NULL, "icon" character varying NOT NULL, "link" character varying, CONSTRAINT "PK_8a651318b8de577e8e217676466" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(`ALTER TABLE "users" ADD "badge_ids" text`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "badge_ids"`);
|
||||
}
|
||||
}
|
||||
22
src/util/schemas/BulkBanSchema.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export interface BulkBanSchema {
|
||||
user_ids: string[];
|
||||
delete_message_seconds?: number;
|
||||
}
|
||||