Compare commits

..

8 Commits

18 changed files with 501 additions and 901 deletions

2
.gitignore vendored
View File

@ -1,7 +1,7 @@
### Node template ### Node template
# Configurations # Configurations
config.json config.json
.vscode
# Logs # Logs
logs logs
*.log *.log

View File

@ -1,4 +1,4 @@
# Contributing to LOC's Community Relations Gamma Edition System # Contributing to LOC's Community Relations Alpha Edition System
Thank you for considering contributing to this project! Your contributions are highly valued, and were excited to collaborate with you. Thank you for considering contributing to this project! Your contributions are highly valued, and were excited to collaborate with you.

View File

@ -1,7 +1,9 @@
# Community Relations v2 Gamma Edition System - CRRA/G # Community Relations Alpha Edition System - CRRA
[![License](https://img.shields.io/badge/license-AGPLv3-blue.svg)](LICENSE) [![License](https://img.shields.io/badge/license-AGPLv3-blue.svg)](LICENSE)
A brief description of what your project does, what problem it solves, and why its useful.
--- ---
## Table of Contents ## Table of Contents
@ -86,4 +88,4 @@ __Library of Code Department of Engineering__ @:
- [Email](mailto:engineering@libraryofcode.org) - [Email](mailto:engineering@libraryofcode.org)
--- ---
Thank you for checking out CRv2! Thank you for checking out CRRA!

View File

@ -13,9 +13,6 @@ export type MemberAdditionalAcknowledgement =
| "Chair of the Board of Governors" | "Chair of the Board of Governors"
| "Vice Chair of the Board of Governors" | "Vice Chair of the Board of Governors"
| "Voting Seat Member of the Board of Governors" | "Voting Seat Member of the Board of Governors"
| "Non-Voting Seat Member of the Board of Governors"
| "Commissioner of the Contract Review Committee"
| "Member of the Contract Review Committee"
| string; | string;
export const MemberGuildRoleIDMap = { export const MemberGuildRoleIDMap = {

View File

@ -1,4 +1,4 @@
import { prop, getModelForClass, Ref } from "@typegoose/typegoose"; import { prop, getModelForClass } from "@typegoose/typegoose";
import Member from "./Member"; import Member from "./Member";
/* TODO /* TODO
@ -14,7 +14,7 @@ export type PartnerTitle =
| "Deputy Director of Engineering" | "Deputy Director of Engineering"
| "Deputy Director of Operations" | "Deputy Director of Operations"
| "Services Manager" | "Services Manager"
| "Community Manager" | "Project Manager"
| "Engineering Core Partner" | "Engineering Core Partner"
| "Operations Core Partner" | "Operations Core Partner"
| "Community Moderator" | "Community Moderator"
@ -51,9 +51,6 @@ export default class Partner implements SharedMemberAttributes {
@prop({ required: true }) @prop({ required: true })
public roleType: PartnerRoleType | undefined; public roleType: PartnerRoleType | undefined;
@prop()
public isKeyHolder: boolean | undefined;
@prop({ required: true }) @prop({ required: true })
public commissionType: PartnerCommissionType | undefined; public commissionType: PartnerCommissionType | undefined;
@ -63,9 +60,9 @@ export default class Partner implements SharedMemberAttributes {
@prop({ required: true }) @prop({ required: true })
public title: PartnerTitle | "Partner" | undefined; public title: PartnerTitle | "Partner" | undefined;
@prop({ ref: () => Partner }) @prop()
// //
public directReport?: Ref<Partner> | string | undefined; public directReport: Partner | string | undefined;
@prop() @prop()
// this field dictates if the partner is able to perform developer commands, such as "eval" // this field dictates if the partner is able to perform developer commands, such as "eval"

View File

@ -2,11 +2,10 @@ import DiscordInteractionCommand from "../../util/DiscordInteractionCommand";
import { ChatInputCommandInteraction } from "discord.js"; import { ChatInputCommandInteraction } from "discord.js";
import { inspect } from "util"; import { inspect } from "util";
import { discordBotToken } from "../../config.json"; import { discordBotToken } from "../../config.json";
import { PartnerModel } from "../../database/Partner";
export default class Eval extends DiscordInteractionCommand { export default class Eval extends DiscordInteractionCommand {
// This is a list of IDs that are allowed to use this command. // This is a list of IDs that are allowed to use this command.
private listOfAllowedIDs: string[] = []; private listOfAllowedIDs: string[];
constructor() { constructor() {
super("eval", "Executes arbitrary JS code and returns the output."); super("eval", "Executes arbitrary JS code and returns the output.");
@ -26,13 +25,9 @@ export default class Eval extends DiscordInteractionCommand {
option.setName("depth").setDescription("The depth of the inspection.").setRequired(false) option.setName("depth").setDescription("The depth of the inspection.").setRequired(false)
); );
// this checks against the database and adds all the partners that are "allowed to perform dev commands" this.listOfAllowedIDs = [
// doing the database check in the initialization prevents us from having to check the database every time this command is ran "278620217221971968", // Matthew
PartnerModel.find({ canPerformDevCommands: true }).then((partners) => { ];
for (const partner of partners) {
this.listOfAllowedIDs.push(partner.discordID as string);
}
});
} }
public async execute(interaction: ChatInputCommandInteraction) { public async execute(interaction: ChatInputCommandInteraction) {
@ -68,7 +63,7 @@ export default class Eval extends DiscordInteractionCommand {
evaled = "undefined"; evaled = "undefined";
} }
} catch (error) { } catch (error) {
// @ts-expect-error No throws in this block return anything other than an error. // @ts-ignore
evaled = error.stack; evaled = error.stack;
} }

View File

@ -1,13 +1,392 @@
import DiscordInteractionCommand from "../../util/DiscordInteractionCommand"; import { MemberModel } from "../../database/Member";
import { ChatInputCommandInteraction } from "discord.js"; import Partner, {
PartnerModel,
PartnerCommissionType,
PartnerRoleType,
PartnerDepartment,
PartnerTitle,
} from "../../database/Partner";
export default class Ping extends DiscordInteractionCommand { import DiscordInteractionCommand from "../../util/DiscordInteractionCommand";
import {
ChatInputCommandInteraction,
InteractionContextType,
PermissionFlagsBits,
} from "discord.js";
//TODO: ad email validation
//TODO: cover all partner model properties in cooresponding sub commands
const partnerTitles: PartnerTitle[] = [
"Director of Engineering",
"Director of Operations",
"Deputy Director of Engineering",
"Deputy Director of Operations",
"Services Manager",
"Project Manager",
"Engineering Core Partner",
"Operations Core Partner",
"Community Moderator",
"Technician",
];
export default class PartnerCommand extends DiscordInteractionCommand {
constructor() { constructor() {
super("partner", "Manipulates partner information."); super("partner", "Manipulates partner information.");
this.builder
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
.setContexts(InteractionContextType.Guild);
this.builder.addSubcommand((c) =>
c
.setName("add")
.setDescription("test")
.addUserOption((option) =>
option.setName("partner").setDescription("the partner you want to add.").setRequired(true)
)
.addStringOption((option) =>
option.setName("email").setDescription("their email address.").setRequired(true)
)
.addStringOption((option) =>
option
.setName("role-type")
.setDescription("their roleType.")
.setChoices(this.formatOptionsForDiscordFromEnum(PartnerRoleType))
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("commission-type")
.setDescription("their commissionType.")
.setChoices(this.formatOptionsForDiscordFromEnum(PartnerCommissionType))
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("title")
.setDescription("their title.")
.setChoices(this.formatPartnerTitlesArrayForDiscord())
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("department")
.setDescription("their department.")
.setChoices(this.formatOptionsForDiscordFromEnum(PartnerDepartment))
.setRequired(true)
)
.addUserOption((option) =>
option.setName("direct-report").setDescription("their direct report.")
)
);
this.builder.addSubcommand((c) =>
c
.setName("get")
.setDescription("get")
.addUserOption((option) =>
option
.setName("partner")
.setDescription("the partner you want to get info abouts.")
.setRequired(true)
)
);
this.builder.addSubcommand((c) =>
c
.setName("delete")
.setDescription("delete")
.addUserOption((option) =>
option
.setName("partner")
.setDescription("the partner you want to delete.")
.setRequired(true)
)
);
this.builder.addSubcommand((c) =>
c
.setName("update")
.setDescription("update")
.addUserOption((option) =>
option
.setName("partner")
.setDescription("the partner you want to update.")
.setRequired(true)
)
.addUserOption((option) =>
option.setName("direct-report").setDescription("their direct report.")
)
.addStringOption((option) => option.setName("email").setDescription("their email address."))
.addStringOption((option) =>
option
.setName("role-type")
.setDescription("their roleType.")
.setChoices(this.formatOptionsForDiscordFromEnum(PartnerRoleType))
)
.addStringOption((option) =>
option
.setName("commission-type")
.setDescription("their commissionType.")
.setChoices(this.formatOptionsForDiscordFromEnum(PartnerCommissionType))
)
.addStringOption((option) =>
option
.setName("title")
.setDescription("their title.")
.setChoices(this.formatPartnerTitlesArrayForDiscord())
)
.addStringOption((option) =>
option
.setName("department")
.setDescription("their department.")
.setChoices(this.formatOptionsForDiscordFromEnum(PartnerDepartment))
)
);
} }
public async execute(interaction: ChatInputCommandInteraction): Promise<void> { public async execute(interaction: ChatInputCommandInteraction): Promise<void> {
if (interaction.options?.getSubcommand(true) === "add") { const subcommandName = interaction.options.getSubcommand(true);
switch (subcommandName) {
case "get":
await this.handleGetSubcommand(interaction);
break;
case "add":
await this.handleAddSubcommand(interaction);
break;
case "delete":
await this.handleDeleteSubcommand(interaction);
break;
case "update":
await this.handleUpdateSubcommand(interaction);
break;
default:
//discord does not allow parent/main commands to be excutable, this range is limited to options entered via commandbuiler
break;
} }
} }
async handleAddSubcommand(interaction: ChatInputCommandInteraction) {
const partnerOption = interaction.options.getUser("partner", true);
const directReport = interaction.options.getUser("direct-report", false);
const partnerOptionEmailAddress = interaction.options.getString("email", true);
const partnerOptionRoleType = interaction.options.getString("role-type", true);
const partnerOptionCommisioComissionType = interaction.options.getString(
"commission-type",
true
);
const partnerOptionDepartment = interaction.options.getString("department", true);
const partnerOptionTitle = interaction.options.getString("title", true);
const partner = await PartnerModel.findOne({ discordID: partnerOption.id }).exec();
if (partner)
return interaction.reply({
content: "The specified user already has a partner entry.",
ephemeral: false,
});
/*
const member = await MemberModel.findOne({ discordID: partnerOption.id }).exec();
if (!member)
return interaction.reply({
content: "The specified partner does not have a base member entry.",
ephemeral: false,
});
*/
let directReportPartnerDocumentFromDb;
if (directReport) {
directReportPartnerDocumentFromDb = await PartnerModel.findOne({
discordID: directReport.id,
}).exec();
if (!directReportPartnerDocumentFromDb)
return interaction.reply({
content: `the specified directReport ${directReport.username} does not have an entry in partner database, please add them first them before assigning subordinates`,
ephemeral: false,
});
}
let newPartner = new PartnerModel({
discordID: partnerOption.id,
emailAddress: partnerOptionEmailAddress,
roleType: partnerOptionRoleType,
commissionType: partnerOptionCommisioComissionType,
department: partnerOptionDepartment,
title: partnerOptionTitle,
directReport:
directReport && directReportPartnerDocumentFromDb
? directReportPartnerDocumentFromDb._id
: null,
});
await newPartner.save();
return interaction.reply({
content: `\`\`\`\n${JSON.stringify(newPartner, null, 2)}\n\`\`\``,
ephemeral: false,
});
}
async handleGetSubcommand(interaction: ChatInputCommandInteraction) {
const partnerOption = interaction.options.getUser("partner", true);
let partner = await PartnerModel.findOne({ discordID: partnerOption.id }).exec();
if (!partner)
return interaction.reply({
content: "The specified partner does not an entry in the database.",
ephemeral: false,
});
if (partner.directReport) await partner.populate("directReport");
if (partner.directReport && partner.directReport instanceof Partner) {
console.log(partner.directReport);
return interaction.reply({
content: `Raw entry \`\`\`\n${JSON.stringify(partner, null, 2)}\n\`\`\`\n\nDirect report: \`\`\`\n${JSON.stringify(partner.directReport, null, 2)}\n\`\`\``,
ephemeral: false,
});
}
return interaction.reply({
content: `Raw entry \`\`\`\n${JSON.stringify(partner, null, 2)}\n\`\`\``,
ephemeral: false,
});
}
async handleDeleteSubcommand(interaction: ChatInputCommandInteraction) {
const partnerOption = interaction.options.getUser("partner", true);
const partner = await PartnerModel.findOne({ discordID: partnerOption.id })
.populate("directReport")
.exec();
if (!partner)
return interaction.reply({
content: "The specified user does not have an entry.",
ephemeral: false,
});
if (
partner.directReport &&
partner.directReport instanceof Partner &&
interaction.user.id !== partner.directReport.discordID
)
return interaction.reply({
content:
"You're not authorized to delete this partner's information, only their direct report can.",
ephemeral: false,
});
await PartnerModel.findByIdAndDelete(partner.id);
return interaction.reply({
content: `removed partner entry from the database.`,
ephemeral: false,
});
}
async handleUpdateSubcommand(interaction: ChatInputCommandInteraction) {
const partnerOption = interaction.options.getUser("partner", true);
const directReport = interaction.options.getUser("direct-report");
const partnerOptionEmailAddress = interaction.options.getString("email");
const partnerOptionRoleType = interaction.options.getString("role-type");
const partnerOptionCommisioComissionType = interaction.options.getString("commission-type");
const partnerOptionDepartment = interaction.options.getString("department");
const partnerOptionTitle = interaction.options.getString("title");
if (
!directReport &&
!partnerOptionEmailAddress &&
!partnerOptionEmailAddress &&
!partnerOptionRoleType &&
!partnerOptionCommisioComissionType &&
!partnerOptionDepartment &&
!partnerOptionTitle
) {
return interaction.reply({
content: "You need to select atleast one option to update",
ephemeral: false,
});
}
let partner = await PartnerModel.findOne({ discordID: partnerOption.id }).exec();
if (!partner)
return interaction.reply({
content: "The specified partner does not have an entry.",
ephemeral: false,
});
if (partner.directReport) partner = await partner.populate("directReport");
console.log(partner.directReport);
if (
partner.directReport instanceof PartnerModel &&
interaction.user.id !== partner.directReport.discordID
)
return interaction.reply({
content:
"You're not authorized to update this partner's information, only their direct report can.",
ephemeral: false,
});
let directReportPartnerDocumentFromDb;
if (directReport) {
directReportPartnerDocumentFromDb = await PartnerModel.findOne({
discordID: directReport.id,
}).exec();
if (!directReportPartnerDocumentFromDb)
return interaction.reply({
content: `the specified directReport ${directReport.username} does not have an entry in partner database, please add them first them before assigning subordinates`,
ephemeral: false,
});
}
let updateObj = {
discordID: partnerOption.id,
emailAddress: partnerOptionEmailAddress,
roleType: partnerOptionRoleType,
commissionType: partnerOptionCommisioComissionType,
department: partnerOptionDepartment,
title: partnerOptionTitle,
directReport:
directReport && directReportPartnerDocumentFromDb
? directReportPartnerDocumentFromDb.id
: null,
};
try {
let updatedPartner = await this.updatePartnerInfo(partner.id, updateObj);
return interaction.reply({
content: `updated partner!\n\n\`\`\`\n${JSON.stringify(updatedPartner, null, 2)}\n\`\`\``,
ephemeral: false,
});
} catch (error) {
return interaction.reply({
content: `an error occured !\n\n\`\`\`\n${JSON.stringify(error, null, 2)}\n\`\`\``,
ephemeral: false,
});
}
}
private formatPartnerTitlesArrayForDiscord(): { name: PartnerTitle; value: string }[] {
return partnerTitles.map((title) => ({
name: title,
value: title,
}));
}
private formatOptionsForDiscordFromEnum(args: any): { name: string; value: string }[] {
return Object.entries(args)
.filter(([key, value]) => typeof value === "number") // Filter out reverse mappings
.map(([key, value]) => ({
name: key,
value: (value as number).toString(),
}));
}
private async updatePartnerInfo<T>(id: string, updateObj: Partial<T>) {
// Remove keys with falsy values (undefined, null, etc.)
const filteredUpdate = Object.fromEntries(
Object.entries(updateObj).filter(([key, value]) => key !== "discordID" && value)
);
if (Object.keys(filteredUpdate).length === 0) {
throw new Error(
"Error in Partner update command, no options specified on update, you can safely ignore this error."
);
}
// Find and update the document by ID with only the valid fields
return await PartnerModel.findByIdAndUpdate(id, filteredUpdate, { new: true });
}
} }

View File

@ -1,34 +0,0 @@
import DiscordInteractionCommand, {
DiscordInteractionCommandSkeleton,
} from "../../util/DiscordInteractionCommand";
import { guildID } from "../../config.json";
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
import { MemberModel } from "../../database/Member";
import { PartnerModel } from "../../database/Partner";
export default class PartnerAdd implements DiscordInteractionCommandSkeleton {
public GUILD_ID: string;
public name: string;
public description: string;
public builder: SlashCommandBuilder;
constructor() {
this.name = "partner";
this.description = "Creates a new partner entry.";
this.builder = new SlashCommandBuilder();
this.GUILD_ID = guildID;
}
public async execute(interaction: ChatInputCommandInteraction) {
const member = MemberModel.findOne({ discordID: interaction.user.id });
if (!member)
return interaction.reply({
content: "The specified partner does not have a base member entry.",
ephemeral: true,
});
if (!(await PartnerModel.findOne({ discordID: interaction.user.id })))
return interaction.reply({
content: "The specified partner already has a partner entry.",
ephemeral: true,
});
}
}

View File

@ -1,293 +0,0 @@
import DiscordInteractionCommand from "../../util/DiscordInteractionCommand";
import { ChatInputCommandInteraction, EmbedBuilder } from "discord.js";
import axios from "axios";
interface CertificateDetails {
bitLength: number;
connection: {
cipherSuite: string;
tlsVersion: string;
};
emailAddresses: [];
extendedKeyUsage: number[];
extendedKeyUsageAsText: string[];
fingerprint: string;
issuer: {
commonName: string;
country: string[];
locality: never; // TODO: needs clarification
province: string[];
organization: string[];
organizationalUnit: never; // TODO: needs clarification
};
keyUsageAsText: string[];
notAfter: Date;
notBefore: Date;
publicKeyAlgorithm: string;
san: string[];
serialNumber: string;
signatureAlgorithm: string;
status: boolean;
subject: {
commonName: string;
country: string[];
locality: never; // TODO: needs clarification
province: string[];
organization: string[];
organizationalUnit: never; // TODO: needs clarification
};
validationType: "DV" | "OV" | "EV";
}
// Define an enum for security levels
enum SecurityLevel {
MostSecure,
Secure,
LessSecure,
NotSecure,
}
interface CipherSuite {
cipher: string;
securityLevel: SecurityLevel;
}
const CipherSuites: CipherSuite[] = [
// Most Secure (TLS 1.3 AEAD Ciphers)
{ cipher: "TLS_AES_256_GCM_SHA384", securityLevel: SecurityLevel.MostSecure },
{ cipher: "TLS_CHACHA20_POLY1305_SHA256", securityLevel: SecurityLevel.MostSecure },
{ cipher: "TLS_AES_128_GCM_SHA256", securityLevel: SecurityLevel.MostSecure },
// Secure (TLS 1.2 AEAD Ciphers with Forward Secrecy)
{ cipher: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", securityLevel: SecurityLevel.Secure },
{ cipher: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", securityLevel: SecurityLevel.Secure },
{ cipher: "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", securityLevel: SecurityLevel.Secure },
{ cipher: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", securityLevel: SecurityLevel.Secure },
{ cipher: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", securityLevel: SecurityLevel.Secure },
{ cipher: "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", securityLevel: SecurityLevel.Secure },
{ cipher: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", securityLevel: SecurityLevel.Secure },
{ cipher: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", securityLevel: SecurityLevel.Secure },
{ cipher: "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", securityLevel: SecurityLevel.Secure },
// Less Secure (CBC with TLS 1.2 and SHA-256/SHA-384)
{ cipher: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", securityLevel: SecurityLevel.LessSecure },
{ cipher: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", securityLevel: SecurityLevel.LessSecure },
{ cipher: "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
{ cipher: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
{ cipher: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
{ cipher: "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
{ cipher: "TLS_RSA_WITH_AES_256_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
{ cipher: "TLS_RSA_WITH_AES_128_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
// Not Secure (CBC with TLS 1.0/1.1 or SHA-1)
{ cipher: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
{ cipher: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
{ cipher: "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
{ cipher: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
{ cipher: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
{ cipher: "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
{ cipher: "TLS_RSA_WITH_AES_256_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
{ cipher: "TLS_RSA_WITH_AES_128_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
{ cipher: "TLS_RSA_WITH_3DES_EDE_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
{ cipher: "TLS_RSA_WITH_RC4_128_SHA", securityLevel: SecurityLevel.NotSecure },
{ cipher: "TLS_RSA_WITH_RC4_128_MD5", securityLevel: SecurityLevel.NotSecure },
];
export default class TLS extends DiscordInteractionCommand {
constructor() {
super("tls", "Receives TLS information about an HTTP server with a FQDN.");
this.builder.addStringOption((option) => {
return option
.setName("fqdn")
.setDescription(
"The Fully Qualified Domain Name (FQDN) for the server you want to perform the TLS/SSL lookup for."
)
.setRequired(true)
.setMinLength(3);
});
}
public getCipherSecurityLevel(cipher: string): SecurityLevel | null {
const result = CipherSuites.find((entry) => entry.cipher === cipher);
return result ? result.securityLevel : null;
}
public async execute(interaction: ChatInputCommandInteraction) {
await interaction.deferReply({ ephemeral: false });
try {
const certAPIReq = await axios.get(`https://certapi.libraryofcode.org/`, {
params: { q: interaction.options.getString("fqdn", true) },
});
if (certAPIReq.status !== 200) {
return interaction.editReply({
content:
"Could not fetch information for this FQDN's HTTP server. Please check the FQDN, its server, and try again.",
});
}
const certData: CertificateDetails = certAPIReq.data;
if (!certData.status) {
return interaction.editReply({
content: "Issue when fetching this FQDN's TLS certificate. Please try again later.",
});
}
const embed = new EmbedBuilder();
embed.setAuthor({
name: interaction.options.getString("fqdn", true),
iconURL: `https://${interaction.options.getString("fqdn", true)}/favicon.ico`,
});
let desc = "";
if (certData.validationType === "EV") {
desc += `**Certificate issued to:** __${certData.subject.organization[0]} [${certData.subject.country[0]}]__\n**Verified by:** __${certData.issuer.organization[0]}__\n\n`;
} else if (certData.issuer.organization) {
desc += `**Verified by:** __${certData.issuer.organization[0]}__\n\n`;
}
if (certData.subject) {
desc += "## Subject\n";
if (certData.subject.organization && certData.issuer.commonName) {
desc += `__**${certData.subject.organization[0]} (${certData.subject.commonName})**__\n`;
}
if (certData.subject.commonName) {
desc += `**Common Name:** ${certData.subject.commonName}\n`;
}
if (certData.subject.organization) {
desc += `**Organization:** ${certData.subject.organization[0]}\n`;
}
if (certData.subject.organizationalUnit) {
desc += `**Organizational Unit:** ${certData.subject.organizationalUnit[0]}\n`;
}
if (certData.subject.locality) {
desc += `**Locality:** ${certData.subject.locality[0]}\n`;
}
if (certData.subject.province) {
desc += `**State/Province:** ${certData.subject.province[0]}\n`;
}
if (certData.subject.country) {
desc += `**Country:** ${certData.subject.country[0]}\n`;
}
}
if (certData.issuer) {
desc += "## Issuer\n";
if (certData.issuer.organization && certData.issuer.commonName) {
desc += `__**${certData.issuer.organization[0]} (${certData.issuer.commonName})**__\n`;
}
if (certData.issuer.commonName) {
desc += `**Common Name:** ${certData.issuer.commonName}\n`;
}
if (certData.issuer.organization) {
desc += `**Organization:** ${certData.issuer.organization[0]}\n`;
}
if (certData.issuer.organizationalUnit) {
desc += `**Organizational Unit:** ${certData.issuer.organizationalUnit[0]}\n`;
}
if (certData.subject.locality) {
desc += `**Locality:** ${certData.subject.locality[0]}\n`;
}
if (certData.issuer.province) {
desc += `**State/Province:** ${certData.issuer.province[0]}\n`;
}
if (certData.issuer.country) {
desc += `**Country:** ${certData.issuer.country[0]}\n`;
}
}
embed.setDescription(desc);
let validationType:
| "Domain Validation (DV)"
| "Organization Validation (OV)"
| ":lock: Extended Validation (EV)"
| string;
switch (certData.validationType) {
case "DV":
validationType = "Domain Validation (DV)";
break;
case "OV":
validationType = "Organization Validation (OV)";
embed.setColor("#4287f5");
break;
case "EV":
embed.setColor("#42f554");
validationType = ":lock: Extended Validation (EV)";
break;
default:
validationType = "N/A';";
break;
}
let cipherSuiteText: string = "";
switch (this.getCipherSecurityLevel(certData.connection.cipherSuite)) {
case SecurityLevel.MostSecure:
cipherSuiteText = `:green_circle: ${certData.connection.cipherSuite} (${certData.connection.tlsVersion})`;
break;
case SecurityLevel.Secure:
cipherSuiteText = `:yellow_circle: ${certData.connection.cipherSuite} (${certData.connection.tlsVersion})`;
break;
case SecurityLevel.LessSecure:
cipherSuiteText = `:orange_circle: ${certData.connection.cipherSuite} (${certData.connection.tlsVersion})`;
break;
case SecurityLevel.NotSecure:
cipherSuiteText = `:red_circle: ${certData.connection.cipherSuite} (${certData.connection.tlsVersion})`;
break;
default:
cipherSuiteText = `:grey_question: ${certData.connection.cipherSuite} (${certData.connection.tlsVersion})`;
break;
}
embed.addFields(
{
name: "Validation Type",
value: validationType,
inline: true,
},
{
name: "Cipher Suite",
value: cipherSuiteText,
inline: false,
},
{
name: "Public Key Algorithm",
value: `${certData.publicKeyAlgorithm} ${certData.bitLength}`,
inline: true,
},
{
name: "Signature Algorithm",
value: certData.signatureAlgorithm,
inline: true,
},
{ name: "Not Before", value: new Date(certData.notBefore).toUTCString(), inline: true },
{ name: "Not After", value: new Date(certData.notAfter).toUTCString(), inline: true },
{
name: "Serial Number",
value: certData.serialNumber,
inline: true,
}
);
console.log(certData); // TODO: Remove after testing.
if (certData.keyUsageAsText?.length)
embed.addFields({
name: "Key Usages",
value: certData.keyUsageAsText.join(", "),
inline: true,
});
if (certData.extendedKeyUsageAsText?.length)
embed.addFields({
name: "Extended Key Usages",
value: certData.extendedKeyUsageAsText.join(", "),
inline: true,
});
// embed.addField('Common Name', x509.data.subject.commonName, true);
// embed.addField('Issuer', x509.data.issuer.commonName, true);
// embed.addBlankField();
// embed.addField('Public Key Algorithm', x509.data.publicKeyAlgorithm, true);
// embed.addField('Not Before', new Date(x509.data.notBefore).toUTCString(), true);
// embed.addField('Not After', new Date(x509.data.notAfter).toUTCString(), true);
// if (x509.data.keyUsageAsText.length) embed.addField('Key Usages', x509.data.keyUsageAsText.join(', '), true);
// if (x509.data.extendedKeyUsageAsText.length) embed.addField('Extended Key Usages', x509.data.extendedKeyUsageAsText.join(', '), true);
return await interaction.editReply({ embeds: [embed] });
} catch (err) {
return interaction.editReply({ content: `Error processing retrieval from FQDN: ${err}` });
}
}
}

View File

@ -1,15 +1,14 @@
import DiscordInteractionCommand from "../../util/DiscordInteractionCommand"; import DiscordInteractionCommand from "../../util/DiscordInteractionCommand";
import { MemberAdditionalAcknowledgement, MemberModel } from "../../database/Member"; import { MemberModel } from "../../database/Member";
import Partner, { import Partner, {
PartnerCommissionType, PartnerCommissionType,
PartnerDepartment, PartnerDepartment,
PartnerModel, PartnerModel,
PartnerRoleType, PartnerRoleType,
} from "../../database/Partner"; } from "../../database/Partner";
import { ChatInputCommandInteraction, EmbedBuilder, GuildMember, Snowflake } from "discord.js"; import { ChatInputCommandInteraction, EmbedBuilder, GuildMember } from "discord.js";
import MemberUtil from "../../util/MemberUtil"; import MemberUtil from "../../util/MemberUtil";
import EmojiConfig from "../../util/EmojiConfig"; import EmojiConfig from "../../util/EmojiConfig";
import Formatters from "../../util/Formatters";
export default class Whois extends DiscordInteractionCommand { export default class Whois extends DiscordInteractionCommand {
constructor() { constructor() {
@ -37,7 +36,7 @@ export default class Whois extends DiscordInteractionCommand {
const embed = new EmbedBuilder(); const embed = new EmbedBuilder();
// if the role type is managerial, add a [k] to the end of the name // if the role type is managerial, add a [k] to the end of the name
// if the partner exists, set the iconURL to the organizational logo // if the partner exists, set the iconURL to the organizational logo
const formattedName = Formatters.formatName(guildMember, partner); const formattedName = MemberUtil.formatName(guildMember, partner);
embed.setAuthor({ name: formattedName.text, iconURL: formattedName.iconURL }); embed.setAuthor({ name: formattedName.text, iconURL: formattedName.iconURL });
// set the thumbnail to the user's avatar // set the thumbnail to the user's avatar
embed.setThumbnail(guildMember.user.displayAvatarURL()); embed.setThumbnail(guildMember.user.displayAvatarURL());
@ -80,23 +79,16 @@ export default class Whois extends DiscordInteractionCommand {
break; break;
} }
if (partner.directReport) { if (partner.directReport) {
// fetch direct report object ref if (partner.directReport instanceof Partner) {
await partner.populate("directReport"); embedDescription += `**Direct Report**: ${partner.directReport.title}\n`;
// ensures that the population propagated correctly before adding to embed
if (partner.directReport instanceof PartnerModel) {
const directReportGuildMember = await guild?.members.fetch(
partner.directReport.discordID as Snowflake
);
// fetches GuildMember for the direct report
embedDescription += `**Direct Report**: ${directReportGuildMember ? Formatters.formatName(directReportGuildMember, partner.directReport).text + ", " : ""}${partner.directReport.title}\n`;
} }
} }
} }
embed.setColor(guildMember.displayColor); embed.setColor(guildMember.displayColor);
if (embedDescription?.length > 0) if (embedDescription?.length > 0) embed.setDescription(embedDescription);
embed.setDescription(`${embedDescription}\n\n<@${guildMember.id}>`);
// add status to embed // add status to embed
if (guildMember.presence?.status) { if (guildMember.presence?.status) {
// TODO: this currently doesn't work for some reason
switch (guildMember.presence.status) { switch (guildMember.presence.status) {
case "online": case "online":
embed.addFields({ name: "Status", value: "Online", inline: true }); embed.addFields({ name: "Status", value: "Online", inline: true });
@ -107,7 +99,7 @@ export default class Whois extends DiscordInteractionCommand {
case "dnd": case "dnd":
embed.addFields({ name: "Status", value: "Do Not Disturb", inline: true }); embed.addFields({ name: "Status", value: "Do Not Disturb", inline: true });
break; break;
case "offline": case "offline" || "invisible":
embed.addFields({ name: "Status", value: "Online", inline: true }); embed.addFields({ name: "Status", value: "Online", inline: true });
break; break;
default: default:
@ -115,87 +107,9 @@ export default class Whois extends DiscordInteractionCommand {
embed.addFields({ name: "Status", value: "", inline: true }); embed.addFields({ name: "Status", value: "", inline: true });
break; break;
} }
} else {
embed.addFields({ name: "Status", value: "Offline", inline: true });
}
// calculations for joined / created at
embed.addFields(
{
name: "Joined At",
value: guildMember.joinedTimestamp
? `<t:${Math.floor(guildMember.joinedTimestamp / 1000)}>`
: "Invalid Date",
inline: true,
},
{
name: "Created At",
value: guildMember.user.createdTimestamp
? `<t:${Math.floor(guildMember.user.createdTimestamp / 1000)}>`
: "Invalid Date",
inline: true,
}
);
// TODO: calculations for commscore
embed.addFields({ name: "CommScore™", value: "[PLACEHOLDER]", inline: false });
// role calculation (sorting roles by their position)
let roleString = "";
for (const role of guildMember.roles.valueOf().sort((a, b) => b.position - a.position)) {
roleString += `<@&${role[1].id}> `;
}
if (roleString) {
embed.addFields({ name: "Roles", value: roleString });
} else {
embed.addFields({ name: "Roles", value: "None" });
}
// listing permissions
const serializedPermissions = guildMember.permissions.serialize();
const permissionsArray: string[] = [];
// adding serialized string representation of permissions to array to use in embed field
if (serializedPermissions.Administrator) permissionsArray.push("Administrator");
if (serializedPermissions.ManageGuild) permissionsArray.push("Manage Guild");
if (serializedPermissions.ManageChannels) permissionsArray.push("Manage Channels");
if (serializedPermissions.ManageRoles) permissionsArray.push("Manage Roles");
if (serializedPermissions.ManageMessages) permissionsArray.push("Manage Messages");
if (serializedPermissions.ManageEvents) permissionsArray.push("Manage Events");
if (serializedPermissions.ManageNicknames) permissionsArray.push("Manage Nicknames");
if (serializedPermissions.ManageEmojisAndStickers) permissionsArray.push("Manage Emojis");
if (serializedPermissions.ManageWebhooks) permissionsArray.push("Manage Webhooks");
if (serializedPermissions.ModerateMembers) permissionsArray.push("Moderate Members");
if (serializedPermissions.BanMembers) permissionsArray.push("Ban Members");
if (serializedPermissions.KickMembers) permissionsArray.push("Kick Members");
if (serializedPermissions.DeafenMembers) permissionsArray.push("Deafen Members");
// setting key permissions embed field
if (permissionsArray?.length > 0) {
embed.addFields({ name: "Key Permissions", value: permissionsArray.join(", ") });
}
// determining acknowledgements: MemberAdditionalAcknowledgement || "Guild Owner", "Guild Admin", "Guild Manager", "Guild Moderator"
const acknowledgementsArray: MemberAdditionalAcknowledgement[] = [];
if (guildMember.id === guildMember.guild.ownerId) {
acknowledgementsArray.push("Guild Owner");
} else if (serializedPermissions.Administrator) {
acknowledgementsArray.push("Guild Admin");
} else if (serializedPermissions.ManageGuild) {
acknowledgementsArray.push("Guild Manager");
} else if (serializedPermissions.ModerateMembers || serializedPermissions.ManageMessages) {
acknowledgementsArray.push("Guild Moderator");
}
if (partner?.canPerformDevCommands) {
acknowledgementsArray.push("System Developer");
}
// adding acknowledgements to embed
if (acknowledgementsArray.length > 0) {
embed.addFields({ name: "Acknowledgements", value: acknowledgementsArray.join(", ") });
} }
embed.setFooter({ embed.setFooter({
text: `Discord ID: ${guildMember.id}${databaseMember ? `| Internal ID: ${databaseMember?._id}` : ""}${partner ? ` | Partner ID: ${partner?.id}` : ""}`, text: `Discord ID: ${guildMember.id}${databaseMember ? `Internal ID: ${databaseMember?._id}` : ""}`,
}); });
return await interaction.editReply({ embeds: [embed] }); return await interaction.editReply({ embeds: [embed] });

View File

@ -1,5 +1,4 @@
export { default as Eval } from "./Eval"; export { default as Eval } from "./Eval";
export { default as Partner } from "./Partner"; export { default as Partner } from "./Partner";
export { default as Ping } from "./Ping"; export { default as Ping } from "./Ping";
export { default as TLS } from "./TLS";
export { default as Whois } from "./Whois"; export { default as Whois } from "./Whois";

View File

@ -1,13 +1,14 @@
import { Client, GatewayIntentBits, Partials, REST, Routes } from "discord.js"; import { Client, GatewayIntentBits, Partials, REST, Routes } from 'discord.js';
import { discordBotToken, discordClientID, mongoDBConnectionURI } from "./config.json"; import { discordBotToken, discordClientID, MongoDbUrl } from './config.json';
import Collection from "./util/Collection"; import Collection from './util/Collection';
import DiscordInteractionCommand from "./util/DiscordInteractionCommand"; import DiscordInteractionCommand from './util/DiscordInteractionCommand';
import DiscordEvent from "./util/DiscordEvent"; import DiscordEvent from './util/DiscordEvent';
import * as DiscordInteractionCommandsIndex from "./discord/commands"; import * as DiscordInteractionCommandsIndex from './discord/commands';
import * as DiscordEventsIndex from "./discord/events"; import * as DiscordEventsIndex from './discord/events';
import mongoose from "mongoose"; import mongoose from 'mongoose';
export const DiscordInteractionCommands: Collection<DiscordInteractionCommand> = new Collection(); export const DiscordInteractionCommands: Collection<DiscordInteractionCommand> =
new Collection();
export const DiscordEvents: Collection<DiscordEvent> = new Collection(); export const DiscordEvents: Collection<DiscordEvent> = new Collection();
// Instantiates a new Discord client // Instantiates a new Discord client
@ -21,7 +22,12 @@ const discordClient = new Client({
GatewayIntentBits.GuildInvites, GatewayIntentBits.GuildInvites,
GatewayIntentBits.GuildModeration, GatewayIntentBits.GuildModeration,
], ],
partials: [Partials.GuildMember, Partials.Message, Partials.User, Partials.Channel], partials: [
Partials.GuildMember,
Partials.Message,
Partials.User,
Partials.Channel,
],
}); });
const discordREST = new REST().setToken(discordBotToken); const discordREST = new REST().setToken(discordBotToken);
// const stripeClient = new Stripe(stripeToken, { typescript: true }); // const stripeClient = new Stripe(stripeToken, { typescript: true });
@ -29,11 +35,12 @@ const discordREST = new REST().setToken(discordBotToken);
export async function main() { export async function main() {
// Connect to the databases // Connect to the databases
try { try {
mongoose.connection.once("open", () => { //@ts-ignore
console.info("[Info - Database] Connected to MongoDB"); mongoose.connection.once('open', () => {
console.info('[Info - Database] Connected to MongoDB');
}); });
// TODO: Fetch the MongoDB URI from the config file // TODO: Fetch the MongoDB URI from the config file
await mongoose.connect(mongoDBConnectionURI, {}); await mongoose.connect(MongoDbUrl, {});
} catch (error) { } catch (error) {
console.error(`[Error - Database] Failed to connect to MongoDB: ${error}`); console.error(`[Error - Database] Failed to connect to MongoDB: ${error}`);
process.exit(1); process.exit(1);
@ -42,7 +49,9 @@ export async function main() {
for (const Command of Object.values(DiscordInteractionCommandsIndex)) { for (const Command of Object.values(DiscordInteractionCommandsIndex)) {
const instance = new Command(); const instance = new Command();
DiscordInteractionCommands.add(instance.name, instance); DiscordInteractionCommands.add(instance.name, instance);
console.info(`[Info - Discord] Loaded interaction command: ${instance.name}`); console.info(
`[Info - Discord] Loaded interaction command: ${instance.name}`
);
} }
// Load Discord events // Load Discord events
for (const Event of Object.values(DiscordEventsIndex)) { for (const Event of Object.values(DiscordEventsIndex)) {
@ -54,19 +63,22 @@ export async function main() {
await discordClient.login(discordBotToken); await discordClient.login(discordBotToken);
try { try {
console.log(`Started refreshing ${DiscordInteractionCommands.size} application (/) commands.`); console.log(
`Started refreshing ${DiscordInteractionCommands.size} application (/) commands.`
);
const interactionCommandsData = []; const interactionCommandsData = [];
for (const command of DiscordInteractionCommands.values()) { for (const command of DiscordInteractionCommands.values()) {
interactionCommandsData.push(command.builder.toJSON()); interactionCommandsData.push(command.builder.toJSON());
} }
// The put method is used to fully refresh all commands in the guild with the current set // The put method is used to fully refresh all commands in the guild with the current set
const data = await discordREST.put(Routes.applicationCommands(discordClientID), { const data = await discordREST.put(
body: interactionCommandsData, Routes.applicationCommands(discordClientID),
}); { body: interactionCommandsData }
);
// @ts-ignore console.log(
console.log(`Successfully reloaded ${data?.length} application (/) commands.`); `Successfully reloaded ${interactionCommandsData?.length} application (/) commands.`
);
} catch (error) { } catch (error) {
// And of course, make sure you catch and log any errors! // And of course, make sure you catch and log any errors!
console.error(error); console.error(error);

352
package-lock.json generated
View File

@ -1,16 +1,12 @@
{ {
"name": "crv2", "name": "crra",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "crv2",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@okta/okta-sdk-nodejs": "^7.1.1",
"@typegoose/typegoose": "^12.2.0", "@typegoose/typegoose": "^12.2.0",
"auth0": "^4.12.0",
"axios": "^1.8.4",
"discord.js": "^14.14.1", "discord.js": "^14.14.1",
"mongoose": "^8.2.2", "mongoose": "^8.2.2",
"stripe": "^14.21.0", "stripe": "^14.21.0",
@ -330,9 +326,9 @@
} }
}, },
"node_modules/@eslint/plugin-kit": { "node_modules/@eslint/plugin-kit": {
"version": "0.2.3", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.1.tgz",
"integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", "integrity": "sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"levn": "^0.4.1" "levn": "^0.4.1"
@ -473,30 +469,6 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/@okta/okta-sdk-nodejs": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@okta/okta-sdk-nodejs/-/okta-sdk-nodejs-7.1.1.tgz",
"integrity": "sha512-sY6y749Lo7NAchp+aV+0Px9Yj4Zsz7CJGfQEJBHAHjnNnRk3+XQkiJobal1w4xATsgHTZrNHFMqYp8yRiLWerA==",
"license": "Apache-2.0",
"dependencies": {
"@types/node-forge": "^1.3.1",
"deep-copy": "^1.4.2",
"eckles": "^1.4.1",
"form-data": "^4.0.0",
"https-proxy-agent": "^5.0.0",
"js-yaml": "^4.1.0",
"lodash": "^4.17.20",
"njwt": "^2.0.1",
"node-fetch": "^2.6.7",
"parse-link-header": "^2.0.0",
"rasha": "^1.2.5",
"safe-flat": "^2.0.2",
"url-parse": "^1.5.10"
},
"engines": {
"node": ">=14.0"
}
},
"node_modules/@pkgjs/parseargs": { "node_modules/@pkgjs/parseargs": {
"version": "0.11.0", "version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@ -615,15 +587,6 @@
"undici-types": "~5.26.4" "undici-types": "~5.26.4"
} }
}, },
"node_modules/@types/node-forge": {
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz",
"integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/uuid": { "node_modules/@types/uuid": {
"version": "9.0.8", "version": "9.0.8",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
@ -890,18 +853,6 @@
"node": ">=0.4.0" "node": ">=0.4.0"
} }
}, },
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"license": "MIT",
"dependencies": {
"debug": "4"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/ajv": { "node_modules/ajv": {
"version": "6.12.6", "version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@ -980,41 +931,8 @@
"node_modules/argparse": { "node_modules/argparse": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
}, "dev": true
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/auth0": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/auth0/-/auth0-4.12.0.tgz",
"integrity": "sha512-5WDAHb8EvWSmRyA9D+FTBrHdEL1RM48PTPHVPxSmzbiAXrhR4pSgwSJyoGjia2+rvMR2NMXhtMfuRRqosEp7PA==",
"dependencies": {
"jose": "^4.13.2",
"undici-types": "^6.15.0",
"uuid": "^9.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/auth0/node_modules/undici-types": {
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
},
"node_modules/axios": {
"version": "1.8.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
"integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
}, },
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
@ -1383,17 +1301,6 @@
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
"dev": true "dev": true
}, },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": { "node_modules/commander": {
"version": "12.1.0", "version": "12.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
@ -1416,9 +1323,9 @@
"devOptional": true "devOptional": true
}, },
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.6", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dependencies": { "dependencies": {
"path-key": "^3.1.0", "path-key": "^3.1.0",
"shebang-command": "^2.0.0", "shebang-command": "^2.0.0",
@ -1449,15 +1356,6 @@
} }
} }
}, },
"node_modules/deep-copy": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/deep-copy/-/deep-copy-1.4.2.tgz",
"integrity": "sha512-VxZwQ/1+WGQPl5nE67uLhh7OqdrmqI1OazrraO9Bbw/M8Bt6Mol/RxzDA6N6ZgRXpsG/W9PgUj8E1LHHBEq2GQ==",
"license": "MIT",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/deep-is": { "node_modules/deep-is": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@ -1480,14 +1378,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/diff": { "node_modules/diff": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
@ -1543,24 +1433,6 @@
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
}, },
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
"license": "Apache-2.0",
"dependencies": {
"safe-buffer": "^5.0.1"
}
},
"node_modules/eckles": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/eckles/-/eckles-1.4.1.tgz",
"integrity": "sha512-auWyk/k8oSkVHaD4RxkPadKsLUcIwKgr/h8F7UZEueFDBO7BsE4y+H6IMUDbfqKIFPg/9MxV6KcBdJCmVVcxSA==",
"license": "MPL-2.0",
"bin": {
"eckles": "bin/eckles.js"
}
},
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
"version": "9.2.2", "version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
@ -2000,26 +1872,6 @@
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
"dev": true "dev": true
}, },
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/foreground-child": { "node_modules/foreground-child": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
@ -2035,19 +1887,6 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/form-data": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@ -2228,19 +2067,6 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"license": "MIT",
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/human-signals": { "node_modules/human-signals": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
@ -2409,18 +2235,11 @@
"@pkgjs/parseargs": "^0.11.0" "@pkgjs/parseargs": "^0.11.0"
} }
}, },
"node_modules/jose": {
"version": "4.15.9",
"resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz",
"integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/js-yaml": { "node_modules/js-yaml": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"dependencies": { "dependencies": {
"argparse": "^2.0.1" "argparse": "^2.0.1"
}, },
@ -2800,25 +2619,6 @@
"node": ">=8.6" "node": ">=8.6"
} }
}, },
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mimic-fn": { "node_modules/mimic-fn": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
@ -3088,77 +2888,6 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true "dev": true
}, },
"node_modules/njwt": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/njwt/-/njwt-2.0.1.tgz",
"integrity": "sha512-HwFeZsPJ1aOhIjMjqT9Qv7BOsQbkxjRVPPSdFXNOTEkfKpr9+O6OX+dSN6TxxIErSYSqrmlDR4H2zOGOpEbZLA==",
"license": "Apache-2.0",
"dependencies": {
"@types/node": "^15.0.1",
"ecdsa-sig-formatter": "^1.0.5",
"uuid": "^8.3.2"
},
"engines": {
"node": ">=12.0"
}
},
"node_modules/njwt/node_modules/@types/node": {
"version": "15.14.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz",
"integrity": "sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==",
"license": "MIT"
},
"node_modules/njwt/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"license": "MIT",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/node-fetch/node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"license": "MIT"
},
"node_modules/node-fetch/node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"license": "BSD-2-Clause"
},
"node_modules/node-fetch/node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"license": "MIT",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/npm-run-path": { "node_modules/npm-run-path": {
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
@ -3276,15 +3005,6 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/parse-link-header": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/parse-link-header/-/parse-link-header-2.0.0.tgz",
"integrity": "sha512-xjU87V0VyHZybn2RrCX5TIFGxTVZE6zqqZWMPlIKiSKuWh/X5WZdt+w1Ki1nXB+8L/KtL+nZ4iq+sfI6MrhhMw==",
"license": "MIT",
"dependencies": {
"xtend": "~4.0.1"
}
},
"node_modules/parse5": { "node_modules/parse5": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
@ -3395,12 +3115,6 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/punycode": { "node_modules/punycode": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@ -3423,12 +3137,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
"license": "MIT"
},
"node_modules/queue-microtask": { "node_modules/queue-microtask": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@ -3449,15 +3157,6 @@
} }
] ]
}, },
"node_modules/rasha": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/rasha/-/rasha-1.2.5.tgz",
"integrity": "sha512-KxtX+/fBk+wM7O3CNgwjSh5elwFilLvqWajhr6wFr2Hd63JnKTTi43Tw+Jb1hxJQWOwoya+NZWR2xztn3hCrTw==",
"license": "MPL-2.0",
"bin": {
"rasha": "bin/rasha.js"
}
},
"node_modules/reflect-metadata": { "node_modules/reflect-metadata": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz",
@ -3471,12 +3170,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
"license": "MIT"
},
"node_modules/resolve-from": { "node_modules/resolve-from": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@ -3575,12 +3268,6 @@
} }
] ]
}, },
"node_modules/safe-flat": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/safe-flat/-/safe-flat-2.1.0.tgz",
"integrity": "sha512-qr5iVWMYuN21dkijya23k6apc2BV1hiCG75vjToKDTzWlbR4SLbLbCnowPJ2pngnwGT2nMEeZKOglBE4pksj6g==",
"license": "MIT"
},
"node_modules/semver": { "node_modules/semver": {
"version": "7.6.0", "version": "7.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
@ -4217,16 +3904,6 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/url-parse": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"license": "MIT",
"dependencies": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
}
},
"node_modules/uuid": { "node_modules/uuid": {
"version": "9.0.1", "version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
@ -4391,15 +4068,6 @@
} }
} }
}, },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"license": "MIT",
"engines": {
"node": ">=0.4"
}
},
"node_modules/y18n": { "node_modules/y18n": {
"version": "5.0.8", "version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View File

@ -1,5 +1,4 @@
{ {
"name": "crv2",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.13.0", "@eslint/js": "^9.13.0",
@ -18,10 +17,7 @@
"typescript-eslint": "^8.11.0" "typescript-eslint": "^8.11.0"
}, },
"dependencies": { "dependencies": {
"@okta/okta-sdk-nodejs": "^7.1.1",
"@typegoose/typegoose": "^12.2.0", "@typegoose/typegoose": "^12.2.0",
"auth0": "^4.12.0",
"axios": "^1.8.4",
"discord.js": "^14.14.1", "discord.js": "^14.14.1",
"mongoose": "^8.2.2", "mongoose": "^8.2.2",
"stripe": "^14.21.0", "stripe": "^14.21.0",
@ -44,4 +40,4 @@
"prettier --write" "prettier --write"
] ]
} }
} }

View File

@ -11,11 +11,11 @@
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */ /* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */ // "jsx": "preserve", /* Specify what JSX code is generated. */
"experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ "experimentalDecorators": true /* Enable experimental support for legacy experimental decorators. */,
"emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ "emitDecoratorMetadata": true /* Emit design-type metadata for decorated declarations in source files. */,
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
@ -25,7 +25,7 @@
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */ /* Modules */
"module": "commonjs", /* Specify what module code is generated. */ "module": "commonjs" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */ // "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
@ -39,7 +39,7 @@
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
"resolveJsonModule": true, /* Enable importing .json files. */ "resolveJsonModule": true /* Enable importing .json files. */,
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */ // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
@ -55,7 +55,7 @@
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */ "outDir": "./dist" /* Specify an output folder for all emitted files. */,
// "removeComments": true, /* Disable emitting comments. */ // "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */ // "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
@ -77,12 +77,12 @@
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */ /* Type Checking */
"strict": true, /* Enable all strict type-checking options. */ "strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
@ -104,6 +104,6 @@
/* Completeness */ /* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */
} }
} }

View File

@ -8,5 +8,5 @@ export default abstract class DiscordEvent {
this.client = client; this.client = client;
this.execute = this.execute.bind(this); this.execute = this.execute.bind(this);
} }
public abstract execute(...args: never[]): Error | Promise<void>; public abstract execute(...args: any[]): Error | Promise<void>;
} }

View File

@ -1,67 +0,0 @@
import { Guild, GuildMember, User } from "discord.js";
import Partner, { PartnerCommissionType, PartnerRoleType } from "../database/Partner";
import { FormatNameOptions } from "./MemberUtil";
export default class Formatters {
public static formatStandardDate(date: Date | string | number): string {
const resolvedDate = new Date(date);
if (!resolvedDate) return "";
const year = resolvedDate.getFullYear();
const month = String(resolvedDate.getMonth() + 1).padStart(2, "0");
const day = String(resolvedDate.getDate()).padStart(2, "0");
const hours = String(resolvedDate.getHours()).padStart(2, "0");
const minutes = String(resolvedDate.getMinutes()).padStart(2, "0");
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
return `${year}-${month}-${day} @ ${hours}:${minutes} (${timeZone})`;
}
// TODO: comments and extended formatting
public static formatName(
target: GuildMember | User,
partner?: Partner | null
): FormatNameOptions {
console.debug(
`[MemberUtil] Formatting name for ${target.displayName} at url ${target instanceof GuildMember ? target.user.displayAvatarURL() : target.displayAvatarURL()}`
);
// if the partner is designated as a KeyHolder, add a [k] to the end of the name
// if the partner exists, set the iconURL to the organizational logo
if (
partner?.isKeyHolder ||
("roles" in target && target.roles.cache.has("1014978134573064293"))
) {
return {
text: `${target.displayName} [k]`,
iconURL: target.displayAvatarURL(),
};
} else if (partner?.roleType === PartnerRoleType.MANAGERIAL) {
// if a partner is of RoleType MANAGERIAL, add [m] to their name
return {
text: `${target.displayName} [m]`,
iconURL:
target instanceof GuildMember
? target.user.displayAvatarURL()
: target.displayAvatarURL(),
};
} else if (partner?.commissionType === PartnerCommissionType.CONTRACTUAL) {
// if the commission type is contractual, add a [c] to the end of the name
return {
text: `${target.displayName} [c]`,
iconURL:
target instanceof GuildMember
? target.user.displayAvatarURL()
: target.displayAvatarURL(),
};
} else {
// otherwise, just set the author to the member's display name
return {
text: target.displayName,
iconURL:
target instanceof GuildMember
? target.user.displayAvatarURL()
: target.displayAvatarURL(),
};
}
}
}

View File

@ -8,14 +8,13 @@ import Partner, {
import Member, { MemberAdditionalAcknowledgement, MemberModel } from "../database/Member"; import Member, { MemberAdditionalAcknowledgement, MemberModel } from "../database/Member";
import { Client, GuildMember, User } from "discord.js"; import { Client, GuildMember, User } from "discord.js";
import { guildID } from "../config.json"; import { guildID } from "../config.json";
import { Ref } from "@typegoose/typegoose";
export interface PartnerOptions { export interface PartnerOptions {
roleType: PartnerRoleType; roleType: PartnerRoleType;
commissionType: PartnerCommissionType; commissionType: PartnerCommissionType;
department: PartnerDepartment; department: PartnerDepartment;
title: PartnerTitle; title: PartnerTitle;
directReport: Ref<Partner>; directReport: Partner | string;
} }
export interface FormatNameOptions { export interface FormatNameOptions {
@ -26,7 +25,7 @@ export interface FormatNameOptions {
// TODO: Add the rest of the remaining role configurations // TODO: Add the rest of the remaining role configurations
export const PartnerDiscordRoleMap = { export const PartnerDiscordRoleMap = {
// Director of Engineering, Management, Staff, Technician, Core Team, Play Caller // Director of Engineering, Management, Staff, Technician, Core Team, Play Caller
Engineering: [ "Director of Engineering": [
"1077646568091570236", "1077646568091570236",
"1077646956890951690", "1077646956890951690",
"446104438969466890", "446104438969466890",
@ -35,7 +34,7 @@ export const PartnerDiscordRoleMap = {
"1014978134573064293", "1014978134573064293",
], ],
// Director of Operations, Management, Staff, Moderator, Core Team, Play Caller // Director of Operations, Management, Staff, Moderator, Core Team, Play Caller
Operations: [ "Director of Operations": [
"1077647072163020840", "1077647072163020840",
"1077646956890951690", "1077646956890951690",
"446104438969466890", "446104438969466890",
@ -80,4 +79,40 @@ export default class MemberUtil {
{ $push: { additionalAcknowledgement: acknowledgement } } { $push: { additionalAcknowledgement: acknowledgement } }
); );
} }
// TODO: comments and extended formatting
public static formatName(
target: GuildMember | User,
partner?: Partner | null
): FormatNameOptions {
console.log(
`[MemberUtil] Formatting name for ${target.displayName} at url ${target instanceof GuildMember ? target.user.displayAvatarURL() : target.displayAvatarURL()}`
);
// if the role type is managerial, add a [k] to the end of the name
// if the partner exists, set the iconURL to the organizational logo
if (partner?.roleType == PartnerRoleType.MANAGERIAL) {
return {
text: `${target.displayName} [k]`,
iconURL: target.displayAvatarURL(),
};
} else if (partner?.commissionType == PartnerCommissionType.CONTRACTUAL) {
// if the commission type is contractual, add a [c] to the end of the name
return {
text: `${target.displayName} [c]`,
iconURL:
target instanceof GuildMember
? target.user.displayAvatarURL()
: target.displayAvatarURL(),
};
} else {
// otherwise, just set the author to the member's display name
return {
text: target.displayName,
iconURL:
target instanceof GuildMember
? target.user.displayAvatarURL()
: target.displayAvatarURL(),
};
}
}
} }