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
|
1
.gitignore
vendored
@ -19,3 +19,4 @@ build
|
||||
*.tmp
|
||||
tmp/
|
||||
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"
|
||||
|
||||
# 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,7 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<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" />
|
||||
@ -23,32 +22,39 @@
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
</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>
|
||||
@ -65,38 +71,50 @@
|
||||
{locationCountryName}
|
||||
</p>
|
||||
<div>
|
||||
<div style="
|
||||
<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>
|
||||
"
|
||||
>Verify Login</a
|
||||
>
|
||||
</div>
|
||||
<hr />
|
||||
<div style="
|
||||
<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>
|
||||
<a
|
||||
href="{actionUrl}"
|
||||
target="_blank"
|
||||
style="word-wrap: break-word"
|
||||
>{actionUrl}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</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,7 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<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" />
|
||||
@ -23,32 +22,39 @@
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
</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>
|
||||
@ -57,22 +63,28 @@
|
||||
ignore this email.
|
||||
</p>
|
||||
<div>
|
||||
<div style="
|
||||
<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>
|
||||
"
|
||||
>Reset Password</a
|
||||
>
|
||||
</div>
|
||||
<hr />
|
||||
<div style="text-align: center">
|
||||
@ -80,11 +92,15 @@
|
||||
Alternatively, you can directly paste this link into
|
||||
your browser:
|
||||
</p>
|
||||
<a href="{actionUrl}" target="_blank" style="word-wrap: break-word;">{actionUrl}</a>
|
||||
<a
|
||||
href="{actionUrl}"
|
||||
target="_blank"
|
||||
style="word-wrap: break-word"
|
||||
>{actionUrl}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</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,7 +10,7 @@
|
||||
"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.",
|
||||
"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"
|
||||
}
|
||||
|
1257
assets/openapi.json
@ -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>
|
126551
assets/schemas.json
@ -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" });
|
||||
// };
|
||||
}
|
61
flake.lock
Normal file
@ -0,0 +1,61 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1705309234,
|
||||
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1708118438,
|
||||
"narHash": "sha256-kk9/0nuVgA220FcqH/D2xaN6uGyHp/zoxPNUmPCMmEE=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "5863c27340ba4de8f83e7e3c023b9599c3cb3c80",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
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
|
501
package-lock.json
generated
@ -38,10 +38,12 @@
|
||||
"morgan": "^1.10.0",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"murmurhash-js": "^1.0.0",
|
||||
"mysql": "*",
|
||||
"node-2fa": "^2.0.3",
|
||||
"node-fetch": "^2.6.12",
|
||||
"node-os-utils": "^1.3.7",
|
||||
"nodemailer": "^6.9.4",
|
||||
"pg": "*",
|
||||
"picocolors": "^1.0.0",
|
||||
"probe-image-size": "^7.2.3",
|
||||
"proxy-agent": "^6.3.0",
|
||||
@ -72,9 +74,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",
|
||||
@ -83,9 +85,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"
|
||||
}
|
||||
},
|
||||
@ -952,9 +956,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
|
||||
"integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
|
||||
"integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.4",
|
||||
@ -997,9 +1001,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "8.47.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz",
|
||||
"integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==",
|
||||
"version": "8.56.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
|
||||
"integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@ -1017,13 +1021,13 @@
|
||||
"integrity": "sha512-PdUmzpvcUM3Rh39kvz9RdbPVYhMjBjdV7Suw7ZduP7urRLsZR8l5tzgSWKm7TExwBYDFwTnYrZbnE0rQ3N5NLQ=="
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
|
||||
"integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==",
|
||||
"version": "0.11.14",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
||||
"integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@humanwhocodes/object-schema": "^1.2.1",
|
||||
"debug": "^4.1.1",
|
||||
"@humanwhocodes/object-schema": "^2.0.2",
|
||||
"debug": "^4.3.1",
|
||||
"minimatch": "^3.0.5"
|
||||
},
|
||||
"engines": {
|
||||
@ -1044,9 +1048,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/object-schema": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
|
||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
|
||||
"integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
@ -2210,9 +2214,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz",
|
||||
"integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==",
|
||||
"version": "7.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz",
|
||||
"integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/send": {
|
||||
@ -2263,32 +2267,33 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
|
||||
"integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
|
||||
"integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "5.62.0",
|
||||
"@typescript-eslint/type-utils": "5.62.0",
|
||||
"@typescript-eslint/utils": "5.62.0",
|
||||
"@eslint-community/regexpp": "^4.5.1",
|
||||
"@typescript-eslint/scope-manager": "6.21.0",
|
||||
"@typescript-eslint/type-utils": "6.21.0",
|
||||
"@typescript-eslint/utils": "6.21.0",
|
||||
"@typescript-eslint/visitor-keys": "6.21.0",
|
||||
"debug": "^4.3.4",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.2.0",
|
||||
"natural-compare-lite": "^1.4.0",
|
||||
"semver": "^7.3.7",
|
||||
"tsutils": "^3.21.0"
|
||||
"ignore": "^5.2.4",
|
||||
"natural-compare": "^1.4.0",
|
||||
"semver": "^7.5.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
"@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha",
|
||||
"eslint": "^7.0.0 || ^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
@ -2297,25 +2302,26 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz",
|
||||
"integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz",
|
||||
"integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.62.0",
|
||||
"@typescript-eslint/types": "5.62.0",
|
||||
"@typescript-eslint/typescript-estree": "5.62.0",
|
||||
"@typescript-eslint/scope-manager": "6.21.0",
|
||||
"@typescript-eslint/types": "6.21.0",
|
||||
"@typescript-eslint/typescript-estree": "6.21.0",
|
||||
"@typescript-eslint/visitor-keys": "6.21.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
"eslint": "^7.0.0 || ^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
@ -2324,16 +2330,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz",
|
||||
"integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==",
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz",
|
||||
"integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.62.0",
|
||||
"@typescript-eslint/visitor-keys": "5.62.0"
|
||||
"@typescript-eslint/types": "6.21.0",
|
||||
"@typescript-eslint/visitor-keys": "6.21.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@ -2341,25 +2347,25 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz",
|
||||
"integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==",
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz",
|
||||
"integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "5.62.0",
|
||||
"@typescript-eslint/utils": "5.62.0",
|
||||
"@typescript-eslint/typescript-estree": "6.21.0",
|
||||
"@typescript-eslint/utils": "6.21.0",
|
||||
"debug": "^4.3.4",
|
||||
"tsutils": "^3.21.0"
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "*"
|
||||
"eslint": "^7.0.0 || ^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
@ -2368,12 +2374,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz",
|
||||
"integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==",
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz",
|
||||
"integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@ -2381,21 +2387,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz",
|
||||
"integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==",
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz",
|
||||
"integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.62.0",
|
||||
"@typescript-eslint/visitor-keys": "5.62.0",
|
||||
"@typescript-eslint/types": "6.21.0",
|
||||
"@typescript-eslint/visitor-keys": "6.21.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
"semver": "^7.3.7",
|
||||
"tsutils": "^3.21.0"
|
||||
"minimatch": "9.0.3",
|
||||
"semver": "^7.5.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@ -2407,49 +2414,78 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz",
|
||||
"integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==",
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@types/semver": "^7.3.12",
|
||||
"@typescript-eslint/scope-manager": "5.62.0",
|
||||
"@typescript-eslint/types": "5.62.0",
|
||||
"@typescript-eslint/typescript-estree": "5.62.0",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"semver": "^7.3.7"
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
|
||||
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz",
|
||||
"integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@types/json-schema": "^7.0.12",
|
||||
"@types/semver": "^7.5.0",
|
||||
"@typescript-eslint/scope-manager": "6.21.0",
|
||||
"@typescript-eslint/types": "6.21.0",
|
||||
"@typescript-eslint/typescript-estree": "6.21.0",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
"eslint": "^7.0.0 || ^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz",
|
||||
"integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==",
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz",
|
||||
"integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.62.0",
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
"@typescript-eslint/types": "6.21.0",
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@ungap/structured-clone": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
|
||||
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
@ -2964,6 +3000,15 @@
|
||||
"resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz",
|
||||
"integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg=="
|
||||
},
|
||||
"node_modules/buffer-writer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
|
||||
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||
@ -3845,18 +3890,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "8.47.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz",
|
||||
"integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==",
|
||||
"version": "8.56.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
|
||||
"integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
"@eslint/eslintrc": "^2.1.2",
|
||||
"@eslint/js": "^8.47.0",
|
||||
"@humanwhocodes/config-array": "^0.11.10",
|
||||
"@eslint/eslintrc": "^2.1.4",
|
||||
"@eslint/js": "8.56.0",
|
||||
"@humanwhocodes/config-array": "^0.11.13",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@nodelib/fs.walk": "^1.2.8",
|
||||
"@ungap/structured-clone": "^1.2.0",
|
||||
"ajv": "^6.12.4",
|
||||
"chalk": "^4.0.0",
|
||||
"cross-spawn": "^7.0.2",
|
||||
@ -3898,19 +3944,6 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-scope": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esrecurse": "^4.3.0",
|
||||
"estraverse": "^4.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-visitor-keys": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
|
||||
@ -4045,7 +4078,7 @@
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
|
||||
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
|
||||
"devOptional": true,
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
@ -4620,9 +4653,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "13.21.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
|
||||
"integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==",
|
||||
"version": "13.24.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
|
||||
"integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"type-fest": "^0.20.2"
|
||||
@ -5704,6 +5737,60 @@
|
||||
"resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
|
||||
"integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw=="
|
||||
},
|
||||
"node_modules/mysql": {
|
||||
"version": "2.18.1",
|
||||
"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
|
||||
"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"bignumber.js": "9.0.0",
|
||||
"readable-stream": "2.3.7",
|
||||
"safe-buffer": "5.1.2",
|
||||
"sqlstring": "2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mysql/node_modules/bignumber.js": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
|
||||
"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/mysql/node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/mysql/node_modules/readable-stream": {
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/mysql/node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mz": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
||||
@ -5726,12 +5813,6 @@
|
||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/natural-compare-lite": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
|
||||
"integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/needle": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz",
|
||||
@ -6262,6 +6343,12 @@
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/packet-reader": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
|
||||
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@ -6357,6 +6444,96 @@
|
||||
"url": "https://github.com/sponsors/Borewit"
|
||||
}
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz",
|
||||
"integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"buffer-writer": "2.0.0",
|
||||
"packet-reader": "1.0.0",
|
||||
"pg-connection-string": "^2.6.2",
|
||||
"pg-pool": "^3.6.1",
|
||||
"pg-protocol": "^1.6.0",
|
||||
"pg-types": "^2.1.0",
|
||||
"pgpass": "1.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"pg-cloudflare": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg-native": ">=3.0.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"pg-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pg-cloudflare": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
|
||||
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/pg-connection-string": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz",
|
||||
"integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/pg-int8": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-pool": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz",
|
||||
"integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==",
|
||||
"optional": true,
|
||||
"peerDependencies": {
|
||||
"pg": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-protocol": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
|
||||
"integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/pg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"pg-int8": "1.0.1",
|
||||
"postgres-array": "~2.0.0",
|
||||
"postgres-bytea": "~1.0.0",
|
||||
"postgres-date": "~1.0.4",
|
||||
"postgres-interval": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pgpass": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"split2": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
@ -6389,6 +6566,45 @@
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-bytea": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
||||
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-date": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-interval": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@ -7091,6 +7307,15 @@
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/sqlite3": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.6.tgz",
|
||||
@ -7120,6 +7345,15 @@
|
||||
"integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/sqlstring": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
|
||||
"integrity": "sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ssri": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
|
||||
@ -7688,6 +7922,18 @@
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"node_modules/ts-api-utils": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz",
|
||||
"integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
@ -7735,27 +7981,6 @@
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz",
|
||||
"integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig=="
|
||||
},
|
||||
"node_modules/tsutils": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
|
||||
"integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^1.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
|
||||
}
|
||||
},
|
||||
"node_modules/tsutils/node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
|
@ -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",
|
||||
},
|
||||
|
@ -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({
|
||||
|
@ -56,6 +56,7 @@ router.post(
|
||||
edited_timestamp: null,
|
||||
flags: 1,
|
||||
components: [],
|
||||
poll: {},
|
||||
}).status(200);
|
||||
},
|
||||
);
|
||||
|
@ -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,
|
||||
|
@ -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}`;
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -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;
|
@ -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;
|
@ -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);
|
||||
},
|
||||
|
@ -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({
|
||||
|
@ -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,
|
||||
@ -185,7 +190,6 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
otherwise backfilling won't work **/
|
||||
message.type = MessageType.REPLY;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: stickers/activity
|
||||
if (
|
||||
@ -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 } });
|
||||
|
@ -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,
|
||||
);
|
||||
|
||||
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(required)) {
|
||||
if (!req.permission!.has(new Permissions(perm))) {
|
||||
throw DiscordApiErrors.MISSING_PERMISSIONS.withParams(
|
||||
opts.permission as string,
|
||||
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);
|
||||
|
@ -47,13 +47,15 @@ export default class BattleNetConnection extends Connection {
|
||||
settings: BattleNetSettings = new BattleNetSettings();
|
||||
|
||||
init(): void {
|
||||
const settings =
|
||||
ConnectionLoader.getConnectionConfig<BattleNetSettings>(
|
||||
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}`);
|
||||
}
|
||||
|
||||
|
@ -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}`);
|
||||
}
|
||||
|
||||
|
@ -53,13 +53,15 @@ export default class EpicGamesConnection extends Connection {
|
||||
settings: EpicGamesSettings = new EpicGamesSettings();
|
||||
|
||||
init(): void {
|
||||
const settings =
|
||||
ConnectionLoader.getConnectionConfig<EpicGamesSettings>(
|
||||
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}`);
|
||||
}
|
||||
|
||||
|
@ -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}`);
|
||||
}
|
||||
|
||||
|
@ -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}`);
|
||||
}
|
||||
|
||||
|
@ -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}`);
|
||||
}
|
||||
|
||||
|
@ -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}`);
|
||||
}
|
||||
|
||||
|
@ -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}`);
|
||||
}
|
||||
|
||||
|
@ -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}`);
|
||||
}
|
||||
|
||||
|
@ -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}`);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|