move types
This commit is contained in:
parent
a48d250564
commit
e164964937
|
@ -2,25 +2,26 @@ import Nano from "nano";
|
||||||
import { DocumentInsertResponse } from "nano";
|
import { DocumentInsertResponse } from "nano";
|
||||||
import { getTimestamp } from "./time-utils";
|
import { getTimestamp } from "./time-utils";
|
||||||
import env from "./env-vars";
|
import env from "./env-vars";
|
||||||
|
import * as T from "../../../website/src/lib/account-types";
|
||||||
|
|
||||||
const nano = Nano(env.couchDbURL);
|
const nano = Nano(env.couchDbURL);
|
||||||
const usersDb = nano.db.use("test-users");
|
const usersDb = nano.db.use("test-users");
|
||||||
|
|
||||||
export function updateLastActive(user: LingdocsUser): LingdocsUser {
|
export function updateLastActive(user: T.LingdocsUser): T.LingdocsUser {
|
||||||
return {
|
return {
|
||||||
...user,
|
...user,
|
||||||
lastActive: getTimestamp(),
|
lastActive: getTimestamp(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateLastLogin(user: LingdocsUser): LingdocsUser {
|
export function updateLastLogin(user: T.LingdocsUser): T.LingdocsUser {
|
||||||
return {
|
return {
|
||||||
...user,
|
...user,
|
||||||
lastLogin: getTimestamp(),
|
lastLogin: getTimestamp(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function processAPIResponse(user: LingdocsUser, response: DocumentInsertResponse): LingdocsUser | undefined {
|
function processAPIResponse(user: T.LingdocsUser, response: DocumentInsertResponse): T.LingdocsUser | undefined {
|
||||||
if (response.ok !== true) return undefined;
|
if (response.ok !== true) return undefined;
|
||||||
return {
|
return {
|
||||||
...user,
|
...user,
|
||||||
|
@ -29,7 +30,7 @@ function processAPIResponse(user: LingdocsUser, response: DocumentInsertResponse
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getLingdocsUser(field: "email" | "userId" | "githubId" | "googleId" | "twitterId", value: string): Promise<undefined | LingdocsUser> {
|
export async function getLingdocsUser(field: "email" | "userId" | "githubId" | "googleId" | "twitterId", value: string): Promise<undefined | T.LingdocsUser> {
|
||||||
const user = await usersDb.find({
|
const user = await usersDb.find({
|
||||||
selector: field === "githubId"
|
selector: field === "githubId"
|
||||||
? { github: { id: value }}
|
? { github: { id: value }}
|
||||||
|
@ -42,10 +43,10 @@ export async function getLingdocsUser(field: "email" | "userId" | "githubId" | "
|
||||||
if (!user.docs.length) {
|
if (!user.docs.length) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return user.docs[0] as LingdocsUser;
|
return user.docs[0] as T.LingdocsUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function insertLingdocsUser(user: LingdocsUser): Promise<LingdocsUser> {
|
export async function insertLingdocsUser(user: T.LingdocsUser): Promise<T.LingdocsUser> {
|
||||||
const res = await usersDb.insert(user);
|
const res = await usersDb.insert(user);
|
||||||
const newUser = processAPIResponse(user, res);
|
const newUser = processAPIResponse(user, res);
|
||||||
if (!newUser) {
|
if (!newUser) {
|
||||||
|
@ -54,7 +55,7 @@ export async function insertLingdocsUser(user: LingdocsUser): Promise<LingdocsUs
|
||||||
return newUser;
|
return newUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteLingdocsUser(uuid: UUID): Promise<void> {
|
export async function deleteLingdocsUser(uuid: T.UUID): Promise<void> {
|
||||||
const user = await getLingdocsUser("userId", uuid);
|
const user = await getLingdocsUser("userId", uuid);
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
// TODO: cleanup userdbs etc
|
// TODO: cleanup userdbs etc
|
||||||
|
@ -64,24 +65,24 @@ export async function deleteLingdocsUser(uuid: UUID): Promise<void> {
|
||||||
|
|
||||||
// TODO: TO MAKE THIS SAFER, PASS IN JUST THE UPDATING FIELDS!!
|
// TODO: TO MAKE THIS SAFER, PASS IN JUST THE UPDATING FIELDS!!
|
||||||
// TODO: take out the updated object - do just an ID, and then use the toUpdate safe thing
|
// TODO: take out the updated object - do just an ID, and then use the toUpdate safe thing
|
||||||
export async function updateLingdocsUser(uuid: UUID, toUpdate:
|
export async function updateLingdocsUser(uuid: T.UUID, toUpdate:
|
||||||
// TODO: OR USE REDUCER??
|
// TODO: OR USE REDUCER??
|
||||||
{ name: string } |
|
{ name: string } |
|
||||||
{ name?: string, email: string, emailVerified: Hash } |
|
{ name?: string, email: string, emailVerified: T.Hash } |
|
||||||
{ email: string, emailVerified: true } |
|
{ email: string, emailVerified: true } |
|
||||||
{ emailVerified: Hash } |
|
{ emailVerified: T.Hash } |
|
||||||
{ emailVerified: true } |
|
{ emailVerified: true } |
|
||||||
{ password: Hash } |
|
{ password: T.Hash } |
|
||||||
{ google: GoogleProfile | undefined } |
|
{ google: T.GoogleProfile | undefined } |
|
||||||
{ github: GitHubProfile | undefined } |
|
{ github: T.GitHubProfile | undefined } |
|
||||||
{ twitter: TwitterProfile | undefined } |
|
{ twitter: T.TwitterProfile | undefined } |
|
||||||
{
|
{
|
||||||
passwordReset: {
|
passwordReset: {
|
||||||
tokenHash: Hash,
|
tokenHash: T.Hash,
|
||||||
requestedOn: TimeStamp,
|
requestedOn: T.TimeStamp,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
): Promise<LingdocsUser> {
|
): Promise<T.LingdocsUser> {
|
||||||
const user = await getLingdocsUser("userId", uuid);
|
const user = await getLingdocsUser("userId", uuid);
|
||||||
if (!user) throw new Error("unable to update - user not found " + uuid);
|
if (!user) throw new Error("unable to update - user not found " + uuid);
|
||||||
if ("password" in toUpdate) {
|
if ("password" in toUpdate) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import nodemailer from "nodemailer";
|
import nodemailer from "nodemailer";
|
||||||
import inProd from "./inProd";
|
import inProd from "./inProd";
|
||||||
import env from "./env-vars";
|
import env from "./env-vars";
|
||||||
|
import * as T from "../../../website/src/lib/account-types";
|
||||||
|
|
||||||
type Address = string | { name: string, address: string };
|
type Address = string | { name: string, address: string };
|
||||||
|
|
||||||
|
@ -9,7 +10,7 @@ const from: Address = {
|
||||||
address: "admin@lingdocs.com",
|
address: "admin@lingdocs.com",
|
||||||
};
|
};
|
||||||
|
|
||||||
function getAddress(user: LingdocsUser): Address {
|
function getAddress(user: T.LingdocsUser): Address {
|
||||||
// TODO: Guard against ""
|
// TODO: Guard against ""
|
||||||
if (!user.name) return user.email || "";
|
if (!user.name) return user.email || "";
|
||||||
return {
|
return {
|
||||||
|
@ -40,7 +41,7 @@ async function sendEmail(to: Address, subject: string, text: string) {
|
||||||
// TODO: MAKE THIS
|
// TODO: MAKE THIS
|
||||||
const baseURL = inProd ? "https://account.lingdocs.com" : "http://localhost:4000";
|
const baseURL = inProd ? "https://account.lingdocs.com" : "http://localhost:4000";
|
||||||
|
|
||||||
export async function sendVerificationEmail(user: LingdocsUser, token: URLToken) {
|
export async function sendVerificationEmail(user: T.LingdocsUser, token: T.URLToken) {
|
||||||
const content = `Hello ${user.name},
|
const content = `Hello ${user.name},
|
||||||
|
|
||||||
Please verify your email by visiting this link: ${baseURL}/email-verification/${user.userId}/${token}
|
Please verify your email by visiting this link: ${baseURL}/email-verification/${user.userId}/${token}
|
||||||
|
@ -49,7 +50,7 @@ LingDocs Admin`;
|
||||||
await sendEmail(getAddress(user), "Please Verify Your E-mail", content);
|
await sendEmail(getAddress(user), "Please Verify Your E-mail", content);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sendPasswordResetEmail(user: LingdocsUser, token: URLToken) {
|
export async function sendPasswordResetEmail(user: T.LingdocsUser, token: T.URLToken) {
|
||||||
const content = `Hello ${user.name},
|
const content = `Hello ${user.name},
|
||||||
|
|
||||||
Please visit this link to reset your password: ${baseURL}/password-reset/${user.userId}/${token}
|
Please visit this link to reset your password: ${baseURL}/password-reset/${user.userId}/${token}
|
||||||
|
|
|
@ -1,23 +1,24 @@
|
||||||
import { hash, compare } from "bcryptjs";
|
import { hash, compare } from "bcryptjs";
|
||||||
import { randomBytes } from "crypto";
|
import { randomBytes } from "crypto";
|
||||||
import base64url from "base64url";
|
import base64url from "base64url";
|
||||||
|
import * as T from "../../../website/src/lib/account-types";
|
||||||
|
|
||||||
const tokenSize = 24;
|
const tokenSize = 24;
|
||||||
|
|
||||||
export async function getHash(p: string): Promise<Hash> {
|
export async function getHash(p: string): Promise<T.Hash> {
|
||||||
return await hash(p, 10) as Hash;
|
return await hash(p, 10) as T.Hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getEmailTokenAndHash(): Promise<{ token: URLToken, hash: Hash }> {
|
export async function getEmailTokenAndHash(): Promise<{ token: T.URLToken, hash: T.Hash }> {
|
||||||
const token = getURLToken();
|
const token = getURLToken();
|
||||||
const h = await getHash(token);
|
const h = await getHash(token);
|
||||||
return { token, hash: h };
|
return { token, hash: h };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getURLToken(): URLToken {
|
export function getURLToken(): T.URLToken {
|
||||||
return base64url(randomBytes(tokenSize)) as URLToken;
|
return base64url(randomBytes(tokenSize)) as T.URLToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compareToHash(s: string, hash: Hash): Promise<boolean> {
|
export function compareToHash(s: string, hash: T.Hash): Promise<boolean> {
|
||||||
return compare(s, hash);
|
return compare(s, hash);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
export function getTimestamp(): TimeStamp {
|
import * as T from "../../../website/src/lib/account-types";
|
||||||
return Date.now() as TimeStamp;
|
|
||||||
|
export function getTimestamp(): T.TimeStamp {
|
||||||
|
return Date.now() as T.TimeStamp;
|
||||||
}
|
}
|
|
@ -7,12 +7,13 @@ import {
|
||||||
import { getTimestamp } from "../lib/time-utils";
|
import { getTimestamp } from "../lib/time-utils";
|
||||||
import { sendVerificationEmail } from "../lib/mail-utils";
|
import { sendVerificationEmail } from "../lib/mail-utils";
|
||||||
import { outsideProviders } from "../middleware/setup-passport";
|
import { outsideProviders } from "../middleware/setup-passport";
|
||||||
|
import * as T from "../../../website/src/lib/account-types";
|
||||||
|
|
||||||
function getUUID(): UUID {
|
function getUUID(): T.UUID {
|
||||||
return uuidv4() as UUID;
|
return uuidv4() as T.UUID;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canRemoveOneOutsideProvider(user: LingdocsUser): boolean {
|
export function canRemoveOneOutsideProvider(user: T.LingdocsUser): boolean {
|
||||||
if (user.email && user.password) {
|
if (user.email && user.password) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +21,7 @@ export function canRemoveOneOutsideProvider(user: LingdocsUser): boolean {
|
||||||
return providersPresent.length > 1;
|
return providersPresent.length > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getVerifiedEmail({ emails }: ProviderProfile): string | false {
|
export function getVerifiedEmail({ emails }: T.ProviderProfile): string | false {
|
||||||
return (
|
return (
|
||||||
emails
|
emails
|
||||||
&& emails.length
|
&& emails.length
|
||||||
|
@ -29,7 +30,7 @@ export function getVerifiedEmail({ emails }: ProviderProfile): string | false {
|
||||||
) ? emails[0].value : false;
|
) ? emails[0].value : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEmailFromGoogleProfile(profile: GoogleProfile): { email: string | undefined, verified: boolean } {
|
function getEmailFromGoogleProfile(profile: T.GoogleProfile): { email: string | undefined, verified: boolean } {
|
||||||
if (!profile.emails || profile.emails.length === 0) {
|
if (!profile.emails || profile.emails.length === 0) {
|
||||||
return { email: undefined, verified: false };
|
return { email: undefined, verified: false };
|
||||||
}
|
}
|
||||||
|
@ -49,20 +50,20 @@ export async function createNewUser(input: {
|
||||||
passwordPlainText: string,
|
passwordPlainText: string,
|
||||||
} | {
|
} | {
|
||||||
strategy: "github",
|
strategy: "github",
|
||||||
profile: GitHubProfile,
|
profile: T.GitHubProfile,
|
||||||
} | {
|
} | {
|
||||||
strategy: "google",
|
strategy: "google",
|
||||||
profile: GoogleProfile,
|
profile: T.GoogleProfile,
|
||||||
} | {
|
} | {
|
||||||
strategy: "twitter",
|
strategy: "twitter",
|
||||||
profile: TwitterProfile,
|
profile: T.TwitterProfile,
|
||||||
}): Promise<LingdocsUser> {
|
}): Promise<T.LingdocsUser> {
|
||||||
const userId = getUUID();
|
const userId = getUUID();
|
||||||
const now = getTimestamp();
|
const now = getTimestamp();
|
||||||
if (input.strategy === "local") {
|
if (input.strategy === "local") {
|
||||||
const email = await getEmailTokenAndHash();
|
const email = await getEmailTokenAndHash();
|
||||||
const password = await getHash(input.passwordPlainText);
|
const password = await getHash(input.passwordPlainText);
|
||||||
const newUser: LingdocsUser = {
|
const newUser: T.LingdocsUser = {
|
||||||
_id: userId,
|
_id: userId,
|
||||||
userId,
|
userId,
|
||||||
email: input.email,
|
email: input.email,
|
||||||
|
@ -80,7 +81,7 @@ export async function createNewUser(input: {
|
||||||
}
|
}
|
||||||
// GitHub || Twitter
|
// GitHub || Twitter
|
||||||
if (input.strategy === "github" || input.strategy === "twitter") {
|
if (input.strategy === "github" || input.strategy === "twitter") {
|
||||||
const newUser: LingdocsUser = {
|
const newUser: T.LingdocsUser = {
|
||||||
_id: userId,
|
_id: userId,
|
||||||
userId,
|
userId,
|
||||||
emailVerified: false,
|
emailVerified: false,
|
||||||
|
@ -99,7 +100,7 @@ export async function createNewUser(input: {
|
||||||
const { email, verified } = getEmailFromGoogleProfile(input.profile);
|
const { email, verified } = getEmailFromGoogleProfile(input.profile);
|
||||||
if (email && !verified) {
|
if (email && !verified) {
|
||||||
const em = await getEmailTokenAndHash();
|
const em = await getEmailTokenAndHash();
|
||||||
const newUser: LingdocsUser = {
|
const newUser: T.LingdocsUser = {
|
||||||
_id: userId,
|
_id: userId,
|
||||||
userId,
|
userId,
|
||||||
email,
|
email,
|
||||||
|
@ -115,7 +116,7 @@ export async function createNewUser(input: {
|
||||||
sendVerificationEmail(user, em.token);
|
sendVerificationEmail(user, em.token);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
const newUser: LingdocsUser = {
|
const newUser: T.LingdocsUser = {
|
||||||
_id: userId,
|
_id: userId,
|
||||||
userId,
|
userId,
|
||||||
email,
|
email,
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
getVerifiedEmail,
|
getVerifiedEmail,
|
||||||
} from "../lib/user-utils";
|
} from "../lib/user-utils";
|
||||||
import env from "../lib/env-vars";
|
import env from "../lib/env-vars";
|
||||||
|
import * as T from "../../../website/src/lib/account-types";
|
||||||
|
|
||||||
export const outsideProviders: ("github" | "google" | "twitter")[] = ["github", "google", "twitter"];
|
export const outsideProviders: ("github" | "google" | "twitter")[] = ["github", "google", "twitter"];
|
||||||
|
|
||||||
|
@ -116,7 +117,7 @@ function setupPassport(passport: PassportStatic) {
|
||||||
async function(req: any, accessToken: any, refreshToken: any, profileRaw: any, done: any) {
|
async function(req: any, accessToken: any, refreshToken: any, profileRaw: any, done: any) {
|
||||||
// not getting refresh token
|
// not getting refresh token
|
||||||
const { _json, _raw, ...profile } = profileRaw;
|
const { _json, _raw, ...profile } = profileRaw;
|
||||||
const ghProfile: GitHubProfile = { ...profile, accessToken };
|
const ghProfile: T.GitHubProfile = { ...profile, accessToken };
|
||||||
try {
|
try {
|
||||||
if (req.isAuthenticated()) {
|
if (req.isAuthenticated()) {
|
||||||
if (!req.user) done(new Error("user lost"));
|
if (!req.user) done(new Error("user lost"));
|
||||||
|
@ -142,7 +143,7 @@ function setupPassport(passport: PassportStatic) {
|
||||||
cb(null, user.userId);
|
cb(null, user.userId);
|
||||||
});
|
});
|
||||||
|
|
||||||
passport.deserializeUser(async (userId: UUID, cb) => {
|
passport.deserializeUser(async (userId: T.UUID, cb) => {
|
||||||
try {
|
try {
|
||||||
const user = await getLingdocsUser("userId", userId);
|
const user = await getLingdocsUser("userId", userId);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|
|
@ -5,15 +5,15 @@ import {
|
||||||
} from "../lib/couch-db";
|
} from "../lib/couch-db";
|
||||||
import {
|
import {
|
||||||
getHash,
|
getHash,
|
||||||
getURLToken,
|
|
||||||
compareToHash,
|
compareToHash,
|
||||||
getEmailTokenAndHash,
|
getEmailTokenAndHash,
|
||||||
} from "../lib/password-utils";
|
} from "../lib/password-utils";
|
||||||
import {
|
import {
|
||||||
sendVerificationEmail,
|
sendVerificationEmail,
|
||||||
} from "../lib/mail-utils";
|
} from "../lib/mail-utils";
|
||||||
|
import * as T from "../../../website/src/lib/account-types";
|
||||||
|
|
||||||
function sendResponse(res: Response, payload: APIResponse) {
|
function sendResponse(res: Response, payload: T.APIResponse) {
|
||||||
return res.send(payload);
|
return res.send(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ apiRouter.use((req, res, next) => {
|
||||||
if (req.isAuthenticated()) {
|
if (req.isAuthenticated()) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
const r: APIResponse = { ok: false, error: "401 Unauthorized" };
|
const r: T.APIResponse = { ok: false, error: "401 Unauthorized" };
|
||||||
return res.status(401).send(r);
|
return res.status(401).send(r);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
} from "../lib/mail-utils";
|
} from "../lib/mail-utils";
|
||||||
import { outsideProviders } from "../middleware/setup-passport";
|
import { outsideProviders } from "../middleware/setup-passport";
|
||||||
import inProd from "../lib/inProd";
|
import inProd from "../lib/inProd";
|
||||||
|
import * as T from "../../../website/src/lib/account-types";
|
||||||
|
|
||||||
const authRouter = (passport: PassportStatic) => {
|
const authRouter = (passport: PassportStatic) => {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
@ -71,7 +72,7 @@ const authRouter = (passport: PassportStatic) => {
|
||||||
return res.render("login", { recaptcha: "fail", inProd });
|
return res.render("login", { recaptcha: "fail", inProd });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
passport.authenticate("local", (err, user: LingdocsUser | undefined, info) => {
|
passport.authenticate("local", (err, user: T.LingdocsUser | undefined, info) => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
if (!user && info.message === "email not found") {
|
if (!user && info.message === "email not found") {
|
||||||
return res.send({ ok: false, newSignup: true });
|
return res.send({ ok: false, newSignup: true });
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
type Hash = string & { __brand: "Hashed String" };
|
|
||||||
type UUID = string & { __brand: "Random Unique UID" };
|
|
||||||
type TimeStamp = number & { __brand: "UNIX Timestamp in milliseconds" };
|
|
||||||
type UserDbPassword = string & { __brand: "password for an individual user couchdb" };
|
|
||||||
type URLToken = string & { __brand: "Base 64 URL Token" };
|
|
||||||
type EmailVerified = true | Hash | false;
|
|
||||||
type ActionComplete = { ok: true, message: string };
|
|
||||||
type ActionError = { ok: false, error: string };
|
|
||||||
type APIResponse = ActionComplete | ActionError | { ok: true, user: LingdocsUser };
|
|
||||||
|
|
||||||
type WoutRJ<T> = Omit<T, "_raw"|"_json">;
|
|
||||||
|
|
||||||
type GoogleProfile = WoutRJ<import("passport-google-oauth").Profile> & { refreshToken: string, accessToken: string };
|
|
||||||
type GitHubProfile = WoutRJ<import("passport-github2").Profile> & { accessToken: string };
|
|
||||||
type TwitterProfile = WoutRJ<import("passport-twitter").Profile> & { token: string, tokenSecret: string };
|
|
||||||
type ProviderProfile = GoogleProfile | GitHubProfile | TwitterProfile;
|
|
||||||
type UserLevel = "basic" | "student" | "editor";
|
|
||||||
|
|
||||||
// TODO: TYPE GUARDING SO WE NEVER HAVE A USER WITH NO Id or Password
|
|
||||||
type LingdocsUser = {
|
|
||||||
userId: UUID,
|
|
||||||
password?: Hash,
|
|
||||||
name: string,
|
|
||||||
email?: string,
|
|
||||||
emailVerified: EmailVerified,
|
|
||||||
github?: GitHubProfile,
|
|
||||||
google?: GoogleProfile,
|
|
||||||
twitter?: TwitterProfile,
|
|
||||||
passwordReset?: {
|
|
||||||
tokenHash: Hash,
|
|
||||||
requestedOn: TimeStamp,
|
|
||||||
},
|
|
||||||
tests: [],
|
|
||||||
lastLogin: TimeStamp,
|
|
||||||
lastActive: TimeStamp,
|
|
||||||
} & ({ level: "basic"} | { level: "student" | "editor", userDbPassword: UserDbPassword })
|
|
||||||
& import("nano").MaybeDocument;
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
export type Hash = string & { __brand: "Hashed String" };
|
||||||
|
export type UUID = string & { __brand: "Random Unique UID" };
|
||||||
|
export type TimeStamp = number & { __brand: "UNIX Timestamp in milliseconds" };
|
||||||
|
export type UserDbPassword = string & { __brand: "password for an individual user couchdb" };
|
||||||
|
export type URLToken = string & { __brand: "Base 64 URL Token" };
|
||||||
|
export type EmailVerified = true | Hash | false;
|
||||||
|
export type ActionComplete = { ok: true, message: string };
|
||||||
|
export type ActionError = { ok: false, error: string };
|
||||||
|
export type APIResponse = ActionComplete | ActionError | { ok: true, user: LingdocsUser };
|
||||||
|
|
||||||
|
export type WoutRJ<T> = Omit<T, "_raw"|"_json">;
|
||||||
|
|
||||||
|
export type GoogleProfile = WoutRJ<import("passport-google-oauth").Profile> & { refreshToken: string, accessToken: string };
|
||||||
|
export type GitHubProfile = WoutRJ<import("passport-github2").Profile> & { accessToken: string };
|
||||||
|
export type TwitterProfile = WoutRJ<import("passport-twitter").Profile> & { token: string, tokenSecret: string };
|
||||||
|
export type ProviderProfile = GoogleProfile | GitHubProfile | TwitterProfile;
|
||||||
|
export type UserLevel = "basic" | "student" | "editor";
|
||||||
|
|
||||||
|
// TODO: TYPE GUARDING SO WE NEVER HAVE A USER WITH NO Id or Password
|
||||||
|
export type LingdocsUser = {
|
||||||
|
userId: UUID,
|
||||||
|
password?: Hash,
|
||||||
|
name: string,
|
||||||
|
email?: string,
|
||||||
|
emailVerified: EmailVerified,
|
||||||
|
github?: GitHubProfile,
|
||||||
|
google?: GoogleProfile,
|
||||||
|
twitter?: TwitterProfile,
|
||||||
|
passwordReset?: {
|
||||||
|
tokenHash: Hash,
|
||||||
|
requestedOn: TimeStamp,
|
||||||
|
},
|
||||||
|
tests: [],
|
||||||
|
lastLogin: TimeStamp,
|
||||||
|
lastActive: TimeStamp,
|
||||||
|
} & ({ level: "basic"} | { level: "student" | "editor", userDbPassword: UserDbPassword })
|
||||||
|
& import("nano").MaybeDocument;
|
Loading…
Reference in New Issue