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
5 changes: 3 additions & 2 deletions backend/__tests__/__testData__/connections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ export async function createConnection(
data: Partial<ConnectionsDal.DBConnection>,
maxPerUser = 25,
): Promise<ConnectionsDal.DBConnection> {
const defaultName = "user" + new ObjectId().toHexString();
const result = await ConnectionsDal.create(
{
uid: data.initiatorUid ?? new ObjectId().toHexString(),
name: data.initiatorName ?? "user" + new ObjectId().toHexString(),
name: data.initiatorName ?? defaultName,
},
{
uid: data.receiverUid ?? new ObjectId().toHexString(),
name: data.receiverName ?? "user" + new ObjectId().toHexString(),
name: data.receiverName ?? defaultName,
},
maxPerUser,
);
Expand Down
251 changes: 74 additions & 177 deletions frontend/src/ts/auth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {

import Ape from "./ape";
import { updateFromServer as updateConfigFromServer } from "./config";
import { navigate } from "./controllers/route-controller";
import * as DB from "./db";
import * as Notifications from "./elements/notifications";
import {
Expand All @@ -26,18 +25,16 @@ import {
import * as RegisterCaptchaModal from "./modals/register-captcha";
import { showPopup } from "./modals/simple-modals-base";
import * as AuthEvent from "./observables/auth-event";
import * as LoginPage from "./pages/login";
import * as Sentry from "./sentry";
import { showLoaderBar, hideLoaderBar } from "./signals/loader-bar";
import * as ConnectionState from "./states/connection";
import { addBanner } from "./stores/banners";
import { qs, qsa } from "./utils/dom";
import * as Misc from "./utils/misc";

export const gmailProvider = new GoogleAuthProvider();
export const githubProvider = new GithubAuthProvider();

async function sendVerificationEmail(): Promise<void> {
export async function sendVerificationEmail(): Promise<void> {
if (!isAuthAvailable()) {
Notifications.add("Authentication uninitialized", -1, {
duration: 3,
Expand All @@ -46,9 +43,7 @@ async function sendVerificationEmail(): Promise<void> {
}

showLoaderBar();
qs(".sendVerificationEmail")?.disable();
const response = await Ape.users.verificationEmail();
qs(".sendVerificationEmail")?.enable();
if (response.status !== 200) {
hideLoaderBar();
Notifications.add("Failed to request verification email", -1, { response });
Expand Down Expand Up @@ -97,8 +92,6 @@ async function getDataAndInit(): Promise<boolean> {
return true;
} catch (error) {
console.error(error);
LoginPage.enableInputs();
qs("header nav .view-account")?.setStyle({ opacity: "1" });
if (error instanceof DB.SnapshotInitError) {
if (error.responseCode === 429) {
Notifications.add(
Expand Down Expand Up @@ -155,127 +148,95 @@ export async function onAuthStateChanged(
void Sentry.clearUser();
}

let keyframes = [
{
percentage: 90,
durationMs: 1000,
text: "Downloading user data...",
},
];

//undefined means navigate to whatever the current window.location.pathname is
await navigate(undefined, {
force: true,
loadingOptions: {
loadingMode: () => {
if (user !== null) {
return "sync";
} else {
return "none";
}
},
loadingPromise: async () => {
await userPromise;
},
style: "bar",
keyframes: keyframes,
},
});

AuthEvent.dispatch({
type: "authStateChanged",
data: { isUserSignedIn: user !== null },
data: { isUserSignedIn: user !== null, loadPromise: userPromise },
});
}

export async function signIn(email: string, password: string): Promise<void> {
export async function signIn(
email: string,
password: string,
rememberMe: boolean,
): Promise<
| {
success: true;
}
| {
success: false;
message: string;
}
> {
if (!isAuthAvailable()) {
Notifications.add("Authentication uninitialized", -1);
return;
}
if (!ConnectionState.get()) {
Notifications.add("You are offline", 0, {
duration: 2,
});
return;
return { success: false, message: "Authentication uninitialized" };
}

LoginPage.showPreloader();
LoginPage.disableInputs();
LoginPage.disableSignUpButton();

if (email === "" || password === "") {
Notifications.add("Please fill in all fields", 0);
LoginPage.hidePreloader();
LoginPage.enableInputs();
LoginPage.enableSignUpButton();
return;
}

const rememberMe =
qs<HTMLInputElement>(".pageLogin .login #rememberMe input")?.isChecked() ??
false;

const { error } = await tryCatch(
signInWithEmailAndPassword(email, password, rememberMe),
);

if (error !== null) {
Notifications.add(error.message, -1);
LoginPage.hidePreloader();
LoginPage.enableInputs();
LoginPage.updateSignupButton();
return;
return { success: false, message: error.message };
}
return { success: true };
}

async function signInWithProvider(provider: AuthProvider): Promise<void> {
async function signInWithProvider(
provider: AuthProvider,
rememberMe: boolean,
): Promise<
| {
success: true;
}
| {
success: false;
message: string;
}
> {
if (!isAuthAvailable()) {
Notifications.add("Authentication uninitialized", -1, {
duration: 3,
});
return;
}
if (!ConnectionState.get()) {
Notifications.add("You are offline", 0, {
duration: 2,
});
return;
return { success: false, message: "Authentication uninitialized" };
}

LoginPage.showPreloader();
LoginPage.disableInputs();
LoginPage.disableSignUpButton();
const rememberMe =
qs<HTMLInputElement>(".pageLogin .login #rememberMe input")?.isChecked() ??
false;

const { error } = await tryCatch(signInWithPopup(provider, rememberMe));

if (error !== null) {
if (error.message !== "") {
Notifications.add(error.message, -1);
}
LoginPage.hidePreloader();
LoginPage.enableInputs();
LoginPage.updateSignupButton();
return;
return { success: false, message: error.message };
}
return { success: true };
}

async function signInWithGoogle(): Promise<void> {
return signInWithProvider(gmailProvider);
export async function signInWithGoogle(rememberMe: boolean): Promise<
| {
success: true;
}
| {
success: false;
message: string;
}
> {
return signInWithProvider(gmailProvider, rememberMe);
}

async function signInWithGitHub(): Promise<void> {
return signInWithProvider(githubProvider);
export async function signInWithGitHub(rememberMe: boolean): Promise<
| {
success: true;
}
| {
success: false;
message: string;
}
> {
return signInWithProvider(githubProvider, rememberMe);
}

async function addGoogleAuth(): Promise<void> {
export async function addGoogleAuth(): Promise<void> {
return addAuthProvider("Google", gmailProvider);
}

async function addGithubAuth(): Promise<void> {
export async function addGithubAuth(): Promise<void> {
return addAuthProvider("GitHub", githubProvider);
}

Expand Down Expand Up @@ -324,38 +285,27 @@ export function signOut(): void {
void authSignOut();
}

async function signUp(): Promise<void> {
export async function signUp(
name: string,
email: string,
password: string,
): Promise<
| {
success: true;
}
| {
success: false;
message: string;
}
> {
if (!isAuthAvailable()) {
Notifications.add("Authentication uninitialized", -1, {
duration: 3,
});
return;
}
if (!ConnectionState.get()) {
Notifications.add("You are offline", 0, {
duration: 2,
});
return;
return { success: false, message: "Authentication uninitialized" };
}
await RegisterCaptchaModal.show();
const captchaToken = await RegisterCaptchaModal.promise;
if (captchaToken === undefined || captchaToken === "") {
Notifications.add("Please complete the captcha", -1);
return;
return { success: false, message: "Please complete the captcha" };
}
LoginPage.disableInputs();
LoginPage.disableSignUpButton();
LoginPage.showPreloader();

const signupData = LoginPage.getSignupData();
if (signupData === false) {
LoginPage.hidePreloader();
LoginPage.enableInputs();
LoginPage.updateSignupButton();
Notifications.add("Please fill in all fields", 0);
return;
}
const { name: nname, email, password } = signupData;

try {
const createdAuthUser = await createUserWithEmailAndPassword(
Expand All @@ -365,7 +315,7 @@ async function signUp(): Promise<void> {

const signInResponse = await Ape.users.create({
body: {
name: nname,
name: name,
captcha: captchaToken,
email,
uid: createdAuthUser.user.uid,
Expand All @@ -375,13 +325,13 @@ async function signUp(): Promise<void> {
throw new Error(`Failed to sign in: ${signInResponse.body.message}`);
}

await updateProfile(createdAuthUser.user, { displayName: nname });
await updateProfile(createdAuthUser.user, { displayName: name });
await sendVerificationEmail();
LoginPage.hidePreloader();
await onAuthStateChanged(true, createdAuthUser.user);
resetIgnoreAuthCallback();

Notifications.add("Account created", 1);
return { success: true };
} catch (e) {
let message = Misc.createErrorMessage(e, "Failed to create account");

Expand All @@ -395,60 +345,7 @@ async function signUp(): Promise<void> {
}

Notifications.add(message, -1);
LoginPage.hidePreloader();
LoginPage.enableInputs();
LoginPage.updateSignupButton();
signOut();
return;
return { success: false, message };
}
}

qs(".pageLogin .login form")?.on("submit", (e) => {
e.preventDefault();
const email =
qsa<HTMLInputElement>(".pageLogin .login input")?.[0]?.getValue() ?? "";
const password =
qsa<HTMLInputElement>(".pageLogin .login input")?.[1]?.getValue() ?? "";
void signIn(email, password);
});

qs(".pageLogin .login button.signInWithGoogle")?.on("click", () => {
void signInWithGoogle();
});

qs(".pageLogin .login button.signInWithGitHub")?.on("click", () => {
void signInWithGitHub();
});

qs("nav .accountButtonAndMenu .menu button.signOut")?.on("click", () => {
if (!isAuthAvailable()) {
Notifications.add("Authentication uninitialized", -1, {
duration: 3,
});
return;
}
signOut();
});

qs(".pageLogin .register form")?.on("submit", (e) => {
e.preventDefault();
void signUp();
});

qs(".pageAccountSettings")?.onChild("click", "#addGoogleAuth", () => {
void addGoogleAuth();
});

qs(".pageAccountSettings")?.onChild("click", "#addGithubAuth", () => {
void addGithubAuth();
});

qs(".pageAccount")?.onChild("click", ".sendVerificationEmail", () => {
if (!ConnectionState.get()) {
Notifications.add("You are offline", 0, {
duration: 2,
});
return;
}
void sendVerificationEmail();
});
Loading
Loading