permissions!
This commit is contained in:
parent
8cde6799df
commit
6a5a85c773
33 changed files with 683 additions and 666 deletions
|
@ -2,7 +2,7 @@
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
"tabWidth": 4,
|
"tabWidth": 4,
|
||||||
"useTabs": false,
|
"useTabs": false,
|
||||||
"printWidth": 120,
|
"printWidth": 80,
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"singleQuote": false,
|
"singleQuote": false,
|
||||||
"quoteProps": "preserve",
|
"quoteProps": "preserve",
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import { FindCursor, Filter as MDBFilter, ObjectId } from "mongodb";
|
import { FindCursor, Filter as MDBFilter, ObjectId, Sort } from "mongodb";
|
||||||
import { mainDB } from "./mongodb";
|
import { mainDB } from "./mongodb";
|
||||||
|
|
||||||
// Functionally identical to the MongoDB type functions, but this is here if you want to extend TrollCall to another NoSQL database.
|
// Functionally identical to the MongoDB type functions, but this is here if you want to extend TrollCall to another NoSQL database.
|
||||||
|
// That would be hard, though, as some parts of the codebase touch raw MongoDB concepts.
|
||||||
|
|
||||||
export type WithId<TSchema> = Omit<TSchema, "_id"> & {
|
export type WithId<TSchema> = Omit<TSchema, "_id"> & {
|
||||||
_id: ObjectId;
|
_id: ObjectId;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Document = { [key: string]: any };
|
export type Document = { [key: string]: any };
|
||||||
export type Filter<TypeSchema> = MDBFilter<Document>;
|
export type Filter<T> = MDBFilter<Document>;
|
||||||
|
|
||||||
export async function createOne(collection: string, doc: any) {
|
export async function createOne(collection: string, doc: any) {
|
||||||
const selectedCollection = mainDB.collection(collection);
|
const selectedCollection = mainDB.collection(collection);
|
||||||
|
@ -20,9 +21,13 @@ export async function readOne(collection: string, find: Filter<Document>) {
|
||||||
return await selectedCollection.findOne(find);
|
return await selectedCollection.findOne(find);
|
||||||
} // MongoDB and its finicky type safety
|
} // MongoDB and its finicky type safety
|
||||||
|
|
||||||
export function readMany(collection: string, find: Filter<Document>) {
|
export function readMany(
|
||||||
|
collection: string,
|
||||||
|
find: Filter<Document>,
|
||||||
|
sort: Sort
|
||||||
|
) {
|
||||||
const selectedCollection = mainDB.collection(collection);
|
const selectedCollection = mainDB.collection(collection);
|
||||||
return selectedCollection.find(find);
|
return selectedCollection.find(find).sort(sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function countMany(collection: string, find: any) {
|
export async function countMany(collection: string, find: any) {
|
||||||
|
@ -45,7 +50,10 @@ export async function deleteOne(collection: string, find: any) {
|
||||||
return await selectedCollection.findOneAndDelete(find);
|
return await selectedCollection.findOneAndDelete(find);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function cursorToArray<T>(cursor: FindCursor<T>, func: (input: any) => any = x => x) {
|
export async function cursorToArray<T>(
|
||||||
|
cursor: FindCursor<T>,
|
||||||
|
func: (input: any) => any = x => x
|
||||||
|
) {
|
||||||
let array: T[] = [];
|
let array: T[] = [];
|
||||||
while (await cursor.hasNext()) {
|
while (await cursor.hasNext()) {
|
||||||
array.push(await func(await cursor.next()));
|
array.push(await func(await cursor.next()));
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { ClientFlair, ServerFlair } from "@/types/flair";
|
import { ClientFlair, ServerFlair } from "@/types/flair";
|
||||||
import { sanitize } from "../utility/merge";
|
import { sanitize } from "../utility/merge";
|
||||||
|
|
||||||
export async function ServerFlairToClientFlair(serverFlair: ServerFlair): Promise<ClientFlair> {
|
export async function ServerFlairToClientFlair(
|
||||||
|
serverFlair: ServerFlair
|
||||||
|
): Promise<ClientFlair> {
|
||||||
const sanitizedFlair = sanitize(serverFlair);
|
const sanitizedFlair = sanitize(serverFlair);
|
||||||
let clientFlair: ClientFlair = {
|
let clientFlair: ClientFlair = {
|
||||||
...sanitizedFlair
|
...sanitizedFlair
|
||||||
|
|
|
@ -1,23 +1,56 @@
|
||||||
import { Class, TrueSign } from "@/types/assist/extended_zodiac";
|
import { Class, TrueSign } from "@/types/assist/extended_zodiac";
|
||||||
|
import { SubmitTroll } from "@/types/client/troll";
|
||||||
import { ClientTroll, ServerTroll } from "@/types/troll";
|
import { ClientTroll, ServerTroll } from "@/types/troll";
|
||||||
import { getManyFlairs } from "../flair";
|
import { getManyFlairs } from "../flair";
|
||||||
import { getManyUsers } from "../user";
|
import { cutArray, cutObject, sanitize } from "../utility/merge";
|
||||||
import { cutArray, sanitize } from "../utility/merge";
|
|
||||||
import { ServerFlairToClientFlair } from "./flair";
|
import { ServerFlairToClientFlair } from "./flair";
|
||||||
import { ServerUserToClientUser } from "./user";
|
|
||||||
|
|
||||||
export async function ServerTrollToClientTroll(serverTroll: ServerTroll): Promise<ClientTroll> {
|
export async function ServerTrollToClientTroll(
|
||||||
|
serverTroll: ServerTroll
|
||||||
|
): Promise<ClientTroll> {
|
||||||
const sanitizedTroll = sanitize(serverTroll);
|
const sanitizedTroll = sanitize(serverTroll);
|
||||||
const owners = await getManyUsers({ _id: { $in: serverTroll.owners } }, ServerUserToClientUser);
|
const flairs = await getManyFlairs(
|
||||||
const flairs = await getManyFlairs({ _id: { $in: serverTroll.flairs } }, ServerFlairToClientFlair);
|
{ _id: { $in: serverTroll.flairs } },
|
||||||
|
ServerFlairToClientFlair
|
||||||
|
);
|
||||||
let clientTroll: ClientTroll = {
|
let clientTroll: ClientTroll = {
|
||||||
...sanitizedTroll,
|
...sanitizedTroll,
|
||||||
trueSign: TrueSign[serverTroll.trueSign],
|
trueSign: TrueSign[serverTroll.trueSign],
|
||||||
falseSign: serverTroll.falseSign != null ? TrueSign[serverTroll.falseSign] : null,
|
falseSign:
|
||||||
|
serverTroll.falseSign != null
|
||||||
|
? TrueSign[serverTroll.falseSign]
|
||||||
|
: null,
|
||||||
class: Class[serverTroll.class],
|
class: Class[serverTroll.class],
|
||||||
owners: cutArray(owners),
|
owners: [],
|
||||||
flairs: cutArray(flairs)
|
flairs: cutArray(flairs)
|
||||||
};
|
};
|
||||||
|
|
||||||
return clientTroll;
|
return clientTroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function SubmitTrollToServerTroll(
|
||||||
|
submitTroll: Partial<SubmitTroll>
|
||||||
|
): Omit<Partial<ServerTroll>, "_id"> {
|
||||||
|
let serverTroll: Omit<Partial<ServerTroll>, "_id"> = {
|
||||||
|
...submitTroll,
|
||||||
|
quirks: submitTroll.quirks
|
||||||
|
? Object.fromEntries(submitTroll.quirks)
|
||||||
|
: undefined,
|
||||||
|
owners: [],
|
||||||
|
flairs: [],
|
||||||
|
updatedDate: new Date()
|
||||||
|
};
|
||||||
|
return serverTroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MergeServerTrolls(
|
||||||
|
submitTroll: ServerTroll,
|
||||||
|
merge: Partial<Omit<ServerTroll, "_id">>
|
||||||
|
): ServerTroll {
|
||||||
|
let serverTroll: ServerTroll = {
|
||||||
|
...submitTroll,
|
||||||
|
...cutObject(merge),
|
||||||
|
updatedDate: new Date()
|
||||||
|
};
|
||||||
|
return serverTroll;
|
||||||
|
}
|
||||||
|
|
|
@ -6,9 +6,14 @@ import { getManyFlairs } from "../flair";
|
||||||
import { cutArray, cutObject, removeCode, sanitize } from "../utility/merge";
|
import { cutArray, cutObject, removeCode, sanitize } from "../utility/merge";
|
||||||
import { ServerFlairToClientFlair } from "./flair";
|
import { ServerFlairToClientFlair } from "./flair";
|
||||||
|
|
||||||
export async function ServerUserToClientUser(serverUser: ServerUser): Promise<ClientUser> {
|
export async function ServerUserToClientUser(
|
||||||
|
serverUser: ServerUser
|
||||||
|
): Promise<ClientUser> {
|
||||||
const sanitizedUser = removeCode(sanitize(serverUser));
|
const sanitizedUser = removeCode(sanitize(serverUser));
|
||||||
const flairs = await getManyFlairs({ _id: { $in: serverUser.flairs } }, ServerFlairToClientFlair);
|
const flairs = await getManyFlairs(
|
||||||
|
{ _id: { $in: serverUser.flairs } },
|
||||||
|
ServerFlairToClientFlair
|
||||||
|
);
|
||||||
let clientUser: ClientUser = {
|
let clientUser: ClientUser = {
|
||||||
...sanitizedUser,
|
...sanitizedUser,
|
||||||
trueSign: TrueSign[serverUser.trueSign],
|
trueSign: TrueSign[serverUser.trueSign],
|
||||||
|
@ -18,7 +23,9 @@ export async function ServerUserToClientUser(serverUser: ServerUser): Promise<Cl
|
||||||
return clientUser;
|
return clientUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SubmitUserToServerUser(submitUser: Partial<SubmitUser>): Omit<Partial<ServerUser>, "_id"> {
|
export function SubmitUserToServerUser(
|
||||||
|
submitUser: Partial<SubmitUser>
|
||||||
|
): Omit<Partial<ServerUser>, "_id"> {
|
||||||
let serverUser: Omit<Partial<ServerUser>, "_id"> = {
|
let serverUser: Omit<Partial<ServerUser>, "_id"> = {
|
||||||
...submitUser,
|
...submitUser,
|
||||||
flairs: [],
|
flairs: [],
|
||||||
|
@ -28,7 +35,10 @@ export function SubmitUserToServerUser(submitUser: Partial<SubmitUser>): Omit<Pa
|
||||||
return serverUser;
|
return serverUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MergeServerUsers(submitUser: ServerUser, merge: Partial<Omit<ServerUser, "_id">>): ServerUser {
|
export function MergeServerUsers(
|
||||||
|
submitUser: ServerUser,
|
||||||
|
merge: Partial<Omit<ServerUser, "_id">>
|
||||||
|
): ServerUser {
|
||||||
let serverUser: ServerUser = {
|
let serverUser: ServerUser = {
|
||||||
...submitUser,
|
...submitUser,
|
||||||
...cutObject(merge),
|
...cutObject(merge),
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
import { ServerFlair } from "@/types/flair";
|
import { ServerFlair } from "@/types/flair";
|
||||||
|
import { Sort } from "mongodb";
|
||||||
import { Filter, cursorToArray, readMany, readOne } from "../db/crud";
|
import { Filter, cursorToArray, readMany, readOne } from "../db/crud";
|
||||||
|
|
||||||
|
const FlairSort: Sort = { updatedDate: -1, _id: -1 };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function that returns one ServerFlairs from the database.
|
* A function that returns one ServerFlairs from the database.
|
||||||
* @param query A partial Find query. Can contain an ID.
|
* @param query A partial Find query. Can contain an ID.
|
||||||
* @returns A ServerFlair.
|
* @returns A ServerFlair.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export async function getSingleFlair(query: Filter<ServerFlair>): Promise<ServerFlair | null> {
|
export async function getSingleFlair(
|
||||||
|
query: Filter<ServerFlair>
|
||||||
|
): Promise<ServerFlair | null> {
|
||||||
const flair = (await readOne("flairs", query)) as ServerFlair | null;
|
const flair = (await readOne("flairs", query)) as ServerFlair | null;
|
||||||
return flair;
|
return flair;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +28,9 @@ export async function getManyFlairs(
|
||||||
query: Filter<ServerFlair>,
|
query: Filter<ServerFlair>,
|
||||||
func?: (input: any) => any
|
func?: (input: any) => any
|
||||||
): Promise<(ServerFlair | null)[]> {
|
): Promise<(ServerFlair | null)[]> {
|
||||||
const flair = (await cursorToArray(readMany("flairs", query), func)) as (ServerFlair | null)[];
|
const flair = (await cursorToArray(
|
||||||
|
readMany("flairs", query, FlairSort),
|
||||||
|
func
|
||||||
|
)) as (ServerFlair | null)[];
|
||||||
return flair;
|
return flair;
|
||||||
}
|
}
|
||||||
|
|
27
src/lib/trollcall/perms.ts
Normal file
27
src/lib/trollcall/perms.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { Levels, Permissions } from "@/permissions";
|
||||||
|
import { ServerUser } from "@/types/user";
|
||||||
|
|
||||||
|
export function getLevel(user: ServerUser) {
|
||||||
|
let highestLevel = "USER";
|
||||||
|
for (let level of Permissions) {
|
||||||
|
if (user.flairs.some(oid => level.values.includes(oid.toString())))
|
||||||
|
highestLevel = level.name;
|
||||||
|
}
|
||||||
|
return highestLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compareLevels(level: string, compareLevel: string) {
|
||||||
|
return Levels[level] > Levels[compareLevel];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compareCredentials(
|
||||||
|
user: ServerUser,
|
||||||
|
cookies: Partial<{
|
||||||
|
[key: string]: string;
|
||||||
|
}>
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
user.code === cookies.TROLLCALL_CODE &&
|
||||||
|
user.name === cookies.TROLLCALL_NAME
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,5 +1,15 @@
|
||||||
import { ServerTroll } from "@/types/troll";
|
import { ServerTroll } from "@/types/troll";
|
||||||
import { Filter, cursorToArray, readMany, readOne } from "../db/crud";
|
import { Sort } from "mongodb";
|
||||||
|
import {
|
||||||
|
Filter,
|
||||||
|
createOne,
|
||||||
|
cursorToArray,
|
||||||
|
readMany,
|
||||||
|
readOne,
|
||||||
|
replaceOne
|
||||||
|
} from "../db/crud";
|
||||||
|
|
||||||
|
const TrollSort: Sort = { updatedDate: -1, _id: -1 };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function that returns one ServerTrolls from the database.
|
* A function that returns one ServerTrolls from the database.
|
||||||
|
@ -7,7 +17,9 @@ import { Filter, cursorToArray, readMany, readOne } from "../db/crud";
|
||||||
* @returns A ServerTroll.
|
* @returns A ServerTroll.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export async function getSingleTroll(query: Filter<ServerTroll>): Promise<ServerTroll | null> {
|
export async function getSingleTroll(
|
||||||
|
query: Filter<ServerTroll>
|
||||||
|
): Promise<ServerTroll | null> {
|
||||||
const troll = (await readOne("trolls", query)) as ServerTroll | null;
|
const troll = (await readOne("trolls", query)) as ServerTroll | null;
|
||||||
return troll;
|
return troll;
|
||||||
}
|
}
|
||||||
|
@ -19,10 +31,59 @@ export async function getSingleTroll(query: Filter<ServerTroll>): Promise<Server
|
||||||
* @returns An array of ServerTrolls.
|
* @returns An array of ServerTrolls.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export async function getManyTrolls(
|
export async function getManyTrolls<T>(
|
||||||
query: Filter<ServerTroll>,
|
query: Filter<ServerTroll>,
|
||||||
func?: (input: any) => any
|
func?: (input: any) => T
|
||||||
): Promise<(ServerTroll | null)[]> {
|
): Promise<(Awaited<T> | null)[]> {
|
||||||
const troll = (await cursorToArray(readMany("trolls", query), func)) as (ServerTroll | null)[];
|
const troll = (await cursorToArray(
|
||||||
|
readMany("trolls", query, TrollSort),
|
||||||
|
func
|
||||||
|
)) as (Awaited<T> | null)[];
|
||||||
return troll;
|
return troll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function that returns many ServerTrolls from the database using a FindCursor, limited by count.
|
||||||
|
* @param query A partial Find query. Can contain an ID.
|
||||||
|
* @param func A function to run on every ServerTroll returned. Helps reduce loops.
|
||||||
|
* @returns An array of ServerTrolls.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function getManyPagedTrolls<T>(
|
||||||
|
query: Filter<ServerTroll>,
|
||||||
|
func?: (input: any) => T,
|
||||||
|
count: number = 5,
|
||||||
|
page: number = 0
|
||||||
|
): Promise<(Awaited<T> | null)[]> {
|
||||||
|
const find = readMany("trolls", query, TrollSort)
|
||||||
|
.limit(count)
|
||||||
|
.skip(page * count);
|
||||||
|
const user = (await cursorToArray(find, func)) as (Awaited<T> | null)[];
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function that puts one ServerTroll into the database.
|
||||||
|
* @param troll A ServerTroll.
|
||||||
|
* @returns A ServerTroll, or null, depending on if the operation succeeded.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function createTroll(
|
||||||
|
troll: Omit<ServerTroll, "_id">
|
||||||
|
): Promise<Omit<ServerTroll, "_id"> | null> {
|
||||||
|
const newTroll = await createOne("trolls", troll);
|
||||||
|
return newTroll.acknowledged ? troll : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function that changes one database troll with the given params.
|
||||||
|
* @param troll A ServerTroll.
|
||||||
|
* @returns A ServerTroll, or null, depending on if the operation succeeded.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function changeTroll(
|
||||||
|
troll: ServerTroll
|
||||||
|
): Promise<ServerTroll | null> {
|
||||||
|
const newTroll = await replaceOne("trolls", { _id: troll._id }, troll);
|
||||||
|
return newTroll.acknowledged ? troll : null;
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,15 @@
|
||||||
import { ServerUser } from "@/types/user";
|
import { ServerUser } from "@/types/user";
|
||||||
import { Filter, createOne, cursorToArray, readMany, readOne, replaceOne } from "../db/crud";
|
import { Sort } from "mongodb";
|
||||||
|
import {
|
||||||
|
Filter,
|
||||||
|
createOne,
|
||||||
|
cursorToArray,
|
||||||
|
readMany,
|
||||||
|
readOne,
|
||||||
|
replaceOne
|
||||||
|
} from "../db/crud";
|
||||||
|
|
||||||
|
const UserSort: Sort = { updatedDate: -1, _id: -1 };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function that returns one ServerUser from the database.
|
* A function that returns one ServerUser from the database.
|
||||||
|
@ -7,7 +17,9 @@ import { Filter, createOne, cursorToArray, readMany, readOne, replaceOne } from
|
||||||
* @returns A ServerUser.
|
* @returns A ServerUser.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export async function getSingleUser(query: Filter<ServerUser>): Promise<ServerUser | null> {
|
export async function getSingleUser(
|
||||||
|
query: Filter<ServerUser>
|
||||||
|
): Promise<ServerUser | null> {
|
||||||
const user = (await readOne("users", query)) as ServerUser | null;
|
const user = (await readOne("users", query)) as ServerUser | null;
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +35,30 @@ export async function getManyUsers<T>(
|
||||||
query: Filter<ServerUser>,
|
query: Filter<ServerUser>,
|
||||||
func?: (input: any) => T
|
func?: (input: any) => T
|
||||||
): Promise<(Awaited<T> | null)[]> {
|
): Promise<(Awaited<T> | null)[]> {
|
||||||
const user = (await cursorToArray(readMany("users", query), func)) as (Awaited<T> | null)[];
|
const user = (await cursorToArray(
|
||||||
|
readMany("users", query, UserSort),
|
||||||
|
func
|
||||||
|
)) as (Awaited<T> | null)[];
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function that returns many ServerUsers from the database using a FindCursor, limited by count.
|
||||||
|
* @param query A partial Find query. Can contain an ID.
|
||||||
|
* @param func A function to run on every ServerUser returned. Helps reduce loops.
|
||||||
|
* @returns An array of ServerUsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function getManyPagedUsers<T>(
|
||||||
|
query: Filter<ServerUser>,
|
||||||
|
func?: (input: any) => T,
|
||||||
|
count: number = 5,
|
||||||
|
page: number = 0
|
||||||
|
): Promise<(Awaited<T> | null)[]> {
|
||||||
|
const find = readMany("users", query, UserSort)
|
||||||
|
.limit(count)
|
||||||
|
.skip(page * count);
|
||||||
|
const user = (await cursorToArray(find, func)) as (Awaited<T> | null)[];
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +68,9 @@ export async function getManyUsers<T>(
|
||||||
* @returns A ServerUser, or null, depending on if the operation succeeded.
|
* @returns A ServerUser, or null, depending on if the operation succeeded.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export async function createUser(user: Omit<ServerUser, "_id">): Promise<Omit<ServerUser, "_id"> | null> {
|
export async function createUser(
|
||||||
|
user: Omit<ServerUser, "_id">
|
||||||
|
): Promise<Omit<ServerUser, "_id"> | null> {
|
||||||
const newUser = await createOne("users", user);
|
const newUser = await createOne("users", user);
|
||||||
return newUser.acknowledged ? user : null;
|
return newUser.acknowledged ? user : null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,13 +25,17 @@ export function cutObject<T extends { [key: string]: any }>(object: T) {
|
||||||
return cut as T;
|
return cut as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sanitize<Type extends WithId<{}>>(serverType: Type): Omit<Type, "_id"> {
|
export function sanitize<Type extends WithId<{}>>(
|
||||||
|
serverType: Type
|
||||||
|
): Omit<Type, "_id"> {
|
||||||
const sanitized: Partial<Type> = serverType;
|
const sanitized: Partial<Type> = serverType;
|
||||||
delete sanitized._id;
|
delete sanitized._id;
|
||||||
return sanitized as Omit<Type, "_id">;
|
return sanitized as Omit<Type, "_id">;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeCode<Type extends { code: string }>(serverType: Type): Omit<Type, "code"> {
|
export function removeCode<Type extends { code: string }>(
|
||||||
|
serverType: Type
|
||||||
|
): Omit<Type, "code"> {
|
||||||
const sanitized: Partial<Type> = serverType;
|
const sanitized: Partial<Type> = serverType;
|
||||||
delete sanitized.code;
|
delete sanitized.code;
|
||||||
return sanitized as Omit<Type, "code">;
|
return sanitized as Omit<Type, "code">;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import "@/styles/globals.css";
|
|
||||||
import type { AppProps } from "next/app";
|
import type { AppProps } from "next/app";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
|
3
src/pages/_error.tsx
Normal file
3
src/pages/_error.tsx
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default function ErrorPage() {
|
||||||
|
return "404";
|
||||||
|
}
|
21
src/pages/api/troll/.../[[...page]]/index.ts
Normal file
21
src/pages/api/troll/.../[[...page]]/index.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { ServerTrollToClientTroll } from "@/lib/trollcall/convert/troll";
|
||||||
|
import { getManyPagedTrolls } from "@/lib/trollcall/troll";
|
||||||
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) {
|
||||||
|
const { method, query } = req;
|
||||||
|
const page = query.page ? query.page[0] : 0;
|
||||||
|
if (method === "GET") {
|
||||||
|
const trolls = await getManyPagedTrolls(
|
||||||
|
{},
|
||||||
|
ServerTrollToClientTroll,
|
||||||
|
5,
|
||||||
|
page
|
||||||
|
);
|
||||||
|
if (trolls == null) return res.status(404).end();
|
||||||
|
res.json(trolls);
|
||||||
|
} else return res.status(405).end();
|
||||||
|
}
|
28
src/pages/api/troll/[user]/.../[[...page]]/index.ts
Normal file
28
src/pages/api/troll/[user]/.../[[...page]]/index.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { ServerTrollToClientTroll } from "@/lib/trollcall/convert/troll";
|
||||||
|
import { getManyPagedTrolls } from "@/lib/trollcall/troll";
|
||||||
|
import { getSingleUser } from "@/lib/trollcall/user";
|
||||||
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) {
|
||||||
|
const { method, query } = req;
|
||||||
|
const page = query.page ? query.page[0] : 0;
|
||||||
|
if (method === "GET") {
|
||||||
|
const user = await getSingleUser({
|
||||||
|
name: query.user
|
||||||
|
});
|
||||||
|
if (user == null) return res.status(404).end();
|
||||||
|
const trolls = await getManyPagedTrolls(
|
||||||
|
{
|
||||||
|
"owners.0": user._id
|
||||||
|
},
|
||||||
|
ServerTrollToClientTroll,
|
||||||
|
5,
|
||||||
|
page
|
||||||
|
);
|
||||||
|
if (trolls == null) return res.status(404).end();
|
||||||
|
res.json(trolls);
|
||||||
|
} else return res.status(405).end();
|
||||||
|
}
|
73
src/pages/api/troll/[user]/[troll]/index.ts
Normal file
73
src/pages/api/troll/[user]/[troll]/index.ts
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import {
|
||||||
|
MergeServerTrolls,
|
||||||
|
ServerTrollToClientTroll,
|
||||||
|
SubmitTrollToServerTroll
|
||||||
|
} from "@/lib/trollcall/convert/troll";
|
||||||
|
import {
|
||||||
|
compareCredentials,
|
||||||
|
compareLevels,
|
||||||
|
getLevel
|
||||||
|
} from "@/lib/trollcall/perms";
|
||||||
|
import { changeTroll, getSingleTroll } from "@/lib/trollcall/troll";
|
||||||
|
import { getSingleUser } from "@/lib/trollcall/user";
|
||||||
|
import { SubmitTroll } from "@/types/client/troll";
|
||||||
|
import { PartialUserSchema } from "@/types/client/user";
|
||||||
|
import { Router } from "express";
|
||||||
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
|
export const trollRouter = Router();
|
||||||
|
|
||||||
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) {
|
||||||
|
const { query, cookies, method, body } = req;
|
||||||
|
if (method === "GET") {
|
||||||
|
const user = await getSingleUser({
|
||||||
|
name: query.user
|
||||||
|
});
|
||||||
|
if (user == null) return res.status(404).end();
|
||||||
|
const troll = await getSingleTroll({
|
||||||
|
"name.0": query.troll,
|
||||||
|
"owners.0": user._id
|
||||||
|
});
|
||||||
|
if (troll == null) return res.status(404).end();
|
||||||
|
const serverTroll = await ServerTrollToClientTroll(troll);
|
||||||
|
res.json(serverTroll);
|
||||||
|
} else if (method === "PUT") {
|
||||||
|
let validatedTroll: Partial<SubmitTroll>;
|
||||||
|
try {
|
||||||
|
validatedTroll = (await PartialUserSchema.validate(
|
||||||
|
body
|
||||||
|
)) as Partial<SubmitTroll>;
|
||||||
|
} catch (err) {
|
||||||
|
return res.status(400).send(err);
|
||||||
|
}
|
||||||
|
const checkUser = await getSingleUser({
|
||||||
|
name: query.user
|
||||||
|
});
|
||||||
|
if (checkUser == null) return res.status(404).end();
|
||||||
|
if (!compareCredentials(checkUser, cookies)) {
|
||||||
|
const thisUser = await getSingleUser({
|
||||||
|
name: query.user
|
||||||
|
});
|
||||||
|
if (thisUser == null || !compareCredentials(thisUser, cookies))
|
||||||
|
return res.status(403).end();
|
||||||
|
if (!compareLevels(getLevel(thisUser), "MODERATOR"))
|
||||||
|
return res.status(403).end();
|
||||||
|
}
|
||||||
|
const editingTroll = await getSingleTroll({
|
||||||
|
"name.0": query.troll,
|
||||||
|
"owners": checkUser._id
|
||||||
|
});
|
||||||
|
if (editingTroll == null) return res.status(404).end();
|
||||||
|
const serverTroll = SubmitTrollToServerTroll(validatedTroll);
|
||||||
|
const bothTrolls = MergeServerTrolls(editingTroll, serverTroll);
|
||||||
|
const newTroll = await changeTroll(bothTrolls);
|
||||||
|
if (newTroll == null) return res.status(503).end();
|
||||||
|
res.json(newTroll);
|
||||||
|
} else return res.status(405).end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1119731948118605995
|
||||||
|
// 1089603977131331584
|
42
src/pages/api/troll/index.ts
Normal file
42
src/pages/api/troll/index.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import { SubmitTrollToServerTroll } from "@/lib/trollcall/convert/troll";
|
||||||
|
import { compareCredentials } from "@/lib/trollcall/perms";
|
||||||
|
import { createTroll, getSingleTroll } from "@/lib/trollcall/troll";
|
||||||
|
import { getSingleUser } from "@/lib/trollcall/user";
|
||||||
|
import { SubmitTrollSchema } from "@/types/client/troll";
|
||||||
|
import { ServerTroll } from "@/types/troll";
|
||||||
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) {
|
||||||
|
const { body, method, cookies } = req;
|
||||||
|
if (method === "POST") {
|
||||||
|
let validatedTroll;
|
||||||
|
try {
|
||||||
|
validatedTroll = await SubmitTrollSchema.validate(body);
|
||||||
|
} catch (err) {
|
||||||
|
return res.status(400).send(err);
|
||||||
|
}
|
||||||
|
const checkUser = await getSingleUser({
|
||||||
|
name: cookies.TROLLCALL_NAME
|
||||||
|
});
|
||||||
|
if (checkUser == null) return res.status(404).end();
|
||||||
|
if (!compareCredentials(checkUser, cookies))
|
||||||
|
return res.status(403).end();
|
||||||
|
const checkExistingTroll = await getSingleTroll({
|
||||||
|
"name.0": validatedTroll.name[0],
|
||||||
|
"owners": checkUser._id
|
||||||
|
});
|
||||||
|
if (checkExistingTroll != null) return res.status(409).end();
|
||||||
|
// we are sure this object is full, so cast partial
|
||||||
|
let serverTroll = SubmitTrollToServerTroll(validatedTroll) as Omit<
|
||||||
|
ServerTroll,
|
||||||
|
"_id"
|
||||||
|
>;
|
||||||
|
serverTroll.owners[0] = checkUser._id;
|
||||||
|
const newTroll = await createTroll(serverTroll);
|
||||||
|
if (newTroll == null) return res.status(503).end();
|
||||||
|
res.json(newTroll);
|
||||||
|
} else return res.status(405).end();
|
||||||
|
}
|
21
src/pages/api/user/.../[[...page]]/index.ts
Normal file
21
src/pages/api/user/.../[[...page]]/index.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { ServerUserToClientUser } from "@/lib/trollcall/convert/user";
|
||||||
|
import { getManyPagedUsers } from "@/lib/trollcall/user";
|
||||||
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) {
|
||||||
|
const { method, query } = req;
|
||||||
|
const page = query.page ? query.page[0] : 0;
|
||||||
|
if (method === "GET") {
|
||||||
|
const users = await getManyPagedUsers(
|
||||||
|
{},
|
||||||
|
ServerUserToClientUser,
|
||||||
|
5,
|
||||||
|
page
|
||||||
|
);
|
||||||
|
if (users == null) return res.status(404).end();
|
||||||
|
res.json(users);
|
||||||
|
} else return res.status(405).end();
|
||||||
|
}
|
|
@ -1,10 +1,22 @@
|
||||||
import { MergeServerUsers, ServerUserToClientUser, SubmitUserToServerUser } from "@/lib/trollcall/convert/user";
|
import {
|
||||||
|
MergeServerUsers,
|
||||||
|
ServerUserToClientUser,
|
||||||
|
SubmitUserToServerUser
|
||||||
|
} from "@/lib/trollcall/convert/user";
|
||||||
|
import {
|
||||||
|
compareCredentials,
|
||||||
|
compareLevels,
|
||||||
|
getLevel
|
||||||
|
} from "@/lib/trollcall/perms";
|
||||||
import { changeUser, getSingleUser } from "@/lib/trollcall/user";
|
import { changeUser, getSingleUser } from "@/lib/trollcall/user";
|
||||||
import { PartialUserSchema, SubmitUser } from "@/types/client/user";
|
import { PartialUserSchema, SubmitUser } from "@/types/client/user";
|
||||||
import { serialize } from "cookie";
|
import { serialize } from "cookie";
|
||||||
import { NextApiRequest, NextApiResponse } from "next";
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) {
|
||||||
const { body, cookies, query, method } = req;
|
const { body, cookies, query, method } = req;
|
||||||
if (method === "GET") {
|
if (method === "GET") {
|
||||||
const user = await getSingleUser({
|
const user = await getSingleUser({
|
||||||
|
@ -16,7 +28,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||||
} else if (method === "PUT") {
|
} else if (method === "PUT") {
|
||||||
let validatedUser: Partial<SubmitUser>;
|
let validatedUser: Partial<SubmitUser>;
|
||||||
try {
|
try {
|
||||||
validatedUser = (await PartialUserSchema.validate(body)) as Partial<SubmitUser>;
|
validatedUser = (await PartialUserSchema.validate(
|
||||||
|
body
|
||||||
|
)) as Partial<SubmitUser>;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return res.status(400).send(err);
|
return res.status(400).send(err);
|
||||||
}
|
}
|
||||||
|
@ -24,16 +38,33 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||||
name: query.user
|
name: query.user
|
||||||
});
|
});
|
||||||
if (checkExistingUser == null) return res.status(404).end();
|
if (checkExistingUser == null) return res.status(404).end();
|
||||||
if (checkExistingUser.code !== cookies.TROLLCALL_CODE || checkExistingUser.name !== cookies.TROLLCALL_NAME)
|
if (!compareCredentials(checkExistingUser, cookies)) {
|
||||||
return res.status(403).end();
|
const thisUser = await getSingleUser({
|
||||||
|
name: query.user
|
||||||
|
});
|
||||||
|
if (thisUser == null || !compareCredentials(thisUser, cookies))
|
||||||
|
return res.status(403).end();
|
||||||
|
if (!compareLevels(getLevel(thisUser), "MODERATOR"))
|
||||||
|
return res.status(403).end();
|
||||||
|
}
|
||||||
const serverUser = SubmitUserToServerUser(validatedUser);
|
const serverUser = SubmitUserToServerUser(validatedUser);
|
||||||
const bothUsers = MergeServerUsers(checkExistingUser, serverUser);
|
const bothUsers = MergeServerUsers(checkExistingUser, serverUser);
|
||||||
const newUser = await changeUser(bothUsers);
|
const newUser = await changeUser(bothUsers);
|
||||||
if (newUser == null) return res.status(503).end();
|
if (newUser == null) return res.status(503).end();
|
||||||
// Give cookies, redundant style
|
// Give cookies, redundant style
|
||||||
res.setHeader("Set-Cookie", [
|
res.setHeader("Set-Cookie", [
|
||||||
serialize("TROLLCALL_NAME", newUser.name, { path: "/", maxAge: 31540000 }),
|
serialize("TROLLCALL_NAME", newUser.name, {
|
||||||
serialize("TROLLCALL_NAME", newUser.code, { path: "/", maxAge: 31540000 })
|
path: "/",
|
||||||
|
maxAge: 31540000
|
||||||
|
}),
|
||||||
|
serialize("TROLLCALL_CODE", newUser.code, {
|
||||||
|
path: "/",
|
||||||
|
maxAge: 31540000
|
||||||
|
}),
|
||||||
|
serialize("TROLLCALL_PFP", newUser.pfp ?? "", {
|
||||||
|
path: "/",
|
||||||
|
maxAge: 31540000
|
||||||
|
})
|
||||||
]).json(newUser);
|
]).json(newUser);
|
||||||
} else return res.status(405).end();
|
} else return res.status(405).end();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
import { ServerTrollToClientTroll } from "@/lib/trollcall/convert/troll";
|
|
||||||
import { getSingleTroll } from "@/lib/trollcall/troll";
|
|
||||||
import { getSingleUser } from "@/lib/trollcall/user";
|
|
||||||
import { Router } from "express";
|
|
||||||
import { NextApiRequest, NextApiResponse } from "next";
|
|
||||||
|
|
||||||
export const trollRouter = Router();
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
const { query, method } = req;
|
|
||||||
if (method === "GET") {
|
|
||||||
const user = await getSingleUser({
|
|
||||||
name: query.user
|
|
||||||
});
|
|
||||||
if (user == null) return res.status(404).end();
|
|
||||||
const troll = await getSingleTroll({
|
|
||||||
"name.0": query.troll,
|
|
||||||
"owners.0": user._id
|
|
||||||
});
|
|
||||||
if (troll == null) return res.status(404).end();
|
|
||||||
const serverTroll = await ServerTrollToClientTroll(troll);
|
|
||||||
res.json(serverTroll);
|
|
||||||
} else return res.status(405).end();
|
|
||||||
}
|
|
|
@ -5,26 +5,44 @@ import { ServerUser } from "@/types/user";
|
||||||
import { serialize } from "cookie";
|
import { serialize } from "cookie";
|
||||||
import { NextApiRequest, NextApiResponse } from "next";
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(
|
||||||
const { body } = req;
|
req: NextApiRequest,
|
||||||
let validatedUser;
|
res: NextApiResponse
|
||||||
try {
|
) {
|
||||||
validatedUser = await SubmitUserSchema.validate(body);
|
const { body, method } = req;
|
||||||
} catch (err) {
|
if (method === "POST") {
|
||||||
return res.status(400).send(err);
|
let validatedUser;
|
||||||
}
|
try {
|
||||||
const checkExistingUser = await getSingleUser({
|
validatedUser = await SubmitUserSchema.validate(body);
|
||||||
name: validatedUser.name
|
} catch (err) {
|
||||||
});
|
return res.status(400).send(err);
|
||||||
if (checkExistingUser != null) return res.status(409).end();
|
}
|
||||||
// we are sure this object is full, so cast partial
|
const checkExistingUser = await getSingleUser({
|
||||||
const serverUser = SubmitUserToServerUser(validatedUser) as Omit<ServerUser, "_id">;
|
name: validatedUser.name
|
||||||
const newUser = await createUser(serverUser);
|
});
|
||||||
if (newUser == null) return res.status(503).end();
|
if (checkExistingUser != null) return res.status(409).end();
|
||||||
// Give cookies
|
// we are sure this object is full, so cast partial
|
||||||
res.setHeader("Set-Cookie", [
|
const serverUser = SubmitUserToServerUser(validatedUser) as Omit<
|
||||||
serialize("TROLLCALL_NAME", newUser.name, { path: "/", maxAge: 31540000 }),
|
ServerUser,
|
||||||
serialize("TROLLCALL_NAME", newUser.code, { path: "/", maxAge: 31540000 })
|
"_id"
|
||||||
]);
|
>;
|
||||||
res.json(newUser);
|
const newUser = await createUser(serverUser);
|
||||||
|
if (newUser == null) return res.status(503).end();
|
||||||
|
// Give cookies
|
||||||
|
res.setHeader("Set-Cookie", [
|
||||||
|
serialize("TROLLCALL_NAME", newUser.name, {
|
||||||
|
path: "/",
|
||||||
|
maxAge: 31540000
|
||||||
|
}),
|
||||||
|
serialize("TROLLCALL_CODE", newUser.code, {
|
||||||
|
path: "/",
|
||||||
|
maxAge: 31540000
|
||||||
|
}),
|
||||||
|
serialize("TROLLCALL_PFP", newUser.pfp ?? "", {
|
||||||
|
path: "/",
|
||||||
|
maxAge: 31540000
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
res.json(newUser);
|
||||||
|
} else return res.status(405).end();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,115 +1,3 @@
|
||||||
import styles from "@/styles/Home.module.css";
|
|
||||||
import { Inter } from "next/font/google";
|
|
||||||
import Head from "next/head";
|
|
||||||
import Image from "next/image";
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
const inter = Inter({ subsets: ["latin"] });
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return "Welcome home!";
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<title>Create Next App</title>
|
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content="Generated by create next app"
|
|
||||||
/>
|
|
||||||
<meta
|
|
||||||
name="viewport"
|
|
||||||
content="width=device-width, initial-scale=1"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="icon"
|
|
||||||
href="/favicon.ico"
|
|
||||||
/>
|
|
||||||
</Head>
|
|
||||||
<main className={`${styles.main} ${inter.className}`}>
|
|
||||||
<div className={styles.description}>
|
|
||||||
<p>
|
|
||||||
Get started by editing
|
|
||||||
<code className={styles.code}>src/pages/index.tsx</code>
|
|
||||||
</p>
|
|
||||||
<div>
|
|
||||||
<a
|
|
||||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
By{" "}
|
|
||||||
<Image
|
|
||||||
src="/vercel.svg"
|
|
||||||
alt="Vercel Logo"
|
|
||||||
className={styles.vercelLogo}
|
|
||||||
width={100}
|
|
||||||
height={24}
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.center}>
|
|
||||||
<Image
|
|
||||||
className={styles.logo}
|
|
||||||
src="/next.svg"
|
|
||||||
alt="Next.js Logo"
|
|
||||||
width={180}
|
|
||||||
height={37}
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.grid}>
|
|
||||||
<a
|
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
|
||||||
className={styles.card}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<h2>
|
|
||||||
Docs <span>-></span>
|
|
||||||
</h2>
|
|
||||||
<p>Find in-depth information about Next.js features and API.</p>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a
|
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
|
||||||
className={styles.card}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<h2>
|
|
||||||
Learn <span>-></span>
|
|
||||||
</h2>
|
|
||||||
<p>Learn about Next.js in an interactive course with quizzes!</p>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a
|
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
|
||||||
className={styles.card}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<h2>
|
|
||||||
Templates <span>-></span>
|
|
||||||
</h2>
|
|
||||||
<p>Discover and deploy boilerplate example Next.js projects.</p>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a
|
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
|
||||||
className={styles.card}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<h2>
|
|
||||||
Deploy <span>-></span>
|
|
||||||
</h2>
|
|
||||||
<p>Instantly deploy your Next.js site to a shareable URL with Vercel.</p>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
24
src/permissions.ts
Normal file
24
src/permissions.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
export const Permissions = [
|
||||||
|
{
|
||||||
|
name: "SUPPORTER",
|
||||||
|
values: ["6487ec00cd90481163238f79", "648b76878de421ccb5937f54"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MODERATOR",
|
||||||
|
values: ["649ef0a9c62f6a4c8eb3f98a"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ADMIN",
|
||||||
|
values: ["6488077200893ada4afdd09c"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OWNER",
|
||||||
|
values: ["6487eb5fcd90481163238f6a"]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
export const Levels: { [key: string]: number } = {
|
||||||
|
"SUPPORTER": 0,
|
||||||
|
"MODERATOR": 1,
|
||||||
|
"ADMIN": 2,
|
||||||
|
"OWNER": 3
|
||||||
|
};
|
|
@ -1,229 +0,0 @@
|
||||||
.main {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 6rem;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
display: inherit;
|
|
||||||
justify-content: inherit;
|
|
||||||
align-items: inherit;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
max-width: var(--max-width);
|
|
||||||
width: 100%;
|
|
||||||
z-index: 2;
|
|
||||||
font-family: var(--font-mono);
|
|
||||||
}
|
|
||||||
|
|
||||||
.description a {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description p {
|
|
||||||
position: relative;
|
|
||||||
margin: 0;
|
|
||||||
padding: 1rem;
|
|
||||||
background-color: rgba(var(--callout-rgb), 0.5);
|
|
||||||
border: 1px solid rgba(var(--callout-border-rgb), 0.3);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
.code {
|
|
||||||
font-weight: 700;
|
|
||||||
font-family: var(--font-mono);
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(4, minmax(25%, auto));
|
|
||||||
width: var(--max-width);
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
padding: 1rem 1.2rem;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
background: rgba(var(--card-rgb), 0);
|
|
||||||
border: 1px solid rgba(var(--card-border-rgb), 0);
|
|
||||||
transition: background 200ms, border 200ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card span {
|
|
||||||
display: inline-block;
|
|
||||||
transition: transform 200ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card h2 {
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 0.7rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card p {
|
|
||||||
margin: 0;
|
|
||||||
opacity: 0.6;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
line-height: 1.5;
|
|
||||||
max-width: 30ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
position: relative;
|
|
||||||
padding: 4rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center::before {
|
|
||||||
background: var(--secondary-glow);
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 480px;
|
|
||||||
height: 360px;
|
|
||||||
margin-left: -400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center::after {
|
|
||||||
background: var(--primary-glow);
|
|
||||||
width: 240px;
|
|
||||||
height: 180px;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center::before,
|
|
||||||
.center::after {
|
|
||||||
content: '';
|
|
||||||
left: 50%;
|
|
||||||
position: absolute;
|
|
||||||
filter: blur(45px);
|
|
||||||
transform: translateZ(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
/* Enable hover only on non-touch devices */
|
|
||||||
@media (hover: hover) and (pointer: fine) {
|
|
||||||
.card:hover {
|
|
||||||
background: rgba(var(--card-rgb), 0.1);
|
|
||||||
border: 1px solid rgba(var(--card-border-rgb), 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover span {
|
|
||||||
transform: translateX(4px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion) {
|
|
||||||
.card:hover span {
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile */
|
|
||||||
@media (max-width: 700px) {
|
|
||||||
.content {
|
|
||||||
padding: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
margin-bottom: 120px;
|
|
||||||
max-width: 320px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
padding: 1rem 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card h2 {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center {
|
|
||||||
padding: 8rem 0 6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center::before {
|
|
||||||
transform: none;
|
|
||||||
height: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description a {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description p,
|
|
||||||
.description div {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
position: fixed;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description p {
|
|
||||||
align-items: center;
|
|
||||||
inset: 0 0 auto;
|
|
||||||
padding: 2rem 1rem 1.4rem;
|
|
||||||
border-radius: 0;
|
|
||||||
border: none;
|
|
||||||
border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25);
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
rgba(var(--background-start-rgb), 1),
|
|
||||||
rgba(var(--callout-rgb), 0.5)
|
|
||||||
);
|
|
||||||
background-clip: padding-box;
|
|
||||||
backdrop-filter: blur(24px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.description div {
|
|
||||||
align-items: flex-end;
|
|
||||||
pointer-events: none;
|
|
||||||
inset: auto 0 0;
|
|
||||||
padding: 2rem;
|
|
||||||
height: 200px;
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
transparent 0%,
|
|
||||||
rgb(var(--background-end-rgb)) 40%
|
|
||||||
);
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tablet and Smaller Desktop */
|
|
||||||
@media (min-width: 701px) and (max-width: 1120px) {
|
|
||||||
.grid {
|
|
||||||
grid-template-columns: repeat(2, 50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
.vercelLogo {
|
|
||||||
filter: invert(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes rotate {
|
|
||||||
from {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
:root {
|
|
||||||
--max-width: 1100px;
|
|
||||||
--border-radius: 12px;
|
|
||||||
--font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono',
|
|
||||||
'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro',
|
|
||||||
'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace;
|
|
||||||
|
|
||||||
--foreground-rgb: 0, 0, 0;
|
|
||||||
--background-start-rgb: 214, 219, 220;
|
|
||||||
--background-end-rgb: 255, 255, 255;
|
|
||||||
|
|
||||||
--primary-glow: conic-gradient(
|
|
||||||
from 180deg at 50% 50%,
|
|
||||||
#16abff33 0deg,
|
|
||||||
#0885ff33 55deg,
|
|
||||||
#54d6ff33 120deg,
|
|
||||||
#0071ff33 160deg,
|
|
||||||
transparent 360deg
|
|
||||||
);
|
|
||||||
--secondary-glow: radial-gradient(
|
|
||||||
rgba(255, 255, 255, 1),
|
|
||||||
rgba(255, 255, 255, 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
--tile-start-rgb: 239, 245, 249;
|
|
||||||
--tile-end-rgb: 228, 232, 233;
|
|
||||||
--tile-border: conic-gradient(
|
|
||||||
#00000080,
|
|
||||||
#00000040,
|
|
||||||
#00000030,
|
|
||||||
#00000020,
|
|
||||||
#00000010,
|
|
||||||
#00000010,
|
|
||||||
#00000080
|
|
||||||
);
|
|
||||||
|
|
||||||
--callout-rgb: 238, 240, 241;
|
|
||||||
--callout-border-rgb: 172, 175, 176;
|
|
||||||
--card-rgb: 180, 185, 188;
|
|
||||||
--card-border-rgb: 131, 134, 135;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
|
||||||
--foreground-rgb: 255, 255, 255;
|
|
||||||
--background-start-rgb: 0, 0, 0;
|
|
||||||
--background-end-rgb: 0, 0, 0;
|
|
||||||
|
|
||||||
--primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
|
|
||||||
--secondary-glow: linear-gradient(
|
|
||||||
to bottom right,
|
|
||||||
rgba(1, 65, 255, 0),
|
|
||||||
rgba(1, 65, 255, 0),
|
|
||||||
rgba(1, 65, 255, 0.3)
|
|
||||||
);
|
|
||||||
|
|
||||||
--tile-start-rgb: 2, 13, 46;
|
|
||||||
--tile-end-rgb: 2, 5, 19;
|
|
||||||
--tile-border: conic-gradient(
|
|
||||||
#ffffff80,
|
|
||||||
#ffffff40,
|
|
||||||
#ffffff30,
|
|
||||||
#ffffff20,
|
|
||||||
#ffffff10,
|
|
||||||
#ffffff10,
|
|
||||||
#ffffff80
|
|
||||||
);
|
|
||||||
|
|
||||||
--callout-rgb: 20, 20, 20;
|
|
||||||
--callout-border-rgb: 108, 108, 108;
|
|
||||||
--card-rgb: 100, 100, 100;
|
|
||||||
--card-border-rgb: 200, 200, 200;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
max-width: 100vw;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
color: rgb(var(--foreground-rgb));
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
transparent,
|
|
||||||
rgb(var(--background-end-rgb))
|
|
||||||
)
|
|
||||||
rgb(var(--background-start-rgb));
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
html {
|
|
||||||
color-scheme: dark;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,100 +3,100 @@ import * as yup from "yup";
|
||||||
export type ColorTypes = [number, number, number];
|
export type ColorTypes = [number, number, number];
|
||||||
|
|
||||||
export const ColorSchema = yup.tuple([
|
export const ColorSchema = yup.tuple([
|
||||||
yup.number().required().min(0).max(255),
|
yup.number().required().min(0).max(255),
|
||||||
yup.number().required().min(0).max(255),
|
yup.number().required().min(0).max(255),
|
||||||
yup.number().required().min(0).max(255),
|
yup.number().required().min(0).max(255)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const clamp = (n: number, mi: number, ma: number) =>
|
const clamp = (n: number, mi: number, ma: number) =>
|
||||||
Math.max(mi, Math.min(n, ma));
|
Math.max(mi, Math.min(n, ma));
|
||||||
|
|
||||||
export class Color3 {
|
export class Color3 {
|
||||||
R: number;
|
R: number;
|
||||||
G: number;
|
G: number;
|
||||||
B: number;
|
B: number;
|
||||||
constructor(red: number, green: number, blue: number) {
|
constructor(red: number, green: number, blue: number) {
|
||||||
this.R = red;
|
this.R = red;
|
||||||
this.G = green;
|
this.G = green;
|
||||||
this.B = blue;
|
this.B = blue;
|
||||||
}
|
}
|
||||||
static clone(color3: Color3) {
|
static clone(color3: Color3) {
|
||||||
return new Color3(color3.R, color3.G, color3.B);
|
return new Color3(color3.R, color3.G, color3.B);
|
||||||
}
|
}
|
||||||
static fromRGB(red: number, green: number, blue: number) {
|
static fromRGB(red: number, green: number, blue: number) {
|
||||||
return new Color3(red / 255, green / 255, blue / 255);
|
return new Color3(red / 255, green / 255, blue / 255);
|
||||||
}
|
}
|
||||||
static fromHex(hex: string) {
|
static fromHex(hex: string) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const hexSplit: [number, number, number] = (
|
const hexSplit: [number, number, number] = (
|
||||||
hex.match(new RegExp(`[0-9a-f]{1,${hex.length / 3}}`, "gi")) ?? [
|
hex.match(new RegExp(`[0-9a-f]{1,${hex.length / 3}}`, "gi")) ?? [
|
||||||
"0",
|
"0",
|
||||||
"0",
|
"0",
|
||||||
"0",
|
"0"
|
||||||
]
|
]
|
||||||
).map((x) => parseInt(x, 16) / 255);
|
).map(x => parseInt(x, 16) / 255);
|
||||||
return new Color3(...hexSplit);
|
return new Color3(...hexSplit);
|
||||||
}
|
}
|
||||||
static fromInt(int: number) {
|
static fromInt(int: number) {
|
||||||
return new Color3(
|
return new Color3(
|
||||||
(int & 0xff0000) >> 16,
|
(int & 0xff0000) >> 16,
|
||||||
(int & 0x00ff00) >> 8,
|
(int & 0x00ff00) >> 8,
|
||||||
int & 0x0000ff
|
int & 0x0000ff
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
static assumeColor(
|
static assumeColor(
|
||||||
value: [number, number, number] | string | number,
|
value: [number, number, number] | string | number,
|
||||||
rgb?: boolean
|
rgb?: boolean
|
||||||
) {
|
) {
|
||||||
if (Color3.isColor(value)) {
|
if (Color3.isColor(value)) {
|
||||||
if (Array.isArray(value))
|
if (Array.isArray(value))
|
||||||
return rgb ? Color3.fromRGB(...value) : new Color3(...value);
|
return rgb ? Color3.fromRGB(...value) : new Color3(...value);
|
||||||
else if (typeof value === "string") return Color3.fromHex(value);
|
else if (typeof value === "string") return Color3.fromHex(value);
|
||||||
else if (typeof value === "number") return Color3.fromInt(value);
|
else if (typeof value === "number") return Color3.fromInt(value);
|
||||||
|
}
|
||||||
|
throw new Error("Not a valid color type");
|
||||||
|
}
|
||||||
|
static isColor(
|
||||||
|
value: [number, number, number] | string | number,
|
||||||
|
rgb?: boolean
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
(Array.isArray(value) &&
|
||||||
|
value.length === 3 &&
|
||||||
|
value.every(x => typeof x === "number" && !isNaN(x))) ||
|
||||||
|
(typeof value === "string" && !isNaN(parseInt(value, 16))) ||
|
||||||
|
(typeof value === "number" && !isNaN(value))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
throw new Error("Not a valid color type");
|
|
||||||
}
|
|
||||||
static isColor(
|
|
||||||
value: [number, number, number] | string | number,
|
|
||||||
rgb?: boolean
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
(Array.isArray(value) &&
|
|
||||||
value.length === 3 &&
|
|
||||||
value.every((x) => typeof x === "number" && !isNaN(x))) ||
|
|
||||||
(typeof value === "string" && !isNaN(parseInt(value, 16))) ||
|
|
||||||
(typeof value === "number" && !isNaN(value))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toHex() {
|
toHex() {
|
||||||
return this.toInt().toString(16).padStart(6, "0");
|
return this.toInt().toString(16).padStart(6, "0");
|
||||||
}
|
}
|
||||||
toInt() {
|
toInt() {
|
||||||
return (
|
return (
|
||||||
(Math.round(this.R * 255) << 16) +
|
(Math.round(this.R * 255) << 16) +
|
||||||
(Math.round(this.G * 255) << 8) +
|
(Math.round(this.G * 255) << 8) +
|
||||||
Math.round(this.B * 255)
|
Math.round(this.B * 255)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
toRGB(): [number, number, number] {
|
toRGB(): [number, number, number] {
|
||||||
return [this.R * 255, this.G * 255, this.B * 255];
|
return [this.R * 255, this.G * 255, this.B * 255];
|
||||||
}
|
}
|
||||||
multiply(mult: number) {
|
multiply(mult: number) {
|
||||||
return new Color3(this.R * mult, this.G * mult, this.B * mult);
|
return new Color3(this.R * mult, this.G * mult, this.B * mult);
|
||||||
}
|
}
|
||||||
lighten(mult: number) {
|
lighten(mult: number) {
|
||||||
return new Color3(
|
return new Color3(
|
||||||
clamp(this.R + (mult / 100) * (1 - this.R), 0, 1),
|
clamp(this.R + (mult / 100) * (1 - this.R), 0, 1),
|
||||||
clamp(this.G + (mult / 100) * (1 - this.G), 0, 1),
|
clamp(this.G + (mult / 100) * (1 - this.G), 0, 1),
|
||||||
clamp(this.B + (mult / 100) * (1 - this.B), 0, 1)
|
clamp(this.B + (mult / 100) * (1 - this.B), 0, 1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
darken(mult: number) {
|
darken(mult: number) {
|
||||||
return new Color3(
|
return new Color3(
|
||||||
clamp(this.R - (mult / 100) * this.R, 0, 1),
|
clamp(this.R - (mult / 100) * this.R, 0, 1),
|
||||||
clamp(this.G - (mult / 100) * this.G, 0, 1),
|
clamp(this.G - (mult / 100) * this.G, 0, 1),
|
||||||
clamp(this.B - (mult / 100) * this.B, 0, 1)
|
clamp(this.B - (mult / 100) * this.B, 0, 1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -316,7 +316,9 @@ export const SignColorSchema = yup.object({
|
||||||
description: yup.string().required(),
|
description: yup.string().required(),
|
||||||
sign: yup.string().required(),
|
sign: yup.string().required(),
|
||||||
color: ColorSchema.required(),
|
color: ColorSchema.required(),
|
||||||
dates: yup.tuple([yup.string().required(), yup.string().required()]).required()
|
dates: yup
|
||||||
|
.tuple([yup.string().required(), yup.string().required()])
|
||||||
|
.required()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type SignColorType = {
|
export type SignColorType = {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
|
|
||||||
export const PolicySchema = yup
|
export const PolicySchema = yup
|
||||||
.string()
|
.string()
|
||||||
.oneOf(["yes", "ask", "no"])
|
.oneOf(["yes", "ask", "no"])
|
||||||
.default("no");
|
.default("no");
|
||||||
|
|
|
@ -1,51 +1,53 @@
|
||||||
export function AdaptivePossessive(owner: string, possession: string) {
|
export function AdaptivePossessive(owner: string, possession: string) {
|
||||||
return owner + (owner.endsWith("s") ? "'" : "'s") + " " + possession;
|
return owner + (owner.endsWith("s") ? "'" : "'s") + " " + possession;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PronounGrouper(
|
export function PronounGrouper(
|
||||||
pronouns: [string, string, string],
|
pronouns: [string, string, string],
|
||||||
sep?: string,
|
sep?: string,
|
||||||
amount?: number
|
amount?: number
|
||||||
) {
|
) {
|
||||||
return pronouns.slice(0, amount ?? 2).join(sep ?? "/");
|
return pronouns.slice(0, amount ?? 2).join(sep ?? "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function HeightConverter(inches: number) {
|
export function HeightConverter(inches: number) {
|
||||||
var feetandinches = Math.floor(inches / 12) + "'" + (inches % 12) + '"';
|
var feetandinches = Math.floor(inches / 12) + "'" + (inches % 12) + '"';
|
||||||
var meters = Math.floor((inches / 39.37) * 100) / 100 + "m";
|
var meters = Math.floor((inches / 39.37) * 100) / 100 + "m";
|
||||||
return feetandinches + " (" + meters + ")";
|
return feetandinches + " (" + meters + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AgeConverter(sweeps: number, years?: boolean) {
|
export function AgeConverter(sweeps: number, years?: boolean) {
|
||||||
return years
|
return years
|
||||||
? `${Math.round(sweeps * 2.1667 * 10) / 10} years`
|
? `${Math.round(sweeps * 2.1667 * 10) / 10} years`
|
||||||
: `${sweeps} sweeps`;
|
: `${sweeps} sweeps`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Pluralize(stringe: string) {
|
export function Pluralize(stringe: string) {
|
||||||
if (stringe.match(/(?:s|ch|sh|x|z|[^aeiou]o)$/)) return stringe + "es";
|
if (stringe.match(/(?:s|ch|sh|x|z|[^aeiou]o)$/)) return stringe + "es";
|
||||||
else if (stringe.match(/[aeiou](?:y|o)$/)) return stringe + "s";
|
else if (stringe.match(/[aeiou](?:y|o)$/)) return stringe + "s";
|
||||||
else if (stringe.match(/[^aeiou]y$/)) return stringe.slice(0, -1) + "ies";
|
else if (stringe.match(/[^aeiou]y$/)) return stringe.slice(0, -1) + "ies";
|
||||||
else if (stringe.match(/(?:f|fe)$/)) return stringe.slice(0, -1) + "ves";
|
else if (stringe.match(/(?:f|fe)$/)) return stringe.slice(0, -1) + "ves";
|
||||||
else return stringe + "s";
|
else return stringe + "s";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ArraySample(array: any[]) {
|
export function ArraySample(array: any[]) {
|
||||||
return array[Math.floor(Math.random() * array.length)];
|
return array[Math.floor(Math.random() * array.length)];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ProperNounCase(string: string) {
|
export function ProperNounCase(string: string) {
|
||||||
return string
|
return string
|
||||||
.split(" ")
|
.split(" ")
|
||||||
.map((x) => x.charAt(0).toUpperCase() + x.slice(1).toLowerCase())
|
.map(x => x.charAt(0).toUpperCase() + x.slice(1).toLowerCase())
|
||||||
.join(" ");
|
.join(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PesterchumNameFormatter(string: string) {
|
export function PesterchumNameFormatter(string: string) {
|
||||||
return (
|
return (
|
||||||
string +
|
string +
|
||||||
" [" +
|
" [" +
|
||||||
string.replace(/^(([a-z])[a-z]+)(([A-Z])[a-z]+)$/, "$2$4").toUpperCase() +
|
string
|
||||||
"]"
|
.replace(/^(([a-z])[a-z]+)(([A-Z])[a-z]+)$/, "$2$4")
|
||||||
);
|
.toUpperCase() +
|
||||||
|
"]"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,5 +2,5 @@ import { ObjectId } from "mongodb";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
|
|
||||||
export const ObjectIdSchema = yup.mixed((value): value is ObjectId =>
|
export const ObjectIdSchema = yup.mixed((value): value is ObjectId =>
|
||||||
ObjectId.isValid(value)
|
ObjectId.isValid(value)
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,8 +3,17 @@ import { QuirkSchema } from "../quirks";
|
||||||
|
|
||||||
export const SubmitQuirkHolderSchema = yup
|
export const SubmitQuirkHolderSchema = yup
|
||||||
.array()
|
.array()
|
||||||
.of(yup.tuple([yup.string().required().lowercase(), QuirkSchema.required()]).required())
|
.of(
|
||||||
|
yup
|
||||||
|
.tuple([
|
||||||
|
yup.string().required().lowercase(),
|
||||||
|
QuirkSchema.required()
|
||||||
|
])
|
||||||
|
.required()
|
||||||
|
)
|
||||||
.required()
|
.required()
|
||||||
.test("has-default", 'Needs "default" Quirk Mode', v => v.some(([k, v]) => k === "default" || k === "Default"));
|
.test("has-default", 'Needs "default" Quirk Mode', v =>
|
||||||
|
v.some(([k, v]) => k === "default" || k === "Default")
|
||||||
|
);
|
||||||
|
|
||||||
export type SubmitQuirkHolder = yup.InferType<typeof SubmitQuirkHolderSchema>;
|
export type SubmitQuirkHolder = yup.InferType<typeof SubmitQuirkHolderSchema>;
|
||||||
|
|
|
@ -79,11 +79,26 @@ export const SubmitTrollSchema = yup
|
||||||
// Personal
|
// Personal
|
||||||
preferences: yup
|
preferences: yup
|
||||||
.object({
|
.object({
|
||||||
love: yup.array().of(yup.string().required().min(5).max(100)).required().min(3).max(10),
|
love: yup
|
||||||
hate: yup.array().of(yup.string().required().min(5).max(100)).required().min(3).max(10)
|
.array()
|
||||||
|
.of(yup.string().required().min(5).max(100))
|
||||||
|
.required()
|
||||||
|
.min(3)
|
||||||
|
.max(10),
|
||||||
|
hate: yup
|
||||||
|
.array()
|
||||||
|
.of(yup.string().required().min(5).max(100))
|
||||||
|
.required()
|
||||||
|
.min(3)
|
||||||
|
.max(10)
|
||||||
})
|
})
|
||||||
.required(),
|
.required(),
|
||||||
facts: yup.array().of(yup.string().required().min(5).max(100)).required().min(3).max(10),
|
facts: yup
|
||||||
|
.array()
|
||||||
|
.of(yup.string().required().min(5).max(100))
|
||||||
|
.required()
|
||||||
|
.min(3)
|
||||||
|
.max(10),
|
||||||
|
|
||||||
// Hiveswap identity
|
// Hiveswap identity
|
||||||
trueSign: yup.string().required().oneOf(TrueSignKeys),
|
trueSign: yup.string().required().oneOf(TrueSignKeys),
|
||||||
|
@ -100,7 +115,10 @@ export const SubmitTrollSchema = yup
|
||||||
username: yup
|
username: yup
|
||||||
.string()
|
.string()
|
||||||
.required()
|
.required()
|
||||||
.matches(/^(([a-z])[a-z]+)(([A-Z])[a-z]+)$/, "Username must match Pesterchum formatting."),
|
.matches(
|
||||||
|
/^(([a-z])[a-z]+)(([A-Z])[a-z]+)$/,
|
||||||
|
"Username must match Pesterchum formatting."
|
||||||
|
),
|
||||||
textColor: ColorSchema.notRequired(), // default to trueSign color if undefined,
|
textColor: ColorSchema.notRequired(), // default to trueSign color if undefined,
|
||||||
quirks: SubmitQuirkHolderSchema.required(), // DO NOT HANDLE RIGHT NOW.
|
quirks: SubmitQuirkHolderSchema.required(), // DO NOT HANDLE RIGHT NOW.
|
||||||
// Handled! :D
|
// Handled! :D
|
||||||
|
@ -208,7 +226,10 @@ export const PartialTrollSchema = yup
|
||||||
// Trollian
|
// Trollian
|
||||||
username: yup
|
username: yup
|
||||||
.string()
|
.string()
|
||||||
.matches(/^(([a-z])[a-z]+)(([A-Z])[a-z]+)$/, "Username must match Pesterchum formatting."),
|
.matches(
|
||||||
|
/^(([a-z])[a-z]+)(([A-Z])[a-z]+)$/,
|
||||||
|
"Username must match Pesterchum formatting."
|
||||||
|
),
|
||||||
textColor: yup.tuple([
|
textColor: yup.tuple([
|
||||||
yup.number().min(0).max(255),
|
yup.number().min(0).max(255),
|
||||||
yup.number().min(0).max(255),
|
yup.number().min(0).max(255),
|
||||||
|
|
|
@ -40,7 +40,11 @@ export const PartialUserSchema = yup
|
||||||
return v === "" ? null : v;
|
return v === "" ? null : v;
|
||||||
})
|
})
|
||||||
.oneOf(TrueSignKeys),
|
.oneOf(TrueSignKeys),
|
||||||
color: yup.tuple([yup.number().min(0).max(255), yup.number().min(0).max(255), yup.number().min(0).max(255)]),
|
color: yup.tuple([
|
||||||
|
yup.number().min(0).max(255),
|
||||||
|
yup.number().min(0).max(255),
|
||||||
|
yup.number().min(0).max(255)
|
||||||
|
]),
|
||||||
pfp: yup.string().url(),
|
pfp: yup.string().url(),
|
||||||
code: yup.string().max(256, "Too secure!!")
|
code: yup.string().max(256, "Too secure!!")
|
||||||
// flairs: yup.array().of(ClientFlairSchema).required(),
|
// flairs: yup.array().of(ClientFlairSchema).required(),
|
||||||
|
|
|
@ -70,7 +70,8 @@ export const QuirkReplacementTypes: {
|
||||||
},
|
},
|
||||||
case: {
|
case: {
|
||||||
find: null,
|
find: null,
|
||||||
replace: 'Change the case of all text. "lower" for lowercase. "upper" for uppercase.'
|
replace:
|
||||||
|
'Change the case of all text. "lower" for lowercase. "upper" for uppercase.'
|
||||||
},
|
},
|
||||||
case_simple: {
|
case_simple: {
|
||||||
find: "Text to find what you want to change the case of.",
|
find: "Text to find what you want to change the case of.",
|
||||||
|
|
Loading…
Reference in a new issue