Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
};
};

devShell = pkgs.mkShell {
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
nodejs
nodePackages.typescript
Expand Down
14 changes: 9 additions & 5 deletions src/api/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,21 @@ import {
initEvent,
registerRoutes,
} from "@spacebar/util";
import {
Authentication,
CORS,
ImageProxy,
BodyParser,
ErrorHandler,
initRateLimits,
initTranslation,
} from "./middlewares";
import { Request, Response, Router } from "express";
import { Server, ServerOptions } from "lambert-server";
import "missing-native-js-functions";
import morgan from "morgan";
import path from "path";
import { red } from "picocolors";
import { Authentication, CORS, ImageProxy } from "./middlewares/";
import { BodyParser } from "./middlewares/BodyParser";
import { ErrorHandler } from "./middlewares/ErrorHandler";
import { initRateLimits } from "./middlewares/RateLimit";
import { initTranslation } from "./middlewares/Translation";
import { initInstance } from "./util/handlers/Instance";

const PUBLIC_ASSETS_FOLDER = path.join(
Expand Down
3 changes: 2 additions & 1 deletion src/api/middlewares/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ export * from "./Authentication";
export * from "./BodyParser";
export * from "./CORS";
export * from "./ErrorHandler";
export * from "./RateLimit";
export * from "./ImageProxy";
export * from "./RateLimit";
export * from "./Translation";
147 changes: 147 additions & 0 deletions src/api/routes/guilds/#guild_id/auto-moderation/rules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2024 Spacebar and Spacebar Contributors

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

import { route } from "@spacebar/api";
import { User, AutomodRuleSchema, AutomodRule } from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";

const router: Router = Router();

router.get(
"/",
route({
permission: ["MANAGE_GUILD"],
responses: {
200: {
body: "AutomodRuleSchemaWithId[]",
},
403: {
body: "APIErrorResponse",
},
},
}),
async (req: Request, res: Response) => {
const { guild_id } = req.params;
const rules = await AutomodRule.find({ where: { guild_id } });
return res.json(rules);
},
);

router.post(
"/",
route({
// requestBody: "AutomodRuleSchema",
permission: ["MANAGE_GUILD"],
responses: {
200: {
body: "AutomodRuleSchemaWithId",
},
400: {
body: "APIErrorResponse",
},
403: {
body: "APIErrorResponse",
},
},
}),
async (req: Request, res: Response) => {
const { guild_id } = req.params;
if (req.user_id !== req.body.creator_id)
throw new HTTPError(
"You can't create a rule for someone else",
403,
);

if (guild_id !== req.body.guild_id)
throw new HTTPError(
"You can't create a rule for another guild",
403,
);

if (req.body.id) {
throw new HTTPError("You can't specify an ID for a new rule", 400);
}

const data = req.body as AutomodRuleSchema;

const created = AutomodRule.create({
creator: await User.findOneOrFail({
where: { id: data.creator_id },
}),
...data,
});

const savedRule = await AutomodRule.save(created);
return res.json(savedRule);
},
);

router.patch(
"/:rule_id",
route({
// requestBody: "AutomodRuleSchema
permission: ["MANAGE_GUILD"],
responses: {
200: {
body: "AutomodRuleSchemaWithId",
},
400: {
body: "APIErrorResponse",
},
403: {
body: "APIErrorResponse",
},
},
}),
async (req: Request, res: Response) => {
const { rule_id } = req.params;
const rule = await AutomodRule.findOneOrFail({
where: { id: rule_id },
});

const data = req.body as AutomodRuleSchema;

AutomodRule.merge(rule, data);
const savedRule = await AutomodRule.save(rule);
return res.json(savedRule);
},
);

router.delete(
"/:rule_id",
route({
permission: ["MANAGE_GUILD"],
responses: {
204: {},
403: {
body: "APIErrorResponse",
},
404: {
body: "APIErrorResponse",
},
},
}),
async (req: Request, res: Response) => {
const { rule_id } = req.params;
await AutomodRule.delete({ id: rule_id });
return res.status(204).send();
},
);

export default router;
4 changes: 2 additions & 2 deletions src/api/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ try {
}

if (cluster.isPrimary && process.env.NODE_ENV == "production") {
console.log(`Primary ${process.pid} is running`);
console.log(`Primary PID: ${process.pid}`);

// Fork workers.
for (let i = 0; i < cores; i++) {
cluster.fork();
}

cluster.on("exit", (worker) => {
console.log(`worker ${worker.process.pid} died, restart worker`);
console.log(`Worker ${worker.process.pid} died, restarting worker`);
cluster.fork();
});
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/bundle/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ async function main() {

Sentry.errorHandler(app);

console.log(`[Server] ${green(`listening on port ${bold(port)}`)}`);
console.log(`[Server] ${green(`Listening on port ${bold(port)}`)}`);
}

main().catch(console.error);
112 changes: 54 additions & 58 deletions src/bundle/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,19 @@

// process.env.MONGOMS_DEBUG = "true";
import moduleAlias from "module-alias";

moduleAlias(__dirname + "../../../package.json");

import "reflect-metadata";
import cluster, { Worker } from "cluster";
import os from "os";
import { red, bold, yellow, cyan } from "picocolors";
import { red, bold, yellow, cyan, blueBright, redBright } from "picocolors";
import { initStats } from "./stats";
import { config } from "dotenv";

config();
import { execSync } from "child_process";
import { centerString, Logo } from "@spacebar/util";

const cores = process.env.THREADS ? parseInt(process.env.THREADS) : 1;

Expand All @@ -41,73 +44,66 @@ function getCommitOrFail() {

if (cluster.isPrimary) {
const commit = getCommitOrFail();
Logo.printLogo().then(()=>{
const unformatted = `spacebar-server | !! Pre-release build !!`;
const formatted = `${blueBright("spacebar-server")} | ${redBright("⚠️ Pre-release build ⚠️")}`;
console.log(
bold(centerString(unformatted, 86).replace(unformatted, formatted)),
);

console.log(
bold(`
███████╗██████╗ █████╗ ██████╗███████╗██████╗ █████╗ ██████╗
██╔════╝██╔══██╗██╔══██╗██╔════╝██╔════╝██╔══██╗██╔══██╗██╔══██╗
███████╗██████╔╝███████║██║ █████╗ ██████╔╝███████║██████╔╝
╚════██║██╔═══╝ ██╔══██║██║ ██╔══╝ ██╔══██╗██╔══██║██╔══██╗
███████║██║ ██║ ██║╚██████╗███████╗██████╔╝██║ ██║██║ ██║
╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝

spacebar-server | ${yellow(
`Pre-release (${
commit !== null
? commit.slice(0, 7)
: "Unknown (Git cannot be found)"
})`,
)}
const unformattedGitHeader = `Commit Hash: ${commit !== null ? `${commit} (${commit.slice(0, 7)})` : "Unknown (Git cannot be found)"}`;
const formattedGitHeader = `Commit Hash: ${commit !== null ? `${cyan(commit)} (${yellow(commit.slice(0, 7))})` : "Unknown (Git cannot be found)"}`;
console.log(
bold(
centerString(unformattedGitHeader, 86).replace(
unformattedGitHeader,
formattedGitHeader,
),
),
);
console.log(`Cores: ${cyan(os.cpus().length)} (Using ${cores} thread(s).)`);

Commit Hash: ${
commit !== null
? `${cyan(commit)} (${yellow(commit.slice(0, 7))})`
: "Unknown (Git cannot be found)"
if (commit == null) {
console.log(yellow(`Warning: Git is not installed or not in PATH.`));
}
Cores: ${cyan(os.cpus().length)} (Using ${cores} thread(s).)
`),
);

if (commit == null) {
console.log(yellow(`Warning: Git is not installed or not in PATH.`));
}
initStats();

console.log(`[Process] Starting with ${cores} threads`);

initStats();
if (cores === 1) {
require("./Server");
} else {
process.env.EVENT_TRANSMISSION = "process";

console.log(`[Process] starting with ${cores} threads`);
// Fork workers.
for (let i = 0; i < cores; i++) {
// Delay each worker start if using sqlite database to prevent locking it
const delay = process.env.DATABASE?.includes("://") ? 0 : i * 1000;
setTimeout(() => {
cluster.fork();
console.log(`[Process] Worker ${cyan(i)} started.`);
}, delay);
}

if (cores === 1) {
require("./Server");
} else {
process.env.EVENT_TRANSMISSION = "process";
cluster.on("message", (sender: Worker, message) => {
for (const id in cluster.workers) {
const worker = cluster.workers[id];
if (worker === sender || !worker) continue;
worker.send(message);
}
});

// Fork workers.
for (let i = 0; i < cores; i++) {
// Delay each worker start if using sqlite database to prevent locking it
const delay = process.env.DATABASE?.includes("://") ? 0 : i * 1000;
setTimeout(() => {
cluster.on("exit", (worker) => {
console.log(
`[Worker] ${red(
`PID ${worker.process.pid} died, restarting ...`,
)}`,
);
cluster.fork();
console.log(`[Process] worker ${cyan(i)} started.`);
}, delay);
});
}

cluster.on("message", (sender: Worker, message) => {
for (const id in cluster.workers) {
const worker = cluster.workers[id];
if (worker === sender || !worker) continue;
worker.send(message);
}
});

cluster.on("exit", (worker) => {
console.log(
`[Worker] ${red(
`died with PID: ${worker.process.pid} , restarting ...`,
)}`,
);
cluster.fork();
});
}
});
} else {
require("./Server");
}
Loading
Loading