Merge branch 'dev/cherry-plugins-improvements' into staging

This commit is contained in:
TheArcaneBrony
2022-08-23 19:02:05 +02:00
392 changed files with 6012 additions and 12196 deletions
+55
View File
@@ -0,0 +1,55 @@
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=$(git hash-object -t tree /dev/null)
fi
# If you want to allow non-ASCII filenames set this variable to true.
allownonascii=$(git config --type=bool hooks.allownonascii)
# Redirect output to stderr.
exec 1>&2
# Cross platform projects tend to avoid non-ASCII filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$allownonascii" != "true" ] &&
# Note that the use of brackets around a tr range is ok here, (it's
# even required, for portability to Solaris 10's /usr/bin/tr), since
# the square bracket bytes happen to fall in the designated range.
test $(git diff --cached --name-only --diff-filter=A -z $against |
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
cat <<\EOF
Error: Attempt to add a non-ASCII file name.
This can cause problems if you want to work with people on other platforms.
To be portable it is advisable to rename the file.
If you know what you are doing you can disable this check using:
git config hooks.allownonascii true
EOF
exit 1
fi
rm -rf dist
#npx prettier -w .
npx --yes -p prettier@latest -p pretty-quick pretty-quick
git update-index --again
# If there are whitespace errors, print the offending file names and fail.
#exec git diff-index --check --cached $against --
+9 -9
View File
@@ -1,11 +1,11 @@
blank_issues_enabled: true
contact_links:
- name: Fosscord Documentation
url: https://docs.fosscord.com/
about: Need documentation and examples for the Fosscord? Head over to Fosscord's official documentation.
- name: Discord's Developer Documentation
url: https://discord.com/developers/docs/intro
about: Need help with the Discord resources? Head here instead of asking on Fosscord!
- name: Fosscord' Official Discord server
url: https://discord.com/invite/Ms5Ev7S6bF
about: Need help with the server? Talk with us in our official server.
- name: Fosscord Documentation
url: https://docs.fosscord.com/
about: Need documentation and examples for the Fosscord? Head over to Fosscord's official documentation.
- name: Discord's Developer Documentation
url: https://discord.com/developers/docs/intro
about: Need help with the Discord resources? Head here instead of asking on Fosscord!
- name: Fosscord' Official Discord server
url: https://discord.com/invite/Ms5Ev7S6bF
about: Need help with the server? Talk with us in our official server.
+8 -4
View File
@@ -1,13 +1,17 @@
## Notes
## Additions
-
-
## Fixes
-
## Download
- [Windows]()
- [MacOS]()
- [Linux]()
- [Windows]()
- [MacOS]()
- [Linux]()
After (extracting) and starting the server executable you can access your own Fosscord server on http://localhost:3001/
+12 -1
View File
@@ -19,5 +19,16 @@ api/assets/plugins/*.js
bundle/depclean.*
*.tmp
tmp/
assets/cache/
*.generated
initial.json
.yarn/cache
build.json
assets/cache/
yarn.lock
.yarn/install-state.gz
dbconf.json
migrations.db
+4 -2
View File
@@ -2,5 +2,7 @@
"tabWidth": 4,
"useTabs": true,
"printWidth": 140,
"trailingComma": "none"
}
"trailingComma": "none",
"pluginSearchDirs": ["./node_modules"],
"plugins": ["prettier-plugin-organize-imports"]
}
+12 -16
View File
@@ -1,17 +1,13 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${file}",
"outFiles": [
"${workspaceFolder}/**/*.js"
]
}
]
}
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": ["<node_internals>/**"],
"program": "${file}",
"outFiles": ["${workspaceFolder}/**/*.js"]
}
]
}
+783
View File
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-3.2.2.cjs
+45 -45
View File
@@ -1,47 +1,47 @@
const localStorage = window.localStorage;
// TODO: remote auth
// window.GLOBAL_ENV.REMOTE_AUTH_ENDPOINT = window.GLOBAL_ENV.GATEWAY_ENDPOINT.replace(/wss?:/, "");
localStorage.setItem("gatewayURL", window.GLOBAL_ENV.GATEWAY_ENDPOINT);
localStorage.setItem(
"DeveloperOptionsStore",
`{"trace":false,"canary":false,"logGatewayEvents":true,"logOverlayEvents":true,"logAnalyticsEvents":true,"sourceMapsEnabled":false,"axeEnabled":false}`
);
const localStorage = window.localStorage;
// TODO: remote auth
// window.GLOBAL_ENV.REMOTE_AUTH_ENDPOINT = window.GLOBAL_ENV.GATEWAY_ENDPOINT.replace(/wss?:/, "");
localStorage.setItem("gatewayURL", window.GLOBAL_ENV.GATEWAY_ENDPOINT);
localStorage.setItem(
"DeveloperOptionsStore",
`{"trace":false,"canary":false,"logGatewayEvents":true,"logOverlayEvents":true,"logAnalyticsEvents":true,"sourceMapsEnabled":false,"axeEnabled":false}`
);
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 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(localStorage.getItem("UserSettingsStore"));
if (settings && !supportedLocales.includes(settings.locale)) {
// fix client locale wrong and client not loading at all
settings.locale = "en-US";
localStorage.setItem("UserSettingsStore", JSON.stringify(settings));
}
const settings = JSON.parse(localStorage.getItem("UserSettingsStore"));
if (settings && !supportedLocales.includes(settings.locale)) {
// fix client locale wrong and client not loading at all
settings.locale = "en-US";
localStorage.setItem("UserSettingsStore", JSON.stringify(settings));
}
+37 -39
View File
@@ -1,44 +1,42 @@
<!DOCTYPE html>
<html class="theme-dark" data-theme="dark">
<head>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no" name="viewport" />
<head>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no" name="viewport" />
<link rel="stylesheet" href="/assets/532.03aaeef88460fae60534.css" integrity="" />
<link rel="icon" href="/assets/07dca80a102d4149e9736d4b162cff6f.ico" />
<title>Discord Test Client Developer Portal</title>
<meta charset="utf-8" data-react-helmet="true" />
</head>
<link rel="stylesheet" href="/assets/532.03aaeef88460fae60534.css" integrity="" />
<link rel="icon" href="/assets/07dca80a102d4149e9736d4b162cff6f.ico" />
<title>Discord Test Client Developer Portal</title>
<meta charset="utf-8" data-react-helmet="true" />
</head>
<body>
<div id="app-mount"></div>
<script>
window.GLOBAL_ENV = {
API_VERSION: 9,
API_ENDPOINT: "/api",
WEBAPP_ENDPOINT: "",
CDN_HOST: `${location.hostname}:3003`,
<body>
<div id="app-mount"></div>
<script>
window.GLOBAL_ENV = {
API_VERSION: 9,
API_ENDPOINT: "/api",
WEBAPP_ENDPOINT: "",
CDN_HOST: `${location.hostname}:3003`,
BRAINTREE_KEY: "production_5st77rrc_49pp2rp4phym7387",
STRIPE_KEY: "pk_live_CUQtlpQUF0vufWpnpUmQvcdi",
MARKETING_ENDPOINT: "//discord.com",
RELEASE_CHANNEL: "stable",
ALGOLIA_KEY: "aca0d7082e4e63af5ba5917d5e96bed0"
};
GLOBAL_ENV.MEDIA_PROXY_ENDPOINT = location.protocol + "//" + GLOBAL_ENV.CDN_HOST;
const localStorage = window.localStorage;
// TODO: remote auth
// window.GLOBAL_ENV.REMOTE_AUTH_ENDPOINT = window.GLOBAL_ENV.GATEWAY_ENDPOINT.replace(/wss?:/, "");
localStorage.setItem("gatewayURL", window.GLOBAL_ENV.GATEWAY_ENDPOINT);
localStorage.setItem(
"DeveloperOptionsStore",
`{"trace":false,"canary":false,"logGatewayEvents":true,"logOverlayEvents":true,"logAnalyticsEvents":true,"sourceMapsEnabled":false,"axeEnabled":false}`
);
</script>
<script src="/assets/38f40c32d3c8a2fdf73b.js" integrity=""></script>
<script src="/assets/aa190934324e05fcc35c.js" integrity=""></script>
<script src="/assets/45664a0209e828a528b4.js" integrity=""></script>
</body>
</html>
BRAINTREE_KEY: "production_5st77rrc_49pp2rp4phym7387",
STRIPE_KEY: "pk_live_CUQtlpQUF0vufWpnpUmQvcdi",
MARKETING_ENDPOINT: "//discord.com",
RELEASE_CHANNEL: "stable",
ALGOLIA_KEY: "aca0d7082e4e63af5ba5917d5e96bed0"
};
GLOBAL_ENV.MEDIA_PROXY_ENDPOINT = location.protocol + "//" + GLOBAL_ENV.CDN_HOST;
const localStorage = window.localStorage;
// TODO: remote auth
// window.GLOBAL_ENV.REMOTE_AUTH_ENDPOINT = window.GLOBAL_ENV.GATEWAY_ENDPOINT.replace(/wss?:/, "");
localStorage.setItem("gatewayURL", window.GLOBAL_ENV.GATEWAY_ENDPOINT);
localStorage.setItem(
"DeveloperOptionsStore",
`{"trace":false,"canary":false,"logGatewayEvents":true,"logOverlayEvents":true,"logAnalyticsEvents":true,"sourceMapsEnabled":false,"axeEnabled":false}`
);
</script>
<script src="/assets/38f40c32d3c8a2fdf73b.js" integrity=""></script>
<script src="/assets/aa190934324e05fcc35c.js" integrity=""></script>
<script src="/assets/45664a0209e828a528b4.js" integrity=""></script>
</body>
</html>
+6 -6
View File
@@ -1,12 +1,12 @@
/* replace tos acceptance popup */
#app-mount > div:nth-child(7) > div > div > div.tooltipContent-bqVLWK {
visibility: hidden;
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;
#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 {
+57 -11
View File
@@ -1,4 +1,3 @@
/* loading spinner */
#app-mount > div.app-1q1i1E > div.container-16j22k.fixClipping-3qAKRb > div.content-1-zrf2 > video {
filter: opacity(1);
@@ -11,14 +10,62 @@
}
/* 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/fosscord/fosscord/master/assets-rebrand/svg/Fosscord-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 {
background-image: url(https://raw.githubusercontent.com/fosscord/fosscord/master/assets-rebrand/svg/Fosscord-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 {
#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 */
@@ -40,7 +87,6 @@
/* 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;
}
#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;
}
+76 -78
View File
@@ -1,84 +1,82 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Discord Test Client</title>
<link rel="stylesheet" href="/assets/fosscord.css" />
<link id="logincss" rel="stylesheet" href="/assets/fosscord-login.css" />
<link id="customcss" rel="stylesheet" href="/assets/user.css" />
<!-- inline plugin marker -->
<!-- preload plugin marker -->
</head>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Discord Test Client</title>
<link rel="stylesheet" href="/assets/fosscord.css" />
<link id="logincss" rel="stylesheet" href="/assets/fosscord-login.css" />
<link id="customcss" rel="stylesheet" href="/assets/user.css" />
<!-- inline plugin marker -->
<!-- preload plugin marker -->
</head>
<body>
<div id="app-mount"></div>
<script>
window.__OVERLAY__ = /overlay/.test(location.pathname);
window.__BILLING_STANDALONE__ = /^\/billing/.test(location.pathname);
window.GLOBAL_ENV = {
API_ENDPOINT: "/api",
API_VERSION: 9,
GATEWAY_ENDPOINT: `${location.protocol === "https:" ? "wss://" : "ws://"}${location.host}`,
WEBAPP_ENDPOINT: "",
CDN_HOST: `${location.hostname}:3003`,
ASSET_ENDPOINT: "",
MEDIA_PROXY_ENDPOINT: "https://media.discordapp.net",
WIDGET_ENDPOINT: `//${location.host}/widget`,
INVITE_HOST: `${location.hostname}/invite`,
GUILD_TEMPLATE_HOST: "${location.host}",
GIFT_CODE_HOST: "${location.hostname}",
RELEASE_CHANNEL: "stable",
MARKETING_ENDPOINT: "//discord.com",
BRAINTREE_KEY: "production_5st77rrc_49pp2rp4phym7387",
STRIPE_KEY: "pk_live_CUQtlpQUF0vufWpnpUmQvcdi",
NETWORKING_ENDPOINT: "//router.discordapp.net",
RTC_LATENCY_ENDPOINT: "//${location.hostname}/rtc",
ACTIVITY_APPLICATION_HOST: "discordsays.com",
PROJECT_ENV: "production",
REMOTE_AUTH_ENDPOINT: "//localhost:3020",
SENTRY_TAGS: { buildId: "75e36d9", buildType: "normal" },
MIGRATION_SOURCE_ORIGIN: "https://${location.hostname}",
MIGRATION_DESTINATION_ORIGIN: "https://${location.hostname}",
HTML_TIMESTAMP: Date.now(),
ALGOLIA_KEY: "aca0d7082e4e63af5ba5917d5e96bed0"
};
GLOBAL_ENV.MEDIA_PROXY_ENDPOINT = location.protocol + "//" + GLOBAL_ENV.CDN_HOST;
const localStorage = window.localStorage;
// TODO: remote auth
// window.GLOBAL_ENV.REMOTE_AUTH_ENDPOINT = window.GLOBAL_ENV.GATEWAY_ENDPOINT.replace(/wss?:/, "");
localStorage.setItem("gatewayURL", window.GLOBAL_ENV.GATEWAY_ENDPOINT);
localStorage.setItem(
"DeveloperOptionsStore",
`{"trace":false,"canary":false,"logGatewayEvents":true,"logOverlayEvents":true,"logAnalyticsEvents":true,"sourceMapsEnabled":false,"axeEnabled":false}`
);
<body>
<div id="app-mount"></div>
<script>
window.__OVERLAY__ = /overlay/.test(location.pathname);
window.__BILLING_STANDALONE__ = /^\/billing/.test(location.pathname);
window.GLOBAL_ENV = {
API_ENDPOINT: "/api",
API_VERSION: 9,
GATEWAY_ENDPOINT: `${location.protocol === "https:" ? "wss://" : "ws://"}${location.host}`,
WEBAPP_ENDPOINT: "",
CDN_HOST: `${location.hostname}:3003`,
ASSET_ENDPOINT: "",
MEDIA_PROXY_ENDPOINT: "https://media.discordapp.net",
WIDGET_ENDPOINT: `//${location.host}/widget`,
INVITE_HOST: `${location.hostname}/invite`,
GUILD_TEMPLATE_HOST: "${location.host}",
GIFT_CODE_HOST: "${location.hostname}",
RELEASE_CHANNEL: "stable",
MARKETING_ENDPOINT: "//discord.com",
BRAINTREE_KEY: "production_5st77rrc_49pp2rp4phym7387",
STRIPE_KEY: "pk_live_CUQtlpQUF0vufWpnpUmQvcdi",
NETWORKING_ENDPOINT: "//router.discordapp.net",
RTC_LATENCY_ENDPOINT: "//${location.hostname}/rtc",
ACTIVITY_APPLICATION_HOST: 'discordsays.com',
PROJECT_ENV: "production",
REMOTE_AUTH_ENDPOINT: "//localhost:3020",
SENTRY_TAGS: { buildId: "75e36d9", buildType: "normal" },
MIGRATION_SOURCE_ORIGIN: "https://${location.hostname}",
MIGRATION_DESTINATION_ORIGIN: "https://${location.hostname}",
HTML_TIMESTAMP: Date.now(),
ALGOLIA_KEY: "aca0d7082e4e63af5ba5917d5e96bed0",
};
GLOBAL_ENV.MEDIA_PROXY_ENDPOINT = location.protocol + "//" + GLOBAL_ENV.CDN_HOST;
const localStorage = window.localStorage;
// TODO: remote auth
// window.GLOBAL_ENV.REMOTE_AUTH_ENDPOINT = window.GLOBAL_ENV.GATEWAY_ENDPOINT.replace(/wss?:/, "");
localStorage.setItem("gatewayURL", window.GLOBAL_ENV.GATEWAY_ENDPOINT);
localStorage.setItem(
"DeveloperOptionsStore",
`{"trace":false,"canary":false,"logGatewayEvents":true,"logOverlayEvents":true,"logAnalyticsEvents":true,"sourceMapsEnabled":false,"axeEnabled":false}`
);
setInterval(() => {
let token = JSON.parse(localStorage.getItem("token"));
if (token) {
let logincss = document.querySelector('#logincss'),
canRemove = logincss ? logincss : "";
if (canRemove !== "") {
document.querySelector("#logincss").remove();
canRemove = "";
setInterval(() => {
let token = JSON.parse(localStorage.getItem("token"));
if (token) {
let logincss = document.querySelector("#logincss"),
canRemove = logincss ? logincss : "";
if (canRemove !== "") {
document.querySelector("#logincss").remove();
canRemove = "";
}
}
}, 1000);
const settings = JSON.parse(localStorage.getItem("UserSettingsStore"));
if (settings && settings.locale.length <= 2) {
// fix client locale wrong and client not loading at all
settings.locale = "en-US";
localStorage.setItem("UserSettingsStore", JSON.stringify(settings));
}
}, 1000)
const settings = JSON.parse(localStorage.getItem("UserSettingsStore"));
if (settings && settings.locale.length <= 2) {
// fix client locale wrong and client not loading at all
settings.locale = "en-US";
localStorage.setItem("UserSettingsStore", JSON.stringify(settings));
}
</script>
<script src="/assets/checkLocale.js"></script>
<script src="/assets/2f2e0c25e45eb2f5a6f1.js"></script>
<script src="/assets/006e72c08a4c69cb66fc.js"></script>
<script src="/assets/2f94a3ba801087653a38.js"></script>
<script src="/assets/f7703f092bdbfc607cc7.js"></script>
<!-- plugin marker -->
</body>
</html>
</script>
<script src="/assets/checkLocale.js"></script>
<script src="/assets/2f2e0c25e45eb2f5a6f1.js"></script>
<script src="/assets/006e72c08a4c69cb66fc.js"></script>
<script src="/assets/2f94a3ba801087653a38.js"></script>
<script src="/assets/f7703f092bdbfc607cc7.js"></script>
<!-- plugin marker -->
</body>
</html>
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
/* Your custom CSS goes here, enjoy! */
/* Your custom CSS goes here, enjoy! */
+16
View File
@@ -0,0 +1,16 @@
{
"compiler": "tsc",
"verbose": true,
"writeBuildLog": true,
"writeAnsiBuildLog": true,
"logErrors": true,
"tsc": {
"prettyErrors": true
},
"clean": true,
"quiet": false,
"steps": {
"pre": ["clean"],
"post": ["remap_imports"]
}
}
+1 -2
View File
@@ -1,2 +1 @@
The Docker image is coming with the dashboard. The planned release date is 2022-12-24.
The Docker image is coming with the dashboard. The planned release date is 2022-12-24.
+3 -4
View File
@@ -1,6 +1,5 @@
version: '3.9'
version: "3.9"
services:
fosscord:
entrypoint: [ "npm", "run", "setup" ]
fosscord:
entrypoint: ["npm", "run", "setup"]
+20 -21
View File
@@ -1,25 +1,24 @@
version: '3.9'
version: "3.9"
services:
fosscord:
container_name: fosscord
image: fosscord
restart: on-failure:5
build: .
ports:
- '3001-3005:3001-3005'
volumes:
- ./:/srv/fosscord-server/
environment:
THREADS: ${THREADS:-1}
HTTP_PORT: 3001
WS_PORT: 3002
CDN_PORT: 3003
RTC_PORT: 3004
ADMIN_PORT: 3005
fosscord:
container_name: fosscord
image: fosscord
restart: on-failure:5
build: .
ports:
- "3001-3005:3001-3005"
volumes:
- ./:/srv/fosscord-server/
environment:
THREADS: ${THREADS:-1}
HTTP_PORT: 3001
WS_PORT: 3002
CDN_PORT: 3003
RTC_PORT: 3004
ADMIN_PORT: 3005
networks:
default:
name: fosscord
driver: bridge
default:
name: fosscord
driver: bridge
+16 -16
View File
@@ -1,18 +1,18 @@
#Fosscord Environment Variables:
|NAME|VALUE|DESCRIPTION|
|----|-----|-----------|
|LOG\_REQUESTS | ports to include, or exclude (-) | logs requests |
|PORT|number|sets port number to listen on|
|CDN|string|CDN address|
|GATEWAY|string|Gateway address|
|NODE\_ENV|production/development|sets node environment|
|DATABASE|database url|points to what database to use|
|EVENT\_TRANSMISSION|string|event transmission type|
|STORAGE\_PROVIDER|s3/file|How to store files for CDN|
|STORAGE\_LOCATION|path|Directory to store files in|
|STORAGE\_BUCKET|s3 bucket name|S3 bucket name|
|DB\_UNSAFE|any|Ignores migrations for database, enabled if defined|
|DB\_VERBOSE|any|Log database queries, enabled if defined|
|DB\_MIGRATE|any|Exit fosscord after connecting to and migrating database, used internally|
|LOG\_INVALID\_BODY|any|Log request method, path and body if invalid|
| NAME | VALUE | DESCRIPTION |
| ------------------ | -------------------------------- | ------------------------------------------------------------------------- |
| LOG_REQUESTS | ports to include, or exclude (-) | logs requests |
| PORT | number | sets port number to listen on |
| CDN | string | CDN address |
| GATEWAY | string | Gateway address |
| NODE_ENV | production/development | sets node environment |
| DATABASE | database url | points to what database to use |
| EVENT_TRANSMISSION | string | event transmission type |
| STORAGE_PROVIDER | s3/file | How to store files for CDN |
| STORAGE_LOCATION | path | Directory to store files in |
| STORAGE_BUCKET | s3 bucket name | S3 bucket name |
| DB_UNSAFE | any | Ignores migrations for database, enabled if defined |
| DB_VERBOSE | any | Log database queries, enabled if defined |
| DB_MIGRATE | any | Exit fosscord after connecting to and migrating database, used internally |
| LOG_INVALID_BODY | any | Log request method, path and body if invalid |
+18 -1
View File
@@ -17,5 +17,22 @@
"files.exclude": {
"*.ansi": true,
}
},
"launch": {
"version": "0.2.0",
"configurations": [
{
"command": "npm run start:bundle:vscode-dbg",
"name": "Run Fosscord with debugger",
"request": "launch",
"type": "node-terminal"
},
{
"command": "kitty npm run start:bundle:vscode-dbg",
"name": "Run Fosscord with debugger (kitty)",
"request": "launch",
"type": "node-terminal"
}
]
}
}
}
BIN
View File
Binary file not shown.
+20 -22
View File
@@ -4,18 +4,18 @@
"description": "",
"main": "src/start.js",
"scripts": {
"setup": "npm install --omit optional && ts-patch install -s && patch-package && npm run build",
"depclean": "node scripts/depclean.js",
"depcheck": "node scripts/depcheck.js",
"build": "node scripts/build.js",
"postinstall": "patch-package",
"genschemas": "node scripts/generate_schemas.js",
"start": "node scripts/build.js && node --enable-source-maps dist/start.js",
"setup": "npm install --omit optional && patch-package && npm run build",
"build": "node scripts/build_new.js",
"start": "npm run build && npm run start:bundle",
"start:bundle": "node --enable-source-maps dist/start.js",
"start:bundle:dbg": "node --enable-source-maps --inspect dist/start.js",
"start:bundle:vscode-dbg": "npm run build clean logerrors pretty-errors && node --enable-source-maps --inspect dist/start.js",
"start:bundle:vscode-dbg": "npm run build && node --enable-source-maps --inspect dist/start.js",
"depclean": "node scripts/depclean.js",
"depcheck": "node scripts/depcheck.js",
"tsnode": "npx ts-node --transpile-only -P tsnode.tsconfig.json src/start.ts",
"genschemas": "node scripts/generate_schemas.js",
"migrate": "cd ../util/ && npm i && node --require ts-node/register node_modules/typeorm/cli.js -f ../util/ormconfig.json migration:run",
"tsnode": "npx ts-node --transpile-only -P tsnode.tsconfig.json src/start.ts"
"postinstall": "patch-package && npx --yes node-git-hooks"
},
"repository": {
"type": "git",
@@ -28,44 +28,45 @@
"url": "https://github.com/fosscord/fosscord-server/issues"
},
"homepage": "https://fosscord.com",
"imports": {
"#*": "./dist/*/index.js"
},
"devDependencies": {
"@babel/core": "^7.18.9",
"@babel/preset-env": "^7.18.9",
"@babel/preset-typescript": "^7.15.0",
"@types/amqplib": "^0.8.1",
"@types/bcrypt": "^5.0.0",
"@types/bcryptjs": "^2.4.2",
"@types/body-parser": "^1.19.0",
"@types/dotenv": "^8.2.0",
"@types/express": "^4.17.12",
"@types/i18next-node-fs-backend": "^2.1.0",
"@types/jsonwebtoken": "^8.5.8",
"@types/morgan": "^1.9.3",
"@types/multer": "^1.4.7",
"@types/node": "^18.7.3",
"@types/node-fetch": "^2.6.2",
"@types/node-os-utils": "^1.3.0",
"@types/ws": "^8.5.3",
"jest": "^28.1.3",
"jest-expect-message": "^1.0.2",
"prettier-plugin-organize-imports": "^3.0.3",
"supertest": "^6.1.6",
"ts-node": "^10.2.1",
"ts-node-dev": "^2.0.0",
"ts-patch": "^2.0.2",
"typescript": "^4.2.3",
"typescript-json-schema": "^0.54.0"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.137.0",
"@babel/preset-typescript": "^7.15.0",
"@ovos-media/ts-transform-paths": "^1.7.18-1",
"@sentry/node": "^7.7.0",
"@sentry/tracing": "^7.7.0",
"@types/node-fetch": "^2.6.2",
"ajv": "^8.6.2",
"ajv": "^8.11.0",
"ajv-formats": "^2.1.1",
"amqplib": "^0.10.1",
"bcrypt": "^5.0.1",
"bcryptjs": "^2.4.3",
"body-parser": "^1.19.0",
"canvas": "^2.9.3",
"cheerio": "^1.0.0-rc.10",
"dotenv": "^16.0.1",
"exif-be-gone": "^1.3.1",
@@ -86,15 +87,12 @@
"node-fetch": "^2.6.7",
"patch-package": "^6.4.7",
"picocolors": "^1.0.0",
"prettier": "^2.7.1",
"proxy-agent": "^5.0.0",
"reflect-metadata": "^0.1.13",
"typeorm": "^0.3.7",
"typescript": "^4.1.2",
"typescript": "^4.2.3",
"ws": "^8.8.1"
},
"optionalDependencies": {
"mysql2": "^2.3.3",
"pg": "^8.7.3",
"sqlite3": "^5.0.11"
}
"packageManager": "yarn@3.2.2"
}
-249
View File
@@ -1,249 +0,0 @@
diff --git a/node_modules/ajv/dist/compile/jtd/parse.js b/node_modules/ajv/dist/compile/jtd/parse.js
index 1eeb1be..7684121 100644
--- a/node_modules/ajv/dist/compile/jtd/parse.js
+++ b/node_modules/ajv/dist/compile/jtd/parse.js
@@ -239,6 +239,9 @@ function parseType(cxt) {
gen.if(fail, () => parsingError(cxt, codegen_1.str `invalid timestamp`));
break;
}
+ case "bigint":
+ parseBigInt(cxt);
+ break
case "float32":
case "float64":
parseNumber(cxt);
@@ -284,6 +287,15 @@ function parseNumber(cxt, maxDigits) {
skipWhitespace(cxt);
gen.if(codegen_1._ `"-0123456789".indexOf(${jsonSlice(1)}) < 0`, () => jsonSyntaxError(cxt), () => parseWith(cxt, parseJson_1.parseJsonNumber, maxDigits));
}
+function parseBigInt(cxt, maxDigits) {
+ const {gen} = cxt
+ skipWhitespace(cxt)
+ gen.if(
+ _`"-0123456789".indexOf(${jsonSlice(1)}) < 0`,
+ () => jsonSyntaxError(cxt),
+ () => parseWith(cxt, parseJson_1.parseJsonBigInt, maxDigits)
+ )
+}
function parseBooleanToken(bool, fail) {
return (cxt) => {
const { gen, data } = cxt;
diff --git a/node_modules/ajv/dist/compile/rules.js b/node_modules/ajv/dist/compile/rules.js
index 82a591f..1ebd8fe 100644
--- a/node_modules/ajv/dist/compile/rules.js
+++ b/node_modules/ajv/dist/compile/rules.js
@@ -1,7 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getRules = exports.isJSONType = void 0;
-const _jsonTypes = ["string", "number", "integer", "boolean", "null", "object", "array"];
+const _jsonTypes = ["string", "number", "integer", "boolean", "null", "object", "array","bigint"];
const jsonTypes = new Set(_jsonTypes);
function isJSONType(x) {
return typeof x == "string" && jsonTypes.has(x);
@@ -13,10 +13,11 @@ function getRules() {
string: { type: "string", rules: [] },
array: { type: "array", rules: [] },
object: { type: "object", rules: [] },
+ bigint: {type: "bigint", rules: []}
};
return {
- types: { ...groups, integer: true, boolean: true, null: true },
- rules: [{ rules: [] }, groups.number, groups.string, groups.array, groups.object],
+ types: { ...groups, integer: true, boolean: true, null: true, bigint: true },
+ rules: [{ rules: [] }, groups.number, groups.string, groups.array, groups.object, groups.bigint],
post: { rules: [] },
all: {},
keywords: {},
diff --git a/node_modules/ajv/dist/compile/validate/dataType.js b/node_modules/ajv/dist/compile/validate/dataType.js
index 6319e76..8b50b4c 100644
--- a/node_modules/ajv/dist/compile/validate/dataType.js
+++ b/node_modules/ajv/dist/compile/validate/dataType.js
@@ -52,7 +52,7 @@ function coerceAndCheckDataType(it, types) {
return checkTypes;
}
exports.coerceAndCheckDataType = coerceAndCheckDataType;
-const COERCIBLE = new Set(["string", "number", "integer", "boolean", "null"]);
+const COERCIBLE = new Set(["string", "number", "integer", "boolean", "null","bigint"]);
function coerceToTypes(types, coerceTypes) {
return coerceTypes
? types.filter((t) => COERCIBLE.has(t) || (coerceTypes === "array" && t === "array"))
@@ -83,6 +83,14 @@ function coerceData(it, types, coerceTo) {
});
function coerceSpecificType(t) {
switch (t) {
+ case "bigint":
+ gen
+ .elseIf(
+ codegen_1._`${dataType} == "boolean" || ${data} === null
+ || (${dataType} == "string" && ${data} && ${data} == BigInt(${data}))`
+ )
+ .assign(coerced, codegen_1._`BigInt(${data})`)
+ return
case "string":
gen
.elseIf(codegen_1._ `${dataType} == "number" || ${dataType} == "boolean"`)
@@ -143,6 +151,9 @@ function checkDataType(dataType, data, strictNums, correct = DataType.Correct) {
case "number":
cond = numCond();
break;
+ case "bigint":
+ cond = codegen_1._`typeof ${data} == "bigint" && isFinite(${data})`
+ break
default:
return codegen_1._ `typeof ${data} ${EQ} ${dataType}`;
}
diff --git a/node_modules/ajv/dist/refs/json-schema-2019-09/meta/validation.json b/node_modules/ajv/dist/refs/json-schema-2019-09/meta/validation.json
index 7027a12..25679c8 100644
--- a/node_modules/ajv/dist/refs/json-schema-2019-09/meta/validation.json
+++ b/node_modules/ajv/dist/refs/json-schema-2019-09/meta/validation.json
@@ -78,7 +78,7 @@
"default": 0
},
"simpleTypes": {
- "enum": ["array", "boolean", "integer", "null", "number", "object", "string"]
+ "enum": ["array", "boolean", "integer", "null", "number", "object", "string","bigint"]
},
"stringArray": {
"type": "array",
diff --git a/node_modules/ajv/dist/refs/json-schema-2020-12/meta/validation.json b/node_modules/ajv/dist/refs/json-schema-2020-12/meta/validation.json
index e0ae13d..57c9036 100644
--- a/node_modules/ajv/dist/refs/json-schema-2020-12/meta/validation.json
+++ b/node_modules/ajv/dist/refs/json-schema-2020-12/meta/validation.json
@@ -78,7 +78,7 @@
"default": 0
},
"simpleTypes": {
- "enum": ["array", "boolean", "integer", "null", "number", "object", "string"]
+ "enum": ["array", "boolean", "integer", "null", "number", "object", "string","bigint"]
},
"stringArray": {
"type": "array",
diff --git a/node_modules/ajv/dist/refs/json-schema-draft-06.json b/node_modules/ajv/dist/refs/json-schema-draft-06.json
index 5410064..774435b 100644
--- a/node_modules/ajv/dist/refs/json-schema-draft-06.json
+++ b/node_modules/ajv/dist/refs/json-schema-draft-06.json
@@ -16,7 +16,7 @@
"allOf": [{"$ref": "#/definitions/nonNegativeInteger"}, {"default": 0}]
},
"simpleTypes": {
- "enum": ["array", "boolean", "integer", "null", "number", "object", "string"]
+ "enum": ["array", "boolean", "integer", "null", "number", "object", "string","bigint"]
},
"stringArray": {
"type": "array",
diff --git a/node_modules/ajv/dist/refs/json-schema-draft-07.json b/node_modules/ajv/dist/refs/json-schema-draft-07.json
index 6a74851..fc6dd7d 100644
--- a/node_modules/ajv/dist/refs/json-schema-draft-07.json
+++ b/node_modules/ajv/dist/refs/json-schema-draft-07.json
@@ -16,7 +16,7 @@
"allOf": [{"$ref": "#/definitions/nonNegativeInteger"}, {"default": 0}]
},
"simpleTypes": {
- "enum": ["array", "boolean", "integer", "null", "number", "object", "string"]
+ "enum": ["array", "boolean", "integer", "null", "number", "object", "string","bigint"]
},
"stringArray": {
"type": "array",
diff --git a/node_modules/ajv/dist/refs/jtd-schema.js b/node_modules/ajv/dist/refs/jtd-schema.js
index 1ee940a..1148887 100644
--- a/node_modules/ajv/dist/refs/jtd-schema.js
+++ b/node_modules/ajv/dist/refs/jtd-schema.js
@@ -38,6 +38,7 @@ const typeForm = (root) => ({
"uint16",
"int32",
"uint32",
+ "bigint",
],
},
},
diff --git a/node_modules/ajv/dist/runtime/parseJson.js b/node_modules/ajv/dist/runtime/parseJson.js
index 2576a6e..e7447b1 100644
--- a/node_modules/ajv/dist/runtime/parseJson.js
+++ b/node_modules/ajv/dist/runtime/parseJson.js
@@ -97,6 +97,71 @@ exports.parseJsonNumber = parseJsonNumber;
parseJsonNumber.message = undefined;
parseJsonNumber.position = 0;
parseJsonNumber.code = 'require("ajv/dist/runtime/parseJson").parseJsonNumber';
+
+function parseJsonBigInt(s, pos, maxDigits) {
+ let numStr = "";
+ let c;
+ parseJsonBigInt.message = undefined;
+ if (s[pos] === "-") {
+ numStr += "-";
+ pos++;
+ }
+ if (s[pos] === "0") {
+ numStr += "0";
+ pos++;
+ }
+ else {
+ if (!parseDigits(maxDigits)) {
+ errorMessage();
+ return undefined;
+ }
+ }
+ if (maxDigits) {
+ parseJsonBigInt.position = pos;
+ return BigInt(numStr);
+ }
+ if (s[pos] === ".") {
+ numStr += ".";
+ pos++;
+ if (!parseDigits()) {
+ errorMessage();
+ return undefined;
+ }
+ }
+ if (((c = s[pos]), c === "e" || c === "E")) {
+ numStr += "e";
+ pos++;
+ if (((c = s[pos]), c === "+" || c === "-")) {
+ numStr += c;
+ pos++;
+ }
+ if (!parseDigits()) {
+ errorMessage();
+ return undefined;
+ }
+ }
+ parseJsonBigInt.position = pos;
+ return BigInt(numStr);
+ function parseDigits(maxLen) {
+ let digit = false;
+ while (((c = s[pos]), c >= "0" && c <= "9" && (maxLen === undefined || maxLen-- > 0))) {
+ digit = true;
+ numStr += c;
+ pos++;
+ }
+ return digit;
+ }
+ function errorMessage() {
+ parseJsonBigInt.position = pos;
+ parseJsonBigInt.message = pos < s.length ? `unexpected token ${s[pos]}` : "unexpected end";
+ }
+}
+exports.parseJsonBigInt = parseJsonBigInt;
+parseJsonBigInt.message = undefined;
+parseJsonBigInt.position = 0;
+parseJsonBigInt.code = 'require("ajv/dist/runtime/parseJson").parseJsonBigInt';
+
+
const escapedChars = {
b: "\b",
f: "\f",
diff --git a/node_modules/ajv/dist/vocabularies/jtd/type.js b/node_modules/ajv/dist/vocabularies/jtd/type.js
index 428bddb..fbc3070 100644
--- a/node_modules/ajv/dist/vocabularies/jtd/type.js
+++ b/node_modules/ajv/dist/vocabularies/jtd/type.js
@@ -45,6 +45,9 @@ const def = {
cond = timestampCode(cxt);
break;
}
+ case "bigint":
+ cond = codegen_1._`typeof ${data} == "bigint" || typeof ${data} == "string"`
+ break
case "float32":
case "float64":
cond = codegen_1._ `typeof ${data} == "number"`;
+2 -4
View File
@@ -3,9 +3,7 @@ const Models = require("../dist/entities");
const { PrimaryColumn } = require("typeorm");
function shouldIncludeEntity(name) {
return ![Models.BaseClassWithoutId, PrimaryColumn, Models.BaseClass, Models.PrimaryGeneratedColumn]
.map((x) => x?.name)
.includes(name);
return ![Models.BaseClassWithoutId, PrimaryColumn, Models.BaseClass, Models.PrimaryGeneratedColumn].map((x) => x?.name).includes(name);
}
async function main() {
@@ -14,7 +12,7 @@ async function main() {
type: "sqlite",
database: ":memory:",
entities: Object.values(Models).filter((x) => x.constructor.name == "Function" && shouldIncludeEntity(x.name)),
synchronize: true,
synchronize: true
});
await db.initialize();
console.log("Initialized database");
+3 -3
View File
@@ -8,7 +8,7 @@ let cores = 1;
try {
cores = Number(process.env.THREADS) || os.cpus().length;
} catch {
console.log("[Bundle] Failed to get thread count! Using 1...")
console.log("[Bundle] Failed to get thread count! Using 1...");
}
if (!token) {
@@ -46,8 +46,8 @@ function connect() {
op: 2,
d: {
token,
properties: {},
},
properties: {}
}
})
);
+2 -2
View File
@@ -14,9 +14,9 @@ async function main() {
consent: true,
date_of_birth: "2000-01-01",
gift_code_sku_id: null,
captcha_key: null,
captcha_key: null
}),
headers: { "content-type": "application/json" },
headers: { "content-type": "application/json" }
});
console.log(i);
}
+80 -36
View File
@@ -2,20 +2,22 @@ const { execSync } = require("child_process");
const path = require("path");
const fs = require("fs");
const { argv, stdout, exit } = require("process");
const { execIn, parts } = require('./utils');
const { execIn, parts, getDirs, walk, sanitizeVarName } = require("./utils");
if(argv.includes("help")) {
if (argv.includes("help")) {
console.log(`Fosscord build script help:
Arguments:
clean Cleans up previous builds
verbose Enable verbose logging
logerrors Log build errors to console
pretty-errors Pretty-print build errors
silent No output to console or files.`);
silent No output to console or files.
propagate-err Exit script with error code if build fails.`);
exit(0);
}
let steps = 1, i = 0;
let steps = 5,
i = 0;
if (argv.includes("clean")) steps++;
const verbose = argv.includes("verbose") || argv.includes("v");
@@ -23,7 +25,7 @@ const logerr = argv.includes("logerrors");
const pretty = argv.includes("pretty-errors");
const silent = argv.includes("silent");
if(silent) console.error = console.log = function(){}
if (silent) console.error = console.log = function () {};
if (argv.includes("clean")) {
console.log(`[${++i}/${steps}] Cleaning...`);
@@ -36,37 +38,79 @@ if (argv.includes("clean")) {
console.log(`[${++i}/${steps}] Compiling src files ...`);
let buildFlags = ''
if(pretty) buildFlags += '--pretty '
let buildFlags = "";
if (pretty) buildFlags += "--pretty ";
try {
execSync(
'node "' +
path.join(__dirname, "..", "node_modules", "typescript", "lib", "tsc.js") +
'" -p "' +
path.join(__dirname, "..") +
'" ' + buildFlags,
{
cwd: path.join(__dirname, ".."),
shell: true,
env: process.env,
encoding: "utf8"
}
)
} catch (error) {
if(verbose || logerr) {
error.stdout.split(/\r?\n/).forEach((line) => {
let _line = line.replace('dist/','',1);
if(!pretty && _line.includes('.ts(')) {
//reformat file path for easy jumping
_line = _line.replace('(',':',1).replace(',',':',1).replace(')','',1)
console.log(`[${++i}/${steps}] Building plugin index...`);
let pluginDir = path.join(__dirname, "..", "src", "plugins");
let output = 'import { Plugin } from "util/plugin";\n';
const dirs = fs.readdirSync(pluginDir).filter((x) => {
try {
fs.readdirSync(path.join(pluginDir, x));
return true;
} catch (e) {
return false;
}
});
dirs.forEach((x) => {
let pluginManifest = require(path.join(pluginDir, x, "plugin.json"));
output += `import * as ${sanitizeVarName(x)} from "./${x}/${pluginManifest.mainClass}";\n`;
});
output += `\nexport const PluginIndex: any = {\n`;
dirs.forEach((x) => {
output += ` "${x}": new ${sanitizeVarName(x)}.default(),\n`; //ctor test: '${path.resolve(path.join(pluginDir, x))}', require('./${x}/plugin.json')
});
output += `};`;
fs.writeFileSync(path.join(__dirname, "..", "src", "plugins", "PluginIndex.ts"), output);
if (!argv.includes("copyonly")) {
console.log(`[${++i}/${steps}] Compiling source code...`);
let buildFlags = "";
if (pretty) buildFlags += "--pretty ";
try {
execSync(
'node "' +
path.join(__dirname, "..", "node_modules", "typescript", "lib", "tsc.js") +
'" -p "' +
path.join(__dirname, "..") +
'" ' +
buildFlags,
{
cwd: path.join(__dirname, ".."),
shell: true,
env: process.env,
encoding: "utf8"
}
console.error(_line);
})
);
} catch (error) {
if (verbose || logerr) {
error.stdout.split(/\r?\n/).forEach((line) => {
let _line = line.replace("dist/", "", 1);
if (!pretty && _line.includes(".ts(")) {
//reformat file path for easy jumping
_line = _line.replace("(", ":", 1).replace(",", ":", 1).replace(")", "", 1);
}
console.error(_line);
});
}
console.error(`Build failed! Please check build.log for info!`);
if (!silent) {
if (pretty) fs.writeFileSync("build.log.ansi", error.stdout);
fs.writeFileSync(
"build.log",
error.stdout.replaceAll(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "")
);
}
throw error;
}
console.error(`Build failed! Please check build.log for info!`);
if(!silent){
if(pretty) fs.writeFileSync("build.log.ansi", error.stdout);
fs.writeFileSync("build.log", error.stdout.replaceAll(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, ''));
}
}
}
console.log(`[${++i}/${steps}] Copying plugin data...`);
let pluginFiles = walk(pluginDir).filter((x) => !x.endsWith(".ts"));
pluginFiles.forEach((x) => {
fs.copyFileSync(x, x.replace("src", "dist"));
});
+18
View File
@@ -0,0 +1,18 @@
const { execSync } = require("child_process");
const path = require("path");
const fs = require("fs");
const { argv, stdout, exit } = require("process");
const { execIn, parts, getDirs, walk, sanitizeVarName } = require("../utils");
module.exports = function (config) {
if (fs.existsSync(config.buildLog)) fs.rmSync(config.buildLog);
if (fs.existsSync(config.buildLogAnsi)) fs.rmSync(config.buildLogAnsi);
if (config.clean) {
console.log(`==> Cleaning...`);
if (fs.existsSync(config.distDir)) {
fs.rmSync(config.distDir, { recursive: true });
if (config.verbose) console.log(`Deleted ${path.resolve(config.distDir)}!`);
}
}
};
+48
View File
@@ -0,0 +1,48 @@
const { execSync } = require("child_process");
const path = require("path");
const fs = require("fs");
const { argv, stdout, exit } = require("process");
const { execIn, parts, getDirs, walk, sanitizeVarName } = require("../utils");
module.exports = function (config) {
console.log("==> Compiling source with tsc...");
let buildFlags = "";
if (config.pretty) buildFlags += "--pretty ";
try {
execSync(
'node "' +
path.join(config.rootDir, "node_modules", "typescript", "lib", "tsc.js") +
'" -p "' +
path.join(config.rootDir) +
'" ' +
buildFlags,
{
cwd: path.join(config.rootDir),
shell: true,
env: process.env,
encoding: "utf8"
}
);
} catch (error) {
if (config.verbose || config.logerr) {
error.stdout.split(/\r?\n/).forEach((line) => {
let _line = line.replace("dist/", "", 1);
if (!config.pretty && _line.includes(".ts(")) {
//reformat file path for easy jumping
_line = _line.replace("(", ":", 1).replace(",", ":", 1).replace(")", "", 1);
}
console.error(_line);
});
}
console.error(`Build failed! Please check build.log for info!`);
if (!config.silent) {
if (config.pretty) fs.writeFileSync(path.join(config.rootDir, "build.log.ansi"), error.stdout);
fs.writeFileSync(
path.join(config.rootDir, "build.log"),
error.stdout.replaceAll(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "")
);
}
throw error;
}
};
+31
View File
@@ -0,0 +1,31 @@
const { execSync } = require("child_process");
const path = require("path");
const fs = require("fs");
const { argv, stdout, exit } = require("process");
const { execIn, parts, getDirs, walk, sanitizeVarName } = require("../utils");
module.exports = function (config) {
console.log(`==> Building plugin index...`);
let output = 'import { Plugin } from "util/plugin";\n';
const dirs = fs.readdirSync(config.pluginDir).filter((x) => {
try {
fs.readdirSync(path.join(config.pluginDir, x));
return true;
} catch (e) {
return false;
}
});
dirs.forEach((x) => {
let pluginManifest = require(path.join(config.pluginDir, x, "plugin.json"));
console.log(` ==> Registering plugin: ${pluginManifest.name} (${pluginManifest.id}) by ${pluginManifest.authors}`);
output += `import * as ${sanitizeVarName(x)} from "./${x}/${pluginManifest.mainClass}";\n`;
});
output += `\nexport const PluginIndex: any = {\n`;
dirs.forEach((x) => {
output += ` "${x}": new ${sanitizeVarName(x)}.default(),\n`; //ctor test: '${path.resolve(path.join(pluginDir, x))}', require('./${x}/plugin.json')
});
output += `};`;
fs.writeFileSync(path.join(config.pluginDir, "PluginIndex.ts"), output);
};
+13
View File
@@ -0,0 +1,13 @@
const { execSync } = require("child_process");
const path = require("path");
const fs = require("fs");
const { argv, stdout, exit } = require("process");
const { execIn, parts, getDirs, walk, sanitizeVarName } = require("../utils");
module.exports = function (config) {
console.log(`==> Copying all plugin resources...`);
let pluginFiles = walk(config.pluginDir).filter((x) => !x.endsWith(".ts"));
pluginFiles.forEach((x) => {
fs.copyFileSync(x, x.replace("src", "dist"));
});
};
+15
View File
@@ -0,0 +1,15 @@
const { execSync } = require("child_process");
const path = require("path");
const fs = require("fs");
const { argv, stdout, exit } = require("process");
const { execIn, parts, getDirs, walk, sanitizeVarName } = require("../utils");
module.exports = function (config) {
console.log(`==> Remapping module imports...`);
let files = walk(config.distDir).filter((x) => x.endsWith(".js"));
files.forEach((x) => {
let fc = fs.readFileSync(x).toString();
fc = fc.replaceAll("@fosscord/", "#");
fs.writeFileSync(x, fc);
});
};
+31
View File
@@ -0,0 +1,31 @@
const { execSync } = require("child_process");
const path = require("path");
const fs = require("fs");
const { argv, stdout, exit } = require("process");
const { execIn, parts, getDirs, walk, sanitizeVarName } = require("./utils");
//file paths
const rootDir = path.join(__dirname, "..");
const srcDir = path.join(rootDir, "src");
const distDir = path.join(rootDir, "dist");
const scriptsDir = path.join(rootDir, "scripts");
const configPath = path.join(rootDir, "build.json");
const buildLog = path.join(rootDir, "build.log");
const buildLogAnsi = path.join(rootDir, "build.log.ansi");
const pluginDir = path.join(srcDir, "plugins");
//more, dont export
const buildStepDir = path.join(scriptsDir, "build");
if (!fs.existsSync(configPath)) {
if (!fs.existsSync(path.join(configPath + ".default"))) {
console.log("build.json.default not found! Exiting!");
exit(1);
}
fs.copyFileSync(configPath + ".default", configPath);
}
let config = { rootDir, srcDir, distDir, configPath, buildLog, buildLogAnsi, pluginDir, ...require(configPath) };
config.steps.pre.forEach((step) => require(path.join(buildStepDir, step))(config));
require(path.join(buildStepDir, "compile_" + config.compiler))(config);
config.steps.post.forEach((step) => require(path.join(buildStepDir, step))(config));
+75
View File
@@ -0,0 +1,75 @@
const path = require("path");
const fs = require("fs").promises;
const { execIn, getLines, walk, projectRoot } = require("./utils");
let printTodos = process.argv.includes("TODOS");
let root = path.resolve(path.join(__dirname, "..", "src"));
let files = walk(root);
let _files = [];
let errors = 0,
warnings = 0,
todos = 0;
Promise.all(files.map(getFile)).then((f) => {
Promise.all(Object.keys(_files).map(checkFile));
console.log(`\n${errors} errors, ${warnings} warnings, ${todos} TODOs.`);
let loc = 0;
Object.values(_files).forEach((x) => {
loc += x.length;
});
console.log("\nStats:\n");
console.log(`Lines of code: ${loc} lines in ${Object.values(_files).length} files.`);
debugger;
});
async function getFile(name) {
let contents = (await fs.readFile(name)).toString().split("\n");
_files[name] = contents;
}
async function checkFile(x) {
_files[x].forEach((line) => scanLine(x, line));
}
function log(file, line, msg) {
let lineNum = _files[file].indexOf(line) + 1;
console.log(msg, "File:", file.replace(root + "/", "") + ":" + lineNum);
}
function scanLine(x, line) {
if (/import/.test(line)) {
if (/import {?.*}? from '.*'/.test(line)) {
log(x, line, `[WARN] Inconsistent import syntax, please use double quotes!`);
warnings++;
}
} else {
if (line.trim().endsWith("TODO:")) {
log(x, line, `[ERRO] Empty TODO!`);
errors++;
} else if (/\/\/\s{0,3}TODO:/.test(line)) {
if (printTodos) log(x, line, `[TODO] Found a TODO: ${line.split("TODO:")[1].trim()}.`);
todos++;
}
if (/(:|=)/.test(line)) {
if (/(:|=) {2,}/.test(line)) {
log(x, line, `[WARN] Multiple spaces in assignment!`);
warnings++;
}
if (/(:|=)\t'/.test(line)) {
log(x, line, `[WARN] Tab in assignment!`);
warnings++;
}
if (/(:|=)\w'/.test(line)) {
log(x, line, `[WARN] Missing space in assignment!`);
warnings++;
}
if (/(:|=) undefined/.test(line) && !/(:|=){2,} undefined/.test(line)) {
log(x, line, `[WARN] Use of undefined!`);
warnings++;
}
}
}
}
+80
View File
@@ -0,0 +1,80 @@
#!/usr/bin/node
const path = require("path");
const fs = require("fs");
const { stdout, exit } = require("process");
const { execIn } = require("./utils.js");
const { ask } = require("./utils/ask.js");
async function main() {
let filename;
if(process.argv[2]) filename = process.argv[2];
else filename = await ask("Please enter the name of your migration: ");
let dbconf;
try {
dbconf = JSON.parse(fs.readFileSync("dbconf.json"));
} catch (e) {
console.log("No dbconf.json found!");
dbconf = {};
}
if(!dbconf["sqlite"])
dbconf.sqlite = {
conn_str: "migrations.db",
migrations_dir: "sqlite",
package: "sqlite3"
}
if(!dbconf["postgres"] && process.env.FC_DB_POSTGRES) {
console.log("Found FC_DB_POSTGRES environment variable. Using it!");
dbconf.postgres = {
conn_str: process.env.FC_DB_POSTGRES,
migrations_dir: "postgres",
package: "pg"
}
}
if(!dbconf["mariadb"] && process.env.FC_DB_MARIADB){
console.log("Found FC_DB_MARIADB environment variable. Using it!");
dbconf.mariadb = {
conn_str: process.env.FC_DB_MARIADB,
migrations_dir: "mariadb",
package: "mysql2"
}
}
fs.writeFileSync("dbconf.json", JSON.stringify(dbconf, null, 4));
//build
execIn(`node scripts/build_new.js`, process.cwd(), {stdio: "inherit"});
if(fs.existsSync(".env") && !fs.existsSync(".env.bak"))
fs.renameSync(".env", ".env.bak");
Object.keys(dbconf).forEach((db) => {
console.log(`Applying migrations for ${db}`);
if(!fs.existsSync(path.join("node_modules", dbconf[db].package)))
execIn(`npm i ${dbconf[db].package}`, process.cwd());
fs.writeFileSync(
`.env`,
`DATABASE=${dbconf[db].conn_str}
THREADS=1
DB_MIGRATE=true
DB_VERBOSE=true`
);
execIn(`node dist/start.js`, process.cwd(), {stdio: "inherit"});
});
Object.keys(dbconf).forEach((db) => {
console.log(`Generating new migrations for ${db}`);
fs.writeFileSync(
`.env`,
`DATABASE=${dbconf[db].conn_str}
THREADS=1
DB_MIGRATE=true
DB_VERBOSE=true`
);
execIn(`node node_modules/typeorm/cli.js migration:generate "src/util/migrations/${db}/${filename}" -d dist/util/util/Database.js -p`, process.cwd(), {stdio: "inherit"});
});
if(fs.existsSync(".env.bak")) {
fs.rmSync(".env");
fs.renameSync(".env.bak", ".env");
}
exit(0);
}
main();
-41
View File
@@ -1,41 +0,0 @@
#!/bin/sh
if [ ! -z "$1" ]
then
FILENAME="$1"
echo "Using filename: $FILENAME"
else
read -p "Enter migration filename: " FILENAME
fi
[ -f ".env" ] && (
mv .env .env.tmp 2>/dev/null
source .env.tmp 2>/dev/null
)
npm run build clean logerrors pretty-errors
make_migration() {
echo "Creating migrations for $2"
mkdir "src/util/migrations/$2" 2>/dev/null
# npm run build clean logerrors pretty-errors
THREADS=1 DATABASE="$1" DB_MIGRATE=a npm run start:bundle
THREADS=1 DATABASE="$1" DB_MIGRATE=a npx typeorm-ts-node-commonjs migration:generate "src/util/migrations/$2/$FILENAME" -d src/util/util/Database.ts -p
#npm run build clean logerrors pretty-errors
#THREADS=1 DATABASE="$1" DB_MIGRATE=a npm run start:bundle
}
npm i sqlite3
make_migration "database.db" "sqlite"
[ -z "$FC_DB_POSTGRES" ] || (
npm i pg
make_migration "$FC_DB_POSTGRES" "postgres"
)
[ -z "$FC_DB_MARIADB" ] || (
npm i mysql2
make_migration "$FC_DB_MARIADB" "mariadb"
)
[ -f ".env.tmp" ] && mv .env.tmp .env 2>/dev/null
+19 -25
View File
@@ -9,22 +9,22 @@ const { execIn, getLines } = require("./utils");
let npmi_extra_flags = "";
const resolveminor = argv.includes("resolveminor");
if(argv.includes("nobuild")) npmi_extra_flags += "--ignore-scripts ";
if (argv.includes("nobuild")) npmi_extra_flags += "--ignore-scripts ";
parts.forEach((part) => {
let partDir = path.join(__dirname, "..", "..", part);
let distDir = path.join(partDir, "dist");
console.log(`Checking updates for ${part} (${partDir})`);
if(part == "bundle") {
execIn(`npm run syncdeps`, partDir)
if (part == "bundle") {
execIn(`npm run syncdeps`, partDir);
}
if(resolveminor) {
fs.rmSync(path.join(partDir, "node_modules"), {
if (resolveminor) {
fs.rmSync(path.join(partDir, "node_modules"), {
recursive: true,
force: true,
force: true
});
execIn(`npm i --save --no-fund --no-audit --no-package-lock ${npmi_extra_flags}`, partDir)
}
execIn(`npm i --save --no-fund --no-audit --no-package-lock ${npmi_extra_flags}`, partDir);
}
let x = [
[
"pkg",
@@ -33,24 +33,18 @@ parts.forEach((part) => {
wanted: "2.0",
latest: "2.0",
dependent: "cdn",
location: "/usr/src/fosscord/bundle/node_packages/pkg",
},
],
location: "/usr/src/fosscord/bundle/node_packages/pkg"
}
]
];
x = Object.entries(
JSON.parse(execIn("npm outdated --json", partDir))
);
x = Object.entries(JSON.parse(execIn("npm outdated --json", partDir)));
x.forEach((a) => {
let pkgname = a[0];
let pkginfo = a[1];
if(!pkginfo.current)
console.log(`MISSING ${pkgname}: ${pkginfo.current} -> ${pkginfo.wanted} (latest: ${pkginfo.latest})`);
else if(pkginfo.latest != pkginfo.wanted){
if(pkginfo.current != pkginfo.wanted)
console.log(`MINOR ${pkgname}: ${pkginfo.current} -> ${pkginfo.wanted}`);
console.log(`MAJOR ${pkgname}: ${pkginfo.current} -> ${pkginfo.latest}`);
}
else
console.log(`MINOR ${pkgname}: ${pkginfo.current} -> ${pkginfo.wanted}`);
let pkgname = a[0];
let pkginfo = a[1];
if (!pkginfo.current) console.log(`MISSING ${pkgname}: ${pkginfo.current} -> ${pkginfo.wanted} (latest: ${pkginfo.latest})`);
else if (pkginfo.latest != pkginfo.wanted) {
if (pkginfo.current != pkginfo.wanted) console.log(`MINOR ${pkgname}: ${pkginfo.current} -> ${pkginfo.wanted}`);
console.log(`MAJOR ${pkgname}: ${pkginfo.current} -> ${pkginfo.latest}`);
} else console.log(`MINOR ${pkgname}: ${pkginfo.current} -> ${pkginfo.wanted}`);
});
});
+8 -17
View File
@@ -3,8 +3,7 @@ const fs = require("fs");
const { env } = require("process");
const { execSync } = require("child_process");
const { argv, stdout, exit } = require("process");
const { execIn, getLines } = require('./utils');
const { execIn, getLines } = require("./utils");
const bundleRequired = ["@ovos-media/ts-transform-paths"];
const removeModules = argv.includes("cleanup");
@@ -15,13 +14,11 @@ execIn("npm i", path.join(__dirname, ".."));
let partDir = path.join(__dirname, "..");
let distDir = path.join(partDir, "dist");
let start = 0;
start = getLines(
execIn("npm ls --parseable --package-lock-only -a", partDir)
);
start = getLines(execIn("npm ls --parseable --package-lock-only -a", partDir));
if (fs.existsSync(distDir))
fs.rmSync(distDir, {
recursive: true,
force: true,
force: true
});
let x = {
dependencies: [],
@@ -29,17 +26,13 @@ let x = {
invalidDirs: [],
invalidFiles: [],
missing: [],
using: [],
using: []
};
let dcproc = execIn("npx depcheck --json", partDir);
if(dcproc.stdout) x = JSON.parse(dcproc.stdout);
if (dcproc.stdout) x = JSON.parse(dcproc.stdout);
else x = JSON.parse(dcproc);
fs.writeFileSync(
path.join(__dirname, "..", `depclean.out.json`),
JSON.stringify(x, null, "\t"),
{ encoding: "utf8" }
);
fs.writeFileSync(path.join(__dirname, "..", `depclean.out.json`), JSON.stringify(x, null, "\t"), { encoding: "utf8" });
let depsToRemove = x.dependencies.join(" ");
if (depsToRemove) execIn(`npm r --save ${depsToRemove}`, partDir);
@@ -50,11 +43,9 @@ if (depsToRemove) execIn(`npm r --save --dev ${depsToRemove}`, partDir);
if (removeModules && fs.existsSync(path.join(partDir, "node_modules")))
fs.rmSync(path.join(partDir, "node_modules"), {
recursive: true,
force: true,
force: true
});
let end = getLines(
execIn("npm ls --parseable --package-lock-only -a", partDir)
);
let end = getLines(execIn("npm ls --parseable --package-lock-only -a", partDir));
console.log(`${part}: ${start} -> ${end} (diff: ${start - end})`);
console.log("Installing required packages for bundle...");
+272
View File
@@ -0,0 +1,272 @@
#!/usr/bin/node
const path = require("path");
const fs = require("fs");
const { stdout, exit } = require("process");
const { execIn } = require("./utils.js");
const { ask } = require("./utils/ask.js");
const data = { env: [], config: { register: {} }, extra_pkgs: [] };
let rights = [];
process.on("SIGINT", function () {
console.log("Caught interrupt signal");
process.exit();
});
console.log("Welcome to Fosscord!");
console.log("Please remember this is pre-release software!");
console.log("We will guide you through some important setup steps.");
console.log();
if (fs.existsSync("package-lock.json")) fs.rmSync("package-lock.json");
if (fs.existsSync("yarn.lock")) fs.rmSync("yarn.lock");
async function main() {
printTitle("Step 1: Database setup");
console.log("1. PostgreSQL (recommended)");
console.log("2. MariaDB/MySQL");
console.log("3. SQLite (not recommended, but good for a simple test)");
while (!data.db) {
let answer = await ask("Please select a database type: ");
if (answer == "1") {
data.db = "postgres";
data.extra_pkgs.push("pg");
} else if (answer == "2") {
data.db = "mariadb";
data.extra_pkgs.push("mysql2");
} else if (answer == "3") {
data.db = "sqlite";
data.extra_pkgs.push("sqlite3");
} else {
console.log("Invalid choice!");
}
}
printTitle("Step 2: Database credentials");
if (data.db != "sqlite") {
console.log("Please enter your database credentials.");
console.log("You can leave the password field empty if you don't want to set a password.");
console.log();
while (!data.db_host) {
data.db_host = await ask("Host: ");
}
while (!data.db_port) {
data.db_port = await ask("Port: ");
}
while (!data.db_user) {
data.db_user = await ask("Username: ");
}
while (!data.db_pass) {
data.db_pass = await ask("Password: ");
}
while (!data.db_name) {
data.db_name = await ask("Database name: ");
}
} else {
console.log("SQLite does not use credentials...");
}
printTitle("Step 3: Domain setup");
console.log("Please enter your domain.");
console.log("You can leave the port field empty if you don't want to set a port.");
console.log();
data.domain = await ask("Domain (default=localhost): ");
if (!data.domain) data.domain = "localhost";
else data.ssl = /y?/i.test(await ask("SSL/HTTPS (Y/n): "));
data.port = await ask("Port (default=3001): ");
if (!data.port) data.port = "3001";
if (data.db != "sqlite")
data.env.push(`DATABASE=${data.db}://${data.db_user}:${data.db_pass}@${data.db_host}:${data.db_port}/${data.db_name}`);
data.env.push(`PORT=${data.port}`);
data.env.push("THREADS=1");
printTitle("Step 4: Default rights");
console.log("Please enter the default rights for new users.");
console.log("Valid rights are: none, discord, full, custom.");
console.log();
let lines = fs.readFileSync(path.join(__dirname, "..", "src", "util", "util", "Rights.ts")).toString();
let lines2 = lines.split("\n");
let lines3 = lines2.filter((y) => y.includes(": BitFlag("));
let lines4 = lines3.map((x) => x.split("//")[0].trim());
let maxRights = 0n;
lines4.forEach((x) => {
maxRights += eval(`rights.${x.replace(":", " = ").replace(",", ";")}`);
});
discordRights = maxRights;
discordRights -= rights.SEND_BACKDATED_EVENTS;
discordRights -= rights.MANAGE_GUILD_DIRECTORY;
discordRights -= rights.CREDITABLE;
discordRights -= rights.BYPASS_RATE_LIMITS;
discordRights -= rights.ADD_MEMBERS;
discordRights -= rights.MANAGE_RATE_LIMITS;
discordRights -= rights.OPERATOR;
data.default_rights = await ask("Rights (default=none): ");
if (!data.default_rights || data.defaultRights == "none") data.config.register.defaultRights = "0";
else if (data.default_rights == "discord") data.config.register.defaultRights = discordRights.toString();
else if (data.default_rights == "full") data.config.register.defaultRights = maxRights.toString();
else if (data.default_rights == "custom") data.config.register.defaultRights = (await askRights()).toString();
if (data.domain != "localhost")
data.config = {
cdn: {
endpointPrivate: `http://localhost:${data.port}`,
endpointPublic: `${data.ssl ? "https" : "http"}://${data.domain}:${data.port}`
},
gateway: {
endpointPrivate: `ws://localhost:${data.port}`,
endpointPublic: `${data.ssl ? "wss" : "ws"}://${data.domain}:${data.port}`
},
...data.config
};
printTitle("Step 5: extra options");
if (/y?/i.test(await ask("Use fast BCrypt implementation (requires a compiler) (Y/n): "))) data.extra_pkgs.push("bcrypt");
if (/y?/i.test(await ask("Enable support for widgets (requires compiler, known to fail on some ARM devices.) (Y/n): ")))
data.extra_pkgs.push("canvas");
printTitle("Step 6: finalizing...");
//save
console.log("==> Writing .env...");
fs.writeFileSync(".env", data.env.join("\n"));
console.log("==> Writing initial.json");
fs.writeFileSync("initial.json", JSON.stringify(data.config, (space = 4)));
//install packages...
console.log("==> Installing packages...");
console.log(" ==> Ensuring yarn is up to date (v3, not v1)...");
execIn("npx yarn set version stable", process.cwd());
console.log(" ==> Installing base packages");
execIn("npx --yes yarn install", process.cwd(), { stdio: "inherit" });
if (data.extra_pkgs.length > 0) {
console.log(" ==> Checking dependencies...");
checkCompilers();
if (data.extra_pkgs.includes("canvas")) checkCanvasDeps();
if (data.extra_pkgs.includes("bcrypt")) checkBcryptDeps();
console.log(` ==> Installing extra packages: ${data.extra_pkgs.join(", ")}...`);
execIn(`npx --yes yarn add -O ${data.extra_pkgs.join(" ")}`, process.cwd(), { stdio: "inherit" });
}
console.log("==> Building...");
execIn("npx --yes yarn run build", process.cwd(), { stdio: "inherit" });
printTitle("Step 6: run your instance!");
console.log("Installation is complete!");
console.log("You can now start your instance by running 'npm run start:bundle'!");
exit(0);
}
main();
async function askRights() {
let w = 0;
let brights = { ...eval(`rights`) };
Object.keys(rights).forEach((x) => {
brights[x] = false;
let str = `[x] ${Object.keys(rights).length}: ${x}`;
if (str.length > w) w = str.length;
});
let resp = "";
let selectedRights = 0n;
while (resp != "q") {
selectedRights = 0n;
Object.keys(brights).forEach((x) => {
if (brights[x]) selectedRights += rights[x];
});
console.clear();
printTitle("Step 4: Default rights");
printTitle(`Current rights: ${selectedRights} (0b${selectedRights.toString(2)}, 0x${selectedRights.toString(16)})`);
let xpos = 0;
Object.keys(rights).forEach((x) => {
let str = `[${brights[x] ? "X" : " "}] ${Object.keys(rights).indexOf(x)}: ${x}`.padEnd(w + 1, " ");
if (xpos + str.length > stdout.columns) {
console.log();
xpos = 0;
}
stdout.write(str);
xpos += str.length;
});
console.log();
resp = await ask("Enter an option, or q to exit: ");
if (/\d{1,}/.test(resp) && resp < Object.keys(rights).length && resp > -1) {
brights[Object.keys(brights)[parseInt(resp)]] ^= true;
}
}
return selectedRights;
}
function printTitle(input) {
let width = stdout.columns / 2 - 1; //40
console.log();
console.log("-".repeat(width - input.length / 2), input, "-".repeat(width - input.length / 2));
console.log();
}
function BitFlag(int) {
return 1n << BigInt(int);
}
function checkCanvasDeps() {
if (
!(
checkDep("pixman", "/usr/include/pixman-1/pixman.h") &&
checkDep("pixman", "/usr/lib/libpixman-1.so") &&
checkDep("cairo", "/usr/include/cairo/cairo.h") &&
checkDep("cairo", "/usr/lib/libcairo.so") &&
checkDep("pango", "/usr/include/pango-1.0/pango/pangocairo.h") &&
checkDep("pango", "/usr/lib/libpango-1.0.so") &&
checkDep("pkgconfig", "/usr/bin/pkg-config")
)
) {
console.log("Canvas requires the following dependencies to be installed: pixman, cairo, pango, pkgconfig");
exit(1);
}
}
function checkBcryptDeps() {
/*if (!(checkDep("bcrypt", "/usr/include/bcrypt.h") && checkDep("bcrypt", "/usr/lib/libbcrypt.so"))) {
console.log("Bcrypt requires the following dependencies to be installed: bcrypt");
exit(1);
}*/
//TODO: check if required
}
function checkCompilers() {
//check for gcc, grep, make, python-is-python3
if (
!(
checkDep("gcc", "/usr/bin/gcc") &&
checkDep("grep", "/usr/bin/grep") &&
checkDep("make", "/usr/bin/make") &&
checkDep("python3", "/usr/bin/python3")
)
) {
console.log("Compiler requirements not met. Please install the following: gcc, grep, make, python3");
exit(1);
}
//check if /usr/bin/python is a symlink to /usr/bin/python3
if (!fs.lstatSync("/usr/bin/python").isSymbolicLink()) {
console.log("/usr/bin/python is not a symlink. Please make sure it is a symlink to /usr/bin/python3");
if (fs.existsSync("/usr/bin/python3")) {
console.log("Hint: sudo ln -s /usr/bin/python3 /usr/bin/python");
}
exit(1);
}
}
function checkDep(name, path, message) {
if (!fs.existsSync(path)) {
console.log(`${name} not found at ${path}! Installation of some modules may fail!`);
console.log(message ?? `Please consult your distro's manual for installation instructions.`);
}
return fs.existsSync(path);
}
+25 -22
View File
@@ -1,34 +1,37 @@
const path = require("path");
const fs = require("fs");
const { execIn, getLines, parts } = require('./utils');
const { execIn, getLines } = require("./utils");
if (!process.argv[2] || !fs.existsSync(process.argv[2])) {
console.log("Please pass a directory that exists!");
process.exit(1);
console.log("Please pass a directory that exists!");
process.exit(1);
}
console.log(`// ${process.argv[2]}/index.ts`)
const recurse = process.argv.includes("--recursive")
console.log(`// ${process.argv[2]}/index.ts`);
const recurse = process.argv.includes("--recursive");
const files = fs.readdirSync(process.argv[2]).filter(x => x.endsWith('.ts') && x != 'index.ts');
const files = fs.readdirSync(process.argv[2]).filter((x) => x.endsWith(".ts") && x != "index.ts");
let output = '';
let output = "";
files.forEach(x => output += `export * from "./${x.replaceAll('.ts','')}";\n`)
files.forEach((x) => (output += `export * from "./${x.replaceAll(".ts", "")}";\n`));
const dirs = fs.readdirSync(process.argv[2]).filter(x => {
try {
fs.readdirSync(path.join(process.argv[2], x));
return true;
} catch (e) {
return false;
}
const dirs = fs.readdirSync(process.argv[2]).filter((x) => {
try {
fs.readdirSync(path.join(process.argv[2], x));
return true;
} catch (e) {
return false;
}
});
dirs.forEach((x) => {
output += `export * from "./${x}/index";\n`;
});
dirs.forEach(x => {
output += `export * from "./${x}/index";\n`
})
console.log(output);
fs.writeFileSync(path.join(process.argv[2], "index.ts"), output)
fs.writeFileSync(path.join(process.argv[2], "index.ts"), output);
dirs.forEach(x => {
if(recurse) console.log(execIn([process.argv[0], process.argv[1], `"${path.join(process.argv[2], x)}"`, "--recursive"].join(' '), process.cwd()))
})
dirs.forEach((x) => {
if (recurse)
console.log(
execIn([process.argv[0], process.argv[1], `"${path.join(process.argv[2], x)}"`, "--recursive"].join(" "), process.cwd())
);
});
+3 -8
View File
@@ -36,7 +36,7 @@ const Excluded = [
"UncheckedPropertiesSchema",
"PropertiesSchema",
"AsyncSchema",
"AnySchema",
"AnySchema"
];
function modify(obj) {
@@ -48,13 +48,8 @@ function modify(obj) {
}
function main() {
const files = [
...walk(path.join(__dirname, "..", "src", "util", "schemas")),
];
const program = TJS.getProgramFromFiles(
files,
compilerOptions
);
const files = [...walk(path.join(__dirname, "..", "src", "util", "schemas"))];
const program = TJS.getProgramFromFiles(files, compilerOptions);
const generator = TJS.buildGenerator(program, settings);
if (!generator || !program) return;
+3 -3
View File
@@ -25,7 +25,7 @@ const {
Template,
User,
VoiceState,
Webhook,
Webhook
} = require("../../dist/entities/index");
async function main() {
@@ -54,7 +54,7 @@ async function main() {
VoiceState,
Webhook,
Message,
Attachment,
Attachment
];
const oldDB = await initDatabase();
@@ -69,7 +69,7 @@ async function main() {
database: isSqlite ? process.env.TO : undefined,
entities,
name: "new",
synchronize: true,
synchronize: true
});
let i = 0;
+9 -9
View File
@@ -6,20 +6,20 @@ const { argv, stdout, exit } = require("process");
const { execIn, getLines, parts } = require("./utils");
let lines = fs.readFileSync(path.join(__dirname, "..", "src", "util", "util","Rights.ts")).toString()
let lines = fs.readFileSync(path.join(__dirname, "..", "src", "util", "util", "Rights.ts")).toString();
let lines2 = lines.split("\n");
let lines3 = lines2.filter(y=>y.includes(": BitFlag("));
let lines4 = lines3.map(x=>x.split("//")[0].trim())
let lines3 = lines2.filter((y) => y.includes(": BitFlag("));
let lines4 = lines3.map((x) => x.split("//")[0].trim());
function BitFlag(int) {
return 1n << eval(`${int}n`);
return 1n << BigInt(int);
}
let rights = []
let rights = [];
let maxRights = 0n;
lines4.forEach(x=>{
maxRights += eval(`rights.${x.replace(':'," = ").replace(",",";")}`)
})
lines4.forEach((x) => {
maxRights += eval(`rights.${x.replace(":", " = ").replace(",", ";")}`);
});
//max rights...
console.log(`Maximum rights: ${maxRights}`);
//discord rights...
@@ -31,4 +31,4 @@ discordRights -= rights.BYPASS_RATE_LIMITS;
discordRights -= rights.ADD_MEMBERS;
discordRights -= rights.MANAGE_RATE_LIMITS;
discordRights -= rights.OPERATOR;
console.log(`Discord-like rights: ${discordRights}`);
console.log(`Discord-like rights: ${discordRights}`);
+1 -1
View File
@@ -6,4 +6,4 @@ const { argv, stdout, exit } = require("process");
const { execIn, getLines, parts } = require("./utils");
execIn("node scripts/generate_schema.js", path.join('.'));
execIn("node scripts/generate_schema.js", path.join("."));
+42 -6
View File
@@ -4,6 +4,8 @@ const { env } = require("process");
const { execSync } = require("child_process");
const { argv, stdout, exit } = require("process");
const projectRoot = path.resolve(path.join(__dirname, ".."));
function copyRecursiveSync(src, dest) {
//if (verbose) console.log(`cpsync: ${src} -> ${dest}`);
let exists = fs.existsSync(src);
@@ -16,10 +18,7 @@ function copyRecursiveSync(src, dest) {
if (isDirectory) {
fs.mkdirSync(dest, { recursive: true });
fs.readdirSync(src).forEach(function (childItemName) {
copyRecursiveSync(
path.join(src, childItemName),
path.join(dest, childItemName)
);
copyRecursiveSync(path.join(src, childItemName), path.join(dest, childItemName));
});
} else {
fs.copyFileSync(src, dest);
@@ -44,8 +43,45 @@ function getLines(output) {
return output.split("\n").length;
}
module.exports = {
function getDirs(dir) {
return fs.readdirSync(dir).filter((x) => {
try {
fs.readdirSync(dir.join(dir, x));
return true;
} catch (e) {
return false;
}
});
}
function walk(dir) {
let results = [];
let list = fs.readdirSync(dir);
list.forEach(function (file) {
file = dir + "/" + file;
let stat = fs.statSync(file);
if (stat && stat.isDirectory()) {
/* Recurse into a subdirectory */
results = results.concat(walk(file));
} else {
results.push(file);
}
});
return results;
}
function sanitizeVarName(str) {
return str.replace("-", "_").replace(/[^\w\s]/gi, "");
}
module.exports = {
//consts
projectRoot,
//functions
copyRecursiveSync, execIn, getLines
copyRecursiveSync,
execIn,
getLines,
getDirs,
walk,
sanitizeVarName
};
+20
View File
@@ -0,0 +1,20 @@
const readline = require("readline");
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
async function ask(question) {
return new Promise((resolve, _reject) => {
return rl.question(question, (answer) => {
resolve(answer);
});
}).catch((err) => {
console.log(err);
});
}
async function askBool(question) {
return /y?/i.test(await ask(question));
}
module.exports = {
ask,
askBool
}
+11 -45
View File
@@ -1,15 +1,15 @@
process.on("unhandledRejection", console.error);
process.on("uncaughtException", console.error);
import http from "http";
import * as Api from "@fosscord/api";
import * as Gateway from "@fosscord/gateway";
import { CDNServer } from "@fosscord/cdn";
import express from "express";
import { green, bold, yellow } from "picocolors";
import * as Gateway from "@fosscord/gateway";
import { Config, getOrInitialiseDatabase } from "@fosscord/util";
import * as Sentry from "@sentry/node";
import * as Tracing from "@sentry/tracing";
import express from "express";
import http from "http";
import { bold, green, yellow } from "picocolors";
// import { PluginLoader } from "@fosscord/util";
const app = express();
@@ -26,59 +26,25 @@ const cdn = new CDNServer({ server, port, production, app });
const gateway = new Gateway.Server({ server, port, production });
//this is what has been added for the /stop API route
process.on('SIGTERM', () => {
setTimeout(()=>process.exit(0), 3000)
process.on("SIGTERM", () => {
setTimeout(() => process.exit(0), 3000);
server.close(() => {
console.log("Stop API has been successfully POSTed, SIGTERM sent")
})
})
console.log("Stop API has been successfully POSTed, SIGTERM sent");
});
});
//this is what has been added for the /stop API route
async function main() {
server.listen(port);
await getOrInitialiseDatabase();
await Config.init();
// only set endpointPublic, if not already set
await Config.set({
cdn: {
endpointClient: "${location.host}",
endpointPrivate: `http://localhost:${port}`,
},
gateway: {
endpointClient:
'${location.protocol === "https:" ? "wss://" : "ws://"}${location.host}',
endpointPrivate: `ws://localhost:${port}`,
...(!Config.get().gateway.endpointPublic && {
endpointPublic: `ws://localhost:${port}`,
}),
},
// regions: {
// default: "fosscord",
// useDefaultAsOptimal: true,
// available: [
// {
// id: "fosscord",
// name: "Fosscord",
// endpoint: "127.0.0.1:3001",
// vip: false,
// custom: false,
// deprecated: false,
// },
// ],
// },
} as any);
//Sentry
if (Config.get().sentry.enabled) {
console.log(
`[Bundle] ${yellow("You are using Sentry! This may slightly impact performance on large loads!")}`
);
console.log(`[Bundle] ${yellow("You are using Sentry! This may slightly impact performance on large loads!")}`);
Sentry.init({
dsn: Config.get().sentry.endpoint,
integrations: [
new Sentry.Integrations.Http({ tracing: true }),
new Tracing.Integrations.Express({ app }),
],
integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })],
tracesSampleRate: Config.get().sentry.traceSampleRate,
environment: Config.get().sentry.environment
});
+15 -10
View File
@@ -1,16 +1,16 @@
import { Server, ServerOptions } from "lambert-server";
import { Authentication, CORS } from "./middlewares/";
import { Config, getOrInitialiseDatabase, initEvent, registerRoutes } from "@fosscord/util";
import { ErrorHandler } from "./middlewares/ErrorHandler";
import { BodyParser } from "./middlewares/BodyParser";
import { Router, Request, Response, NextFunction } from "express";
import { NextFunction, Request, Response, Router } from "express";
import { Server, ServerOptions } from "lambert-server";
import morgan from "morgan";
import path from "path";
import { red } from "picocolors";
import { Authentication, CORS } from "./middlewares/";
import { BodyParser } from "./middlewares/BodyParser";
import { ErrorHandler } from "./middlewares/ErrorHandler";
import { initRateLimits } from "./middlewares/RateLimit";
import TestClient from "./middlewares/TestClient";
import { initTranslation } from "./middlewares/Translation";
import morgan from "morgan";
import { initInstance } from "./util/handlers/Instance";
import { red } from "picocolors"
export interface FosscordServerOptions extends ServerOptions {}
@@ -85,8 +85,13 @@ export class FosscordServer extends Server {
this.app.use(ErrorHandler);
TestClient(this.app);
if (logRequests) console.log(red(`Warning: Request logging is enabled! This will spam your console!\nTo disable this, unset the 'LOG_REQUESTS' environment variable!`));
if (logRequests)
console.log(
red(
`Warning: Request logging is enabled! This will spam your console!\nTo disable this, unset the 'LOG_REQUESTS' environment variable!`
)
);
return super.start();
}
}
}
+1 -1
View File
@@ -1,3 +1,3 @@
export * from "./Server";
export * from "./middlewares/";
export * from "./Server";
export * from "./util/";
+2 -3
View File
@@ -1,6 +1,5 @@
import { checkToken, Config, HTTPError, Rights } from "@fosscord/util";
import { NextFunction, Request, Response } from "express";
import { HTTPError } from "@fosscord/util";
import { checkToken, Config, Rights } from "@fosscord/util";
export const NO_AUTHORIZATION_ROUTES = [
// Authentication routes
@@ -10,7 +9,7 @@ export const NO_AUTHORIZATION_ROUTES = [
"/auth/mfa/totp",
// Routes with a seperate auth system
"/webhooks/",
// Public information endpoints
// Public information endpoints
"/ping",
"/gateway",
"/experiments",
+1 -1
View File
@@ -1,6 +1,6 @@
import { HTTPError } from "@fosscord/util";
import bodyParser, { OptionsJson } from "body-parser";
import { NextFunction, Request, Response } from "express";
import { HTTPError } from "@fosscord/util";
export function BodyParser(opts?: OptionsJson) {
const jsonParser = bodyParser.json(opts);
+1 -2
View File
@@ -1,6 +1,5 @@
import { ApiError, FieldError, HTTPError } from "@fosscord/util";
import { NextFunction, Request, Response } from "express";
import { HTTPError } from "@fosscord/util";
import { ApiError, FieldError } from "@fosscord/util";
const EntityNotFoundErrorRegex = /"(\w+)"/;
export function ErrorHandler(error: Error, req: Request, res: Response, next: NextFunction) {
+3 -3
View File
@@ -1,6 +1,6 @@
import { Config, getRights, listenEvent, Rights } from "@fosscord/util";
import { NextFunction, Request, Response, Router } from "express";
import { getIpAdress } from "@fosscord/api";
import { Config, getRights, listenEvent } from "@fosscord/util";
import { NextFunction, Request, Response, Router } from "express";
import { API_PREFIX_TRAILING_SLASH } from "./Authentication";
// Docs: https://discord.com/developers/docs/topics/rate-limits
@@ -163,7 +163,7 @@ export async function initRateLimits(app: Router) {
app.use("/auth/register", rateLimit({ onlyIp: true, success: true, ...routes.auth.register }));
}
async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits: number; window: number; }) {
async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits: number; window: number }) {
const id = opts.executor_id + opts.bucket_id;
let limit = Cache.get(id);
if (!limit) {
+36 -37
View File
@@ -1,17 +1,17 @@
import express, { Request, Response, Application } from "express";
import fs from "fs";
import path from "path";
import fetch, { Response as FetchResponse, Headers } from "node-fetch";
import ProxyAgent from 'proxy-agent';
import { Config } from "@fosscord/util";
import { AssetCacheItem } from "../util/entities/AssetCacheItem"
import express, { Application, Request, Response } from "express";
import fs from "fs";
import fetch, { Headers, Response as FetchResponse } from "node-fetch";
import path from "path";
import { green } from "picocolors";
import ProxyAgent from "proxy-agent";
import { AssetCacheItem } from "../util/entities/AssetCacheItem";
const AssetsPath = path.join(__dirname, "..", "..", "..", "assets")
const AssetsPath = path.join(__dirname, "..", "..", "..", "assets");
export default function TestClient(app: Application) {
const agent = new ProxyAgent();
//build client page
let html = fs.readFileSync(path.join(AssetsPath, "index.html"), { encoding: "utf8" });
html = applyEnv(html);
@@ -22,31 +22,29 @@ export default function TestClient(app: Application) {
//load asset cache
let newAssetCache: Map<string, AssetCacheItem> = new Map<string, AssetCacheItem>();
let assetCacheDir = path.join(AssetsPath, "cache");
if(process.env.ASSET_CACHE_DIR)
assetCacheDir = process.env.ASSET_CACHE_DIR
if (process.env.ASSET_CACHE_DIR) assetCacheDir = process.env.ASSET_CACHE_DIR;
console.log(`[TestClient] ${green(`Using asset cache path: ${assetCacheDir}`)}`)
if(!fs.existsSync(assetCacheDir)) {
console.log(`[TestClient] ${green(`Using asset cache path: ${assetCacheDir}`)}`);
if (!fs.existsSync(assetCacheDir)) {
fs.mkdirSync(assetCacheDir);
}
if(fs.existsSync(path.join(assetCacheDir, "index.json"))) {
if (fs.existsSync(path.join(assetCacheDir, "index.json"))) {
let rawdata = fs.readFileSync(path.join(assetCacheDir, "index.json"));
newAssetCache = new Map<string, AssetCacheItem>(Object.entries(JSON.parse(rawdata.toString())));
}
app.use("/assets", express.static(path.join(AssetsPath)));
app.use("/assets", express.static(path.join(AssetsPath)));
app.get("/assets/:file", async (req: Request, res: Response) => {
delete req.headers.host;
let response: FetchResponse;
let buffer: Buffer;
let assetCacheItem: AssetCacheItem = new AssetCacheItem(req.params.file);
if(newAssetCache.has(req.params.file)){
if (newAssetCache.has(req.params.file)) {
assetCacheItem = newAssetCache.get(req.params.file)!;
assetCacheItem.Headers.forEach((value: any, name: any) => {
res.set(name, value);
});
}
else {
} else {
console.log(`[TestClient] Downloading file not yet cached! Asset file: ${req.params.file}`);
response = await fetch(`https://discord.com/assets/${req.params.file}`, {
agent,
@@ -55,7 +53,7 @@ export default function TestClient(app: Application) {
...req.headers
}
});
//set cache info
assetCacheItem.Headers = Object.fromEntries(stripHeaders(response.headers));
assetCacheItem.FilePath = path.join(assetCacheDir, req.params.file);
@@ -66,7 +64,7 @@ export default function TestClient(app: Application) {
//download file
fs.writeFileSync(assetCacheItem.FilePath, await response.buffer());
}
assetCacheItem.Headers.forEach((value: string, name: string) => {
res.set(name, value);
});
@@ -77,8 +75,8 @@ export default function TestClient(app: Application) {
res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);
res.set("content-type", "text/html");
if(!useTestClient) return res.send("Test client is disabled on this instance. Use a stand-alone client to connect this instance.")
if (!useTestClient) return res.send("Test client is disabled on this instance. Use a stand-alone client to connect this instance.");
res.send(fs.readFileSync(path.join(__dirname, "..", "..", "..", "assets", "developers.html"), { encoding: "utf8" }));
});
app.get("*", (req: Request, res: Response) => {
@@ -86,23 +84,18 @@ export default function TestClient(app: Application) {
res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);
res.set("content-type", "text/html");
if(req.url.startsWith("/api") || req.url.startsWith("/__development")) return;
if (req.url.startsWith("/api") || req.url.startsWith("/__development")) return;
if(!useTestClient) return res.send("Test client is disabled on this instance. Use a stand-alone client to connect this instance.")
if (!useTestClient) return res.send("Test client is disabled on this instance. Use a stand-alone client to connect this instance.");
if (req.url.startsWith("/invite")) return res.send(html.replace("9b2b7f0632acd0c5e781", "9f24f709a3de09b67c49"));
res.send(html);
});
}
function applyEnv(html: string): string {
const CDN_ENDPOINT = (Config.get().cdn.endpointClient || Config.get()?.cdn.endpointPublic || process.env.CDN || "").replace(
/(https?)?(:\/\/?)/g,
""
);
const GATEWAY_ENDPOINT = Config.get().gateway.endpointClient || Config.get()?.gateway.endpointPublic || process.env.GATEWAY || "";
const CDN_ENDPOINT = (Config.get()?.cdn.endpointPublic || process.env.CDN || "").replace(/(https?)?(:\/\/?)/g, "");
const GATEWAY_ENDPOINT = Config.get()?.gateway.endpointPublic || process.env.GATEWAY || "";
if (CDN_ENDPOINT) {
html = html.replace(/CDN_HOST: .+/, `CDN_HOST: \`${CDN_ENDPOINT}\`,`);
@@ -117,23 +110,29 @@ function applyPlugins(html: string): string {
// plugins
let files = fs.readdirSync(path.join(AssetsPath, "plugins"));
let plugins = "";
files.forEach(x =>{if(x.endsWith(".js")) plugins += `<script src='/assets/plugins/${x}'></script>\n`; });
files.forEach((x) => {
if (x.endsWith(".js")) plugins += `<script src='/assets/plugins/${x}'></script>\n`;
});
return html.replaceAll("<!-- plugin marker -->", plugins);
}
function applyInlinePlugins(html: string): string{
function applyInlinePlugins(html: string): string {
// inline plugins
let files = fs.readdirSync(path.join(AssetsPath, "inline-plugins"));
let plugins = "";
files.forEach(x =>{if(x.endsWith(".js")) plugins += `<script src='/assets/inline-plugins/${x}'></script>\n\n`; });
files.forEach((x) => {
if (x.endsWith(".js")) plugins += `<script src='/assets/inline-plugins/${x}'></script>\n\n`;
});
return html.replaceAll("<!-- inline plugin marker -->", plugins);
}
function applyPreloadPlugins(html: string): string{
function applyPreloadPlugins(html: string): string {
//preload plugins
let files = fs.readdirSync(path.join(AssetsPath, "preload-plugins"));
let plugins = "";
files.forEach(x =>{if(x.endsWith(".js")) plugins += `<script>${fs.readFileSync(path.join(AssetsPath, "preload-plugins", x))}</script>\n`; });
files.forEach((x) => {
if (x.endsWith(".js")) plugins += `<script>${fs.readFileSync(path.join(AssetsPath, "preload-plugins", x))}</script>\n`;
});
return html.replaceAll("<!-- preload plugin marker -->", plugins);
}
@@ -147,7 +146,7 @@ function stripHeaders(headers: Headers): Headers {
"expect-ct",
"access-control-allow-origin",
"content-encoding"
].forEach(headerName => {
].forEach((headerName) => {
headers.delete(headerName);
});
return headers;
+2 -2
View File
@@ -1,9 +1,9 @@
import { Router } from "express";
import fs from "fs";
import path from "path";
import i18next from "i18next";
import i18nextMiddleware from "i18next-http-middleware";
import i18nextBackend from "i18next-node-fs-backend";
import { Router } from "express";
import path from "path";
export async function initTranslation(router: Router) {
const languages = fs.readdirSync(path.join(__dirname, "..", "..", "..", "assets", "locales"));
+1 -1
View File
@@ -1,5 +1,5 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
import { getConnection } from "typeorm";
const router = Router();
+1 -1
View File
@@ -1,5 +1,5 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
import { getConnection } from "typeorm";
const router = Router();
+16 -16
View File
@@ -1,14 +1,14 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
import { Application, Config, FieldErrors, generateToken, OrmUtils, Snowflake, trimSpecial, User, handleFile } from "@fosscord/util";
import { Application, Config, FieldErrors, generateToken, handleFile, OrmUtils, trimSpecial, User } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { verifyToken } from "node-2fa";
const router: Router = Router();
router.post("/", route({}), async (req: Request, res: Response) => {
const app = await Application.findOne({where: {id: req.params.id}});
if(!app) return res.status(404);
const app = await Application.findOne({ where: { id: req.params.id } });
if (!app) return res.status(404);
const username = trimSpecial(app.name);
const discriminator = await User.generateDiscriminator(username);
if (!discriminator) {
@@ -16,8 +16,8 @@ router.post("/", route({}), async (req: Request, res: Response) => {
throw FieldErrors({
username: {
code: "USERNAME_TOO_MANY_USERS",
message: req?.t("auth:register.USERNAME_TOO_MANY_USERS"),
},
message: req?.t("auth:register.USERNAME_TOO_MANY_USERS")
}
});
}
@@ -47,37 +47,37 @@ router.post("/", route({}), async (req: Request, res: Response) => {
flags: "0",
data: {
hash: null,
valid_tokens_since: new Date(),
valid_tokens_since: new Date()
},
settings: {},
extended_settings: {},
fingerprints: [],
notes: {},
notes: {}
});
await user.save();
app.bot = user;
await app.save();
res.send().status(204)
res.send().status(204);
});
router.post("/reset", route({}), async (req: Request, res: Response) => {
let bot = await User.findOne({where: {id: req.params.id}});
let owner = await User.findOne({where: {id: req.user_id}});
if(!bot) return res.status(404);
if(owner?.totp_secret && (!req.body.code || verifyToken(owner.totp_secret, req.body.code))) {
let bot = await User.findOne({ where: { id: req.params.id } });
let owner = await User.findOne({ where: { id: req.user_id } });
if (!bot) return res.status(404);
if (owner?.totp_secret && (!req.body.code || verifyToken(owner.totp_secret, req.body.code))) {
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
}
bot.data = { hash: undefined, valid_tokens_since: new Date() };
await bot.save();
let token = await generateToken(bot.id);
res.json({token}).status(200);
res.json({ token }).status(200);
});
router.patch("/", route({}), async (req: Request, res: Response) => {
if (req.body.avatar) req.body.avatar = await handleFile(`/avatars/${req.params.id}`, req.body.avatar as string);
let app = OrmUtils.mergeDeep(await User.findOne({where: {id: req.params.id}}), req.body);
let app = OrmUtils.mergeDeep(await User.findOne({ where: { id: req.params.id } }), req.body);
await app.save();
res.json(app).status(200);
});
export default router;
export default router;
@@ -1,5 +1,5 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router();
+8 -9
View File
@@ -1,22 +1,22 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
import { Application, OrmUtils, Team, trimSpecial, User } from "@fosscord/util";
import { Application, OrmUtils } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router();
router.get("/", route({}), async (req: Request, res: Response) => {
let results = await Application.findOne({where: {id: req.params.id}, relations: ["owner", "bot"] });
let results = await Application.findOne({ where: { id: req.params.id }, relations: ["owner", "bot"] });
res.json(results).status(200);
});
router.patch("/", route({}), async (req: Request, res: Response) => {
delete req.body.icon;
let app = OrmUtils.mergeDeep(await Application.findOne({where: {id: req.params.id}, relations: ["owner", "bot"]}), req.body);
if(app.bot) {
app.bot.bio = req.body.description
let app = OrmUtils.mergeDeep(await Application.findOne({ where: { id: req.params.id }, relations: ["owner", "bot"] }), req.body);
if (app.bot) {
app.bot.bio = req.body.description;
app.bot?.save();
}
if(req.body.tags) app.tags = req.body.tags;
if (req.body.tags) app.tags = req.body.tags;
await app.save();
res.json(app).status(200);
});
@@ -26,5 +26,4 @@ router.post("/delete", route({}), async (req: Request, res: Response) => {
res.send().status(200);
});
export default router;
export default router;
+2 -3
View File
@@ -1,6 +1,5 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
import { Application, OrmUtils, Team, trimSpecial, User } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router();
@@ -8,4 +7,4 @@ router.get("/", route({}), async (req: Request, res: Response) => {
res.json([]).status(200);
});
export default router;
export default router;
+1 -1
View File
@@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router: Router = Router();
+6 -6
View File
@@ -1,6 +1,6 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
import { Application, OrmUtils, Team, trimSpecial, User } from "@fosscord/util";
import { Application, OrmUtils, trimSpecial, User } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router();
@@ -11,14 +11,14 @@ export interface ApplicationCreateSchema {
router.get("/", route({}), async (req: Request, res: Response) => {
//TODO
let results = await Application.find({where: {owner: {id: req.user_id}}, relations: ["owner", "bot"] });
let results = await Application.find({ where: { owner: { id: req.user_id } }, relations: ["owner", "bot"] });
res.json(results).status(200);
});
router.post("/", route({}), async (req: Request, res: Response) => {
const body = req.body as ApplicationCreateSchema;
const user = await User.findOne({where: {id: req.user_id}})
if(!user) res.status(420);
const user = await User.findOne({ where: { id: req.user_id } });
if (!user) res.status(420);
let app = OrmUtils.mergeDeep(new Application(), {
name: trimSpecial(body.name),
description: "",
@@ -31,4 +31,4 @@ router.post("/", route({}), async (req: Request, res: Response) => {
res.json(app).status(200);
});
export default router;
export default router;
+7 -8
View File
@@ -1,13 +1,12 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api";
import { getIpAdress, IPAnalysis } from "@fosscord/api";
import { getIpAdress, IPAnalysis, route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router();
router.get("/",route({}), async (req: Request, res: Response) => {
//TODO
//Note: It's most likely related to legal. At the moment Discord hasn't finished this too
const country_code = (await IPAnalysis(getIpAdress(req))).country_code;
res.json({ consent_required: false, country_code: country_code, promotional_email_opt_in: { required: true, pre_checked: false}});
router.get("/", route({}), async (req: Request, res: Response) => {
//TODO
//Note: It's most likely related to legal. At the moment Discord hasn't finished this too
const country_code = (await IPAnalysis(getIpAdress(req))).country_code;
res.json({ consent_required: false, country_code: country_code, promotional_email_opt_in: { required: true, pre_checked: false } });
});
export default router;
+13 -6
View File
@@ -1,8 +1,15 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
import bcrypt from "bcrypt";
import { Config, User, generateToken, adjustEmail, FieldErrors, LoginSchema } from "@fosscord/util";
import { adjustEmail, Config, FieldErrors, generateToken, LoginSchema, User } from "@fosscord/util";
import crypto from "crypto";
import { Request, Response, Router } from "express";
let bcrypt: any;
try {
bcrypt = require("bcrypt");
} catch {
bcrypt = require("bcryptjs");
console.log("Warning: using bcryptjs because bcrypt is not installed! Performance will be affected.");
}
const router: Router = Router();
export default router;
@@ -57,9 +64,9 @@ router.post("/", route({ body: "LoginSchema" }), async (req: Request, res: Respo
return res.json({
ticket: ticket,
mfa: true,
sms: false, // TODO
token: null,
})
sms: false, // TODO
token: null
});
}
const token = await generateToken(user.id);
+8 -14
View File
@@ -1,8 +1,8 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api";
import { BackupCode, FieldErrors, generateToken, TotpSchema, User } from "@fosscord/util";
import { verifyToken } from "node-2fa";
import { BackupCode, generateToken, TotpSchema, User } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { verifyToken } from "node-2fa";
const router = Router();
router.post("/", route({ body: "TotpSchema" }), async (req: Request, res: Response) => {
@@ -10,23 +10,17 @@ router.post("/", route({ body: "TotpSchema" }), async (req: Request, res: Respon
const user = await User.findOneOrFail({
where: {
totp_last_ticket: ticket,
totp_last_ticket: ticket
},
select: [
"id",
"totp_secret",
"settings",
],
select: ["id", "totp_secret", "settings"]
});
const backup = await BackupCode.findOne({ where: { code: code, expired: false, consumed: false, user: { id: user.id } } });
if (!backup) {
const ret = verifyToken(user.totp_secret!, code);
if (!ret || ret.delta != 0)
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
}
else {
if (!ret || ret.delta != 0) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
} else {
backup.consumed = true;
await backup.save();
}
@@ -35,7 +29,7 @@ router.post("/", route({ body: "TotpSchema" }), async (req: Request, res: Respon
return res.json({
token: await generateToken(user.id),
user_settings: user.settings,
user_settings: user.settings
});
});
+10 -4
View File
@@ -1,8 +1,14 @@
import { getIpAdress, IPAnalysis, isProxy, route } from "@fosscord/api";
import { adjustEmail, Config, FieldErrors, generateToken, HTTPError, Invite, RegisterSchema, User } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { Config, generateToken, Invite, FieldErrors, User, adjustEmail, trimSpecial, RegisterSchema } from "@fosscord/util";
import { route, getIpAdress, IPAnalysis, isProxy } from "@fosscord/api";
import bcrypt from "bcrypt";
import { HTTPError } from "@fosscord/util";
let bcrypt: any;
try {
bcrypt = require("bcrypt");
} catch {
bcrypt = require("bcryptjs");
console.log("Warning: using bcryptjs because bcrypt is not installed! Performance will be affected.");
}
const router: Router = Router();
@@ -1,4 +1,4 @@
import { Router, Response, Request } from "express";
import { Router } from "express";
const router: Router = Router();
// TODO:
+4 -5
View File
@@ -1,17 +1,16 @@
import { route } from "@fosscord/api";
import {
Channel,
ChannelDeleteEvent,
ChannelPermissionOverwriteType,
ChannelModifySchema,
ChannelType,
ChannelUpdateEvent,
emitEvent,
Recipient,
handleFile,
ChannelModifySchema
OrmUtils,
Recipient
} from "@fosscord/util";
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
import { OrmUtils } from "@fosscord/util";
const router: Router = Router();
// TODO: delete channel
+35 -35
View File
@@ -1,45 +1,45 @@
import { Router, Request, Response } from "express";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api";
import { random } from "@fosscord/api";
import { Channel, Invite, InviteCreateEvent, emitEvent, User, Guild, PublicInviteRelation } from "@fosscord/util";
import { Channel, emitEvent, Guild, HTTPError, Invite, InviteCreateEvent, OrmUtils, PublicInviteRelation, User } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { isTextChannel } from "./messages";
import { OrmUtils } from "@fosscord/util";
const router: Router = Router();
router.post("/", route({ body: "InviteCreateSchema", permission: "CREATE_INSTANT_INVITE", right: "CREATE_INVITES" }),
async (req: Request, res: Response) => {
const { user_id } = req;
const { channel_id } = req.params;
const channel = await Channel.findOneOrFail({ where: { id: channel_id }, select: ["id", "name", "type", "guild_id"] });
isTextChannel(channel.type);
router.post(
"/",
route({ body: "InviteCreateSchema", permission: "CREATE_INSTANT_INVITE", right: "CREATE_INVITES" }),
async (req: Request, res: Response) => {
const { user_id } = req;
const { channel_id } = req.params;
const channel = await Channel.findOneOrFail({ where: { id: channel_id }, select: ["id", "name", "type", "guild_id"] });
isTextChannel(channel.type);
if (!channel.guild_id) {
throw new HTTPError("This channel doesn't exist", 404);
if (!channel.guild_id) {
throw new HTTPError("This channel doesn't exist", 404);
}
const { guild_id } = channel;
const expires_at = new Date(req.body.max_age * 1000 + Date.now());
const invite = await OrmUtils.mergeDeep(new Invite(), {
temporary: req.body.temporary || true,
max_uses: req.body.max_uses,
max_age: req.body.max_age,
expires_at,
guild_id,
channel_id,
inviter_id: user_id
}).save();
//TODO: check this, removed toJSON call
const data = JSON.parse(JSON.stringify(invite));
data.inviter = await User.getPublicUser(req.user_id);
data.guild = await Guild.findOne({ where: { id: guild_id } });
data.channel = channel;
await emitEvent({ event: "INVITE_CREATE", data, guild_id } as InviteCreateEvent);
res.status(201).send(data);
}
const { guild_id } = channel;
const expires_at = new Date(req.body.max_age * 1000 + Date.now());
const invite = await OrmUtils.mergeDeep(new Invite(),{
temporary: req.body.temporary || true,
max_uses: req.body.max_uses,
max_age: req.body.max_age,
expires_at,
guild_id,
channel_id,
inviter_id: user_id
}).save();
//TODO: check this, removed toJSON call
const data = JSON.parse(JSON.stringify(invite));
data.inviter = await User.getPublicUser(req.user_id);
data.guild = await Guild.findOne({ where: { id: guild_id } });
data.channel = channel;
await emitEvent({ event: "INVITE_CREATE", data, guild_id } as InviteCreateEvent);
res.status(201).send(data);
});
);
router.get("/", route({ permission: "MANAGE_CHANNELS" }), async (req: Request, res: Response) => {
const { channel_id } = req.params;
@@ -1,7 +1,6 @@
import { emitEvent, getPermission, MessageAckEvent, ReadState, Snowflake } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
import { OrmUtils } from "@fosscord/util";
import { emitEvent, getPermission, MessageAckEvent, OrmUtils, ReadState } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();
@@ -1,5 +1,5 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router();
@@ -1,25 +1,22 @@
import { handleMessage, postHandleMessage, route } from "@fosscord/api";
import {
Attachment,
Channel,
Embed,
DiscordApiErrors,
emitEvent,
FosscordApiErrors,
getPermission,
getRights,
Message,
HTTPError,
Message,
MessageCreateEvent,
MessageCreateSchema,
MessageDeleteEvent,
MessageUpdateEvent,
Snowflake,
uploadFile,
MessageCreateSchema
uploadFile
} from "@fosscord/util";
import { Router, Response, Request } from "express";
import { Request, Response, Router } from "express";
import multer from "multer";
import { route } from "@fosscord/api";
import { handleMessage, postHandleMessage } from "@fosscord/api";
import { HTTPError } from "@fosscord/util";
const router = Router();
// TODO: message content/embed string length limit
@@ -33,50 +30,53 @@ const messageUpload = multer({
storage: multer.memoryStorage()
}); // max upload 50 mb
router.patch("/", route({ body: "MessageCreateSchema", permission: "SEND_MESSAGES", right: "SEND_MESSAGES" }), async (req: Request, res: Response) => {
const { message_id, channel_id } = req.params;
let body = req.body as MessageCreateSchema;
router.patch(
"/",
route({ body: "MessageCreateSchema", permission: "SEND_MESSAGES", right: "SEND_MESSAGES" }),
async (req: Request, res: Response) => {
const { message_id, channel_id } = req.params;
let body = req.body as MessageCreateSchema;
const message = await Message.findOneOrFail({ where: { id: message_id, channel_id }, relations: ["attachments"] });
const message = await Message.findOneOrFail({ where: { id: message_id, channel_id }, relations: ["attachments"] });
const permissions = await getPermission(req.user_id, undefined, channel_id);
const rights = await getRights(req.user_id);
const permissions = await getPermission(req.user_id, undefined, channel_id);
if ((req.user_id !== message.author_id)) {
if (!rights.has("MANAGE_MESSAGES")) {
permissions.hasThrow("MANAGE_MESSAGES");
body = { flags: body.flags };
// guild admins can only suppress embeds of other messages, no such restriction imposed to instance-wide admins
}
} else rights.hasThrow("SELF_EDIT_MESSAGES");
const rights = await getRights(req.user_id);
const new_message = await handleMessage({
...message,
// TODO: should message_reference be overridable?
// @ts-ignore
message_reference: message.message_reference,
...body,
author_id: message.author_id,
channel_id,
id: message_id,
edited_timestamp: new Date()
});
if (req.user_id !== message.author_id) {
if (!rights.has("MANAGE_MESSAGES")) {
permissions.hasThrow("MANAGE_MESSAGES");
body = { flags: body.flags };
// guild admins can only suppress embeds of other messages, no such restriction imposed to instance-wide admins
}
} else rights.hasThrow("SELF_EDIT_MESSAGES");
await Promise.all([
new_message!.save(),
await emitEvent({
event: "MESSAGE_UPDATE",
const new_message = await handleMessage({
...message,
// TODO: should message_reference be overridable?
// @ts-ignore
message_reference: message.message_reference,
...body,
author_id: message.author_id,
channel_id,
data: { ...new_message, nonce: undefined }
} as MessageUpdateEvent)
]);
id: message_id,
edited_timestamp: new Date()
});
postHandleMessage(message);
await Promise.all([
new_message!.save(),
await emitEvent({
event: "MESSAGE_UPDATE",
channel_id,
data: { ...new_message, nonce: undefined }
} as MessageUpdateEvent)
]);
return res.json(message);
});
postHandleMessage(message);
return res.json(message);
}
);
// Backfill message with specific timestamp
router.put(
@@ -94,7 +94,7 @@ router.put(
const { channel_id, message_id } = req.params;
let body = req.body as MessageCreateSchema;
const attachments: Attachment[] = [];
const rights = await getRights(req.user_id);
rights.hasThrow("SEND_MESSAGES");
@@ -103,13 +103,13 @@ router.put(
throw new HTTPError("Message IDs must be positive integers", 400);
}
const snowflake = Snowflake.deconstruct(message_id)
const snowflake = Snowflake.deconstruct(message_id);
if (Date.now() < snowflake.timestamp) {
// message is in the future
throw FosscordApiErrors.CANNOT_BACKFILL_TO_THE_FUTURE;
}
const exists = await Message.findOne({ where: { id: message_id, channel_id: channel_id }});
const exists = await Message.findOne({ where: { id: message_id, channel_id: channel_id } });
if (exists) {
throw FosscordApiErrors.CANNOT_REPLACE_BY_BACKFILL;
}
@@ -136,19 +136,19 @@ router.put(
channel_id,
attachments,
edited_timestamp: undefined,
timestamp: new Date(snowflake.timestamp),
timestamp: new Date(snowflake.timestamp)
});
//Fix for the client bug
delete message.member
delete message.member;
await Promise.all([
message.save(),
emitEvent({ event: "MESSAGE_CREATE", channel_id: channel_id, data: message } as MessageCreateEvent),
channel.save()
]);
postHandleMessage(message).catch((e) => { }); // no await as it shouldnt block the message send function and silently catch error
postHandleMessage(message).catch((e) => {}); // no await as it shouldnt block the message send function and silently catch error
return res.json(message);
}
@@ -160,7 +160,7 @@ router.get("/", route({ permission: "VIEW_CHANNEL" }), async (req: Request, res:
const message = await Message.findOneOrFail({ where: { id: message_id, channel_id }, relations: ["attachments"] });
const permissions = await getPermission(req.user_id, undefined, channel_id);
if (message.author_id !== req.user_id) permissions.hasThrow("READ_MESSAGE_HISTORY");
return res.json(message);
@@ -171,10 +171,10 @@ router.delete("/", route({}), async (req: Request, res: Response) => {
const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
const message = await Message.findOneOrFail({ where: { id: message_id } });
const rights = await getRights(req.user_id);
if ((message.author_id !== req.user_id)) {
if (message.author_id !== req.user_id) {
if (!rights.has("MANAGE_MESSAGES")) {
const permission = await getPermission(req.user_id, channel.guild_id, channel_id);
permission.hasThrow("MANAGE_MESSAGES");
@@ -1,8 +1,10 @@
import { route } from "@fosscord/api";
import {
Channel,
emitEvent,
Emoji,
getPermission,
HTTPError,
Member,
Message,
MessageReactionAddEvent,
@@ -13,9 +15,7 @@ import {
PublicUserProjection,
User
} from "@fosscord/util";
import { route } from "@fosscord/api";
import { Router, Response, Request } from "express";
import { HTTPError } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { In } from "typeorm";
const router = Router();
@@ -101,48 +101,52 @@ router.get("/:emoji", route({ permission: "VIEW_CHANNEL" }), async (req: Request
res.json(users);
});
router.put("/:emoji/:user_id", route({ permission: "READ_MESSAGE_HISTORY", right: "SELF_ADD_REACTIONS" }), async (req: Request, res: Response) => {
const { message_id, channel_id, user_id } = req.params;
if (user_id !== "@me") throw new HTTPError("Invalid user");
const emoji = getEmoji(req.params.emoji);
router.put(
"/:emoji/:user_id",
route({ permission: "READ_MESSAGE_HISTORY", right: "SELF_ADD_REACTIONS" }),
async (req: Request, res: Response) => {
const { message_id, channel_id, user_id } = req.params;
if (user_id !== "@me") throw new HTTPError("Invalid user");
const emoji = getEmoji(req.params.emoji);
const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
const message = await Message.findOneOrFail({ where: { id: message_id, channel_id } });
const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
const message = await Message.findOneOrFail({ where: { id: message_id, channel_id } });
const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
if (!already_added) req.permission!.hasThrow("ADD_REACTIONS");
if (!already_added) req.permission!.hasThrow("ADD_REACTIONS");
if (emoji.id) {
const external_emoji = await Emoji.findOneOrFail({ where: { id: emoji.id } });
if (!already_added) req.permission!.hasThrow("USE_EXTERNAL_EMOJIS");
emoji.animated = external_emoji.animated;
emoji.name = external_emoji.name;
}
if (already_added) {
if (already_added.user_ids.includes(req.user_id)) return res.sendStatus(204); // Do not throw an error ¯\_(ツ)_/¯ as discord also doesn't throw any error
already_added.count++;
} else message.reactions.push({ count: 1, emoji, user_ids: [req.user_id] });
await message.save();
const member = channel.guild_id && (await Member.findOneOrFail({ where: { id: req.user_id } }));
await emitEvent({
event: "MESSAGE_REACTION_ADD",
channel_id,
data: {
user_id: req.user_id,
channel_id,
message_id,
guild_id: channel.guild_id,
emoji,
member
if (emoji.id) {
const external_emoji = await Emoji.findOneOrFail({ where: { id: emoji.id } });
if (!already_added) req.permission!.hasThrow("USE_EXTERNAL_EMOJIS");
emoji.animated = external_emoji.animated;
emoji.name = external_emoji.name;
}
} as MessageReactionAddEvent);
res.sendStatus(204);
});
if (already_added) {
if (already_added.user_ids.includes(req.user_id)) return res.sendStatus(204); // Do not throw an error ¯\_(ツ)_/¯ as discord also doesn't throw any error
already_added.count++;
} else message.reactions.push({ count: 1, emoji, user_ids: [req.user_id] });
await message.save();
const member = channel.guild_id && (await Member.findOneOrFail({ where: { id: req.user_id } }));
await emitEvent({
event: "MESSAGE_REACTION_ADD",
channel_id,
data: {
user_id: req.user_id,
channel_id,
message_id,
guild_id: channel.guild_id,
emoji,
member
}
} as MessageReactionAddEvent);
res.sendStatus(204);
}
);
router.delete("/:emoji/:user_id", route({}), async (req: Request, res: Response) => {
let { message_id, channel_id, user_id } = req.params;
@@ -1,7 +1,6 @@
import { Router, Response, Request } from "express";
import { Channel, Config, emitEvent, getPermission, getRights, MessageDeleteBulkEvent, Message } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api";
import { Channel, Config, emitEvent, getPermission, getRights, HTTPError, Message, MessageDeleteBulkEvent } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { In } from "typeorm";
const router: Router = Router();
@@ -13,7 +12,7 @@ export default router;
// https://discord.com/developers/docs/resources/channel#bulk-delete-messages
router.post("/", route({ body: "BulkDeleteSchema" }), async (req: Request, res: Response) => {
const { channel_id } = req.params;
const channel = await Channel.findOneOrFail({where:{ id: channel_id} });
const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
if (!channel.guild_id) throw new HTTPError("Can't bulk delete dm channel messages", 400);
const rights = await getRights(req.user_id);
@@ -1,4 +1,4 @@
import { Router, Response, Request } from "express";
import { handleMessage, postHandleMessage, route } from "@fosscord/api";
import {
Attachment,
Channel,
@@ -7,16 +7,15 @@ import {
DmChannelDTO,
emitEvent,
getPermission,
getRights,
HTTPError,
Member,
Message,
MessageCreateEvent,
MessageCreateSchema,
Snowflake,
uploadFile,
Member,
MessageCreateSchema
uploadFile
} from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { handleMessage, postHandleMessage, route } from "@fosscord/api";
import { Request, Response, Router } from "express";
import multer from "multer";
import { FindManyOptions, LessThan, MoreThan } from "typeorm";
import { URL } from "url";
@@ -69,23 +68,20 @@ router.get("/", async (req: Request, res: Response) => {
permissions.hasThrow("VIEW_CHANNEL");
if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]);
let query: FindManyOptions<Message> & { where: { id?: any; }; } = {
let query: FindManyOptions<Message> & { where: { id?: any } } = {
order: { id: "DESC" },
take: limit,
where: { channel_id },
relations: ["author", "webhook", "application", "mentions", "mention_roles", "mention_channels", "sticker_items", "attachments"]
};
if (after) {
if (after > new Snowflake()) return res.status(422);
query.where.id = MoreThan(after);
}
else if (before) {
} else if (before) {
if (before < req.params.channel_id) return res.status(422);
query.where.id = LessThan(before);
}
else if (around) {
} else if (around) {
query.where.id = [
MoreThan((BigInt(around) - BigInt(halfLimit)).toString()),
LessThan((BigInt(around) + BigInt(halfLimit)).toString())
@@ -110,15 +106,14 @@ router.get("/", async (req: Request, res: Response) => {
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}`;
});
/**
Some clients ( discord.js ) only check if a property exists within the response,
which causes erorrs when, say, the `application` property is `null`.
**/
for (let curr in x) {
if (x[curr] === null)
delete x[curr];
if (x[curr] === null) delete x[curr];
}
return x;
@@ -130,7 +125,7 @@ router.get("/", async (req: Request, res: Response) => {
const messageUpload = multer({
limits: {
fileSize: 1024 * 1024 * 100,
fields: 10,
fields: 10
// files: 1
},
storage: multer.memoryStorage()
@@ -162,16 +157,15 @@ router.post(
const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients", "recipients.user"] });
if (!channel.isWritable()) {
throw new HTTPError(`Cannot send messages to channel of type ${channel.type}`, 400)
throw new HTTPError(`Cannot send messages to channel of type ${channel.type}`, 400);
}
const files = req.files as Express.Multer.File[] ?? [];
const files = (req.files as Express.Multer.File[]) ?? [];
for (let currFile of files) {
try {
const file: any = await uploadFile(`/attachments/${channel.id}`, currFile);
attachments.push({ ...file, proxy_url: file.url });
}
catch (error) {
} catch (error) {
return res.status(400).json(error);
}
}
@@ -212,11 +206,11 @@ router.post(
})
);
}
//Defining member fields
//Defining member fields
var member = await Member.findOneOrFail({ where: { id: req.user_id }, relations: ["roles"] });
// TODO: This doesn't work either
// member.roles = member.roles.filter((role) => {
// member.roles = member.roles.filter((role) => {
// return role.id !== role.guild_id;
// }).map((role) => {
// return role.id;
@@ -225,7 +219,7 @@ router.post(
// TODO: Figure this out
// delete message.member.last_message_id;
// delete message.member.index;
await Promise.all([
message.save(),
emitEvent({ event: "MESSAGE_CREATE", channel_id: channel_id, data: message } as MessageCreateEvent),
@@ -233,9 +227,8 @@ router.post(
channel.save()
]);
postHandleMessage(message).catch((e) => { }); // no await as it shouldnt block the message send function and silently catch error
postHandleMessage(message).catch((e) => {}); // no await as it shouldnt block the message send function and silently catch error
return res.json(message);
}
);
@@ -1,17 +1,15 @@
import { route } from "@fosscord/api";
import {
Channel,
ChannelPermissionOverwrite,
ChannelPermissionOverwriteSchema,
ChannelPermissionOverwriteType,
ChannelUpdateEvent,
emitEvent,
getPermission,
HTTPError,
Member,
Role
} from "@fosscord/util";
import { Router, Response, Request } from "express";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router: Router = Router();
@@ -22,7 +20,7 @@ router.put(
const { channel_id, overwrite_id } = req.params;
const body = req.body as ChannelPermissionOverwriteSchema;
let channel = await Channel.findOneOrFail({ where: {id: channel_id} });
let channel = await Channel.findOneOrFail({ where: { id: channel_id } });
if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
if (body.type === 0) {
+2 -12
View File
@@ -1,16 +1,6 @@
import {
Channel,
ChannelPinsUpdateEvent,
Config,
emitEvent,
getPermission,
Message,
MessageUpdateEvent,
DiscordApiErrors
} from "@fosscord/util";
import { Router, Request, Response } from "express";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api";
import { Channel, ChannelPinsUpdateEvent, Config, DiscordApiErrors, emitEvent, Message, MessageUpdateEvent } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router();
+19 -6
View File
@@ -1,10 +1,18 @@
import { HTTPError, PurgeSchema } from "@fosscord/util";
import { route } from "@fosscord/api";
import {
Channel,
Config,
emitEvent,
getPermission,
getRights,
HTTPError,
Message,
MessageDeleteBulkEvent,
PurgeSchema
} from "@fosscord/util";
import { Request, Response, Router } from "express";
import { Between, FindManyOptions, In, Not } from "typeorm";
import { isTextChannel } from "./messages";
import { FindManyOptions, Between, Not } from "typeorm";
import { Channel, Config, emitEvent, getPermission, getRights, Message, MessageDeleteBulkEvent } from "@fosscord/util";
import { Router, Response, Request } from "express";
import { In } from "typeorm";
const router: Router = Router();
@@ -13,7 +21,12 @@ export default router;
/**
TODO: apply the delete bit by bit to prevent client and database stress
**/
router.post("/",route({ /*body: "PurgeSchema",*/ }), async (req: Request, res: Response) => {
router.post(
"/",
route({
/*body: "PurgeSchema",*/
}),
async (req: Request, res: Response) => {
const { channel_id } = req.params;
const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
@@ -1,4 +1,4 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
import {
Channel,
ChannelRecipientAddEvent,
@@ -6,12 +6,12 @@ import {
DiscordApiErrors,
DmChannelDTO,
emitEvent,
OrmUtils,
PublicUserProjection,
Recipient,
User
} from "@fosscord/util";
import { route } from "@fosscord/api";
import { OrmUtils } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router();
@@ -1,6 +1,6 @@
import { Channel, emitEvent, Member, TypingStartEvent } from "@fosscord/util";
import { route } from "@fosscord/api";
import { Router, Request, Response } from "express";
import { Channel, emitEvent, Member, TypingStartEvent } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router();
@@ -1,9 +1,7 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api";
import { Channel, Config, getPermission, trimSpecial, Webhook } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { Channel, Config, DiscordApiErrors, HTTPError, trimSpecial, Webhook } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { isTextChannel } from "./messages/index";
import { DiscordApiErrors } from "@fosscord/util";
const router: Router = Router();
//TODO: implement webhooks
+3 -3
View File
@@ -1,8 +1,8 @@
import { Guild, Config } from "@fosscord/util";
import { Config, Guild } from "@fosscord/util";
import { Router, Request, Response } from "express";
import { route } from "..";
import { Request, Response, Router } from "express";
import { Like } from "typeorm";
import { route } from "..";
const router = Router();
+2 -2
View File
@@ -1,5 +1,5 @@
import { Categories } from "@fosscord/util";
import { Router, Response, Request } from "express";
import { Request, Response, Router } from "express";
import { route } from "..";
const router = Router();
@@ -10,7 +10,7 @@ router.get("/categories", route({}), async (req: Request, res: Response) => {
const { locale, primary_only } = req.query;
const out = primary_only ? await Categories.find() : await Categories.find({ where: {is_primary: true} });
const out = primary_only ? await Categories.find() : await Categories.find({ where: { is_primary: true } });
res.send(out);
});
+3 -3
View File
@@ -1,6 +1,6 @@
import { Router, Response, Request } from "express";
import { Config, Release } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { route } from "..";
import { Release, Config } from "@fosscord/util";
const router = Router();
@@ -10,7 +10,7 @@ router.get("/:branch", route({}), async (req: Request, res: Response) => {
const { platform } = req.query;
//TODO
if(!platform || !["linux", "osx", "win"].includes(platform.toString())) return res.status(404)
if (!platform || !["linux", "osx", "win"].includes(platform.toString())) return res.status(404);
const release = await Release.findOneOrFail({ where: { name: client.releases.upstreamVersion } });
+2 -2
View File
@@ -1,11 +1,11 @@
import { Router, Response, Request } from "express";
import { Request, Response, Router } from "express";
import { route } from "..";
const router = Router();
router.get("/", route({}), (req: Request, res: Response) => {
// TODO:
res.send({ fingerprint: "", assignments: [], guild_experiments:[] });
res.send({ fingerprint: "", assignments: [], guild_experiments: [] });
});
export default router;
+2 -2
View File
@@ -1,6 +1,6 @@
import { Config } from "@fosscord/util";
import { Router, Response, Request } from "express";
import { route, RouteOptions } from "@fosscord/api";
import { Config } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();
+2 -2
View File
@@ -1,6 +1,6 @@
import { Config } from "@fosscord/util";
import { Router, Response, Request } from "express";
import { route, RouteOptions } from "@fosscord/api";
import { Config } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();
+5 -5
View File
@@ -1,7 +1,7 @@
import { Router, Response, Request } from "express";
import fetch from "node-fetch";
import ProxyAgent from 'proxy-agent';
import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
import fetch from "node-fetch";
import ProxyAgent from "proxy-agent";
import { getGifApiKey, parseGifResult } from "./trending";
const router = Router();
@@ -11,7 +11,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
const { q, media_format, locale } = req.query;
const apiKey = getGifApiKey();
const agent = new ProxyAgent();
const response = await fetch(`https://g.tenor.com/v1/search?q=${q}&media_format=${media_format}&locale=${locale}&key=${apiKey}`, {
@@ -20,7 +20,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
headers: { "Content-Type": "application/json" }
});
const { results } = await response.json() as any;
const { results } = (await response.json()) as any;
res.json(results.map(parseGifResult)).status(200);
});
+5 -5
View File
@@ -1,7 +1,7 @@
import { Router, Response, Request } from "express";
import fetch from "node-fetch";
import ProxyAgent from 'proxy-agent';
import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
import fetch from "node-fetch";
import ProxyAgent from "proxy-agent";
import { getGifApiKey, parseGifResult } from "./trending";
const router = Router();
@@ -11,7 +11,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
const { media_format, locale } = req.query;
const apiKey = getGifApiKey();
const agent = new ProxyAgent();
const response = await fetch(`https://g.tenor.com/v1/trending?media_format=${media_format}&locale=${locale}&key=${apiKey}`, {
@@ -20,7 +20,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
headers: { "Content-Type": "application/json" }
});
const { results } = await response.json() as any;
const { results } = (await response.json()) as any;
res.json(results.map(parseGifResult)).status(200);
});
+7 -8
View File
@@ -1,9 +1,8 @@
import { Router, Response, Request } from "express";
import fetch from "node-fetch";
import ProxyAgent from 'proxy-agent';
import { route } from "@fosscord/api";
import { Config } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { Config, HTTPError } from "@fosscord/util";
import { Request, Response, Router } from "express";
import fetch from "node-fetch";
import ProxyAgent from "proxy-agent";
const router = Router();
@@ -34,7 +33,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
const { media_format, locale } = req.query;
const apiKey = getGifApiKey();
const agent = new ProxyAgent();
const [responseSource, trendGifSource] = await Promise.all([
@@ -50,8 +49,8 @@ router.get("/", route({}), async (req: Request, res: Response) => {
})
]);
const { tags } = await responseSource.json() as any;
const { results } = await trendGifSource.json() as any;
const { tags } = (await responseSource.json()) as any;
const { results } = (await trendGifSource.json()) as any;
res.json({
categories: tags.map((x: any) => ({ name: x.searchterm, src: x.image })),
+6 -6
View File
@@ -1,8 +1,8 @@
import { Guild, Config } from "@fosscord/util";
import { Config, Guild } from "@fosscord/util";
import { Router, Request, Response } from "express";
import { Request, Response, Router } from "express";
import { Like } from "typeorm";
import { route } from "..";
import {Like} from "typeorm"
const router = Router();
@@ -13,12 +13,12 @@ router.get("/", route({}), async (req: Request, res: Response) => {
// TODO: implement this with default typeorm query
// const guilds = await Guild.find({ where: { features: "DISCOVERABLE" } }); //, take: Math.abs(Number(limit)) });
const genLoadId = (size: Number) => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
const genLoadId = (size: Number) => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join("");
const guilds = showAllGuilds
? await Guild.find({ take: Math.abs(Number(limit || 24)) })
: await Guild.find({ where: { features: Like('%DISCOVERABLE%') }, take: Math.abs(Number(limit || 24)) });
res.send({ recommended_guilds: guilds, load_id: `server_recs/${genLoadId(32)}`}).status(200);
: await Guild.find({ where: { features: Like("%DISCOVERABLE%") }, take: Math.abs(Number(limit || 24)) });
res.send({ recommended_guilds: guilds, load_id: `server_recs/${genLoadId(32)}` }).status(200);
});
export default router;
@@ -1,5 +1,5 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router();
//TODO: implement audit logs
+29 -19
View File
@@ -1,8 +1,18 @@
import { Request, Response, Router } from "express";
import { DiscordApiErrors, emitEvent, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, Guild, Ban, User, Member, BanRegistrySchema, BanModeratorSchema } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { getIpAdress, route } from "@fosscord/api";
import { OrmUtils } from "@fosscord/util";
import {
Ban,
BanModeratorSchema,
BanRegistrySchema,
DiscordApiErrors,
emitEvent,
GuildBanAddEvent,
GuildBanRemoveEvent,
HTTPError,
Member,
OrmUtils,
User
} from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router();
@@ -44,16 +54,16 @@ router.get("/:user", route({ permission: "BAN_MEMBERS" }), async (req: Request,
const { guild_id } = req.params;
const user_id = req.params.ban;
let ban = await Ban.findOneOrFail({ where: { guild_id, user_id } }) as BanRegistrySchema;
let ban = (await Ban.findOneOrFail({ where: { guild_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. */
ban = ban as BanModeratorSchema;
delete ban.ip
delete ban.ip;
return res.json(ban);
});
@@ -62,14 +72,14 @@ router.put("/:user_id", route({ body: "BanCreateSchema", permission: "BAN_MEMBER
const { guild_id } = req.params;
const banned_user_id = req.params.user_id;
if ( (req.user_id === banned_user_id) && (banned_user_id === req.permission!.cache.guild?.owner_id))
if (req.user_id === banned_user_id && banned_user_id === req.permission!.cache.guild?.owner_id)
throw new HTTPError("You are the guild owner, hence can't ban yourself", 403);
if (req.permission!.cache.guild?.owner_id === banned_user_id) throw new HTTPError("You can't ban the owner", 400);
const banned_user = await User.getPublicUser(banned_user_id);
const ban = OrmUtils.mergeDeep(new Ban(),{
const ban = OrmUtils.mergeDeep(new Ban(), {
user_id: banned_user_id,
guild_id: guild_id,
ip: getIpAdress(req),
@@ -93,14 +103,14 @@ router.put("/:user_id", route({ body: "BanCreateSchema", permission: "BAN_MEMBER
return res.json(ban);
});
router.put("/@me", route({ body: "BanCreateSchema"}), async (req: Request, res: Response) => {
router.put("/@me", route({ body: "BanCreateSchema" }), 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)
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 = OrmUtils.mergeDeep(new Ban(), {
user_id: req.params.user_id,
guild_id: guild_id,
@@ -129,12 +139,12 @@ router.delete("/:user_id", route({ permission: "BAN_MEMBERS" }), async (req: Req
const { guild_id, user_id } = req.params;
let ban = await Ban.findOneOrFail({ where: { guild_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([
Ban.delete({
user_id: user_id,
+2 -3
View File
@@ -1,7 +1,6 @@
import { Router, Response, Request } from "express";
import { Channel, ChannelUpdateEvent, getPermission, emitEvent, ChannelModifySchema, ChannelReorderSchema } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api";
import { Channel, ChannelModifySchema, ChannelReorderSchema, ChannelUpdateEvent, emitEvent, HTTPError } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();
router.get("/", route({}), async (req: Request, res: Response) => {
+2 -3
View File
@@ -1,7 +1,6 @@
import { Channel, emitEvent, GuildDeleteEvent, Guild, Member, Message, Role, Invite, Emoji } from "@fosscord/util";
import { Router, Request, Response } from "express";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api";
import { emitEvent, Guild, GuildDeleteEvent, HTTPError } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();
@@ -1,38 +1,36 @@
import { Guild, Config } from "@fosscord/util";
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router();
router.get("/", route({}), async (req: Request, res: Response) => {
const { guild_id } = req.params;
// TODO:
// Load from database
// Admin control, but for now it allows anyone to be discoverable
const { guild_id } = req.params;
// TODO:
// Load from database
// Admin control, but for now it allows anyone to be discoverable
res.send({
guild_id: guild_id,
safe_environment: true,
healthy: true,
health_score_pending: false,
size: true,
nsfw_properties: {},
protected: true,
sufficient: true,
sufficient_without_grace_period: true,
valid_rules_channel: true,
retention_healthy: true,
engagement_healthy: true,
age: true,
minimum_age: 0,
health_score: {
avg_nonnew_participators: 0,
avg_nonnew_communicators: 0,
num_intentful_joiners: 0,
perc_ret_w1_intentful: 0
},
minimum_size: 0
healthy: true,
health_score_pending: false,
size: true,
nsfw_properties: {},
protected: true,
sufficient: true,
sufficient_without_grace_period: true,
valid_rules_channel: true,
retention_healthy: true,
engagement_healthy: true,
age: true,
minimum_age: 0,
health_score: {
avg_nonnew_participators: 0,
avg_nonnew_communicators: 0,
num_intentful_joiners: 0,
perc_ret_w1_intentful: 0
},
minimum_size: 0
});
});
+15 -3
View File
@@ -1,7 +1,19 @@
import { Router, Request, Response } from "express";
import { Config, DiscordApiErrors, emitEvent, Emoji, EmojiCreateSchema, EmojiModifySchema, GuildEmojisUpdateEvent, handleFile, Member, Snowflake, User } from "@fosscord/util";
import { route } from "@fosscord/api";
import { OrmUtils } from "@fosscord/util";
import {
Config,
DiscordApiErrors,
emitEvent,
Emoji,
EmojiCreateSchema,
EmojiModifySchema,
GuildEmojisUpdateEvent,
handleFile,
Member,
OrmUtils,
Snowflake,
User
} from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();
+19 -10
View File
@@ -1,8 +1,18 @@
import { Request, Response, Router } from "express";
import { DiscordApiErrors, emitEvent, getPermission, getRights, Guild, GuildUpdateEvent, GuildUpdateSchema, handleFile, Member } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api";
import { OrmUtils } from "@fosscord/util";
import {
DiscordApiErrors,
emitEvent,
getPermission,
getRights,
Guild,
GuildUpdateEvent,
GuildUpdateSchema,
handleFile,
HTTPError,
Member,
OrmUtils
} from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();
@@ -21,17 +31,16 @@ router.get("/", route({}), async (req: Request, res: Response) => {
return res.send(guild);
});
router.patch("/", route({ body: "GuildUpdateSchema"}), async (req: Request, res: Response) => {
router.patch("/", route({ body: "GuildUpdateSchema" }), async (req: Request, res: Response) => {
const body = req.body as GuildUpdateSchema;
const { guild_id } = req.params;
const rights = await getRights(req.user_id);
const permission = await getPermission(req.user_id, guild_id);
if (!rights.has("MANAGE_GUILDS")||!permission.has("MANAGE_GUILD"))
if (!rights.has("MANAGE_GUILDS") || !permission.has("MANAGE_GUILD"))
throw DiscordApiErrors.MISSING_PERMISSIONS.withParams("MANAGE_GUILD");
// TODO: guild update check image
if (body.icon) body.icon = await handleFile(`/icons/${guild_id}`, body.icon);

Some files were not shown because too many files have changed in this diff Show More