mirror of
https://github.com/spacebarchat/server.git
synced 2024-11-10 12:42:44 +01:00
✨ accept invite page
This commit is contained in:
parent
ee6cc64b36
commit
269bbdfeae
@ -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#"
|
||||||
|
@ -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",
|
||||||
|
@ -67,6 +67,8 @@ export default function TestClient(app: Application) {
|
|||||||
res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);
|
res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);
|
||||||
res.set("content-type", "text/html");
|
res.set("content-type", "text/html");
|
||||||
|
|
||||||
|
if (req.url.startsWith("/invite")) return res.send(html.replace("9b2b7f0632acd0c5e781", "9f24f709a3de09b67c49"));
|
||||||
|
|
||||||
res.send(html);
|
res.send(html);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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) });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user