1
0
mirror of https://github.com/spacebarchat/server.git synced 2024-09-21 02:01:33 +02:00

accept invite page

This commit is contained in:
Flam3rboy 2021-09-20 17:38:16 +02:00
parent 6c0a69d95b
commit dd9ec0c6a0
6 changed files with 41 additions and 27 deletions

View File

@ -38,7 +38,6 @@
"additionalProperties": false, "additionalProperties": false,
"required": [ "required": [
"consent", "consent",
"password",
"username" "username"
], ],
"$schema": "http://json-schema.org/draft-07/schema#" "$schema": "http://json-schema.org/draft-07/schema#"

View File

@ -19,7 +19,7 @@
ASSET_ENDPOINT: "", ASSET_ENDPOINT: "",
MEDIA_PROXY_ENDPOINT: "https://media.discordapp.net", MEDIA_PROXY_ENDPOINT: "https://media.discordapp.net",
WIDGET_ENDPOINT: `//${location.host}/widget`, WIDGET_ENDPOINT: `//${location.host}/widget`,
INVITE_HOST: `${location.hostname}`, INVITE_HOST: `${location.host}/invite`,
GUILD_TEMPLATE_HOST: "discord.new", GUILD_TEMPLATE_HOST: "discord.new",
GIFT_CODE_HOST: "discord.gift", GIFT_CODE_HOST: "discord.gift",
RELEASE_CHANNEL: "stable", RELEASE_CHANNEL: "stable",

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express"; import { Request, Response, Router } from "express";
import { trimSpecial, User, Snowflake, Config, defaultSettings } from "@fosscord/util"; import { trimSpecial, User, Snowflake, Config, defaultSettings, Member, Invite } from "@fosscord/util";
import bcrypt from "bcrypt"; import bcrypt from "bcrypt";
import { EMAIL_REGEX, FieldErrors, route } from "@fosscord/api"; import { EMAIL_REGEX, FieldErrors, route } from "@fosscord/api";
import "missing-native-js-functions"; import "missing-native-js-functions";
@ -19,7 +19,7 @@ export interface RegisterSchema {
* @minLength 1 * @minLength 1
* @maxLength 72 * @maxLength 72
*/ */
password: string; // TODO: use password strength of config password?: string;
consent: boolean; consent: boolean;
/** /**
* @TJS-format email * @TJS-format email
@ -60,7 +60,6 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
} }
console.log("register", req.body.email, req.body.username, ip); console.log("register", req.body.email, req.body.username, ip);
// TODO: automatically join invite
// TODO: gift_code_sku_id? // TODO: gift_code_sku_id?
// TODO: check password strength // TODO: check password strength
@ -87,13 +86,6 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
}); });
} }
// require invite to register -> e.g. for organizations to send invites to their employees
if (register.requireInvite && !invite) {
throw FieldErrors({
email: { code: "INVITE_ONLY", message: req.t("auth:register.INVITE_ONLY") }
});
}
if (email) { if (email) {
// replace all dots and chars after +, if its a gmail.com 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") } }); if (!email) throw FieldErrors({ email: { code: "INVALID_EMAIL", message: req.t("auth:register.INVALID_EMAIL") } });
@ -109,13 +101,13 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
} }
}); });
} }
} else if (register.email.necessary) { } else if (register.email.required) {
throw FieldErrors({ throw FieldErrors({
email: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") } email: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") }
}); });
} }
if (register.dateOfBirth.necessary && !date_of_birth) { if (register.dateOfBirth.required && !date_of_birth) {
throw FieldErrors({ throw FieldErrors({
date_of_birth: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") } date_of_birth: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") }
}); });
@ -162,8 +154,14 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
// TODO: check captcha // TODO: check captcha
} }
// the salt is saved in the password refer to bcrypt docs if (password) {
password = await bcrypt.hash(password, 12); // the salt is saved in the password refer to bcrypt docs
password = await bcrypt.hash(password, 12);
} else if (register.password.required) {
throw FieldErrors({
password: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") }
});
}
let exists; let exists;
// randomly generates a discriminator between 1 and 9999 and checks max five times if it already exists // randomly generates a discriminator between 1 and 9999 and checks max five times if it already exists
@ -217,6 +215,16 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
fingerprints: [] fingerprints: []
}).save(); }).save();
if (invite) {
// await to fail if the invite doesn't exist (necessary for requireInvite to work properly) (username only signups are possible)
await Invite.joinGuild(user.id, invite);
} else if (register.requireInvite) {
// require invite to register -> e.g. for organizations to send invites to their employees
throw FieldErrors({
email: { code: "INVITE_ONLY", message: req.t("auth:register.INVITE_ONLY") }
});
}
return res.json({ token: await generateToken(user.id) }); return res.json({ token: await generateToken(user.id) });
}); });

View File

@ -15,14 +15,9 @@ router.get("/:code", route({}), async (req: Request, res: Response) => {
router.post("/:code", route({}), async (req: Request, res: Response) => { router.post("/:code", route({}), async (req: Request, res: Response) => {
const { code } = req.params; const { code } = req.params;
const invite = await Invite.joinGuild(req.user_id, code);
const invite = await Invite.findOneOrFail({ code }); res.json(invite);
if (invite.uses++ >= invite.max_uses) await Invite.delete({ code });
else await invite.save();
await Member.addToGuild(req.user_id, invite.guild_id);
res.status(200).send(invite);
}); });
// * cant use permission of route() function because path doesn't have guild_id/channel_id // * cant use permission of route() function because path doesn't have guild_id/channel_id

View File

@ -110,13 +110,13 @@ export interface ConfigValue {
}; };
register: { register: {
email: { email: {
necessary: boolean; // we have to use necessary instead of required as the cli tool uses json schema and can't use required required: boolean;
allowlist: boolean; allowlist: boolean;
blocklist: boolean; blocklist: boolean;
domains: string[]; domains: string[];
}; };
dateOfBirth: { dateOfBirth: {
necessary: boolean; required: boolean;
minimum: number; // in years minimum: number; // in years
}; };
requireCaptcha: boolean; requireCaptcha: boolean;
@ -125,6 +125,7 @@ export interface ConfigValue {
allowMultipleAccounts: boolean; allowMultipleAccounts: boolean;
blockProxies: boolean; blockProxies: boolean;
password: { password: {
required: boolean;
minLength: number; minLength: number;
minNumbers: number; minNumbers: number;
minUpperCase: number; minUpperCase: number;
@ -246,14 +247,14 @@ export const DefaultConfigOptions: ConfigValue = {
}, },
register: { register: {
email: { email: {
necessary: true, required: false,
allowlist: false, allowlist: false,
blocklist: true, blocklist: true,
domains: [], // TODO: efficiently save domain blocklist in database domains: [], // TODO: efficiently save domain blocklist in database
// domains: fs.readFileSync(__dirname + "/blockedEmailDomains.txt", { encoding: "utf8" }).split("\n"), // domains: fs.readFileSync(__dirname + "/blockedEmailDomains.txt", { encoding: "utf8" }).split("\n"),
}, },
dateOfBirth: { dateOfBirth: {
necessary: true, required: false,
minimum: 13, minimum: 13,
}, },
requireInvite: false, requireInvite: false,
@ -262,6 +263,7 @@ export const DefaultConfigOptions: ConfigValue = {
allowMultipleAccounts: true, allowMultipleAccounts: true,
blockProxies: true, blockProxies: true,
password: { password: {
required: false,
minLength: 8, minLength: 8,
minNumbers: 2, minNumbers: 2,
minUpperCase: 2, minUpperCase: 2,

View File

@ -1,4 +1,5 @@
import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn, RelationId } from "typeorm"; import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn, RelationId } from "typeorm";
import { Member } from ".";
import { BaseClass } from "./BaseClass"; import { BaseClass } from "./BaseClass";
import { Channel } from "./Channel"; import { Channel } from "./Channel";
import { Guild } from "./Guild"; import { Guild } from "./Guild";
@ -63,4 +64,13 @@ export class Invite extends BaseClass {
@Column({ nullable: true }) @Column({ nullable: true })
target_user_type?: number; target_user_type?: number;
static async joinGuild(user_id: string, code: string) {
const invite = await Invite.findOneOrFail({ code });
if (invite.uses++ >= invite.max_uses && invite.max_uses !== 0) await Invite.delete({ code });
else await invite.save();
await Member.addToGuild(user_id, invite.guild_id);
return invite;
}
} }