diff --git a/src/api/middlewares/Authentication.ts b/src/api/middlewares/Authentication.ts
index fbf71cd5..00c2e5e6 100644
--- a/src/api/middlewares/Authentication.ts
+++ b/src/api/middlewares/Authentication.ts
@@ -53,7 +53,7 @@ export async function Authentication(req: Request, res: Response, next: NextFunc
})
)
return next();
- if (!req.headers.authorization) return next(new HTTPError(req.t("auth:generic.MISSING_AUTH_HEADER"), 401));
+ if (!req.headers.authorization) return next(new HTTPError("Missing authorization header!", 401));
try {
const { jwtSecret } = Config.get().security;
diff --git a/src/api/middlewares/RateLimit.ts b/src/api/middlewares/RateLimit.ts
index dc93dcef..bb9a334c 100644
--- a/src/api/middlewares/RateLimit.ts
+++ b/src/api/middlewares/RateLimit.ts
@@ -1,4 +1,4 @@
-import { getIpAdress } from "@fosscord/api";
+import { getIpAdress } from "@fosscord/util";
import { Config, getRights, listenEvent } from "@fosscord/util";
import { NextFunction, Request, Response, Router } from "express";
import { API_PREFIX_TRAILING_SLASH } from "./Authentication";
diff --git a/src/api/routes/auth/generate-registration-tokens.ts b/src/api/routes/auth/generate-registration-tokens.ts
new file mode 100644
index 00000000..322db33c
--- /dev/null
+++ b/src/api/routes/auth/generate-registration-tokens.ts
@@ -0,0 +1,29 @@
+import { route } from "@fosscord/api";
+import { Config, random, Rights, ValidRegistrationToken } from "@fosscord/util";
+import { Request, Response, Router } from "express";
+
+
+const router: Router = Router();
+export default router;
+
+router.get("/", route({ right: "OPERATOR" }), async (req: Request, res: Response) => {
+ let count = (req.query.count as unknown) as number ?? 1;
+ let tokens: string[] = [];
+ let dbtokens: ValidRegistrationToken[] = [];
+ for(let i = 0; i < count; i++) {
+ let token = random((req.query.length as unknown as number) ?? 255);
+ let vrt = new ValidRegistrationToken();
+ vrt.token = token;
+ dbtokens.push(vrt);
+ if(req.query.include_url == "true") token = `${Config.get().general.publicUrl}/register?token=${token}`;
+ tokens.push(token);
+ }
+ await ValidRegistrationToken.save(dbtokens, { chunk: 1000, reload: false, transaction: false });
+
+ if(req.query.plain == "true") {
+ if(count == 1) res.send(tokens[0]);
+ else res.send(tokens.join("\n"));
+ }
+ else if(count == 1) res.json({ token: tokens[0] });
+ else res.json({ tokens });
+});
\ No newline at end of file
diff --git a/src/api/routes/auth/location-metadata.ts b/src/api/routes/auth/location-metadata.ts
index b8caf579..4bc7da28 100644
--- a/src/api/routes/auth/location-metadata.ts
+++ b/src/api/routes/auth/location-metadata.ts
@@ -1,4 +1,5 @@
-import { getIpAdress, IPAnalysis, route } from "@fosscord/api";
+import { route } from "@fosscord/api";
+import {getIpAdress, IPAnalysis} from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();
diff --git a/src/api/routes/auth/login.ts b/src/api/routes/auth/login.ts
index 045b86eb..bbd9cf93 100644
--- a/src/api/routes/auth/login.ts
+++ b/src/api/routes/auth/login.ts
@@ -1,5 +1,5 @@
-import { getIpAdress, route, verifyCaptcha } from "@fosscord/api";
-import { adjustEmail, Config, FieldErrors, generateToken, LoginSchema, User } from "@fosscord/util";
+import { route } from "@fosscord/api";
+import { adjustEmail, Config, FieldErrors, generateToken, LoginSchema, User, getIpAdress, verifyCaptcha } from "@fosscord/util";
import crypto from "crypto";
import { Request, Response, Router } from "express";
diff --git a/src/api/routes/auth/register.ts b/src/api/routes/auth/register.ts
index 638b6b79..08e9f7bb 100644
--- a/src/api/routes/auth/register.ts
+++ b/src/api/routes/auth/register.ts
@@ -1,7 +1,7 @@
-import { getIpAdress, IPAnalysis, isProxy, route, verifyCaptcha } from "@fosscord/api";
-import { adjustEmail, Config, FieldErrors, generateToken, HTTPError, Invite, RegisterSchema, User } from "@fosscord/util";
+import { route } from "@fosscord/api";
+import { adjustEmail, Config, FieldErrors, generateToken, HTTPError, Invite, RegisterSchema, User, ValidRegistrationToken, getIpAdress, IPAnalysis, isProxy, verifyCaptcha } from "@fosscord/util";
import { Request, Response, Router } from "express";
-import { yellow } from "picocolors";
+import { red, yellow } from "picocolors";
import { MoreThan } from "typeorm";
let bcrypt: any;
@@ -22,13 +22,6 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
// email will be slightly modified version of the user supplied email -> e.g. protection against GMail Trick
let email = adjustEmail(body.email);
- // check if registration is allowed
- if (!register.allowNewRegistration) {
- throw FieldErrors({
- email: { code: "REGISTRATION_DISABLED", message: req.t("auth:register.REGISTRATION_DISABLED") }
- });
- }
-
// check if the user agreed to the Terms of Service
if (!body.consent) {
throw FieldErrors({
@@ -36,21 +29,6 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
});
}
- if (register.disabled) {
- throw FieldErrors({
- email: {
- code: "DISABLED",
- message: "registration is disabled on this instance"
- }
- });
- }
-
- if (!register.allowGuests) {
- throw FieldErrors({
- email: { code: "GUESTS_DISABLED", message: req.t("auth:register.GUESTS_DISABLED") }
- });
- }
-
if (register.requireCaptcha && security.captcha.enabled) {
const { sitekey, service } = security.captcha;
if (!body.captcha_key) {
@@ -71,24 +49,24 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
}
}
- if (!register.allowMultipleAccounts) {
- // TODO: check if fingerprint was eligible generated
- const exists = await User.findOne({ where: { fingerprints: body.fingerprint }, select: ["id"] });
-
- if (exists) {
- throw FieldErrors({
- email: {
- code: "EMAIL_ALREADY_REGISTERED",
- message: req.t("auth:register.EMAIL_ALREADY_REGISTERED")
- }
- });
- }
+ // check if registration is allowed
+ if (!register.allowNewRegistration) {
+ throw FieldErrors({
+ email: { code: "REGISTRATION_DISABLED", message: req.t("auth:register.REGISTRATION_DISABLED") }
+ });
}
if (register.blockProxies) {
- if (isProxy(await IPAnalysis(ip))) {
- console.log(`proxy ${ip} blocked from registration`);
- throw new HTTPError("Your IP is blocked from registration");
+ let data;
+ try {
+ data = await IPAnalysis(ip);
+ } catch (e: any) {
+ console.warn(red(`[REGISTER]: Failed to analyze IP ${ip}: failed to contact api.ipdata.co!`), e.message);
+ }
+
+ if (data && isProxy(data)) {
+ console.log(yellow(`[REGISTER] Proxy ${ip} blocked from registration!`));
+ throw new HTTPError(req.t("auth:register.IP_BLOCKED"));
}
}
@@ -96,15 +74,10 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
// TODO: check password strength
if (email) {
- // replace all dots and chars after +, if its a gmail.com email
- if (!email) {
- throw FieldErrors({ email: { code: "INVALID_EMAIL", message: req?.t("auth:register.INVALID_EMAIL") } });
- }
-
// check if there is already an account with this email
const exists = await User.findOne({ where: { email: email } });
- if (exists) {
+ if (exists && !register.disabled) {
throw FieldErrors({
email: {
code: "EMAIL_ALREADY_REGISTERED",
@@ -155,14 +128,32 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
});
}
+ //check if email starts with any valid registration token
+ let validToken = false;
+ if (req.get("Referrer") && req.get("Referrer")?.includes("token=")) {
+ let token = req.get("Referrer")?.split("token=")[1].split("&")[0];
+ if (token) {
+ let registrationToken = await ValidRegistrationToken.findOne({ where: { token: token } });
+ if (registrationToken) {
+ console.log(yellow(`[REGISTER] Registration token ${token} used for registration!`));
+ await ValidRegistrationToken.delete(token);
+ validToken = true;
+ }
+ else {
+ console.log(yellow(`[REGISTER] Invalid registration token ${token} used for registration by ${ip}!`));
+ }
+ }
+ }
+
if (
+ !validToken &&
limits.absoluteRate.register.enabled &&
(await await User.count({ where: { created_at: MoreThan(new Date(Date.now() - limits.absoluteRate.register.window)) } })) >=
limits.absoluteRate.register.limit
) {
console.log(
yellow(
- `Global register rate limit exceeded for ${getIpAdress(req)}: ${
+ `[REGISTER] Global register rate limit exceeded for ${getIpAdress(req)}: ${
process.env.LOG_SENSITIVE ? req.body.email : "<email redacted>"
}, ${req.body.username}, ${req.body.invite ?? "No invite given"}`
)
diff --git a/src/api/routes/guilds/#guild_id/bans.ts b/src/api/routes/guilds/#guild_id/bans.ts
index 4600b4cb..e4fe605b 100644
--- a/src/api/routes/guilds/#guild_id/bans.ts
+++ b/src/api/routes/guilds/#guild_id/bans.ts
@@ -1,4 +1,4 @@
-import { getIpAdress, route } from "@fosscord/api";
+import { route } from "@fosscord/api";
import {
Ban,
BanModeratorSchema,
@@ -10,7 +10,8 @@ import {
HTTPError,
Member,
OrmUtils,
- User
+ User,
+ getIpAdress
} from "@fosscord/util";
import { Request, Response, Router } from "express";
diff --git a/src/api/routes/guilds/#guild_id/regions.ts b/src/api/routes/guilds/#guild_id/regions.ts
index aa57ec65..d32ff118 100644
--- a/src/api/routes/guilds/#guild_id/regions.ts
+++ b/src/api/routes/guilds/#guild_id/regions.ts
@@ -1,5 +1,5 @@
-import { getIpAdress, getVoiceRegions, route } from "@fosscord/api";
-import { Guild } from "@fosscord/util";
+import { getVoiceRegions, route } from "@fosscord/api";
+import { Guild, getIpAdress } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();
diff --git a/src/api/routes/guilds/#guild_id/templates.ts b/src/api/routes/guilds/#guild_id/templates.ts
index 448ee033..1f85cdcf 100644
--- a/src/api/routes/guilds/#guild_id/templates.ts
+++ b/src/api/routes/guilds/#guild_id/templates.ts
@@ -1,5 +1,5 @@
-import { generateCode, route } from "@fosscord/api";
-import { Guild, HTTPError, OrmUtils, Template } from "@fosscord/util";
+import { route } from "@fosscord/api";
+import { Guild, HTTPError, OrmUtils, Template, generateCode } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router();
diff --git a/src/api/routes/guilds/#guild_id/widget.json.ts b/src/api/routes/guilds/#guild_id/widget.json.ts
index 368fe46e..66cc456f 100644
--- a/src/api/routes/guilds/#guild_id/widget.json.ts
+++ b/src/api/routes/guilds/#guild_id/widget.json.ts
@@ -1,5 +1,5 @@
-import { random, route } from "@fosscord/api";
-import { Channel, Guild, HTTPError, Invite, Member, OrmUtils, Permissions } from "@fosscord/util";
+import { route } from "@fosscord/api";
+import { Channel, Guild, HTTPError, Invite, Member, OrmUtils, Permissions, random } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router();
diff --git a/src/api/routes/voice/regions.ts b/src/api/routes/voice/regions.ts
index eacdcf11..9071fcd5 100644
--- a/src/api/routes/voice/regions.ts
+++ b/src/api/routes/voice/regions.ts
@@ -1,5 +1,6 @@
-import { getIpAdress, getVoiceRegions, route } from "@fosscord/api";
+import { getVoiceRegions, route } from "@fosscord/api";
import { Request, Response, Router } from "express";
+import { getIpAdress } from "@fosscord/util";
const router: Router = Router();
diff --git a/src/api/util/handlers/Voice.ts b/src/api/util/handlers/Voice.ts
index 4d60eb91..98d28ff0 100644
--- a/src/api/util/handlers/Voice.ts
+++ b/src/api/util/handlers/Voice.ts
@@ -1,5 +1,4 @@
-import { Config } from "@fosscord/util";
-import { distanceBetweenLocations, IPAnalysis } from "../utility/ipAddress";
+import { Config, distanceBetweenLocations, IPAnalysis } from "@fosscord/util";
export async function getVoiceRegions(ipAddress: string, vip: boolean) {
const regions = Config.get().regions;
diff --git a/src/api/util/index.ts b/src/api/util/index.ts
index d06860cd..7223d6f4 100644
--- a/src/api/util/index.ts
+++ b/src/api/util/index.ts
@@ -1,10 +1,4 @@
export * from "./entities/AssetCacheItem";
export * from "./handlers/Message";
export * from "./handlers/route";
-export * from "./handlers/Voice";
-export * from "./utility/Base64";
-export * from "./utility/captcha";
-export * from "./utility/ipAddress";
-export * from "./utility/passwordStrength";
-export * from "./utility/RandomInviteID";
-export * from "./utility/String";
+export * from "./handlers/Voice";
\ No newline at end of file
diff --git a/src/api/util/utility/String.ts b/src/api/util/utility/String.ts
deleted file mode 100644
index a2e491e4..00000000
--- a/src/api/util/utility/String.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { FieldErrors } from "@fosscord/util";
-import { Request } from "express";
-import { ntob } from "./Base64";
-
-export function checkLength(str: string, min: number, max: number, key: string, req: Request) {
- if (str.length < min || str.length > max) {
- throw FieldErrors({
- [key]: {
- code: "BASE_TYPE_BAD_LENGTH",
- message: req.t("common:field.BASE_TYPE_BAD_LENGTH", { length: `${min} - ${max}` })
- }
- });
- }
-}
-
-export function generateCode() {
- return ntob(Date.now() + Math.randomIntBetween(0, 10000));
-}
diff --git a/src/util/config/types/GeneralConfiguration.ts b/src/util/config/types/GeneralConfiguration.ts
index 5cb8df89..6d030645 100644
--- a/src/util/config/types/GeneralConfiguration.ts
+++ b/src/util/config/types/GeneralConfiguration.ts
@@ -3,6 +3,7 @@ import { Snowflake } from "../../util";
export class GeneralConfiguration {
instanceName: string = "Fosscord Instance";
instanceDescription: string | null = "This is a Fosscord instance made in the pre-release days";
+ publicUrl: string = "http://localhost:3001";
frontPage: string | null = null;
tosPage: string | null = null;
correspondenceEmail: string | null = "noreply@localhost.local";
diff --git a/src/util/config/types/RegisterConfiguration.ts b/src/util/config/types/RegisterConfiguration.ts
index 68946272..caeab123 100644
--- a/src/util/config/types/RegisterConfiguration.ts
+++ b/src/util/config/types/RegisterConfiguration.ts
@@ -12,7 +12,6 @@ export class RegisterConfiguration {
allowGuests: boolean = true;
guestsRequireInvite: boolean = true;
allowNewRegistration: boolean = true;
- allowMultipleAccounts: boolean = true;
blockProxies: boolean = true;
incrementingDiscriminators: boolean = false; // random otherwise
defaultRights: string = "0";
diff --git a/src/util/config/types/SecurityConfiguration.ts b/src/util/config/types/SecurityConfiguration.ts
index 5a3d5aa6..229587c3 100644
--- a/src/util/config/types/SecurityConfiguration.ts
+++ b/src/util/config/types/SecurityConfiguration.ts
@@ -17,4 +17,5 @@ export class SecurityConfiguration {
mfaBackupCodeCount: number = 10;
mfaBackupCodeBytes: number = 4;
statsWorldReadable: boolean = true;
+ defaultRegistrationTokenExpiration: number = 1000 * 60 * 60 * 24 * 7; //1 week
}
diff --git a/src/util/entities/Attachment.ts b/src/util/entities/Attachment.ts
index 8392f415..c0ea3dec 100644
--- a/src/util/entities/Attachment.ts
+++ b/src/util/entities/Attachment.ts
@@ -1,6 +1,6 @@
import { BeforeRemove, Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
import { URL } from "url";
-import { deleteFile } from "../util/cdn";
+import { deleteFile } from "../util/CDN";
import { BaseClass } from "./BaseClass";
@Entity("attachments")
diff --git a/src/util/entities/Invite.ts b/src/util/entities/Invite.ts
index f6ba85d7..151fcc59 100644
--- a/src/util/entities/Invite.ts
+++ b/src/util/entities/Invite.ts
@@ -1,4 +1,4 @@
-import { random } from "@fosscord/api";
+import { random } from "@fosscord/util";
import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn, RelationId } from "typeorm";
import { BaseClassWithoutId } from "./BaseClass";
import { Channel } from "./Channel";
diff --git a/src/util/entities/ValidRegistrationTokens.ts b/src/util/entities/ValidRegistrationTokens.ts
new file mode 100644
index 00000000..5d0747b8
--- /dev/null
+++ b/src/util/entities/ValidRegistrationTokens.ts
@@ -0,0 +1,12 @@
+import { BaseEntity, Column, Entity, PrimaryColumn } from "typeorm";
+import { Config } from "..";
+
+@Entity("valid_registration_tokens")
+export class ValidRegistrationToken extends BaseEntity {
+ @PrimaryColumn()
+ token: string;
+ @Column()
+ created_at: Date = new Date();
+ @Column()
+ expires_at: Date = new Date(Date.now() + Config.get().security.defaultRegistrationTokenExpiration);
+}
diff --git a/src/util/entities/index.ts b/src/util/entities/index.ts
index 2b91c2ba..673aac36 100644
--- a/src/util/entities/index.ts
+++ b/src/util/entities/index.ts
@@ -31,3 +31,4 @@ export * from "./User";
export * from "./UserSettings";
export * from "./VoiceState";
export * from "./Webhook";
+export * from "./ValidRegistrationTokens";
\ No newline at end of file
diff --git a/src/util/migrations/mariadb/1663440589234-registration_tokens.ts b/src/util/migrations/mariadb/1663440589234-registration_tokens.ts
new file mode 100644
index 00000000..12690ac4
--- /dev/null
+++ b/src/util/migrations/mariadb/1663440589234-registration_tokens.ts
@@ -0,0 +1,31 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class registrationTokens1663440589234 implements MigrationInterface {
+ name = 'registrationTokens1663440589234'
+
+ public async up(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.query(`
+ CREATE TABLE \`valid_registration_tokens\` (
+ \`id\` varchar(255) NOT NULL,
+ \`token\` varchar(255) NOT NULL,
+ \`created_at\` datetime NOT NULL,
+ \`expires_at\` datetime NOT NULL,
+ PRIMARY KEY (\`id\`)
+ ) ENGINE = InnoDB
+ `);
+ await queryRunner.query(`
+ ALTER TABLE \`users\` DROP COLUMN \`notes\`
+ `);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.query(`
+ ALTER TABLE \`users\`
+ ADD \`notes\` text NOT NULL
+ `);
+ await queryRunner.query(`
+ DROP TABLE \`valid_registration_tokens\`
+ `);
+ }
+
+}
diff --git a/src/util/migrations/mariadb/1663448562034-drop_id_for_registration_tokens.ts b/src/util/migrations/mariadb/1663448562034-drop_id_for_registration_tokens.ts
new file mode 100644
index 00000000..d4b13abb
--- /dev/null
+++ b/src/util/migrations/mariadb/1663448562034-drop_id_for_registration_tokens.ts
@@ -0,0 +1,33 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class dropIdForRegistrationTokens1663448562034 implements MigrationInterface {
+ name = 'dropIdForRegistrationTokens1663448562034'
+
+ public async up(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.query(`
+ ALTER TABLE \`valid_registration_tokens\` DROP PRIMARY KEY
+ `);
+ await queryRunner.query(`
+ ALTER TABLE \`valid_registration_tokens\` DROP COLUMN \`id\`
+ `);
+ await queryRunner.query(`
+ ALTER TABLE \`valid_registration_tokens\`
+ ADD PRIMARY KEY (\`token\`)
+ `);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.query(`
+ ALTER TABLE \`valid_registration_tokens\` DROP PRIMARY KEY
+ `);
+ await queryRunner.query(`
+ ALTER TABLE \`valid_registration_tokens\`
+ ADD \`id\` varchar(255) NOT NULL
+ `);
+ await queryRunner.query(`
+ ALTER TABLE \`valid_registration_tokens\`
+ ADD PRIMARY KEY (\`id\`)
+ `);
+ }
+
+}
diff --git a/src/util/migrations/postgres/1663440587650-registration_tokens.ts b/src/util/migrations/postgres/1663440587650-registration_tokens.ts
new file mode 100644
index 00000000..a794262c
--- /dev/null
+++ b/src/util/migrations/postgres/1663440587650-registration_tokens.ts
@@ -0,0 +1,33 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class registrationTokens1663440587650 implements MigrationInterface {
+ name = 'registrationTokens1663440587650'
+
+ public async up(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.query(`
+ CREATE TABLE "valid_registration_tokens" (
+ "id" character varying NOT NULL,
+ "token" character varying NOT NULL,
+ "created_at" TIMESTAMP NOT NULL,
+ "expires_at" TIMESTAMP NOT NULL,
+ CONSTRAINT "PK_aac42a46cd46369450217de1c8a" PRIMARY KEY ("id")
+ )
+ `);
+ await queryRunner.query(`
+ ALTER TABLE "members"
+ ALTER COLUMN "bio" DROP DEFAULT
+ `);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.query(`
+ ALTER TABLE "members"
+ ALTER COLUMN "bio"
+ SET DEFAULT ''
+ `);
+ await queryRunner.query(`
+ DROP TABLE "valid_registration_tokens"
+ `);
+ }
+
+}
diff --git a/src/util/migrations/postgres/1663448561249-drop_id_for_registration_tokens.ts b/src/util/migrations/postgres/1663448561249-drop_id_for_registration_tokens.ts
new file mode 100644
index 00000000..ce4b72f4
--- /dev/null
+++ b/src/util/migrations/postgres/1663448561249-drop_id_for_registration_tokens.ts
@@ -0,0 +1,33 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class dropIdForRegistrationTokens1663448561249 implements MigrationInterface {
+ name = 'dropIdForRegistrationTokens1663448561249'
+
+ public async up(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.query(`
+ ALTER TABLE "valid_registration_tokens" DROP CONSTRAINT "PK_aac42a46cd46369450217de1c8a"
+ `);
+ await queryRunner.query(`
+ ALTER TABLE "valid_registration_tokens" DROP COLUMN "id"
+ `);
+ await queryRunner.query(`
+ ALTER TABLE "valid_registration_tokens"
+ ADD CONSTRAINT "PK_e0f5c8e3fcefe3134a092c50485" PRIMARY KEY ("token")
+ `);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.query(`
+ ALTER TABLE "valid_registration_tokens" DROP CONSTRAINT "PK_e0f5c8e3fcefe3134a092c50485"
+ `);
+ await queryRunner.query(`
+ ALTER TABLE "valid_registration_tokens"
+ ADD "id" character varying NOT NULL
+ `);
+ await queryRunner.query(`
+ ALTER TABLE "valid_registration_tokens"
+ ADD CONSTRAINT "PK_aac42a46cd46369450217de1c8a" PRIMARY KEY ("id")
+ `);
+ }
+
+}
diff --git a/src/util/migrations/sqlite/1663440585960-registration_tokens.ts b/src/util/migrations/sqlite/1663440585960-registration_tokens.ts
new file mode 100644
index 00000000..daf76be6
--- /dev/null
+++ b/src/util/migrations/sqlite/1663440585960-registration_tokens.ts
@@ -0,0 +1,246 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class registrationTokens1663440585960 implements MigrationInterface {
+ name = 'registrationTokens1663440585960'
+
+ public async up(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.query(`
+ CREATE TABLE "valid_registration_tokens" (
+ "id" varchar PRIMARY KEY NOT NULL,
+ "token" varchar NOT NULL,
+ "created_at" datetime NOT NULL,
+ "expires_at" datetime NOT NULL
+ )
+ `);
+ await queryRunner.query(`
+ CREATE TABLE "temporary_users" (
+ "id" varchar PRIMARY KEY NOT NULL,
+ "username" varchar NOT NULL,
+ "discriminator" varchar NOT NULL,
+ "avatar" varchar,
+ "accent_color" integer,
+ "banner" varchar,
+ "phone" varchar,
+ "desktop" boolean NOT NULL,
+ "mobile" boolean NOT NULL,
+ "premium" boolean NOT NULL,
+ "premium_type" integer NOT NULL,
+ "bot" boolean NOT NULL,
+ "bio" varchar,
+ "system" boolean NOT NULL,
+ "nsfw_allowed" boolean NOT NULL,
+ "mfa_enabled" boolean,
+ "totp_secret" varchar,
+ "totp_last_ticket" varchar,
+ "created_at" datetime NOT NULL,
+ "premium_since" datetime,
+ "verified" boolean NOT NULL,
+ "disabled" boolean NOT NULL,
+ "deleted" boolean NOT NULL,
+ "email" varchar,
+ "flags" varchar NOT NULL,
+ "public_flags" integer NOT NULL,
+ "rights" bigint NOT NULL,
+ "data" text NOT NULL,
+ "fingerprints" text NOT NULL,
+ "extended_settings" text NOT NULL,
+ "settingsId" varchar,
+ CONSTRAINT "UQ_b1dd13b6ed980004a795ca184a6" UNIQUE ("settingsId"),
+ CONSTRAINT "FK_76ba283779c8441fd5ff819c8cf" FOREIGN KEY ("settingsId") REFERENCES "user_settings" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION
+ )
+ `);
+ await queryRunner.query(`
+ INSERT INTO "temporary_users"(
+ "id",
+ "username",
+ "discriminator",
+ "avatar",
+ "accent_color",
+ "banner",
+ "phone",
+ "desktop",
+ "mobile",
+ "premium",
+ "premium_type",
+ "bot",
+ "bio",
+ "system",
+ "nsfw_allowed",
+ "mfa_enabled",
+ "totp_secret",
+ "totp_last_ticket",
+ "created_at",
+ "premium_since",
+ "verified",
+ "disabled",
+ "deleted",
+ "email",
+ "flags",
+ "public_flags",
+ "rights",
+ "data",
+ "fingerprints",
+ "extended_settings",
+ "settingsId"
+ )
+ SELECT "id",
+ "username",
+ "discriminator",
+ "avatar",
+ "accent_color",
+ "banner",
+ "phone",
+ "desktop",
+ "mobile",
+ "premium",
+ "premium_type",
+ "bot",
+ "bio",
+ "system",
+ "nsfw_allowed",
+ "mfa_enabled",
+ "totp_secret",
+ "totp_last_ticket",
+ "created_at",
+ "premium_since",
+ "verified",
+ "disabled",
+ "deleted",
+ "email",
+ "flags",
+ "public_flags",
+ "rights",
+ "data",
+ "fingerprints",
+ "extended_settings",
+ "settingsId"
+ FROM "users"
+ `);
+ await queryRunner.query(`
+ DROP TABLE "users"
+ `);
+ await queryRunner.query(`
+ ALTER TABLE "temporary_users"
+ RENAME TO "users"
+ `);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.query(`
+ ALTER TABLE "users"
+ RENAME TO "temporary_users"
+ `);
+ await queryRunner.query(`
+ CREATE TABLE "users" (
+ "id" varchar PRIMARY KEY NOT NULL,
+ "username" varchar NOT NULL,
+ "discriminator" varchar NOT NULL,
+ "avatar" varchar,
+ "accent_color" integer,
+ "banner" varchar,
+ "phone" varchar,
+ "desktop" boolean NOT NULL,
+ "mobile" boolean NOT NULL,
+ "premium" boolean NOT NULL,
+ "premium_type" integer NOT NULL,
+ "bot" boolean NOT NULL,
+ "bio" varchar,
+ "system" boolean NOT NULL,
+ "nsfw_allowed" boolean NOT NULL,
+ "mfa_enabled" boolean,
+ "totp_secret" varchar,
+ "totp_last_ticket" varchar,
+ "created_at" datetime NOT NULL,
+ "premium_since" datetime,
+ "verified" boolean NOT NULL,
+ "disabled" boolean NOT NULL,
+ "deleted" boolean NOT NULL,
+ "email" varchar,
+ "flags" varchar NOT NULL,
+ "public_flags" integer NOT NULL,
+ "rights" bigint NOT NULL,
+ "data" text NOT NULL,
+ "fingerprints" text NOT NULL,
+ "extended_settings" text NOT NULL,
+ "notes" text NOT NULL,
+ "settingsId" varchar,
+ CONSTRAINT "UQ_b1dd13b6ed980004a795ca184a6" UNIQUE ("settingsId"),
+ CONSTRAINT "FK_76ba283779c8441fd5ff819c8cf" FOREIGN KEY ("settingsId") REFERENCES "user_settings" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION
+ )
+ `);
+ await queryRunner.query(`
+ INSERT INTO "users"(
+ "id",
+ "username",
+ "discriminator",
+ "avatar",
+ "accent_color",
+ "banner",
+ "phone",
+ "desktop",
+ "mobile",
+ "premium",
+ "premium_type",
+ "bot",
+ "bio",
+ "system",
+ "nsfw_allowed",
+ "mfa_enabled",
+ "totp_secret",
+ "totp_last_ticket",
+ "created_at",
+ "premium_since",
+ "verified",
+ "disabled",
+ "deleted",
+ "email",
+ "flags",
+ "public_flags",
+ "rights",
+ "data",
+ "fingerprints",
+ "extended_settings",
+ "settingsId"
+ )
+ SELECT "id",
+ "username",
+ "discriminator",
+ "avatar",
+ "accent_color",
+ "banner",
+ "phone",
+ "desktop",
+ "mobile",
+ "premium",
+ "premium_type",
+ "bot",
+ "bio",
+ "system",
+ "nsfw_allowed",
+ "mfa_enabled",
+ "totp_secret",
+ "totp_last_ticket",
+ "created_at",
+ "premium_since",
+ "verified",
+ "disabled",
+ "deleted",
+ "email",
+ "flags",
+ "public_flags",
+ "rights",
+ "data",
+ "fingerprints",
+ "extended_settings",
+ "settingsId"
+ FROM "temporary_users"
+ `);
+ await queryRunner.query(`
+ DROP TABLE "temporary_users"
+ `);
+ await queryRunner.query(`
+ DROP TABLE "valid_registration_tokens"
+ `);
+ }
+
+}
diff --git a/src/util/migrations/sqlite/1663448560501-drop_id_for_registration_tokens.ts b/src/util/migrations/sqlite/1663448560501-drop_id_for_registration_tokens.ts
new file mode 100644
index 00000000..087cc81f
--- /dev/null
+++ b/src/util/migrations/sqlite/1663448560501-drop_id_for_registration_tokens.ts
@@ -0,0 +1,97 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class dropIdForRegistrationTokens1663448560501 implements MigrationInterface {
+ name = 'dropIdForRegistrationTokens1663448560501'
+
+ public async up(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.query(`
+ CREATE TABLE "temporary_valid_registration_tokens" (
+ "token" varchar NOT NULL,
+ "created_at" datetime NOT NULL,
+ "expires_at" datetime NOT NULL
+ )
+ `);
+ await queryRunner.query(`
+ INSERT INTO "temporary_valid_registration_tokens"("token", "created_at", "expires_at")
+ SELECT "token",
+ "created_at",
+ "expires_at"
+ FROM "valid_registration_tokens"
+ `);
+ await queryRunner.query(`
+ DROP TABLE "valid_registration_tokens"
+ `);
+ await queryRunner.query(`
+ ALTER TABLE "temporary_valid_registration_tokens"
+ RENAME TO "valid_registration_tokens"
+ `);
+ await queryRunner.query(`
+ CREATE TABLE "temporary_valid_registration_tokens" (
+ "token" varchar PRIMARY KEY NOT NULL,
+ "created_at" datetime NOT NULL,
+ "expires_at" datetime NOT NULL
+ )
+ `);
+ await queryRunner.query(`
+ INSERT INTO "temporary_valid_registration_tokens"("token", "created_at", "expires_at")
+ SELECT "token",
+ "created_at",
+ "expires_at"
+ FROM "valid_registration_tokens"
+ `);
+ await queryRunner.query(`
+ DROP TABLE "valid_registration_tokens"
+ `);
+ await queryRunner.query(`
+ ALTER TABLE "temporary_valid_registration_tokens"
+ RENAME TO "valid_registration_tokens"
+ `);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.query(`
+ ALTER TABLE "valid_registration_tokens"
+ RENAME TO "temporary_valid_registration_tokens"
+ `);
+ await queryRunner.query(`
+ CREATE TABLE "valid_registration_tokens" (
+ "token" varchar NOT NULL,
+ "created_at" datetime NOT NULL,
+ "expires_at" datetime NOT NULL
+ )
+ `);
+ await queryRunner.query(`
+ INSERT INTO "valid_registration_tokens"("token", "created_at", "expires_at")
+ SELECT "token",
+ "created_at",
+ "expires_at"
+ FROM "temporary_valid_registration_tokens"
+ `);
+ await queryRunner.query(`
+ DROP TABLE "temporary_valid_registration_tokens"
+ `);
+ await queryRunner.query(`
+ ALTER TABLE "valid_registration_tokens"
+ RENAME TO "temporary_valid_registration_tokens"
+ `);
+ await queryRunner.query(`
+ CREATE TABLE "valid_registration_tokens" (
+ "id" varchar PRIMARY KEY NOT NULL,
+ "token" varchar NOT NULL,
+ "created_at" datetime NOT NULL,
+ "expires_at" datetime NOT NULL
+ )
+ `);
+ await queryRunner.query(`
+ INSERT INTO "valid_registration_tokens"("token", "created_at", "expires_at")
+ SELECT "token",
+ "created_at",
+ "expires_at"
+ FROM "temporary_valid_registration_tokens"
+ `);
+ await queryRunner.query(`
+ DROP TABLE "temporary_valid_registration_tokens"
+ `);
+ }
+
+}
diff --git a/src/api/util/utility/Base64.ts b/src/util/util/Base64.ts
index 46cff77a..46cff77a 100644
--- a/src/api/util/utility/Base64.ts
+++ b/src/util/util/Base64.ts
diff --git a/src/util/util/cdn.ts b/src/util/util/CDN.ts
index 5573b848..5573b848 100644
--- a/src/util/util/cdn.ts
+++ b/src/util/util/CDN.ts
diff --git a/src/api/util/utility/captcha.ts b/src/util/util/Captcha.ts
index 02983f3f..02983f3f 100644
--- a/src/api/util/utility/captcha.ts
+++ b/src/util/util/Captcha.ts
diff --git a/src/api/util/utility/ipAddress.ts b/src/util/util/IPAddress.ts
index c96feb9e..c96feb9e 100644
--- a/src/api/util/utility/ipAddress.ts
+++ b/src/util/util/IPAddress.ts
diff --git a/src/api/util/utility/passwordStrength.ts b/src/util/util/PasswordStrength.ts
index ff83d3df..ff83d3df 100644
--- a/src/api/util/utility/passwordStrength.ts
+++ b/src/util/util/PasswordStrength.ts
diff --git a/src/api/util/utility/RandomInviteID.ts b/src/util/util/RandomInviteID.ts
index feebfd3d..49302916 100644
--- a/src/api/util/utility/RandomInviteID.ts
+++ b/src/util/util/RandomInviteID.ts
@@ -1,13 +1,13 @@
import { Snowflake } from "@fosscord/util";
+import crypto from "crypto";
-export function random(length = 6) {
+export function random(length = 6, chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") {
// Declare all characters
- let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
// Pick characers randomly
let str = "";
for (let i = 0; i < length; i++) {
- str += chars.charAt(Math.floor(Math.random() * chars.length));
+ str += chars.charAt(Math.floor(crypto.randomInt(chars.length)));
}
return str;
diff --git a/src/util/util/String.ts b/src/util/util/String.ts
index 55f11e8d..cd5cb4f2 100644
--- a/src/util/util/String.ts
+++ b/src/util/util/String.ts
@@ -1,4 +1,22 @@
import { SPECIAL_CHAR } from "./Regex";
+import { FieldErrors } from "@fosscord/util";
+import { Request } from "express";
+import { ntob } from "./Base64";
+
+export function checkLength(str: string, min: number, max: number, key: string, req: Request) {
+ if (str.length < min || str.length > max) {
+ throw FieldErrors({
+ [key]: {
+ code: "BASE_TYPE_BAD_LENGTH",
+ message: req.t("common:field.BASE_TYPE_BAD_LENGTH", { length: `${min} - ${max}` })
+ }
+ });
+ }
+}
+
+export function generateCode() {
+ return ntob(Date.now() + Math.randomIntBetween(0, 10000));
+}
export function trimSpecial(str?: string): string {
// @ts-ignore
diff --git a/src/util/util/index.ts b/src/util/util/index.ts
index 11f0b72a..1ef7467c 100644
--- a/src/util/util/index.ts
+++ b/src/util/util/index.ts
@@ -2,7 +2,7 @@ export * from "./ApiError";
export * from "./Array";
export * from "./BitField";
//export * from "./Categories";
-export * from "./cdn";
+export * from "./CDN";
export * from "./Config";
export * from "./Constants";
export * from "./Database";
@@ -23,3 +23,6 @@ export * from "./Snowflake";
export * from "./String";
export * from "./Token";
export * from "./TraverseDirectory";
+export * from "./IPAddress";
+export * from "./RandomInviteID";
+export * from "./Captcha";
\ No newline at end of file
|