Compare commits
No commits in common. "94c10d07e135b4499d2b4733e17187fa41d8f023" and "47e5919893b8bbe57c361c88700f17f06e981dad" have entirely different histories.
94c10d07e1
...
47e5919893
@ -1,3 +0,0 @@
|
|||||||
https://dokku.com/docs/advanced-usage/deployment-tasks/?h=monorepo#changing-the-appjson-location
|
|
||||||
|
|
||||||
https://dokku.com/docs/deployment/builders/dockerfiles/
|
|
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"scripts": {
|
|
||||||
"dokku": {
|
|
||||||
"predeploy": "echo hello-world-predeploy",
|
|
||||||
"postdeploy": "echo hello-world-postdeploy"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
git monorepo.
|
|
||||||
|
|
||||||
pnpm required for workspaces.
|
|
||||||
|
|
||||||
Yarn required for packages/strapi
|
|
||||||
|
|
||||||
Development uses docker compose with dotenv.
|
|
||||||
|
|
||||||
Production uses dokku.
|
|
||||||
|
|
19
Dockerfile
Normal file
19
Dockerfile
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
FROM node:20-slim AS base
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
RUN corepack enable
|
||||||
|
|
||||||
|
FROM base AS build
|
||||||
|
WORKDIR /usr/src/fp-monorepo
|
||||||
|
RUN mkdir /usr/src/next
|
||||||
|
COPY ./pnpm-lock.yaml ./
|
||||||
|
COPY ./pnpm-workspace.yaml ./
|
||||||
|
COPY ./packages/next/package.json ./packages/next/
|
||||||
|
RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store pnpm install
|
||||||
|
COPY . .
|
||||||
|
RUN pnpm deploy --filter=fp-next /usr/src/next
|
||||||
|
|
||||||
|
FROM base AS dev
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /usr/src/next /app
|
||||||
|
CMD ["pnpm", "run", "dev"]
|
||||||
|
|
103
compose.prod.yml
Normal file
103
compose.prod.yml
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
version: '3.4'
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
link2cid:
|
||||||
|
container_name: fp-link2cid
|
||||||
|
image: insanity54/link2cid:latest
|
||||||
|
ports:
|
||||||
|
- "3939:3939"
|
||||||
|
environment:
|
||||||
|
API_KEY: ${LINK2CID_API_KEY}
|
||||||
|
IPFS_URL: "http://ipfs0:5001"
|
||||||
|
|
||||||
|
ipfs0:
|
||||||
|
container_name: fp-ipfs0
|
||||||
|
image: ipfs/kubo:release
|
||||||
|
ports:
|
||||||
|
- "5001:5001"
|
||||||
|
volumes:
|
||||||
|
- ./packages/ipfs0:/data/ipfs
|
||||||
|
|
||||||
|
cluster0:
|
||||||
|
container_name: fp-cluster0
|
||||||
|
image: ipfs/ipfs-cluster:latest
|
||||||
|
depends_on:
|
||||||
|
- ipfs0
|
||||||
|
environment:
|
||||||
|
CLUSTER_PEERNAME: cluster0
|
||||||
|
CLUSTER_SECRET: ${CLUSTER_SECRET} # From shell variable if set
|
||||||
|
CLUSTER_IPFSHTTP_NODEMULTIADDRESS: /dns4/ipfs0/tcp/5001
|
||||||
|
CLUSTER_CRDT_TRUSTEDPEERS: '*' # Trust all peers in Cluster
|
||||||
|
CLUSTER_RESTAPI_HTTPLISTENMULTIADDRESS: /ip4/0.0.0.0/tcp/9094 # Expose API
|
||||||
|
CLUSTER_RESTAPI_BASICAUTHCREDENTIALS: ${CLUSTER_RESTAPI_BASICAUTHCREDENTIALS}
|
||||||
|
CLUSTER_MONITORPINGINTERVAL: 2s # Speed up peer discovery
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:9094:9094"
|
||||||
|
volumes:
|
||||||
|
- ./packages/cluster0:/data/ipfs-cluster
|
||||||
|
|
||||||
|
strapi:
|
||||||
|
container_name: fp-strapi
|
||||||
|
image: elestio/strapi-development
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
environment:
|
||||||
|
ADMIN_PASSWORD: ${STRAPI_ADMIN_PASSWORD}
|
||||||
|
ADMIN_EMAIL: ${STRAPI_ADMIN_EMAIL}
|
||||||
|
BASE_URL: ${STRAPI_BASE_URL}
|
||||||
|
SMTP_HOST: 172.17.0.1
|
||||||
|
SMTP_PORT: 25
|
||||||
|
SMTP_AUTH_STRATEGY: NONE
|
||||||
|
SMTP_FROM_EMAIL: sender@email.com
|
||||||
|
DATABASE_CLIENT: postgres
|
||||||
|
DATABASE_PORT: ${DATABASE_PORT}
|
||||||
|
DATABASE_NAME: ${DATABASE_NAME}
|
||||||
|
DATABASE_USERNAME: ${DATABASE_USERNAME}
|
||||||
|
DATABASE_PASSWORD: ${DATABASE_PASSWORD}
|
||||||
|
JWT_SECRET: ${STRAPI_JWT_SECRET}
|
||||||
|
ADMIN_JWT_SECRET: ${STRAPI_ADMIN_JWT_SECRET}
|
||||||
|
APP_KEYS: ${STRAPI_APP_KEYS}
|
||||||
|
NODE_ENV: development
|
||||||
|
DATABASE_HOST: db
|
||||||
|
API_TOKEN_SALT: ${STRAPI_API_TOKEN_SALT}
|
||||||
|
TRANSFER_TOKEN_SALT: ${STRAPI_TRANSFER_TOKEN_SALT}
|
||||||
|
ports:
|
||||||
|
- "1337:1337"
|
||||||
|
volumes:
|
||||||
|
- ./packages/strapi/config:/opt/app/config
|
||||||
|
- ./packages/strapi/src:/opt/app/src
|
||||||
|
# - ./packages/strapi/package.json:/opt/package.json
|
||||||
|
# - ./packages/strapi/yarn.lock:/opt/yarn.lock
|
||||||
|
- ./packages/strapi/.env:/opt/app/.env
|
||||||
|
- ./packages/strapi/public/uploads:/opt/app/public/uploads
|
||||||
|
# - ./packages/strapi/entrypoint.sh:/opt/app/entrypoint.sh
|
||||||
|
|
||||||
|
next:
|
||||||
|
container_name: fp-next
|
||||||
|
build:
|
||||||
|
context: ./packages/next
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
environment:
|
||||||
|
REVALIDATION_TOKEN: ${NEXT_REVALIDATION_TOKEN}
|
||||||
|
NODE_ENV: production
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
volumes:
|
||||||
|
- ./packages/next/
|
||||||
|
|
||||||
|
|
||||||
|
db:
|
||||||
|
container_name: fp-db
|
||||||
|
image: postgres:latest
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${DATABASE_NAME}
|
||||||
|
POSTGRES_USER: ${DATABASE_USERNAME}
|
||||||
|
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
|
||||||
|
PGDATA: /var/lib/postgresql/data
|
||||||
|
volumes:
|
||||||
|
- ./packages/db/pgdata:/var/lib/postgresql/data
|
||||||
|
ports:
|
||||||
|
- "5433:5432"
|
@ -14,18 +14,12 @@ services:
|
|||||||
link2cid:
|
link2cid:
|
||||||
container_name: fp-link2cid
|
container_name: fp-link2cid
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
build:
|
image: insanity54/link2cid:latest
|
||||||
context: ./packages/link2cid
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
target: dev
|
|
||||||
ports:
|
ports:
|
||||||
- "3939:3939"
|
- "3939:3939"
|
||||||
environment:
|
environment:
|
||||||
API_KEY: ${LINK2CID_API_KEY}
|
API_KEY: ${LINK2CID_API_KEY}
|
||||||
IPFS_URL: "http://ipfs0:5001"
|
IPFS_URL: "http://ipfs0:5001"
|
||||||
PORT: 3939
|
|
||||||
volumes:
|
|
||||||
- ./packages/link2cid/index.js:/app/index.js
|
|
||||||
|
|
||||||
ipfs0:
|
ipfs0:
|
||||||
container_name: fp-ipfs0
|
container_name: fp-ipfs0
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
FROM node:20-slim AS base
|
|
||||||
|
|
||||||
# Install dependencies only when needed
|
|
||||||
FROM base AS deps
|
|
||||||
RUN corepack enable
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
FROM deps AS build
|
|
||||||
ARG NEXT_PUBLIC_SITE_URL=foo
|
|
||||||
ARG NEXT_PUBLIC_STRAPI_URL=foo
|
|
||||||
ARG NEXT_PUBLIC_UPPY_COMPANION_URL=foo
|
|
||||||
ENV NEXT_PUBLIC_SITE_URL ${NEXT_PUBLIC_SITE_URL}
|
|
||||||
ENV NEXT_PUBLIC_STRAPI_URL ${NEXT_PUBLIC_STRAPI_URL}
|
|
||||||
ENV NEXT_PUBLIC_UPPY_COMPANION_URL ${NEXT_PUBLIC_UPPY_COMPANION_URL}
|
|
||||||
WORKDIR /usr/src/app
|
|
||||||
COPY . .
|
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1
|
|
||||||
RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store pnpm install
|
|
||||||
RUN pnpm run -r build
|
|
||||||
RUN pnpm deploy --filter=fp-next /app
|
|
||||||
|
|
||||||
|
|
||||||
FROM deps AS dev
|
|
||||||
WORKDIR /app
|
|
||||||
COPY --from=build /usr/src/app /app
|
|
||||||
CMD ["pnpm", "run", "dev"]
|
|
||||||
|
|
||||||
FROM deps AS next
|
|
||||||
WORKDIR /app
|
|
||||||
COPY --from=build /usr/src/app /app
|
|
||||||
CMD ["pnpm", "start"]
|
|
19
package.json
19
package.json
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "futureporn",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
|
||||||
"kompose": "kompose convert --file compose.yml -c --out ./charts",
|
|
||||||
"deploy:bot": "echo @todo",
|
|
||||||
"deploy:next": "git push origin:next main",
|
|
||||||
"deploy:link2cid": "echo @todo",
|
|
||||||
"deploy:strapi": "echo @todo",
|
|
||||||
"deploy:uppy": "echo @todo",
|
|
||||||
"deploy": "echo @todo"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
|
||||||
"author": "@cj_clippy",
|
|
||||||
"license": "CC0-1.0"
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
lts/iron
|
|
@ -1,19 +0,0 @@
|
|||||||
FROM node:20-alpine AS base
|
|
||||||
ENV PNPM_HOME="/pnpm"
|
|
||||||
ENV PATH="$PNPM_HOME:$PATH"
|
|
||||||
WORKDIR /app
|
|
||||||
RUN corepack enable
|
|
||||||
|
|
||||||
FROM base AS build
|
|
||||||
COPY ./packages/bot/package.json ./
|
|
||||||
COPY ./packages/bot/src ./
|
|
||||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install
|
|
||||||
|
|
||||||
FROM build AS dev
|
|
||||||
ENTRYPOINT ["pnpm"]
|
|
||||||
CMD ["run", "dev"]
|
|
||||||
|
|
||||||
FROM build AS run
|
|
||||||
ENTRYPOINT ["pnpm"]
|
|
||||||
CMD ["start"]
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
|||||||
const { ButtonStyles, Client, ComponentTypes, ChannelTypes } = require("oceanic.js");
|
|
||||||
|
|
||||||
const client = new Client({
|
|
||||||
auth: `Bot ${process.env.DISCORD_TOKEN}`,
|
|
||||||
gateway: {
|
|
||||||
intents: ["GUILD_MESSAGES"] // If the message does not start with a mention to or somehow relate to your client, you will need the MESSAGE_CONTENT intent as well
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on("ready", () => console.log("Ready as", client.user.tag));
|
|
||||||
|
|
||||||
client.on("messageCreate", async (msg) => {
|
|
||||||
if(msg.content.includes("!component")) {
|
|
||||||
await client.rest.channels.createMessage(msg.channelID, {
|
|
||||||
content: `Here's some buttons for you, ${msg.author.mention}.`,
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
// The top level component must always be an action row.
|
|
||||||
// Full list of types: https://docs.oceanic.ws/latest/enums/Constants.ComponentTypes.html
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.MessageActionRow.html
|
|
||||||
type: ComponentTypes.ACTION_ROW,
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.TextButton.html
|
|
||||||
type: ComponentTypes.BUTTON,
|
|
||||||
style: ButtonStyles.PRIMARY, // The style of button - full list: https://docs.oceanic.ws/latest/enums/Constants.ButtonStyles.html
|
|
||||||
customID: "some-string-you-will-see-later",
|
|
||||||
label: "Click!",
|
|
||||||
disabled: false, // If the button is disabled, false by default.
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: ComponentTypes.BUTTON,
|
|
||||||
style: ButtonStyles.PRIMARY,
|
|
||||||
customID: "some-other-string",
|
|
||||||
label: "This Is Disabled",
|
|
||||||
disabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.URLButton.html
|
|
||||||
type: ComponentTypes.BUTTON,
|
|
||||||
style: ButtonStyles.LINK,
|
|
||||||
label: "Open Link",
|
|
||||||
url: "https://docs.oceanic.ws"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// The top level component must always be an action row.
|
|
||||||
// Full list of types: https://docs.oceanic.ws/latest/enums/Constants.ComponentTypes.html
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.MessageActionRow.html
|
|
||||||
type: ComponentTypes.ACTION_ROW,
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.SelectMenu.html
|
|
||||||
type: ComponentTypes.STRING_SELECT,
|
|
||||||
customID: "string-select",
|
|
||||||
disabled: false,
|
|
||||||
maxValues: 1, // The maximum number of values that can be selected (default 1)
|
|
||||||
minValues: 1, // The minimum number of values that can be selected (default 1)
|
|
||||||
options: [
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.SelectOption.html
|
|
||||||
{
|
|
||||||
default: true, // If this option is selected by default
|
|
||||||
description: "The description of the option", // Optional description
|
|
||||||
label: "Option One",
|
|
||||||
value: "value-1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Option Two",
|
|
||||||
value: "option-2"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
placeholder: "Some Placeholder Text"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// The top level component must always be an action row.
|
|
||||||
// Full list of types: https://docs.oceanic.ws/latest/enums/Constants.ComponentTypes.html
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.MessageActionRow.html
|
|
||||||
type: ComponentTypes.ACTION_ROW,
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.SelectMenu.html
|
|
||||||
type: ComponentTypes.CHANNEL_SELECT,
|
|
||||||
channelTypes: [ChannelTypes.GUILD_TEXT, ChannelTypes.GUILD_VOICE], // The types of channels that can be selected
|
|
||||||
customID: "channel-select",
|
|
||||||
disabled: false,
|
|
||||||
maxValues: 1, // The maximum number of values that can be selected (default 1)
|
|
||||||
minValues: 1, // The minimum number of values that can be selected (default 1)
|
|
||||||
placeholder: "Some Placeholder Text"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// An error handler
|
|
||||||
client.on("error", (error) => {
|
|
||||||
console.error("Something went wrong:", error);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Connect to Discord
|
|
||||||
client.connect();
|
|
@ -1,94 +0,0 @@
|
|||||||
const { Client } = require("oceanic.js");
|
|
||||||
const { readFileSync } = require("fs");
|
|
||||||
|
|
||||||
const client = new Client({
|
|
||||||
auth: `Bot ${process.env.DISCORD_TOKEN}`,
|
|
||||||
gateway: {
|
|
||||||
intents: ["GUILD_MESSAGES"] // If the message does not start with a mention to or somehow relate to your client, you will need the MESSAGE_CONTENT intent as well
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on("ready", () => console.log("Ready as", client.user.tag));
|
|
||||||
|
|
||||||
client.on("messageCreate", async (msg) => {
|
|
||||||
if(msg.content.includes("!embed")) {
|
|
||||||
console.log(`'!embeds' was seen in chat!`)
|
|
||||||
console.log(msg)
|
|
||||||
await client.rest.channels.createMessage(msg.channelID, {
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.EmbedOptions.html
|
|
||||||
// Up to 10 in one message
|
|
||||||
embeds: [
|
|
||||||
{
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.EmbedAuthorOptions.html
|
|
||||||
author: {
|
|
||||||
name: "Author Name",
|
|
||||||
// An image url, or attachment://filename.ext
|
|
||||||
iconURL: "https://i.furry.cool/DonPride.png", // Optional
|
|
||||||
url: "https://docs.oceanic.ws" // Optional
|
|
||||||
},
|
|
||||||
// Array of https://docs.oceanic.ws/latest/interfaces/Types_Channels.EmbedField.html
|
|
||||||
// Up to 25 in one message
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: "Field One",
|
|
||||||
value: "Field One Value",
|
|
||||||
inline: true // If this field should be displayed inline (default: false)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Field Two",
|
|
||||||
value: "Field Two Value",
|
|
||||||
inline: false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.EmbedFooterOptions.html
|
|
||||||
footer: {
|
|
||||||
text: "Footer Text",
|
|
||||||
// An image url, or attachment://filename.ext
|
|
||||||
iconURL: "https://i.furry.cool/DonPride.png" // Optional
|
|
||||||
},
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.EmbedImageOptions.html
|
|
||||||
image: {
|
|
||||||
// An image url, or attachment://filename.ext
|
|
||||||
url: "https://i.furry.cool/DonPride.png"
|
|
||||||
},
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.EmbedThumbnailOptions.html
|
|
||||||
thumbnail: {
|
|
||||||
// An image url, or attachment://filename.ext
|
|
||||||
url: "https://i.furry.cool/DonPride.png"
|
|
||||||
},
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.EmbedOptions.html
|
|
||||||
color: 0xFFA500, // Base-10 color (0x prefix can be used for hex codes)
|
|
||||||
description: "My Cool Embed",
|
|
||||||
timestamp: new Date().toISOString(), // The current time - ISO 8601 format
|
|
||||||
title: "My Amazing Embed",
|
|
||||||
url: "https://docs.oceanic.ws"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
} else if(msg.content.includes("!file")) {
|
|
||||||
await client.rest.channels.createMessage(msg.channelID, {
|
|
||||||
embeds: [
|
|
||||||
{
|
|
||||||
image: {
|
|
||||||
// This can also be used for author & footer images
|
|
||||||
url: "attachment://image.png"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
files: [
|
|
||||||
{
|
|
||||||
name: "image.png",
|
|
||||||
contents: readFileSync(`${__dirname}/image.png`)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// An error handler
|
|
||||||
client.on("error", (error) => {
|
|
||||||
console.error("Something went wrong:", error);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Connect to Discord
|
|
||||||
client.connect();
|
|
@ -1,261 +0,0 @@
|
|||||||
const { ButtonStyles, Client, ComponentTypes, ChannelTypes } = require("oceanic.js");
|
|
||||||
|
|
||||||
const client = new Client({
|
|
||||||
auth: `Bot ${process.env.DISCORD_TOKEN}`,
|
|
||||||
gateway: {
|
|
||||||
intents: ["GUILD_MESSAGES"] // If the message does not start with a mention to or somehow relate to your client, you will need the MESSAGE_CONTENT intent as well
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on("ready", () => console.log("Ready as", client.user.tag));
|
|
||||||
|
|
||||||
client.on("messageCreate", async (msg) => {
|
|
||||||
console.log(msg.content)
|
|
||||||
if(msg.content.includes("!test")) {
|
|
||||||
await client.rest.channels.createMessage(msg.channelID, {
|
|
||||||
content: `HGERE IZ BUTTN'z 5 u, ${msg.author.mention}.`,
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
// The top level component must always be an action row.
|
|
||||||
// Full list of types: https://docs.oceanic.ws/latest/enums/Constants.ComponentTypes.html
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.MessageActionRow.html
|
|
||||||
type: ComponentTypes.ACTION_ROW,
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.TextButton.html
|
|
||||||
type: ComponentTypes.BUTTON,
|
|
||||||
style: ButtonStyles.PRIMARY, // The style of button - full list: https://docs.oceanic.ws/latest/enums/Constants.ButtonStyles.html
|
|
||||||
customID: "some-string-you-will-see-later",
|
|
||||||
label: "Click!",
|
|
||||||
disabled: false, // If the button is disabled, false by default.
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: ComponentTypes.BUTTON,
|
|
||||||
style: ButtonStyles.PRIMARY,
|
|
||||||
customID: "some-other-string",
|
|
||||||
label: "This Is Disabled",
|
|
||||||
disabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.URLButton.html
|
|
||||||
type: ComponentTypes.BUTTON,
|
|
||||||
style: ButtonStyles.LINK,
|
|
||||||
label: "Open Link",
|
|
||||||
url: "https://docs.oceanic.ws"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// The top level component must always be an action row.
|
|
||||||
// Full list of types: https://docs.oceanic.ws/latest/enums/Constants.ComponentTypes.html
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.MessageActionRow.html
|
|
||||||
type: ComponentTypes.ACTION_ROW,
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.SelectMenu.html
|
|
||||||
type: ComponentTypes.STRING_SELECT,
|
|
||||||
customID: "string-select",
|
|
||||||
disabled: false,
|
|
||||||
maxValues: 1, // The maximum number of values that can be selected (default 1)
|
|
||||||
minValues: 1, // The minimum number of values that can be selected (default 1)
|
|
||||||
options: [
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.SelectOption.html
|
|
||||||
{
|
|
||||||
default: true, // If this option is selected by default
|
|
||||||
description: "The description of the option", // Optional description
|
|
||||||
label: "Option One",
|
|
||||||
value: "value-1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Option Two",
|
|
||||||
value: "option-2"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
placeholder: "Some Placeholder Text"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// The top level component must always be an action row.
|
|
||||||
// Full list of types: https://docs.oceanic.ws/latest/enums/Constants.ComponentTypes.html
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.MessageActionRow.html
|
|
||||||
type: ComponentTypes.ACTION_ROW,
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.SelectMenu.html
|
|
||||||
type: ComponentTypes.CHANNEL_SELECT,
|
|
||||||
channelTypes: [ChannelTypes.GUILD_TEXT, ChannelTypes.GUILD_VOICE], // The types of channels that can be selected
|
|
||||||
customID: "channel-select",
|
|
||||||
disabled: false,
|
|
||||||
maxValues: 1, // The maximum number of values that can be selected (default 1)
|
|
||||||
minValues: 1, // The minimum number of values that can be selected (default 1)
|
|
||||||
placeholder: "Some Placeholder Text"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
client.on("interactionCreate", async(interaction) => {
|
|
||||||
console.log(`interaction!@`)
|
|
||||||
console.log(interaction)
|
|
||||||
switch(interaction.type) {
|
|
||||||
// https://docs.oceanic.ws/latest/classes/CommandInteraction.CommandInteraction.html
|
|
||||||
case InteractionTypes.APPLICATION_COMMAND: {
|
|
||||||
// defer interactions as soon as possible, you have three seconds to send any initial response
|
|
||||||
// if you wait too long, the interaction may be invalidated
|
|
||||||
await interaction.defer();
|
|
||||||
// If you want the response to be ephemeral, you can provide the flag to the defer function, like so:
|
|
||||||
// await interaction.defer(MessageFlags.EPHEMERAL);
|
|
||||||
|
|
||||||
// data = https://docs.oceanic.ws/latest/interfaces/Types_Interactions.ApplicationCommandInteractionData.html
|
|
||||||
switch(interaction.data.type) {
|
|
||||||
// Chat Input commands are what you use in the chat, i.e. slash commands
|
|
||||||
case ApplicationCommandTypes.CHAT_INPUT: {
|
|
||||||
if(interaction.data.name === "greet") {
|
|
||||||
// assume we have two options, user (called user) then string (called greeting) - first is required, second is not
|
|
||||||
|
|
||||||
// Get an option named `user` with the type USER - https://docs.oceanic.ws/dev/classes/InteractionOptionsWrapper.InteractionOptionsWrapper.html#getUser
|
|
||||||
// Setting the second parameter to true will throw an error if the option is not present
|
|
||||||
const user = interaction.data.options.getUser("user", true);
|
|
||||||
const greeting = interaction.data.options.getString("greeting", false) || "Hello, ";
|
|
||||||
|
|
||||||
// since we've already deferred the interaction, we cannot use createMessage (this is an initial response)
|
|
||||||
// we can only have one initial response, so we use createFollowup
|
|
||||||
await interaction.createFollowup({
|
|
||||||
content: `${greeting} ${user.mention}!`,
|
|
||||||
allowedMentions: {
|
|
||||||
users: [user.id]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chat Input application command interactions also have a set of resolved data, which is structured as so:
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Interactions.ApplicationCommandInteractionResolvedData.html
|
|
||||||
// the options wrapper pulls values out of resolved automatically, if you use the right method
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// User application commands are shown in the context menu when right-clicking on users
|
|
||||||
// `data` will have a target (and targetID) property with the user that the command was executed on
|
|
||||||
// These don't have options
|
|
||||||
case ApplicationCommandTypes.USER: {
|
|
||||||
if(interaction.data.name === "ping") {
|
|
||||||
await interaction.createFollowup({
|
|
||||||
content: `Pong! ${interaction.data.target.mention}`,
|
|
||||||
allowedMentions: {
|
|
||||||
users: [interaction.data.target.id]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Message application commands are shown in the context menu when right-clicking on messages
|
|
||||||
// `data` will have a target (and targetID) property with the message that the command was executed on
|
|
||||||
// Same as user commands, these don't have options
|
|
||||||
case ApplicationCommandTypes.MESSAGE: {
|
|
||||||
if(interaction.data.name === "author") {
|
|
||||||
await interaction.createFollowup({
|
|
||||||
content: `${interaction.data.target.author.mention} is the author of that message!`,
|
|
||||||
allowedMentions: {
|
|
||||||
users: [interaction.data.target.author.id]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://docs.oceanic.ws/latest/classes/ComponentInteraction.ComponentInteraction.html
|
|
||||||
case InteractionTypes.MESSAGE_COMPONENT: {
|
|
||||||
// same spiel as above
|
|
||||||
await interaction.defer();
|
|
||||||
// when you create a message with components, this will correspond with what you provided as the customID there
|
|
||||||
if(interaction.data.componentType === ComponentTypes.BUTTON) {
|
|
||||||
if(interaction.data.customID === "edit-message") {
|
|
||||||
// Edits the original message. This has an initial response variant: editParent
|
|
||||||
await interaction.editOriginal({
|
|
||||||
content: `This message was edited by ${interaction.user.mention}!`,
|
|
||||||
allowedMentions: {
|
|
||||||
users: [interaction.user.id]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if(interaction.data.customID === "my-amazing-button") {
|
|
||||||
await interaction.createFollowup({
|
|
||||||
content: "You clicked an amazing button!"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if(interaction.data.componentType === ComponentTypes.SELECT_MENU) {
|
|
||||||
// The `values` property under data contains all the selected values
|
|
||||||
await interaction.createFollowup({
|
|
||||||
content: `You selected: **${interaction.data.values.join("**, **")}**`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://docs.oceanic.ws/latest/classes/AutocompleteInteraction.AutocompleteInteraction.html
|
|
||||||
case InteractionTypes.APPLICATION_COMMAND_AUTOCOMPLETE: {
|
|
||||||
// Autocomplete Interactions cannot be deferred
|
|
||||||
switch(interaction.data.name) {
|
|
||||||
case "test-autocomplete": {
|
|
||||||
// Autocomplete interactions data has a partial `options` property, which is the tree of options that are currently being filled in
|
|
||||||
// along with one at the end, which will have focused
|
|
||||||
// Setting the first parameter to true will throw an error if no focused option is present
|
|
||||||
const option = interaction.data.options.getFocused(true);
|
|
||||||
switch(option.name) {
|
|
||||||
case "test-option": {
|
|
||||||
return interaction.result([
|
|
||||||
{
|
|
||||||
name: "Choice 1",
|
|
||||||
nameLocalizations: {
|
|
||||||
"es-ES": "Opción 1"
|
|
||||||
},
|
|
||||||
value: "choice-1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Choice 2",
|
|
||||||
nameLocalizations: {
|
|
||||||
"es-ES": "Opción 2"
|
|
||||||
},
|
|
||||||
value: "choice-2"
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://docs.oceanic.ws/latest/classes/ModalSubmitInteraction.ModalSubmitInteraction.html
|
|
||||||
case InteractionTypes.MODAL_SUBMIT: {
|
|
||||||
// this will correspond with the customID you provided when creating the modal
|
|
||||||
switch(interaction.data.customID) {
|
|
||||||
case "test-modal": {
|
|
||||||
// the `components` property under data contains all the components that were submitted
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.ModalActionRow.html
|
|
||||||
console.log(interaction.data.components);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// An error handler
|
|
||||||
client.on("error", (error) => {
|
|
||||||
console.error("Something went wrong:", error);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Connect to Discord
|
|
||||||
client.connect();
|
|
@ -1,171 +0,0 @@
|
|||||||
const { Client, InteractionTypes, MessageFlags, ComponentTypes, ApplicationCommandTypes } = require("oceanic.js");
|
|
||||||
|
|
||||||
const client = new Client({
|
|
||||||
auth: `Bot ${process.env.DISCORD_TOKEN}`,
|
|
||||||
gateway: {
|
|
||||||
intents: 0 // No intents are needed if you are only using interactions
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
client.on("ready", async() => {
|
|
||||||
console.log("Ready as", client.user.tag);
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on("interactionCreate", async(interaction) => {
|
|
||||||
switch(interaction.type) {
|
|
||||||
// https://docs.oceanic.ws/latest/classes/CommandInteraction.CommandInteraction.html
|
|
||||||
case InteractionTypes.APPLICATION_COMMAND: {
|
|
||||||
// defer interactions as soon as possible, you have three seconds to send any initial response
|
|
||||||
// if you wait too long, the interaction may be invalidated
|
|
||||||
await interaction.defer();
|
|
||||||
// If you want the response to be ephemeral, you can provide the flag to the defer function, like so:
|
|
||||||
// await interaction.defer(MessageFlags.EPHEMERAL);
|
|
||||||
|
|
||||||
// data = https://docs.oceanic.ws/latest/interfaces/Types_Interactions.ApplicationCommandInteractionData.html
|
|
||||||
switch(interaction.data.type) {
|
|
||||||
// Chat Input commands are what you use in the chat, i.e. slash commands
|
|
||||||
case ApplicationCommandTypes.CHAT_INPUT: {
|
|
||||||
if(interaction.data.name === "greet") {
|
|
||||||
// assume we have two options, user (called user) then string (called greeting) - first is required, second is not
|
|
||||||
|
|
||||||
// Get an option named `user` with the type USER - https://docs.oceanic.ws/dev/classes/InteractionOptionsWrapper.InteractionOptionsWrapper.html#getUser
|
|
||||||
// Setting the second parameter to true will throw an error if the option is not present
|
|
||||||
const user = interaction.data.options.getUser("user", true);
|
|
||||||
const greeting = interaction.data.options.getString("greeting", false) || "Hello, ";
|
|
||||||
|
|
||||||
// since we've already deferred the interaction, we cannot use createMessage (this is an initial response)
|
|
||||||
// we can only have one initial response, so we use createFollowup
|
|
||||||
await interaction.createFollowup({
|
|
||||||
content: `${greeting} ${user.mention}!`,
|
|
||||||
allowedMentions: {
|
|
||||||
users: [user.id]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chat Input application command interactions also have a set of resolved data, which is structured as so:
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Interactions.ApplicationCommandInteractionResolvedData.html
|
|
||||||
// the options wrapper pulls values out of resolved automatically, if you use the right method
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// User application commands are shown in the context menu when right-clicking on users
|
|
||||||
// `data` will have a target (and targetID) property with the user that the command was executed on
|
|
||||||
// These don't have options
|
|
||||||
case ApplicationCommandTypes.USER: {
|
|
||||||
if(interaction.data.name === "ping") {
|
|
||||||
await interaction.createFollowup({
|
|
||||||
content: `Pong! ${interaction.data.target.mention}`,
|
|
||||||
allowedMentions: {
|
|
||||||
users: [interaction.data.target.id]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Message application commands are shown in the context menu when right-clicking on messages
|
|
||||||
// `data` will have a target (and targetID) property with the message that the command was executed on
|
|
||||||
// Same as user commands, these don't have options
|
|
||||||
case ApplicationCommandTypes.MESSAGE: {
|
|
||||||
if(interaction.data.name === "author") {
|
|
||||||
await interaction.createFollowup({
|
|
||||||
content: `${interaction.data.target.author.mention} is the author of that message!`,
|
|
||||||
allowedMentions: {
|
|
||||||
users: [interaction.data.target.author.id]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://docs.oceanic.ws/latest/classes/ComponentInteraction.ComponentInteraction.html
|
|
||||||
case InteractionTypes.MESSAGE_COMPONENT: {
|
|
||||||
// same spiel as above
|
|
||||||
await interaction.defer();
|
|
||||||
// when you create a message with components, this will correspond with what you provided as the customID there
|
|
||||||
if(interaction.data.componentType === ComponentTypes.BUTTON) {
|
|
||||||
if(interaction.data.customID === "edit-message") {
|
|
||||||
// Edits the original message. This has an initial response variant: editParent
|
|
||||||
await interaction.editOriginal({
|
|
||||||
content: `This message was edited by ${interaction.user.mention}!`,
|
|
||||||
allowedMentions: {
|
|
||||||
users: [interaction.user.id]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if(interaction.data.customID === "my-amazing-button") {
|
|
||||||
await interaction.createFollowup({
|
|
||||||
content: "You clicked an amazing button!"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if(interaction.data.componentType === ComponentTypes.SELECT_MENU) {
|
|
||||||
// The `values` property under data contains all the selected values
|
|
||||||
await interaction.createFollowup({
|
|
||||||
content: `You selected: **${interaction.data.values.join("**, **")}**`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://docs.oceanic.ws/latest/classes/AutocompleteInteraction.AutocompleteInteraction.html
|
|
||||||
case InteractionTypes.APPLICATION_COMMAND_AUTOCOMPLETE: {
|
|
||||||
// Autocomplete Interactions cannot be deferred
|
|
||||||
switch(interaction.data.name) {
|
|
||||||
case "test-autocomplete": {
|
|
||||||
// Autocomplete interactions data has a partial `options` property, which is the tree of options that are currently being filled in
|
|
||||||
// along with one at the end, which will have focused
|
|
||||||
// Setting the first parameter to true will throw an error if no focused option is present
|
|
||||||
const option = interaction.data.options.getFocused(true);
|
|
||||||
switch(option.name) {
|
|
||||||
case "test-option": {
|
|
||||||
return interaction.result([
|
|
||||||
{
|
|
||||||
name: "Choice 1",
|
|
||||||
nameLocalizations: {
|
|
||||||
"es-ES": "Opción 1"
|
|
||||||
},
|
|
||||||
value: "choice-1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Choice 2",
|
|
||||||
nameLocalizations: {
|
|
||||||
"es-ES": "Opción 2"
|
|
||||||
},
|
|
||||||
value: "choice-2"
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://docs.oceanic.ws/latest/classes/ModalSubmitInteraction.ModalSubmitInteraction.html
|
|
||||||
case InteractionTypes.MODAL_SUBMIT: {
|
|
||||||
// this will correspond with the customID you provided when creating the modal
|
|
||||||
switch(interaction.data.customID) {
|
|
||||||
case "test-modal": {
|
|
||||||
// the `components` property under data contains all the components that were submitted
|
|
||||||
// https://docs.oceanic.ws/latest/interfaces/Types_Channels.ModalActionRow.html
|
|
||||||
console.log(interaction.data.components);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// An error handler
|
|
||||||
client.on("error", (error) => {
|
|
||||||
console.error("Something went wrong:", error);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Connect to Discord
|
|
||||||
client.connect();
|
|
@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "fp-bot",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.ts",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
|
||||||
"dev": "node --import=tsx --watch ./src/index.ts"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
|
||||||
"author": "",
|
|
||||||
"license": "CC0-1.0",
|
|
||||||
"engines": {
|
|
||||||
"node": "v20.x.x",
|
|
||||||
"npm": ">=6.x.x"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"tsx": "^4.7.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@types/express": "^4.17.21",
|
|
||||||
"@types/node": "^20.11.0",
|
|
||||||
"discordeno": "^18.0.1",
|
|
||||||
"express": "^4.18.2",
|
|
||||||
"oceanic.js": "^1.9.0"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
import { getBotIdFromToken, Intents } from 'discordeno';
|
|
||||||
|
|
||||||
|
|
||||||
/** The bot id, derived from the bot token. */
|
|
||||||
export const BOT_ID = getBotIdFromToken(process.env.DISCORD_TOKEN as string);
|
|
||||||
export const EVENT_HANDLER_URL = `http://${process.env.EVENT_HANDLER_HOST}:${process.env.EVENT_HANDLER_PORT}`;
|
|
||||||
export const REST_URL = `http://${process.env.REST_HOST}:${process.env.REST_PORT}`;
|
|
||||||
export const GATEWAY_URL = `http://${process.env.GATEWAY_HOST}:${process.env.GATEWAY_PORT}`;
|
|
||||||
|
|
||||||
// Gateway Proxy Configurations
|
|
||||||
/** The gateway intents you would like to use. */
|
|
||||||
export const INTENTS: Intents =
|
|
||||||
// SETUP-DD-TEMP: Add the intents you want enabled here. Or Delete the intents you don't want in your bot.
|
|
||||||
Intents.DirectMessageReactions |
|
|
||||||
Intents.DirectMessageTyping |
|
|
||||||
Intents.DirectMessages |
|
|
||||||
Intents.GuildBans |
|
|
||||||
Intents.GuildEmojis |
|
|
||||||
Intents.GuildIntegrations |
|
|
||||||
Intents.GuildInvites |
|
|
||||||
Intents.GuildMembers |
|
|
||||||
Intents.GuildMessageReactions |
|
|
||||||
Intents.GuildMessageTyping |
|
|
||||||
Intents.GuildMessages |
|
|
||||||
Intents.GuildPresences |
|
|
||||||
Intents.GuildVoiceStates |
|
|
||||||
Intents.GuildWebhooks |
|
|
||||||
Intents.Guilds;
|
|
@ -1,58 +0,0 @@
|
|||||||
|
|
||||||
import { BASE_URL, createRestManager } from 'discordeno';
|
|
||||||
import express, { Request, Response } from 'express';
|
|
||||||
// import { setupAnalyticsHooks } from '../analytics.js';
|
|
||||||
import { REST_URL } from './configs.js';
|
|
||||||
|
|
||||||
const DISCORD_TOKEN = process.env.DISCORD_TOKEN as string;
|
|
||||||
const REST_AUTHORIZATION = process.env.REST_AUTHORIZATION as string;
|
|
||||||
const REST_PORT = process.env.REST_PORT as string;
|
|
||||||
|
|
||||||
const rest = createRestManager({
|
|
||||||
token: DISCORD_TOKEN,
|
|
||||||
secretKey: REST_AUTHORIZATION,
|
|
||||||
customUrl: REST_URL,
|
|
||||||
debug: console.log,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add send fetching analytics hook to rest
|
|
||||||
// setupAnalyticsHooks(rest);
|
|
||||||
|
|
||||||
// @ts-expect-error
|
|
||||||
rest.convertRestError = (errorStack, data) => {
|
|
||||||
if (!data) return { message: errorStack.message };
|
|
||||||
return { ...data, message: errorStack.message };
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
app.use(
|
|
||||||
express.urlencoded({
|
|
||||||
extended: true,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
app.use(express.json());
|
|
||||||
|
|
||||||
app.all('/*', async (req: Request, res: Response) => {
|
|
||||||
if (!REST_AUTHORIZATION || REST_AUTHORIZATION !== req.headers.authorization) {
|
|
||||||
return res.status(401).json({ error: 'Invalid authorization key.' });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await rest.runMethod(rest, req.method, `${BASE_URL}${req.url}`, req.body);
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
res.status(200).json(result);
|
|
||||||
} else {
|
|
||||||
res.status(204).json();
|
|
||||||
}
|
|
||||||
} catch (error: any) {
|
|
||||||
console.log(error);
|
|
||||||
res.status(500).json(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(REST_PORT, () => {
|
|
||||||
console.log(`REST listening at ${REST_URL}`);
|
|
||||||
});
|
|
@ -1,9 +0,0 @@
|
|||||||
# REST Proxy
|
|
||||||
|
|
||||||
This folder will contain the code for our REST proxy. This is going to become the single source that all of our bot will
|
|
||||||
use to communciate to the Discord API.
|
|
||||||
|
|
||||||
## Further Steps
|
|
||||||
|
|
||||||
- Express framework to create the listener however, you can replace it with anything you like. Express is quite a
|
|
||||||
bloated framework. Feel free to optimize to a better framework.
|
|
@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es2022",
|
|
||||||
"module": "es2022",
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"outDir": "./dist",
|
|
||||||
"rootDir": "./src",
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"importHelpers": true,
|
|
||||||
"allowUnusedLabels": false,
|
|
||||||
"noImplicitOverride": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true,
|
|
||||||
"noUncheckedIndexedAccess": true,
|
|
||||||
"strict": true,
|
|
||||||
"stripInternal": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"useUnknownInCatchVariables": false,
|
|
||||||
"allowUnreachableCode": false,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"moduleResolution": "node"
|
|
||||||
},
|
|
||||||
"include": ["./src/**/*", ".env"],
|
|
||||||
"ts-node": {
|
|
||||||
"esm": true,
|
|
||||||
"experimentalSpecifierResolution": "node",
|
|
||||||
"swc": true
|
|
||||||
}
|
|
||||||
}
|
|
144
packages/link2cid/.gitignore
vendored
144
packages/link2cid/.gitignore
vendored
@ -1,144 +0,0 @@
|
|||||||
# Created by https://www.toptal.com/developers/gitignore/api/node
|
|
||||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node
|
|
||||||
|
|
||||||
### Node ###
|
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
.pnpm-debug.log*
|
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# Snowpack dependency directory (https://snowpack.dev/)
|
|
||||||
web_modules/
|
|
||||||
|
|
||||||
# TypeScript cache
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Optional stylelint cache
|
|
||||||
.stylelintcache
|
|
||||||
|
|
||||||
# Microbundle cache
|
|
||||||
.rpt2_cache/
|
|
||||||
.rts2_cache_cjs/
|
|
||||||
.rts2_cache_es/
|
|
||||||
.rts2_cache_umd/
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variable files
|
|
||||||
.env
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
.env.local
|
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
|
||||||
.cache
|
|
||||||
.parcel-cache
|
|
||||||
|
|
||||||
# Next.js build output
|
|
||||||
.next
|
|
||||||
out
|
|
||||||
|
|
||||||
# Nuxt.js build / generate output
|
|
||||||
.nuxt
|
|
||||||
dist
|
|
||||||
|
|
||||||
# Gatsby files
|
|
||||||
.cache/
|
|
||||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
|
||||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
|
||||||
# public
|
|
||||||
|
|
||||||
# vuepress build output
|
|
||||||
.vuepress/dist
|
|
||||||
|
|
||||||
# vuepress v2.x temp and cache directory
|
|
||||||
.temp
|
|
||||||
|
|
||||||
# Docusaurus cache and generated files
|
|
||||||
.docusaurus
|
|
||||||
|
|
||||||
# Serverless directories
|
|
||||||
.serverless/
|
|
||||||
|
|
||||||
# FuseBox cache
|
|
||||||
.fusebox/
|
|
||||||
|
|
||||||
# DynamoDB Local files
|
|
||||||
.dynamodb/
|
|
||||||
|
|
||||||
# TernJS port file
|
|
||||||
.tern-port
|
|
||||||
|
|
||||||
# Stores VSCode versions used for testing VSCode extensions
|
|
||||||
.vscode-test
|
|
||||||
|
|
||||||
# yarn v2
|
|
||||||
.yarn/cache
|
|
||||||
.yarn/unplugged
|
|
||||||
.yarn/build-state.yml
|
|
||||||
.yarn/install-state.gz
|
|
||||||
.pnp.*
|
|
||||||
|
|
||||||
### Node Patch ###
|
|
||||||
# Serverless Webpack directories
|
|
||||||
.webpack/
|
|
||||||
|
|
||||||
# Optional stylelint cache
|
|
||||||
|
|
||||||
# SvelteKit build / generate output
|
|
||||||
.svelte-kit
|
|
||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/node
|
|
@ -1,20 +0,0 @@
|
|||||||
# Reference-- https://pnpm.io/docker
|
|
||||||
|
|
||||||
FROM node:20-alpine AS base
|
|
||||||
ENV PNPM_HOME="/pnpm"
|
|
||||||
ENV PATH="$PNPM_HOME:$PATH"
|
|
||||||
RUN corepack enable
|
|
||||||
WORKDIR /app
|
|
||||||
COPY ./package.json /app
|
|
||||||
EXPOSE 3939
|
|
||||||
|
|
||||||
FROM base AS dev
|
|
||||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install
|
|
||||||
CMD ["pnpm", "run", "dev"]
|
|
||||||
|
|
||||||
FROM base
|
|
||||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod
|
|
||||||
COPY ./index.js /app
|
|
||||||
ENTRYPOINT ["pnpm"]
|
|
||||||
CMD ["start"]
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
|||||||
# link2cid
|
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
I wish I could give [kubo](https://github.com/ipfs/kubo) or [IPFS cluster](https://ipfscluster.io/) a URI to a file and then they would download the file and add to ipfs, returning me a [CID](https://docs.ipfs.tech/concepts/glossary/#cid).
|
|
||||||
|
|
||||||
However, neither [kubo](https://github.com/ipfs/kubo) nor [IPFS cluster](https://ipfscluster.io/) can do this.
|
|
||||||
|
|
||||||
link2cid solves this issue with a REST API for adding a file at `url` to IPFS.
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Configure environment
|
|
||||||
|
|
||||||
Create a `.env` file. See `.env.example` for an example. Important environment variables are `API_KEY`, `PORT`, and `IPFS_URL`.
|
|
||||||
|
|
||||||
Install and run
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm install
|
|
||||||
pnpm start
|
|
||||||
```
|
|
||||||
|
|
||||||
Make a GET REST request to `/add` with `url` as a query parameter. Expect a [SSE](https://wikipedia.org/wiki/Server-sent_events) response.
|
|
||||||
|
|
||||||
## dokku
|
|
||||||
|
|
||||||
dokku builder-dockerfile:set link2cid dockerfile-path link2cid.Dockerfile
|
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
#### [HTTPIE](https://httpie.io)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
http -A bearer -a $API_KEY --stream 'http://localhost:3939/add?url=https://upload.wikimedia.org/wikipedia/commons/7/70/Example.png' Accept:text/event-stream
|
|
||||||
HTTP/1.1 200 OK
|
|
||||||
Access-Control-Allow-Origin: *
|
|
||||||
Cache-Control: no-cache
|
|
||||||
Connection: keep-alive
|
|
||||||
Content-Type: text/event-stream; charset=utf-8
|
|
||||||
Date: Thu, 21 Dec 2023 11:20:24 GMT
|
|
||||||
Transfer-Encoding: identity
|
|
||||||
X-Powered-By: Express
|
|
||||||
|
|
||||||
:ok
|
|
||||||
|
|
||||||
event: dlProgress
|
|
||||||
data: {
|
|
||||||
"percent": 100
|
|
||||||
}
|
|
||||||
|
|
||||||
event: addProgress
|
|
||||||
data: {
|
|
||||||
"percent": 100
|
|
||||||
}
|
|
||||||
|
|
||||||
event: end
|
|
||||||
data: {
|
|
||||||
"cid": "bafkreidj3jo7efguloaixz6vgivljlmowagagjtqv4yanyqgty2hrvg6km"
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Javascript
|
|
||||||
|
|
||||||
@todo this is incomplete/untested
|
|
||||||
|
|
||||||
```js
|
|
||||||
await fetch('http://localhost:3939/add?url=https://upload.wikimedia.org/wikipedia/commons/7/70/Example.png', {
|
|
||||||
headers: {
|
|
||||||
'accept': 'text/event-stream',
|
|
||||||
'authorization': `Bearer ${API_KEY}`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Dev notes
|
|
||||||
|
|
||||||
### Generate API_KEY
|
|
||||||
|
|
||||||
```js
|
|
||||||
require('crypto').randomBytes(64).toString('hex')
|
|
||||||
```
|
|
||||||
|
|
||||||
### `TypeError: data.split is not a function`
|
|
||||||
|
|
||||||
If you see this error, make sure data in SSE event payload is a string, not a number.
|
|
@ -1,284 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
require('dotenv').config();
|
|
||||||
const express = require('express');
|
|
||||||
const bodyParser = require('body-parser');
|
|
||||||
const cors = require('cors');
|
|
||||||
const fs = require('fs');
|
|
||||||
const fsp = require('fs/promises');
|
|
||||||
const { openAsBlob } = require('node:fs');
|
|
||||||
const { rm, stat } = require('fs/promises');
|
|
||||||
const os = require('os');
|
|
||||||
const path = require('path');
|
|
||||||
const SseStream = require('ssestream').default;
|
|
||||||
const { Transform, Readable } = require('node:stream');
|
|
||||||
const { pipeline } = require('node:stream/promises');
|
|
||||||
const { differenceInSeconds } = require('date-fns');
|
|
||||||
|
|
||||||
const cidRegex = /Qm[1-9A-HJ-NP-Za-km-z]{44,}|b[A-Za-z2-7]{58,}|B[A-Z2-7]{58,}|z[1-9A-HJ-NP-Za-km-z]{48,}|F[0-9A-F]{50,}/;
|
|
||||||
const app = express();
|
|
||||||
app.use(cors());
|
|
||||||
app.use(bodyParser.json());
|
|
||||||
app.use(bodyParser.urlencoded({ extended: false }));
|
|
||||||
|
|
||||||
// environment variables
|
|
||||||
const port = process.env.PORT || 3000;
|
|
||||||
const ipfsUrl = process.env.IPFS_URL || 'http://localhost:5001';
|
|
||||||
if (!process.env.API_KEY) throw new Error('API_KEY was missing in env');
|
|
||||||
|
|
||||||
|
|
||||||
// greetz https://stackoverflow.com/a/51302466/1004931
|
|
||||||
async function downloadFile(url, filePath, sse) {
|
|
||||||
console.log(`downloading url=${url} to filePath=${filePath}`);
|
|
||||||
const res = await fetch(url);
|
|
||||||
const fileSize = res.headers.get('content-length');
|
|
||||||
const fileStream = fs.createWriteStream(filePath, { flags: 'wx' });
|
|
||||||
|
|
||||||
let downloadedBytes = 0;
|
|
||||||
const logInterval = 1 * 1024 * 1024; // 1MB in bytes
|
|
||||||
|
|
||||||
const progressLogger = new Transform({
|
|
||||||
transform(chunk, encoding, callback) {
|
|
||||||
downloadedBytes += chunk.length;
|
|
||||||
|
|
||||||
if (downloadedBytes % logInterval < chunk.length) {
|
|
||||||
console.log(`${downloadedBytes / (1024 * 1024)} MB processed`);
|
|
||||||
const progress = (downloadedBytes / fileSize) * 100;
|
|
||||||
console.log(`Download Progress: ${progress.toFixed(2)}%`);
|
|
||||||
sse.write({
|
|
||||||
event: 'dlProgress',
|
|
||||||
data: `${Math.floor(progress)}`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.push(chunk);
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
await pipeline(
|
|
||||||
res.body,
|
|
||||||
progressLogger,
|
|
||||||
fileStream
|
|
||||||
)
|
|
||||||
|
|
||||||
console.log('download finished');
|
|
||||||
|
|
||||||
// verify the file
|
|
||||||
// If we don't, we get text error messages sent to kubo which gets added and it's a bad time.
|
|
||||||
console.log(`fileSize=${fileSize}. downloadedBytes=${downloadedBytes}`);
|
|
||||||
if (fileSize != downloadedBytes) throw new Error('downloadedBytes did not match fileSize');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function healthRes(_, res) {
|
|
||||||
const version = await getPackageVersion();
|
|
||||||
res.json({ error: false, message: `*link2cid ${version} pisses on the floor*` });
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getPackageVersion() {
|
|
||||||
const packageJsonFile = await fsp.readFile(path.join(__dirname, 'package.json'), { encoding: 'utf-8' });
|
|
||||||
const json = JSON.parse(packageJsonFile);
|
|
||||||
return json.version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* We use this to upload files and get progress notifications
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
async function streamingPostFetch(
|
|
||||||
url,
|
|
||||||
formData,
|
|
||||||
basename,
|
|
||||||
sse,
|
|
||||||
filesize
|
|
||||||
) {
|
|
||||||
console.log(`streamingPostFetch with url=${url}, formData=${formData.get('file')}, basename=${basename}, sse=${sse}, filesize=${filesize}`);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new Error(`HTTP error! Status-- ${res.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const reader = res.body?.getReader();
|
|
||||||
if (!reader) {
|
|
||||||
throw new Error('Failed to get reader from response body');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const { done, value } = await reader.read();
|
|
||||||
|
|
||||||
const chunk = new TextDecoder().decode(value);
|
|
||||||
const lines = chunk.split('\n');
|
|
||||||
for (const line of lines) {
|
|
||||||
const trimmedLine = line.trim()
|
|
||||||
if (!!trimmedLine) {
|
|
||||||
console.log(trimmedLine);
|
|
||||||
const json = JSON.parse(trimmedLine);
|
|
||||||
// console.log(`comparing json.Name=${json.Name} with basename=${basename}`);
|
|
||||||
sse.write({
|
|
||||||
event: 'addProgress',
|
|
||||||
data: `${Math.floor(json?.Size / filesize * 100)}`
|
|
||||||
})
|
|
||||||
if (json.Name === basename && json.Hash && json.Size) {
|
|
||||||
// this is the last chunk
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (done) {
|
|
||||||
throw new Error('Response reader finished before receiving a CID which indicates a failiure.');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('An error occurred:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function authenticate(req, res, next) {
|
|
||||||
const apiKey = req.query?.token;
|
|
||||||
if (!apiKey) {
|
|
||||||
const msg = `authorization 'token' was missing from query`;
|
|
||||||
console.error(msg);
|
|
||||||
return res.status(401).json({ error: true, message: msg });
|
|
||||||
}
|
|
||||||
if (apiKey !== process.env.API_KEY) {
|
|
||||||
const msg = 'INCORRECT API_KEY (wrong token)';
|
|
||||||
console.error(msg);
|
|
||||||
return res.status(403).json({ error: true, message: msg });
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function getFormStuff(filePath) {
|
|
||||||
const url = `${ipfsUrl}/api/v0/add?progress=false&cid-version=1&pin=true`;
|
|
||||||
const blob = await openAsBlob(filePath);
|
|
||||||
const basename = path.basename(filePath);
|
|
||||||
const filesize = (await stat(filePath)).size;
|
|
||||||
const formData = new FormData();
|
|
||||||
return {
|
|
||||||
url,
|
|
||||||
blob,
|
|
||||||
basename,
|
|
||||||
filesize,
|
|
||||||
formData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a file from URL to IPFS.
|
|
||||||
*
|
|
||||||
* uses SSE to send progress reports as the script
|
|
||||||
* downloads the file to disk and then does `ipfs add`
|
|
||||||
* finally returning a CID
|
|
||||||
*
|
|
||||||
* events:
|
|
||||||
* - heartbeat
|
|
||||||
* - dlProgress
|
|
||||||
* - addProgress
|
|
||||||
* - end
|
|
||||||
*/
|
|
||||||
async function addHandler(req, res) {
|
|
||||||
console.log(`/add`)
|
|
||||||
let url;
|
|
||||||
const urlStr = req.query.url;
|
|
||||||
if (!urlStr) return res.status(400).json({
|
|
||||||
error: 'url was missing from query'
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
url = new URL(urlStr);
|
|
||||||
} catch (e) {
|
|
||||||
return res.status(400).json({
|
|
||||||
error: e?.message
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const timestamp = new Date().valueOf();
|
|
||||||
const fileName = `${timestamp}-${url.pathname.split('/').at(-1)}`;
|
|
||||||
const destinationFilePath = path.join(os.tmpdir(), fileName);
|
|
||||||
|
|
||||||
console.log(`fileName=${fileName}, destinationFilePath=${destinationFilePath}`);
|
|
||||||
|
|
||||||
const sse = new SseStream(req);
|
|
||||||
sse.pipe(res);
|
|
||||||
|
|
||||||
let hbStartTime = new Date();
|
|
||||||
const heartbeat = setInterval(() => {
|
|
||||||
sse.write({
|
|
||||||
event: 'heartbeat',
|
|
||||||
data: `${differenceInSeconds(new Date(), hbStartTime)}`
|
|
||||||
});
|
|
||||||
}, 15000);
|
|
||||||
|
|
||||||
res.on('close', () => {
|
|
||||||
console.log('Connection closed.');
|
|
||||||
clearTimeout(heartbeat);
|
|
||||||
sse.unpipe(res);
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`Downloading '${urlStr}' to destinationFilePath=${destinationFilePath}`);
|
|
||||||
await downloadFile(urlStr, destinationFilePath, sse);
|
|
||||||
|
|
||||||
|
|
||||||
sse.write({
|
|
||||||
event: 'dlProgress',
|
|
||||||
data: '100'
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log(`'ipfs add' the file ${destinationFilePath}`);
|
|
||||||
const { url: kuboUrl, blob, basename, filesize, formData } = await getFormStuff(destinationFilePath);
|
|
||||||
|
|
||||||
formData.append('file', blob, basename);
|
|
||||||
|
|
||||||
let cid;
|
|
||||||
try {
|
|
||||||
const output = await streamingPostFetch(kuboUrl, formData, basename, sse, filesize);
|
|
||||||
console.log(`streamingPostFetch output as follows.`);
|
|
||||||
console.log(output);
|
|
||||||
if (!output?.Hash) throw new Error('No CID was received from remote IPFS node.');
|
|
||||||
if (!output?.Size) throw new Error(`'ipfs add' was missing Size in its output.`);
|
|
||||||
// if (output.Size !== filesize) throw new Error(`input and output sizes did not match. Expected output.Size ${output.Size} to equal ${filesize}.`);
|
|
||||||
// console.log(`filesize=${filesize} output.Size=${output.Size}`);
|
|
||||||
cid = output.Hash;
|
|
||||||
|
|
||||||
console.log('cleanup');
|
|
||||||
await rm(destinationFilePath);
|
|
||||||
|
|
||||||
console.log('end SSE');
|
|
||||||
clearTimeout(heartbeat);
|
|
||||||
} catch (e) {
|
|
||||||
return sse.end({
|
|
||||||
event: 'end',
|
|
||||||
error: true,
|
|
||||||
message: e
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return sse.end({
|
|
||||||
event: 'end',
|
|
||||||
data: cid
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
app.get('/', authenticate, healthRes);
|
|
||||||
app.get('/health', healthRes);
|
|
||||||
app.get('/add', authenticate, addHandler);
|
|
||||||
|
|
||||||
app.listen(port, async () => {
|
|
||||||
const version = await getPackageVersion();
|
|
||||||
console.log(`link2cid ${version} listening on port ${port}`);
|
|
||||||
});
|
|
@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "link2cid",
|
|
||||||
"version": "3.2.0",
|
|
||||||
"description": "REST API for adding files to IPFS",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
|
||||||
"dev": "pnpm nodemon ./index.js",
|
|
||||||
"start": "node index.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.0.0"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
|
||||||
"author": "",
|
|
||||||
"license": "Unlicense",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/express": "^4.17.21",
|
|
||||||
"body-parser": "^1.20.2",
|
|
||||||
"cors": "^2.8.5",
|
|
||||||
"date-fns": "^3.0.5",
|
|
||||||
"dotenv": "^16.3.1",
|
|
||||||
"express": "^4.18.2",
|
|
||||||
"jsonwebtoken": "^9.0.2",
|
|
||||||
"ssestream": "^1.1.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"nodemon": "^3.0.3"
|
|
||||||
}
|
|
||||||
}
|
|
691
packages/link2cid/pnpm-lock.yaml
generated
691
packages/link2cid/pnpm-lock.yaml
generated
@ -1,691 +0,0 @@
|
|||||||
lockfileVersion: '6.0'
|
|
||||||
|
|
||||||
settings:
|
|
||||||
autoInstallPeers: true
|
|
||||||
excludeLinksFromLockfile: false
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
'@types/express':
|
|
||||||
specifier: ^4.17.21
|
|
||||||
version: 4.17.21
|
|
||||||
body-parser:
|
|
||||||
specifier: ^1.20.2
|
|
||||||
version: 1.20.2
|
|
||||||
cors:
|
|
||||||
specifier: ^2.8.5
|
|
||||||
version: 2.8.5
|
|
||||||
date-fns:
|
|
||||||
specifier: ^3.0.5
|
|
||||||
version: 3.0.5
|
|
||||||
dotenv:
|
|
||||||
specifier: ^16.3.1
|
|
||||||
version: 16.3.1
|
|
||||||
express:
|
|
||||||
specifier: ^4.18.2
|
|
||||||
version: 4.18.2
|
|
||||||
jsonwebtoken:
|
|
||||||
specifier: ^9.0.2
|
|
||||||
version: 9.0.2
|
|
||||||
ssestream:
|
|
||||||
specifier: ^1.1.0
|
|
||||||
version: 1.1.0
|
|
||||||
|
|
||||||
packages:
|
|
||||||
|
|
||||||
/@types/body-parser@1.19.5:
|
|
||||||
resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
|
|
||||||
dependencies:
|
|
||||||
'@types/connect': 3.4.38
|
|
||||||
'@types/node': 20.10.5
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/connect@3.4.38:
|
|
||||||
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
|
|
||||||
dependencies:
|
|
||||||
'@types/node': 20.10.5
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/express-serve-static-core@4.17.41:
|
|
||||||
resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==}
|
|
||||||
dependencies:
|
|
||||||
'@types/node': 20.10.5
|
|
||||||
'@types/qs': 6.9.10
|
|
||||||
'@types/range-parser': 1.2.7
|
|
||||||
'@types/send': 0.17.4
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/express@4.17.21:
|
|
||||||
resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==}
|
|
||||||
dependencies:
|
|
||||||
'@types/body-parser': 1.19.5
|
|
||||||
'@types/express-serve-static-core': 4.17.41
|
|
||||||
'@types/qs': 6.9.10
|
|
||||||
'@types/serve-static': 1.15.5
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/http-errors@2.0.4:
|
|
||||||
resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/mime@1.3.5:
|
|
||||||
resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/mime@3.0.4:
|
|
||||||
resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/node@20.10.5:
|
|
||||||
resolution: {integrity: sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==}
|
|
||||||
dependencies:
|
|
||||||
undici-types: 5.26.5
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/qs@6.9.10:
|
|
||||||
resolution: {integrity: sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/range-parser@1.2.7:
|
|
||||||
resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/send@0.17.4:
|
|
||||||
resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==}
|
|
||||||
dependencies:
|
|
||||||
'@types/mime': 1.3.5
|
|
||||||
'@types/node': 20.10.5
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/serve-static@1.15.5:
|
|
||||||
resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==}
|
|
||||||
dependencies:
|
|
||||||
'@types/http-errors': 2.0.4
|
|
||||||
'@types/mime': 3.0.4
|
|
||||||
'@types/node': 20.10.5
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/accepts@1.3.8:
|
|
||||||
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dependencies:
|
|
||||||
mime-types: 2.1.35
|
|
||||||
negotiator: 0.6.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/array-flatten@1.1.1:
|
|
||||||
resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/body-parser@1.20.1:
|
|
||||||
resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==}
|
|
||||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
|
||||||
dependencies:
|
|
||||||
bytes: 3.1.2
|
|
||||||
content-type: 1.0.5
|
|
||||||
debug: 2.6.9
|
|
||||||
depd: 2.0.0
|
|
||||||
destroy: 1.2.0
|
|
||||||
http-errors: 2.0.0
|
|
||||||
iconv-lite: 0.4.24
|
|
||||||
on-finished: 2.4.1
|
|
||||||
qs: 6.11.0
|
|
||||||
raw-body: 2.5.1
|
|
||||||
type-is: 1.6.18
|
|
||||||
unpipe: 1.0.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/body-parser@1.20.2:
|
|
||||||
resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==}
|
|
||||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
|
||||||
dependencies:
|
|
||||||
bytes: 3.1.2
|
|
||||||
content-type: 1.0.5
|
|
||||||
debug: 2.6.9
|
|
||||||
depd: 2.0.0
|
|
||||||
destroy: 1.2.0
|
|
||||||
http-errors: 2.0.0
|
|
||||||
iconv-lite: 0.4.24
|
|
||||||
on-finished: 2.4.1
|
|
||||||
qs: 6.11.0
|
|
||||||
raw-body: 2.5.2
|
|
||||||
type-is: 1.6.18
|
|
||||||
unpipe: 1.0.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/buffer-equal-constant-time@1.0.1:
|
|
||||||
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/bytes@3.1.2:
|
|
||||||
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/call-bind@1.0.5:
|
|
||||||
resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==}
|
|
||||||
dependencies:
|
|
||||||
function-bind: 1.1.2
|
|
||||||
get-intrinsic: 1.2.2
|
|
||||||
set-function-length: 1.1.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/content-disposition@0.5.4:
|
|
||||||
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dependencies:
|
|
||||||
safe-buffer: 5.2.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/content-type@1.0.5:
|
|
||||||
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/cookie-signature@1.0.6:
|
|
||||||
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/cookie@0.5.0:
|
|
||||||
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/cors@2.8.5:
|
|
||||||
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
|
|
||||||
engines: {node: '>= 0.10'}
|
|
||||||
dependencies:
|
|
||||||
object-assign: 4.1.1
|
|
||||||
vary: 1.1.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/date-fns@3.0.5:
|
|
||||||
resolution: {integrity: sha512-Q4Tq5c5s/Zl/zbgdWf6pejn9ru7UwdIlLfvEEg1hVsQNQ7LKt76qIduagIT9OPK7+JCv1mAKherdU6bOqGYDnw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/debug@2.6.9:
|
|
||||||
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
|
||||||
peerDependencies:
|
|
||||||
supports-color: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
supports-color:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
ms: 2.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/define-data-property@1.1.1:
|
|
||||||
resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==}
|
|
||||||
engines: {node: '>= 0.4'}
|
|
||||||
dependencies:
|
|
||||||
get-intrinsic: 1.2.2
|
|
||||||
gopd: 1.0.1
|
|
||||||
has-property-descriptors: 1.0.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/depd@2.0.0:
|
|
||||||
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/destroy@1.2.0:
|
|
||||||
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
|
|
||||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/dotenv@16.3.1:
|
|
||||||
resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ecdsa-sig-formatter@1.0.11:
|
|
||||||
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
|
|
||||||
dependencies:
|
|
||||||
safe-buffer: 5.2.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ee-first@1.1.1:
|
|
||||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/encodeurl@1.0.2:
|
|
||||||
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/escape-html@1.0.3:
|
|
||||||
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/etag@1.8.1:
|
|
||||||
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/express@4.18.2:
|
|
||||||
resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==}
|
|
||||||
engines: {node: '>= 0.10.0'}
|
|
||||||
dependencies:
|
|
||||||
accepts: 1.3.8
|
|
||||||
array-flatten: 1.1.1
|
|
||||||
body-parser: 1.20.1
|
|
||||||
content-disposition: 0.5.4
|
|
||||||
content-type: 1.0.5
|
|
||||||
cookie: 0.5.0
|
|
||||||
cookie-signature: 1.0.6
|
|
||||||
debug: 2.6.9
|
|
||||||
depd: 2.0.0
|
|
||||||
encodeurl: 1.0.2
|
|
||||||
escape-html: 1.0.3
|
|
||||||
etag: 1.8.1
|
|
||||||
finalhandler: 1.2.0
|
|
||||||
fresh: 0.5.2
|
|
||||||
http-errors: 2.0.0
|
|
||||||
merge-descriptors: 1.0.1
|
|
||||||
methods: 1.1.2
|
|
||||||
on-finished: 2.4.1
|
|
||||||
parseurl: 1.3.3
|
|
||||||
path-to-regexp: 0.1.7
|
|
||||||
proxy-addr: 2.0.7
|
|
||||||
qs: 6.11.0
|
|
||||||
range-parser: 1.2.1
|
|
||||||
safe-buffer: 5.2.1
|
|
||||||
send: 0.18.0
|
|
||||||
serve-static: 1.15.0
|
|
||||||
setprototypeof: 1.2.0
|
|
||||||
statuses: 2.0.1
|
|
||||||
type-is: 1.6.18
|
|
||||||
utils-merge: 1.0.1
|
|
||||||
vary: 1.1.2
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/finalhandler@1.2.0:
|
|
||||||
resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dependencies:
|
|
||||||
debug: 2.6.9
|
|
||||||
encodeurl: 1.0.2
|
|
||||||
escape-html: 1.0.3
|
|
||||||
on-finished: 2.4.1
|
|
||||||
parseurl: 1.3.3
|
|
||||||
statuses: 2.0.1
|
|
||||||
unpipe: 1.0.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/forwarded@0.2.0:
|
|
||||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/fresh@0.5.2:
|
|
||||||
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/function-bind@1.1.2:
|
|
||||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/get-intrinsic@1.2.2:
|
|
||||||
resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==}
|
|
||||||
dependencies:
|
|
||||||
function-bind: 1.1.2
|
|
||||||
has-proto: 1.0.1
|
|
||||||
has-symbols: 1.0.3
|
|
||||||
hasown: 2.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/gopd@1.0.1:
|
|
||||||
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
|
|
||||||
dependencies:
|
|
||||||
get-intrinsic: 1.2.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/has-property-descriptors@1.0.1:
|
|
||||||
resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==}
|
|
||||||
dependencies:
|
|
||||||
get-intrinsic: 1.2.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/has-proto@1.0.1:
|
|
||||||
resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
|
|
||||||
engines: {node: '>= 0.4'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/has-symbols@1.0.3:
|
|
||||||
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
|
|
||||||
engines: {node: '>= 0.4'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/hasown@2.0.0:
|
|
||||||
resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==}
|
|
||||||
engines: {node: '>= 0.4'}
|
|
||||||
dependencies:
|
|
||||||
function-bind: 1.1.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/http-errors@2.0.0:
|
|
||||||
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dependencies:
|
|
||||||
depd: 2.0.0
|
|
||||||
inherits: 2.0.4
|
|
||||||
setprototypeof: 1.2.0
|
|
||||||
statuses: 2.0.1
|
|
||||||
toidentifier: 1.0.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/iconv-lite@0.4.24:
|
|
||||||
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
|
||||||
engines: {node: '>=0.10.0'}
|
|
||||||
dependencies:
|
|
||||||
safer-buffer: 2.1.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/inherits@2.0.4:
|
|
||||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ipaddr.js@1.9.1:
|
|
||||||
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
|
|
||||||
engines: {node: '>= 0.10'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/jsonwebtoken@9.0.2:
|
|
||||||
resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==}
|
|
||||||
engines: {node: '>=12', npm: '>=6'}
|
|
||||||
dependencies:
|
|
||||||
jws: 3.2.2
|
|
||||||
lodash.includes: 4.3.0
|
|
||||||
lodash.isboolean: 3.0.3
|
|
||||||
lodash.isinteger: 4.0.4
|
|
||||||
lodash.isnumber: 3.0.3
|
|
||||||
lodash.isplainobject: 4.0.6
|
|
||||||
lodash.isstring: 4.0.1
|
|
||||||
lodash.once: 4.1.1
|
|
||||||
ms: 2.1.3
|
|
||||||
semver: 7.5.4
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/jwa@1.4.1:
|
|
||||||
resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==}
|
|
||||||
dependencies:
|
|
||||||
buffer-equal-constant-time: 1.0.1
|
|
||||||
ecdsa-sig-formatter: 1.0.11
|
|
||||||
safe-buffer: 5.2.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/jws@3.2.2:
|
|
||||||
resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==}
|
|
||||||
dependencies:
|
|
||||||
jwa: 1.4.1
|
|
||||||
safe-buffer: 5.2.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/lodash.includes@4.3.0:
|
|
||||||
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/lodash.isboolean@3.0.3:
|
|
||||||
resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/lodash.isinteger@4.0.4:
|
|
||||||
resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/lodash.isnumber@3.0.3:
|
|
||||||
resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/lodash.isplainobject@4.0.6:
|
|
||||||
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/lodash.isstring@4.0.1:
|
|
||||||
resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/lodash.once@4.1.1:
|
|
||||||
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/lru-cache@6.0.0:
|
|
||||||
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dependencies:
|
|
||||||
yallist: 4.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/media-typer@0.3.0:
|
|
||||||
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/merge-descriptors@1.0.1:
|
|
||||||
resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/methods@1.1.2:
|
|
||||||
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/mime-db@1.52.0:
|
|
||||||
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/mime-types@2.1.35:
|
|
||||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dependencies:
|
|
||||||
mime-db: 1.52.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/mime@1.6.0:
|
|
||||||
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
|
|
||||||
engines: {node: '>=4'}
|
|
||||||
hasBin: true
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ms@2.0.0:
|
|
||||||
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ms@2.1.3:
|
|
||||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/negotiator@0.6.3:
|
|
||||||
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/object-assign@4.1.1:
|
|
||||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
|
||||||
engines: {node: '>=0.10.0'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/object-inspect@1.13.1:
|
|
||||||
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/on-finished@2.4.1:
|
|
||||||
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dependencies:
|
|
||||||
ee-first: 1.1.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/parseurl@1.3.3:
|
|
||||||
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/path-to-regexp@0.1.7:
|
|
||||||
resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/proxy-addr@2.0.7:
|
|
||||||
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
|
||||||
engines: {node: '>= 0.10'}
|
|
||||||
dependencies:
|
|
||||||
forwarded: 0.2.0
|
|
||||||
ipaddr.js: 1.9.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/qs@6.11.0:
|
|
||||||
resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
|
|
||||||
engines: {node: '>=0.6'}
|
|
||||||
dependencies:
|
|
||||||
side-channel: 1.0.4
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/range-parser@1.2.1:
|
|
||||||
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/raw-body@2.5.1:
|
|
||||||
resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dependencies:
|
|
||||||
bytes: 3.1.2
|
|
||||||
http-errors: 2.0.0
|
|
||||||
iconv-lite: 0.4.24
|
|
||||||
unpipe: 1.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/raw-body@2.5.2:
|
|
||||||
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dependencies:
|
|
||||||
bytes: 3.1.2
|
|
||||||
http-errors: 2.0.0
|
|
||||||
iconv-lite: 0.4.24
|
|
||||||
unpipe: 1.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/safe-buffer@5.2.1:
|
|
||||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/safer-buffer@2.1.2:
|
|
||||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/semver@7.5.4:
|
|
||||||
resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
lru-cache: 6.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/send@0.18.0:
|
|
||||||
resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
|
|
||||||
engines: {node: '>= 0.8.0'}
|
|
||||||
dependencies:
|
|
||||||
debug: 2.6.9
|
|
||||||
depd: 2.0.0
|
|
||||||
destroy: 1.2.0
|
|
||||||
encodeurl: 1.0.2
|
|
||||||
escape-html: 1.0.3
|
|
||||||
etag: 1.8.1
|
|
||||||
fresh: 0.5.2
|
|
||||||
http-errors: 2.0.0
|
|
||||||
mime: 1.6.0
|
|
||||||
ms: 2.1.3
|
|
||||||
on-finished: 2.4.1
|
|
||||||
range-parser: 1.2.1
|
|
||||||
statuses: 2.0.1
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/serve-static@1.15.0:
|
|
||||||
resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==}
|
|
||||||
engines: {node: '>= 0.8.0'}
|
|
||||||
dependencies:
|
|
||||||
encodeurl: 1.0.2
|
|
||||||
escape-html: 1.0.3
|
|
||||||
parseurl: 1.3.3
|
|
||||||
send: 0.18.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/set-function-length@1.1.1:
|
|
||||||
resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==}
|
|
||||||
engines: {node: '>= 0.4'}
|
|
||||||
dependencies:
|
|
||||||
define-data-property: 1.1.1
|
|
||||||
get-intrinsic: 1.2.2
|
|
||||||
gopd: 1.0.1
|
|
||||||
has-property-descriptors: 1.0.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/setprototypeof@1.2.0:
|
|
||||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/side-channel@1.0.4:
|
|
||||||
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
|
|
||||||
dependencies:
|
|
||||||
call-bind: 1.0.5
|
|
||||||
get-intrinsic: 1.2.2
|
|
||||||
object-inspect: 1.13.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ssestream@1.1.0:
|
|
||||||
resolution: {integrity: sha512-UOS3JTuGqGEOH89mfHFwVOJNH2+JX9ebIWuw6WBQXpkVOxbdoY3RMliSHzshL4XVYJJrcul5NkuvDFCzgYu1Lw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/statuses@2.0.1:
|
|
||||||
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/toidentifier@1.0.1:
|
|
||||||
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
|
||||||
engines: {node: '>=0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/type-is@1.6.18:
|
|
||||||
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dependencies:
|
|
||||||
media-typer: 0.3.0
|
|
||||||
mime-types: 2.1.35
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/undici-types@5.26.5:
|
|
||||||
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/unpipe@1.0.0:
|
|
||||||
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/utils-merge@1.0.1:
|
|
||||||
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
|
|
||||||
engines: {node: '>= 0.4.0'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/vary@1.1.2:
|
|
||||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/yallist@4.0.0:
|
|
||||||
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
|
||||||
dev: false
|
|
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
import Link from "next/link"
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||||
|
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons"
|
||||||
|
|
||||||
|
export default async function Page() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="content">
|
||||||
|
<div className="box">
|
||||||
|
|
||||||
|
<div className="block">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h1>The Story of Futureporn</h1>
|
||||||
|
|
||||||
|
<p>2020 was a busy time for me. I started a small business, attended lots of support group meetings, and rode my bicycle more than ever before. I often found myself away from home during times when Melody was streaming on Chaturbate.</p>
|
||||||
|
|
||||||
|
<p>You probably know that unlike other video streaming platforms, Chaturbate doesn’t store any VODs. When I missed a stream, I felt sad. I felt like I had missed out and there’s no way I’d ever find out what happened.</p>
|
||||||
|
|
||||||
|
<p>I’m pretty handy with computer software. Creating programs and websites has been my biggest passion for my entire life. In order to never miss a ProjektMelody livestream again, I resolved to create some software that would automatically record Melody’s Chaturbate streams.</p>
|
||||||
|
|
||||||
|
<p>I put the project on hold for a few months, because I didn’t think I could make a website that could handle the traffic that the Science Team would generate.</p>
|
||||||
|
|
||||||
|
<p>I couldn’t shake the idea, though. I wanted Futureporn to exist no matter what!</p>
|
||||||
|
|
||||||
|
<p>I’ve been working on this project off and on for about a year and a half. It’s gone through several iterations, and each iteration has taught me something new. Right now, the website is usable for finding and downloading ProjektMelody Chaturbate VODs. Every VOD has a link to Melody’s tweet which originally announced the stream, and a title/description derived from said tweet. I have archived all of her known Chaturbate streams.</p>
|
||||||
|
|
||||||
|
<p>The project has evolved over time. Originally, I wanted to have a place to go when I missed one of Melody’s livestreams. Now, the project is becoming a sort of a time capsule. We’ve all seen how Melody has been de-platformed a half dozen times, and I’ve taken this to heart. Platforms are a problem for data preservation! This is one of the reasons for why I chose to use the Inter-Planetary File System (<Link target="_blank" href="https://ipfs.io/">IPFS<FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></Link>.)</p>
|
||||||
|
|
||||||
|
<p>IPFS can end 404s through “pinning,” a way of mirroring a file across several different computers. It’s a way for computers to work together to serve content instead of working independently, thus gaining redundancy and performance benefits. I see a future where pinning files on IPFS becomes as easy as pinning a photo on Pinterest. Fans of ProjektMelody can pin the VODs on Futureporn, increasing that VOD’s replication and servability to future viewers.</p>
|
||||||
|
|
||||||
|
<p>But wait, there’s more! I have been thinking about a bunch of other stuff that could be done with past VODs. I think the most exciting thing would be to use computer vision to parse Melody’s vibrator activity from the video, and export to a data file. This data file could be used to send good vibes to a viewer’s vibrator in-sync with VOD playback. Feel what Melody feels! Very exciting, very sexy! This is a long-term goal for Futureporn.</p>
|
||||||
|
|
||||||
|
<p>I have several goals for Futureporn, as listed on the <Link href="/goals">Goals page</Link>. A bunch of them have to do with increasing video playback performance, user interface design, but there’s a few that are pretty eccentric… Serving ProjektMelody VODs to Mars, for example!</p>
|
||||||
|
|
||||||
|
<p>I hope this site is useful to all the Science Team!</p>
|
||||||
|
|
||||||
|
<article className="mt-5 message is-success">
|
||||||
|
<div className="message-body">
|
||||||
|
<p>Futureporn needs financial support to continue improving. If you enjoy this website, please consider <Link target="_blank" href="https://patreon.com/CJ_Clippy">becoming a patron<FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></Link>.</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
35
packages/next/app/blog/page.tsx
Normal file
35
packages/next/app/blog/page.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
import { siteUrl } from '@/lib/constants';
|
||||||
|
import { IBlogPost } from '@/lib/blog';
|
||||||
|
|
||||||
|
|
||||||
|
export default async function PostsPage() {
|
||||||
|
const res = await fetch(`${siteUrl}/api/blogs`);
|
||||||
|
const posts: IBlogPost[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
slug: '2021-10-29-the-story-of-futureporn',
|
||||||
|
title: 'The Story Of Futureporn'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container mb-5">
|
||||||
|
<div className="content mb-5">
|
||||||
|
|
||||||
|
<h1>All Blog Posts</h1>
|
||||||
|
<hr style={{ width: '220px' }} />
|
||||||
|
|
||||||
|
<div style={{ paddingTop: '40px' }}>
|
||||||
|
{posts.map((post: IBlogPost) => (
|
||||||
|
<article key={post.slug}>
|
||||||
|
<Link href={`/blog/${post.slug}`}>
|
||||||
|
<h2>> {post.title}</h2>
|
||||||
|
</Link>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -6,19 +6,18 @@ export interface IArchiveProgressProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default async function ArchiveProgress ({ vtuber }: IArchiveProgressProps) {
|
export default async function ArchiveProgress ({ vtuber }: IArchiveProgressProps) {
|
||||||
// const streams = await getAllStreamsForVtuber(vtuber.id);
|
const streams = await getAllStreamsForVtuber(vtuber.id);
|
||||||
// const goodStreams = await getAllStreamsForVtuber(vtuber.id, ['good']);
|
const goodStreams = await getAllStreamsForVtuber(vtuber.id, ['good']);
|
||||||
// const issueStreams = await getAllStreamsForVtuber(vtuber.id, ['issue']);
|
const issueStreams = await getAllStreamsForVtuber(vtuber.id, ['issue']);
|
||||||
// const totalStreams = streams.length;
|
const totalStreams = streams.length;
|
||||||
// const eligibleStreams = issueStreams.length+goodStreams.length;
|
const eligibleStreams = issueStreams.length+goodStreams.length;
|
||||||
|
|
||||||
// // Check if totalStreams is not zero before calculating completedPercentage
|
// Check if totalStreams is not zero before calculating completedPercentage
|
||||||
// const completedPercentage = (totalStreams !== 0) ? Math.round(eligibleStreams / totalStreams * 100) : 0;
|
const completedPercentage = (totalStreams !== 0) ? Math.round(eligibleStreams / totalStreams * 100) : 0;
|
||||||
// return (
|
return (
|
||||||
// <div>
|
<div>
|
||||||
// <p className="heading">{eligibleStreams}/{totalStreams} Streams Archived ({completedPercentage}%)</p>
|
<p className="heading">{eligibleStreams}/{totalStreams} Streams Archived ({completedPercentage}%)</p>
|
||||||
// <progress className="progress is-success" value={eligibleStreams} max={totalStreams}>{completedPercentage}%</progress>
|
<progress className="progress is-success" value={eligibleStreams} max={totalStreams}>{completedPercentage}%</progress>
|
||||||
// </div>
|
</div>
|
||||||
// )
|
)
|
||||||
return "@todo"
|
|
||||||
}
|
}
|
125
packages/next/app/components/cal.tsx
Normal file
125
packages/next/app/components/cal.tsx
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
'use client';
|
||||||
|
// greets https://github.com/wa0x6e/cal-heatmap-react-starter/blob/main/src/components/cal-heatmap.tsx
|
||||||
|
|
||||||
|
import CalHeatmap from 'cal-heatmap';
|
||||||
|
// @ts-ignore cal-heatmap is jenk
|
||||||
|
import Legend from 'cal-heatmap/plugins/Legend';
|
||||||
|
// @ts-ignore cal-heatmap is jenk
|
||||||
|
import Tooltip from 'cal-heatmap/plugins/Tooltip';
|
||||||
|
import { DataRecord } from 'cal-heatmap/src/options/Options';
|
||||||
|
import 'cal-heatmap/cal-heatmap.css';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { useEffect, useState, useRef } from 'react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { getSafeDate } from '@/lib/dates';
|
||||||
|
|
||||||
|
export interface ICalProps {
|
||||||
|
data: DataRecord[];
|
||||||
|
slug: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function Cal({ data, slug }: ICalProps) {
|
||||||
|
const router = useRouter();
|
||||||
|
const [cellSize, setCellSize] = useState(13);
|
||||||
|
const [targetElementId, setTargetElementId] = useState('');
|
||||||
|
|
||||||
|
const generateUniqueId = () => {
|
||||||
|
return `cal-${Math.random().toString(36).substring(2, 9)}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const updateCellSize = () => {
|
||||||
|
const windowWidth = window.innerWidth;
|
||||||
|
if (windowWidth > 1400) {
|
||||||
|
setCellSize(15); // Adjust the cell size for width > 1400px
|
||||||
|
} else if (windowWidth > 730) {
|
||||||
|
setCellSize(10); // Adjust the cell size for width > 730px
|
||||||
|
} else {
|
||||||
|
setCellSize(3); // Adjust the cell size for width <= 730px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateCellSize();
|
||||||
|
// Event listener to update cell size on window resize
|
||||||
|
window.addEventListener('resize', updateCellSize);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', updateCellSize);
|
||||||
|
};
|
||||||
|
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTargetElementId(generateUniqueId());
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!targetElementId) return;
|
||||||
|
const cal = new CalHeatmap();
|
||||||
|
// @ts-ignore
|
||||||
|
cal.on('click', (
|
||||||
|
event: string,
|
||||||
|
timestamp: number,
|
||||||
|
value: number
|
||||||
|
) => {
|
||||||
|
router.push(`/vt/${slug}/stream/${getSafeDate(new Date(timestamp))}`);
|
||||||
|
// console.log(`slug=${slug} safeDate=${getSafeDate(new Date(timestamp))}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
cal.paint(
|
||||||
|
{
|
||||||
|
itemSelector: `#${targetElementId}`,
|
||||||
|
scale: {
|
||||||
|
color: {
|
||||||
|
// @ts-ignore this shit is straight from the example website
|
||||||
|
domain: ['missing', 'issue', 'good'],
|
||||||
|
type: 'ordinal',
|
||||||
|
range: ['red', 'yellow', 'green']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
theme: 'dark',
|
||||||
|
verticalOrientation: false,
|
||||||
|
data: {
|
||||||
|
source: data,
|
||||||
|
x: 'date',
|
||||||
|
y: 'value',
|
||||||
|
// @ts-ignore this shit is straight from the example website
|
||||||
|
groupY: d => d[0]
|
||||||
|
},
|
||||||
|
range: 12,
|
||||||
|
date: { start: data[0].date },
|
||||||
|
domain: {
|
||||||
|
type: 'month',
|
||||||
|
gutter: 4,
|
||||||
|
label: { text: 'MMM', textAlign: 'start', position: 'top' }
|
||||||
|
},
|
||||||
|
subDomain: {
|
||||||
|
type: 'ghDay',
|
||||||
|
radius: 2,
|
||||||
|
width: cellSize,
|
||||||
|
height: cellSize,
|
||||||
|
gutter: 4,
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
[
|
||||||
|
Tooltip,
|
||||||
|
{
|
||||||
|
text: ((ts: number, value: string, dayjsDate: dayjs.Dayjs) => {
|
||||||
|
return `${!!value ? value+' - '+dayjsDate.toString() : dayjsDate.toString() }`;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
}, [targetElementId, data, cellSize, router, slug]);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div id={targetElementId}></div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -5,7 +5,6 @@ import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons";
|
|||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
export default async function Contributors() {
|
export default async function Contributors() {
|
||||||
try {
|
|
||||||
const contributors = await getContributors();
|
const contributors = await getContributors();
|
||||||
if (!contributors || contributors.length < 1) return (
|
if (!contributors || contributors.length < 1) return (
|
||||||
<SkeletonTheme baseColor="#000" highlightColor="#000" width="25%">
|
<SkeletonTheme baseColor="#000" highlightColor="#000" width="25%">
|
||||||
@ -31,10 +30,4 @@ export default async function Contributors() {
|
|||||||
return (
|
return (
|
||||||
<>{contributorList}</>
|
<>{contributorList}</>
|
||||||
)
|
)
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof Error) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
return <p>Failed to fetch contributor list</p>
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -25,6 +25,7 @@ export default function Footer() {
|
|||||||
<li><Link href="/tags">Tags</Link></li>
|
<li><Link href="/tags">Tags</Link></li>
|
||||||
<li><Link href="/feed">RSS Feed</Link></li>
|
<li><Link href="/feed">RSS Feed</Link></li>
|
||||||
<li><Link href="/api">API</Link></li>
|
<li><Link href="/api">API</Link></li>
|
||||||
|
<li><Link href="/blog">Blog</Link></li>
|
||||||
<li><Link href="https://status.futureporn.net/" target="_blank"><span className="mr-1">Status</span><FontAwesomeIcon icon={faExternalLinkAlt} className="fab fa-external-link-alt"></FontAwesomeIcon></Link></li>
|
<li><Link href="https://status.futureporn.net/" target="_blank"><span className="mr-1">Status</span><FontAwesomeIcon icon={faExternalLinkAlt} className="fab fa-external-link-alt"></FontAwesomeIcon></Link></li>
|
||||||
<li><Link href="/upload">Upload</Link></li>
|
<li><Link href="/upload">Upload</Link></li>
|
||||||
<li><Link href="/profile">Profile</Link></li>
|
<li><Link href="/profile">Profile</Link></li>
|
||||||
|
@ -11,7 +11,7 @@ export default function Pager({ baseUrl, page, pageCount }: IPagerProps): React.
|
|||||||
|
|
||||||
const getPagePath = (page: any): string => {
|
const getPagePath = (page: any): string => {
|
||||||
const pageNumber = parseInt(page);
|
const pageNumber = parseInt(page);
|
||||||
console.log(`pageNumber=${pageNumber}`);
|
console.log(`pageNumber=${pageNumber}`)
|
||||||
return `${baseUrl}/${pageNumber}`;
|
return `${baseUrl}/${pageNumber}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ export function Tagger({ vod, setTimestamps }: ITaggerProps): React.JSX.Element
|
|||||||
if (authData?.accessToken) {
|
if (authData?.accessToken) {
|
||||||
setIsAuthed(true);
|
setIsAuthed(true);
|
||||||
}
|
}
|
||||||
}, [isAuthed, authData]);
|
}, [isAuthed]);
|
||||||
|
|
||||||
|
|
||||||
async function getRandomSuggestions() {
|
async function getRandomSuggestions() {
|
||||||
|
@ -253,7 +253,7 @@ export default function UploadForm({ vtubers }: IUploadFormProps) {
|
|||||||
{...register('vtuber')}
|
{...register('vtuber')}
|
||||||
>
|
>
|
||||||
{vtubers.map((vtuber: IVtuber) => (
|
{vtubers.map((vtuber: IVtuber) => (
|
||||||
<option key={vtuber.id} value={vtuber.id}>{vtuber.attributes.displayName}</option>
|
<option value={vtuber.id}>{vtuber.attributes.displayName}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,8 +10,8 @@ import LinkableHeading from './linkable-heading';
|
|||||||
|
|
||||||
|
|
||||||
export function getVodTitle(vod: IVod): string {
|
export function getVodTitle(vod: IVod): string {
|
||||||
// console.log('lets getVodTitle, ey?')
|
console.log('lets getVodTitle, ey?')
|
||||||
// console.log(JSON.stringify(vod, null, 2))
|
console.log(JSON.stringify(vod, null, 2))
|
||||||
return vod.attributes.title || vod.attributes.announceTitle || (vod.attributes?.date2 && vod.attributes?.vtuber?.data?.attributes?.displayName) ? `${vod.attributes.vtuber.data.attributes.displayName} ${vod.attributes.date2}` : `VOD ${vod.id}`;
|
return vod.attributes.title || vod.attributes.announceTitle || (vod.attributes?.date2 && vod.attributes?.vtuber?.data?.attributes?.displayName) ? `${vod.attributes.vtuber.data.attributes.displayName} ${vod.attributes.date2}` : `VOD ${vod.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
|
import Tes from '@/assets/svg/tes';
|
||||||
|
|
||||||
export default async function Page() {
|
export default async function Page() {
|
||||||
return (
|
return (
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<div className="box">
|
<div className="box">
|
||||||
<h2>Healthy!</h2>
|
<h2>Healthy!</h2>
|
||||||
|
<Tes></Tes>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
5
packages/next/app/lib/blog.ts
Normal file
5
packages/next/app/lib/blog.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export interface IBlogPost {
|
||||||
|
slug: string;
|
||||||
|
title: string;
|
||||||
|
id: number;
|
||||||
|
}
|
@ -1,10 +1,7 @@
|
|||||||
if (!process.env.NEXT_PUBLIC_SITE_URL) throw new Error('NEXT_PUBLIC_SITE_URL was missing in env');
|
// export const strapiUrl = (process.env.NODE_ENV === 'production') ? 'https://portal.futureporn.net' : 'https://chisel.sbtp:1337'
|
||||||
if (!process.env.NEXT_PUBLIC_STRAPI_URL) throw new Error('NEXT_PUBLIC_STRAPI_URL was missing in env');
|
// export const siteUrl = (process.env.NODE_ENV === 'production') ? 'https://futureporn.net' : 'http://localhost:3000'
|
||||||
if (!process.env.NEXT_PUBLIC_UPPY_COMPANION_URL) throw new Error('NEXT_PUBLIC_UPPY_COMPANION_URL undefined in env');
|
export const siteUrl = process.env.NEXT_PUBLIC_SITE_URL
|
||||||
|
export const strapiUrl = process.env.NEXT_PUBLIC_STRAPI_URL
|
||||||
export const companionUrl = ''+process.env.NEXT_PUBLIC_UPPY_COMPANION_URL
|
|
||||||
export const siteUrl = ''+process.env.NEXT_PUBLIC_SITE_URL
|
|
||||||
export const strapiUrl = ''+process.env.NEXT_PUBLIC_STRAPI_URL
|
|
||||||
export const patreonSupporterBenefitId: string = '4760169'
|
export const patreonSupporterBenefitId: string = '4760169'
|
||||||
export const patreonQuantumSupporterId: string = '10663202'
|
export const patreonQuantumSupporterId: string = '10663202'
|
||||||
export const patreonVideoAccessBenefitId: string = '13462019'
|
export const patreonVideoAccessBenefitId: string = '13462019'
|
||||||
|
@ -80,8 +80,8 @@ export async function getGoals(pledgeSum: number): Promise<IGoals | null> {
|
|||||||
const funded = filterAndSortGoals(openData.concat(closedData), true);
|
const funded = filterAndSortGoals(openData.concat(closedData), true);
|
||||||
const unfunded = filterAndSortGoals(openData.concat(closedData), false);
|
const unfunded = filterAndSortGoals(openData.concat(closedData), false);
|
||||||
|
|
||||||
// console.log('the following are unfunded goals')
|
console.log('the following are unfunded goals')
|
||||||
// console.log(unfunded)
|
console.log(unfunded)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
complete: closedData,
|
complete: closedData,
|
||||||
|
@ -275,7 +275,7 @@ export async function getAllStreamsForVtuber(vtuberId: number, archiveStatuses =
|
|||||||
});
|
});
|
||||||
|
|
||||||
console.log(`strapiUrl=${strapiUrl}`)
|
console.log(`strapiUrl=${strapiUrl}`)
|
||||||
const response = await fetch(`${strapiUrl}/api/streams?${query}`, fetchStreamsOptions)
|
const response = await fetch(`${strapiUrl}/api/streams?${query}`, fetchStreamsOptions);
|
||||||
|
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
// If the response status is not 200 (OK), consider it a network failure
|
// If the response status is not 200 (OK), consider it a network failure
|
||||||
|
@ -120,7 +120,7 @@ export async function getAllVtubers(): Promise<IVtuber[] | null> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log(`Getting ${strapiUrl}/api/vtubers page=${currentPage}`);
|
console.log(`Getting /api/vtubers page=${currentPage}`);
|
||||||
const response = await fetch(`${strapiUrl}/api/vtubers?${query}`, fetchVtubersOptions);
|
const response = await fetch(`${strapiUrl}/api/vtubers?${query}`, fetchVtubersOptions);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
@ -5,8 +5,8 @@ import Uppy from '@uppy/core';
|
|||||||
import AwsS3 from '@uppy/aws-s3';
|
import AwsS3 from '@uppy/aws-s3';
|
||||||
import RemoteSources from '@uppy/remote-sources';
|
import RemoteSources from '@uppy/remote-sources';
|
||||||
import { useAuth } from './components/auth';
|
import { useAuth } from './components/auth';
|
||||||
import { companionUrl } from '@/lib/constants';
|
|
||||||
|
|
||||||
|
const companionUrl = process.env.NEXT_PUBLIC_UPPY_COMPANION_URL!
|
||||||
|
|
||||||
export const UppyContext = createContext(new Uppy());
|
export const UppyContext = createContext(new Uppy());
|
||||||
|
|
||||||
|
70
packages/next/app/vt/[slug]/history/page.tsx
Normal file
70
packages/next/app/vt/[slug]/history/page.tsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
|
||||||
|
import { getVtuberBySlug } from '@/lib/vtubers';
|
||||||
|
import { getAllStreamsForVtuber } from '@/lib/streams';
|
||||||
|
import NotFound from '../not-found';
|
||||||
|
import { DataRecord } from 'cal-heatmap/src/options/Options';
|
||||||
|
import { Cal } from '@/components/cal';
|
||||||
|
|
||||||
|
interface IPageProps {
|
||||||
|
params: {
|
||||||
|
slug: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getArchiveStatusValue(archiveStatus: string): number {
|
||||||
|
if (archiveStatus === 'good') return 2;
|
||||||
|
if (archiveStatus === 'issue') return 1;
|
||||||
|
else return 0 // missing
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortDataRecordsByDate(records: DataRecord[]) {
|
||||||
|
return records.sort((a, b) => {
|
||||||
|
if (typeof a.date === 'string' && typeof b.date === 'string') {
|
||||||
|
return a.date.localeCompare(b.date);
|
||||||
|
} else {
|
||||||
|
// Handle comparison when date is not a string (e.g., when it's a number)
|
||||||
|
// For instance, you might want to convert numbers to strings or use a different comparison logic.
|
||||||
|
// Example assuming number to string conversion:
|
||||||
|
return String(a.date).localeCompare(String(b.date));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default async function Page({ params: { slug } }: IPageProps) {
|
||||||
|
const vtuber = await getVtuberBySlug(slug);
|
||||||
|
if (!vtuber) return <NotFound></NotFound>
|
||||||
|
const streams = await getAllStreamsForVtuber(vtuber.id);
|
||||||
|
const streamsByYear: { [year: string]: DataRecord[] } = {};
|
||||||
|
streams.forEach((stream) => {
|
||||||
|
const date = new Date(stream.attributes.date);
|
||||||
|
const year = date.getFullYear();
|
||||||
|
if (!streamsByYear[year]) {
|
||||||
|
streamsByYear[year] = [];
|
||||||
|
}
|
||||||
|
streamsByYear[year].push({
|
||||||
|
date: new Date(stream.attributes.date).toISOString(),
|
||||||
|
value: stream.attributes.archiveStatus,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// Sort the data records within each year's array
|
||||||
|
for (const year in streamsByYear) {
|
||||||
|
streamsByYear[year] = sortDataRecordsByDate(streamsByYear[year]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{Object.keys(streamsByYear).map((year) => {
|
||||||
|
return (
|
||||||
|
<div key={year} className='section'>
|
||||||
|
<h2 className='title'>{year}</h2>
|
||||||
|
{/* <pre><code>{JSON.stringify(streamsByYear[year], null, 2)}</code></pre> */}
|
||||||
|
<Cal slug={slug} data={streamsByYear[year]} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -30,218 +30,217 @@ export default async function Page({ params }: { params: { slug: string } }) {
|
|||||||
const vods = await getVodsForVtuber(vtuber.id, 1, 9);
|
const vods = await getVodsForVtuber(vtuber.id, 1, 9);
|
||||||
if (!vods) notFound();
|
if (!vods) notFound();
|
||||||
|
|
||||||
return '@todo'
|
const missingStreams = await getAllStreamsForVtuber(vtuber.id, ['missing']);
|
||||||
// const missingStreams = await getAllStreamsForVtuber(vtuber.id, ['missing']);
|
const issueStreams = await getAllStreamsForVtuber(vtuber.id, ['issue']);
|
||||||
// const issueStreams = await getAllStreamsForVtuber(vtuber.id, ['issue']);
|
const goodStreams = await getAllStreamsForVtuber(vtuber.id, ['good']);
|
||||||
// const goodStreams = await getAllStreamsForVtuber(vtuber.id, ['good']);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// // return (
|
|
||||||
// // <>
|
|
||||||
// // <p>hi mom!</p>
|
|
||||||
// // <pre>
|
|
||||||
// // <code>
|
|
||||||
// // {JSON.stringify(missingStreams, null, 2)}
|
|
||||||
// // </code>
|
|
||||||
// // </pre>
|
|
||||||
// // </>
|
|
||||||
// // )
|
|
||||||
|
|
||||||
// return (
|
// return (
|
||||||
// <>
|
// <>
|
||||||
// {vtuber && (
|
// <p>hi mom!</p>
|
||||||
// <>
|
// <pre>
|
||||||
// <div className="box">
|
// <code>
|
||||||
|
// {JSON.stringify(missingStreams, null, 2)}
|
||||||
// <div className="columns is-multiline">
|
// </code>
|
||||||
// <div className="column is-full">
|
// </pre>
|
||||||
// <h1 className="title is-2">{vtuber.attributes.displayName}</h1>
|
|
||||||
// </div>
|
|
||||||
// <div className="column is-one-quarter">
|
|
||||||
// <figure className="image is-rounded is-1by1">
|
|
||||||
// <Image
|
|
||||||
// className="is-rounded"
|
|
||||||
// alt={vtuber.attributes.displayName}
|
|
||||||
// src={vtuber.attributes.image}
|
|
||||||
// fill={true}
|
|
||||||
// placeholder='blur'
|
|
||||||
// blurDataURL={vtuber.attributes.imageBlur}
|
|
||||||
// />
|
|
||||||
// </figure>
|
|
||||||
// </div>
|
|
||||||
// <div className="column is-three-quarters">
|
|
||||||
// <p className="is-size-5 mb-3">{vtuber.attributes.description1}</p>
|
|
||||||
// <p className="is-size-5">{vtuber.attributes.description2}</p>
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
|
|
||||||
// <h2 id="socials" className="title is-3">
|
|
||||||
// <Link href="#socials">Socials</Link>
|
|
||||||
// </h2>
|
|
||||||
|
|
||||||
|
|
||||||
// <div className="column is-full mb-5">
|
|
||||||
// <div className="columns has-text-centered is-multiline is-centered">
|
|
||||||
// {vtuber.attributes.patreon && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link href={vtuber.attributes.patreon} className="subtitle is-5">
|
|
||||||
// <span className="mr-2"><FontAwesomeIcon icon={faPatreon} className="fab fa-patreon" /></span><span className="mr-2">Patreon</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.twitter && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.twitter} className="subtitle is-5">
|
|
||||||
// <span className="mr-2"><FontAwesomeIcon icon={faXTwitter} className="fab fa-x-twitter" /></span><span className="mr-2">Twitter</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.youtube && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.youtube} className="subtitle is-5">
|
|
||||||
// <span className="mr-2"><FontAwesomeIcon icon={faYoutube} className="fab fa-youtube" /></span><span className="mr-2">YouTube</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.twitch && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.twitch} className="subtitle is-5">
|
|
||||||
// <span className="mr-2"><FontAwesomeIcon icon={faTwitch} className="fab fa-twitch" /></span><span className="mr-2">Twitch</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.tiktok && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.tiktok} className="subtitle is-5">
|
|
||||||
// <span className="mr-2"><FontAwesomeIcon icon={faTiktok} className="fab fa-tiktok" /></span><span className="mr-2">TikTok</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.fansly && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.fansly} className='subtitle is-5'>
|
|
||||||
// <span className='mr-2'><FanslyIcon width={20} height={20} className={styles.icon}/></span><span className="mr-2">Fansly</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.onlyfans && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.onlyfans} className='subtitle is-5'>
|
|
||||||
// <span className='mr-2'>
|
|
||||||
// <OnlyfansIcon width={20} height={20} className={styles.icon}></OnlyfansIcon>
|
|
||||||
// </span><span className="mr-2">OnlyFans</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.pornhub && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.pornhub} className='subtitle is-5'>
|
|
||||||
// <span className='mr-2'><PornhubIcon width={20} height={20} className={styles.icon}/></span><span className="mr-2">Pornhub</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.reddit && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.reddit} className="subtitle is-5">
|
|
||||||
// <span className="mr-2"><FontAwesomeIcon icon={faReddit} className="fab fa-reddit" /></span><span className="mr-2">Reddit</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.discord && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.discord} className="subtitle is-5">
|
|
||||||
// <span className="mr-2"><FontAwesomeIcon icon={faDiscord} className="fab fa-discord" /></span><span className="mr-2">Discord</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.instagram && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.instagram} className="subtitle is-5">
|
|
||||||
// <span className="mr-2"><FontAwesomeIcon icon={faInstagram} className="fab fa-instagram" /></span><span className="mr-2">Instagram</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.facebook && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.facebook} className="subtitle is-5">
|
|
||||||
// <span className="mr-2"><FontAwesomeIcon icon={faFacebook} className="fab fa-facebook" /></span><span className="mr-2">Facebook</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.merch && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.merch} className="subtitle is-5">
|
|
||||||
// <span className="mr-2"><FontAwesomeIcon icon={faBagShopping} className="fas fa-bag-shopping" /></span><span className="mr-2">Merch</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.chaturbate && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.chaturbate} className='subtitle is-5'>
|
|
||||||
// <span className='mr-2'><ChaturbateIcon width={20} className={styles.icon}/></span><span className="mr-2">Chaturbate</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.throne && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.throne} className='subtitle is-5'>
|
|
||||||
// <span className='mr-2'><ThroneIcon width={20} height={20} className={styles.icon}/></span><span className="mr-2">Throne</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.linktree && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.linktree} className='subtitle is-5'>
|
|
||||||
// <span className='mr-2'><LinktreeIcon width={20} height={20} className={styles.icon}/></span><span className="mr-2">Linktree</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {vtuber.attributes.carrd && (
|
|
||||||
// <div className="column is-3 is-narrow">
|
|
||||||
// <Link target="_blank" href={vtuber.attributes.carrd} className='subtitle is-5'>
|
|
||||||
// <span className='mr-2'><CarrdIcon width={20} height={20} className={styles.icon}/></span><span className="mr-2">Carrd</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
|
||||||
// </Link>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
|
|
||||||
|
|
||||||
// {/* <h2 id="toys" className="title is-3">
|
|
||||||
// <Link href="#toys">Toys</Link>
|
|
||||||
// </h2>
|
|
||||||
|
|
||||||
// <>
|
|
||||||
// <ToysList vtuber={vtuber} toys={toys} page={1} pageSize={toySampleCount} />
|
|
||||||
// {(toys.pagination.total > toySampleCount) && <Link href={`/vt/${vtuber.slug}/toys/1`} className='button mb-5'>See all of {vtuber.displayName}'s toys</Link>}
|
|
||||||
// </> */}
|
|
||||||
|
|
||||||
// <h2 id="vods" className="title is-3">
|
|
||||||
// <Link href="#vods">Vods</Link>
|
|
||||||
// </h2>
|
|
||||||
|
|
||||||
// <VodsList vtuber={vtuber} vods={vods.data} page={1} pageSize={9} />
|
|
||||||
// {
|
|
||||||
// (vtuber.attributes.vods) ? (
|
|
||||||
// <Link className='button mb-5' href={`/vt/${vtuber.attributes.slug}/vods/1`}>See all {vtuber.attributes.displayName} vods</Link>
|
|
||||||
// ) : (<p className='section'><i>No VODs have been added, yet.</i></p>)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// <h2 id="streams" className='title is-3'>
|
|
||||||
// <Link href="#streams">Streams</Link>
|
|
||||||
// </h2>
|
|
||||||
// <StreamsCalendar missingStreams={missingStreams} issueStreams={issueStreams} goodStreams={goodStreams} />
|
|
||||||
// {/*
|
|
||||||
// <h2 id="progress" className='title is-3'>
|
|
||||||
// <Link href="#progress">Archive Progress</Link>
|
|
||||||
// </h2>
|
|
||||||
// <ArchiveProgress vtuber={vtuber} /> */}
|
|
||||||
|
|
||||||
|
|
||||||
// </div>
|
|
||||||
// </>
|
// </>
|
||||||
// )}
|
// )
|
||||||
// </>
|
|
||||||
// );
|
return (
|
||||||
|
<>
|
||||||
|
{vtuber && (
|
||||||
|
<>
|
||||||
|
<div className="box">
|
||||||
|
|
||||||
|
<div className="columns is-multiline">
|
||||||
|
<div className="column is-full">
|
||||||
|
<h1 className="title is-2">{vtuber.attributes.displayName}</h1>
|
||||||
|
</div>
|
||||||
|
<div className="column is-one-quarter">
|
||||||
|
<figure className="image is-rounded is-1by1">
|
||||||
|
<Image
|
||||||
|
className="is-rounded"
|
||||||
|
alt={vtuber.attributes.displayName}
|
||||||
|
src={vtuber.attributes.image}
|
||||||
|
fill={true}
|
||||||
|
placeholder='blur'
|
||||||
|
blurDataURL={vtuber.attributes.imageBlur}
|
||||||
|
/>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
<div className="column is-three-quarters">
|
||||||
|
<p className="is-size-5 mb-3">{vtuber.attributes.description1}</p>
|
||||||
|
<p className="is-size-5">{vtuber.attributes.description2}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 id="socials" className="title is-3">
|
||||||
|
<Link href="#socials">Socials</Link>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
|
<div className="column is-full mb-5">
|
||||||
|
<div className="columns has-text-centered is-multiline is-centered">
|
||||||
|
{vtuber.attributes.patreon && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link href={vtuber.attributes.patreon} className="subtitle is-5">
|
||||||
|
<span className="mr-2"><FontAwesomeIcon icon={faPatreon} className="fab fa-patreon" /></span><span className="mr-2">Patreon</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.twitter && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.twitter} className="subtitle is-5">
|
||||||
|
<span className="mr-2"><FontAwesomeIcon icon={faXTwitter} className="fab fa-x-twitter" /></span><span className="mr-2">Twitter</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.youtube && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.youtube} className="subtitle is-5">
|
||||||
|
<span className="mr-2"><FontAwesomeIcon icon={faYoutube} className="fab fa-youtube" /></span><span className="mr-2">YouTube</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.twitch && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.twitch} className="subtitle is-5">
|
||||||
|
<span className="mr-2"><FontAwesomeIcon icon={faTwitch} className="fab fa-twitch" /></span><span className="mr-2">Twitch</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.tiktok && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.tiktok} className="subtitle is-5">
|
||||||
|
<span className="mr-2"><FontAwesomeIcon icon={faTiktok} className="fab fa-tiktok" /></span><span className="mr-2">TikTok</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.fansly && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.fansly} className='subtitle is-5'>
|
||||||
|
<span className='mr-2'><FanslyIcon width={20} height={20} className={styles.icon}/></span><span className="mr-2">Fansly</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.onlyfans && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.onlyfans} className='subtitle is-5'>
|
||||||
|
<span className='mr-2'>
|
||||||
|
<OnlyfansIcon width={20} height={20} className={styles.icon}></OnlyfansIcon>
|
||||||
|
</span><span className="mr-2">OnlyFans</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.pornhub && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.pornhub} className='subtitle is-5'>
|
||||||
|
<span className='mr-2'><PornhubIcon width={20} height={20} className={styles.icon}/></span><span className="mr-2">Pornhub</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.reddit && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.reddit} className="subtitle is-5">
|
||||||
|
<span className="mr-2"><FontAwesomeIcon icon={faReddit} className="fab fa-reddit" /></span><span className="mr-2">Reddit</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.discord && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.discord} className="subtitle is-5">
|
||||||
|
<span className="mr-2"><FontAwesomeIcon icon={faDiscord} className="fab fa-discord" /></span><span className="mr-2">Discord</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.instagram && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.instagram} className="subtitle is-5">
|
||||||
|
<span className="mr-2"><FontAwesomeIcon icon={faInstagram} className="fab fa-instagram" /></span><span className="mr-2">Instagram</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.facebook && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.facebook} className="subtitle is-5">
|
||||||
|
<span className="mr-2"><FontAwesomeIcon icon={faFacebook} className="fab fa-facebook" /></span><span className="mr-2">Facebook</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.merch && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.merch} className="subtitle is-5">
|
||||||
|
<span className="mr-2"><FontAwesomeIcon icon={faBagShopping} className="fas fa-bag-shopping" /></span><span className="mr-2">Merch</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.chaturbate && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.chaturbate} className='subtitle is-5'>
|
||||||
|
<span className='mr-2'><ChaturbateIcon width={20} className={styles.icon}/></span><span className="mr-2">Chaturbate</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.throne && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.throne} className='subtitle is-5'>
|
||||||
|
<span className='mr-2'><ThroneIcon width={20} height={20} className={styles.icon}/></span><span className="mr-2">Throne</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.linktree && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.linktree} className='subtitle is-5'>
|
||||||
|
<span className='mr-2'><LinktreeIcon width={20} height={20} className={styles.icon}/></span><span className="mr-2">Linktree</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{vtuber.attributes.carrd && (
|
||||||
|
<div className="column is-3 is-narrow">
|
||||||
|
<Link target="_blank" href={vtuber.attributes.carrd} className='subtitle is-5'>
|
||||||
|
<span className='mr-2'><CarrdIcon width={20} height={20} className={styles.icon}/></span><span className="mr-2">Carrd</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{/* <h2 id="toys" className="title is-3">
|
||||||
|
<Link href="#toys">Toys</Link>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<>
|
||||||
|
<ToysList vtuber={vtuber} toys={toys} page={1} pageSize={toySampleCount} />
|
||||||
|
{(toys.pagination.total > toySampleCount) && <Link href={`/vt/${vtuber.slug}/toys/1`} className='button mb-5'>See all of {vtuber.displayName}'s toys</Link>}
|
||||||
|
</> */}
|
||||||
|
|
||||||
|
<h2 id="vods" className="title is-3">
|
||||||
|
<Link href="#vods">Vods</Link>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<VodsList vtuber={vtuber} vods={vods.data} page={1} pageSize={9} />
|
||||||
|
{
|
||||||
|
(vtuber.attributes.vods) ? (
|
||||||
|
<Link className='button mb-5' href={`/vt/${vtuber.attributes.slug}/vods/1`}>See all {vtuber.attributes.displayName} vods</Link>
|
||||||
|
) : (<p className='section'><i>No VODs have been added, yet.</i></p>)
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2 id="streams" className='title is-3'>
|
||||||
|
<Link href="#streams">Streams</Link>
|
||||||
|
</h2>
|
||||||
|
<StreamsCalendar missingStreams={missingStreams} issueStreams={issueStreams} goodStreams={goodStreams} />
|
||||||
|
{/*
|
||||||
|
<h2 id="progress" className='title is-3'>
|
||||||
|
<Link href="#progress">Archive Progress</Link>
|
||||||
|
</h2>
|
||||||
|
<ArchiveProgress vtuber={vtuber} /> */}
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
const SvgComponent = (props: any) => (
|
const SvgComponent = (props) => (
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlSpace="preserve"
|
xmlSpace="preserve"
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"@uppy/remote-sources": "^1.1.0",
|
"@uppy/remote-sources": "^1.1.0",
|
||||||
"bulma": "^0.9.4",
|
"bulma": "^0.9.4",
|
||||||
"bulma-prefers-dark": "0.1.0-beta.1",
|
"bulma-prefers-dark": "0.1.0-beta.1",
|
||||||
|
"cal-heatmap": "^4.2.4",
|
||||||
"date-fns": "^2.0.0",
|
"date-fns": "^2.0.0",
|
||||||
"date-fns-tz": "^2.0.0",
|
"date-fns-tz": "^2.0.0",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
"**/*.tsx",
|
"**/*.tsx",
|
||||||
"dist/types/**/*.ts",
|
"dist/types/**/*.ts",
|
||||||
".next/types/**/*.ts"
|
".next/types/**/*.ts",
|
||||||
|
"assets/svg/tes.jsx"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules"
|
"node_modules"
|
||||||
|
373
pnpm-lock.yaml
generated
373
pnpm-lock.yaml
generated
@ -28,6 +28,28 @@ importers:
|
|||||||
specifier: ^4.7.0
|
specifier: ^4.7.0
|
||||||
version: 4.7.0
|
version: 4.7.0
|
||||||
|
|
||||||
|
packages/futurebot:
|
||||||
|
dependencies:
|
||||||
|
'@types/express':
|
||||||
|
specifier: ^4.17.21
|
||||||
|
version: 4.17.21
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^20.11.0
|
||||||
|
version: 20.11.5
|
||||||
|
discordeno:
|
||||||
|
specifier: ^18.0.1
|
||||||
|
version: 18.0.1
|
||||||
|
dotenv:
|
||||||
|
specifier: ^16.3.1
|
||||||
|
version: 16.3.1
|
||||||
|
express:
|
||||||
|
specifier: ^4.18.2
|
||||||
|
version: 4.18.2
|
||||||
|
devDependencies:
|
||||||
|
tsx:
|
||||||
|
specifier: ^4.7.0
|
||||||
|
version: 4.7.0
|
||||||
|
|
||||||
packages/next:
|
packages/next:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@fortawesome/fontawesome-free':
|
'@fortawesome/fontawesome-free':
|
||||||
@ -129,6 +151,9 @@ importers:
|
|||||||
bulma-prefers-dark:
|
bulma-prefers-dark:
|
||||||
specifier: 0.1.0-beta.1
|
specifier: 0.1.0-beta.1
|
||||||
version: 0.1.0-beta.1
|
version: 0.1.0-beta.1
|
||||||
|
cal-heatmap:
|
||||||
|
specifier: ^4.2.4
|
||||||
|
version: 4.2.4
|
||||||
date-fns:
|
date-fns:
|
||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.30.0
|
version: 2.30.0
|
||||||
@ -1186,7 +1211,7 @@ packages:
|
|||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv: 6.12.6
|
ajv: 6.12.6
|
||||||
debug: 4.3.4
|
debug: 4.3.4(supports-color@5.5.0)
|
||||||
espree: 9.6.1
|
espree: 9.6.1
|
||||||
globals: 13.24.0
|
globals: 13.24.0
|
||||||
ignore: 5.3.0
|
ignore: 5.3.0
|
||||||
@ -1323,7 +1348,7 @@ packages:
|
|||||||
engines: {node: '>=10.10.0'}
|
engines: {node: '>=10.10.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@humanwhocodes/object-schema': 2.0.2
|
'@humanwhocodes/object-schema': 2.0.2
|
||||||
debug: 4.3.4
|
debug: 4.3.4(supports-color@5.5.0)
|
||||||
minimatch: 3.1.2
|
minimatch: 3.1.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@ -1696,12 +1721,25 @@ packages:
|
|||||||
fastq: 1.16.0
|
fastq: 1.16.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@observablehq/plot@0.6.13:
|
||||||
|
resolution: {integrity: sha512-ebQS4ENodOy+O3WUjhqv9jNPZENAZRQMIdO3ziOlAKfUzSf69+gaFAqqc04SGrQA6JwJjPYnbfeN3YIpNsCF/A==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3: 7.8.5
|
||||||
|
interval-tree-1d: 1.0.4
|
||||||
|
isoformat: 0.2.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@paralleldrive/cuid2@2.2.2:
|
/@paralleldrive/cuid2@2.2.2:
|
||||||
resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==}
|
resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@noble/hashes': 1.3.3
|
'@noble/hashes': 1.3.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@popperjs/core@2.11.8:
|
||||||
|
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@react-hookz/deep-equal@1.0.4:
|
/@react-hookz/deep-equal@1.0.4:
|
||||||
resolution: {integrity: sha512-N56fTrAPUDz/R423pag+n6TXWbvlBZDtTehaGFjK0InmN+V2OFWLE/WmORhmn6Ce7dlwH5+tQN1LJFw3ngTJVg==}
|
resolution: {integrity: sha512-N56fTrAPUDz/R423pag+n6TXWbvlBZDtTehaGFjK0InmN+V2OFWLE/WmORhmn6Ce7dlwH5+tQN1LJFw3ngTJVg==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -2440,7 +2478,7 @@ packages:
|
|||||||
'@typescript-eslint/types': 6.18.1
|
'@typescript-eslint/types': 6.18.1
|
||||||
'@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3)
|
'@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3)
|
||||||
'@typescript-eslint/visitor-keys': 6.18.1
|
'@typescript-eslint/visitor-keys': 6.18.1
|
||||||
debug: 4.3.4
|
debug: 4.3.4(supports-color@5.5.0)
|
||||||
eslint: 8.56.0
|
eslint: 8.56.0
|
||||||
typescript: 5.3.3
|
typescript: 5.3.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@ -2471,7 +2509,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 6.18.1
|
'@typescript-eslint/types': 6.18.1
|
||||||
'@typescript-eslint/visitor-keys': 6.18.1
|
'@typescript-eslint/visitor-keys': 6.18.1
|
||||||
debug: 4.3.4
|
debug: 4.3.4(supports-color@5.5.0)
|
||||||
globby: 11.1.0
|
globby: 11.1.0
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
minimatch: 9.0.3
|
minimatch: 9.0.3
|
||||||
@ -3121,6 +3159,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
/binary-search-bounds@2.0.5:
|
||||||
|
resolution: {integrity: sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/bintrees@1.0.2:
|
/bintrees@1.0.2:
|
||||||
resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==}
|
resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -3292,6 +3334,20 @@ packages:
|
|||||||
responselike: 2.0.1
|
responselike: 2.0.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/cal-heatmap@4.2.4:
|
||||||
|
resolution: {integrity: sha512-TTNoQTRxHXrttOEbkraKv9vy2VpfQIwVLQJTlAfcBusQK9qrBL/UBO+WloAxv2yrR+P8URA2cuXEdc5iztER9g==}
|
||||||
|
dependencies:
|
||||||
|
'@observablehq/plot': 0.6.13
|
||||||
|
'@popperjs/core': 2.11.8
|
||||||
|
d3-color: 3.1.0
|
||||||
|
d3-fetch: 3.0.1
|
||||||
|
d3-selection: 3.0.0
|
||||||
|
d3-transition: 3.0.1(d3-selection@3.0.0)
|
||||||
|
dayjs: 1.11.10
|
||||||
|
eventemitter3: 5.0.1
|
||||||
|
lodash-es: 4.17.21
|
||||||
|
dev: false
|
||||||
|
|
||||||
/call-bind@1.0.5:
|
/call-bind@1.0.5:
|
||||||
resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==}
|
resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3406,6 +3462,11 @@ packages:
|
|||||||
delayed-stream: 1.0.0
|
delayed-stream: 1.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/commander@7.2.0:
|
||||||
|
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/common-tags@1.8.2:
|
/common-tags@1.8.2:
|
||||||
resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==}
|
resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==}
|
||||||
engines: {node: '>=4.0.0'}
|
engines: {node: '>=4.0.0'}
|
||||||
@ -3516,6 +3577,254 @@ packages:
|
|||||||
resolution: {integrity: sha512-tI+NjVRS485QlSxHeM3JIjdEZSJMLOZVp41/vvWukDmIkZSoYG9gLYl9GFZGBpY42UbRVo1eMlF7XkI/IiDHzQ==}
|
resolution: {integrity: sha512-tI+NjVRS485QlSxHeM3JIjdEZSJMLOZVp41/vvWukDmIkZSoYG9gLYl9GFZGBpY42UbRVo1eMlF7XkI/IiDHzQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/d3-array@3.2.4:
|
||||||
|
resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
internmap: 2.0.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-axis@3.0.0:
|
||||||
|
resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-brush@3.0.0:
|
||||||
|
resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3-dispatch: 3.0.1
|
||||||
|
d3-drag: 3.0.0
|
||||||
|
d3-interpolate: 3.0.1
|
||||||
|
d3-selection: 3.0.0
|
||||||
|
d3-transition: 3.0.1(d3-selection@3.0.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-chord@3.0.1:
|
||||||
|
resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3-path: 3.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-color@3.1.0:
|
||||||
|
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-contour@4.0.2:
|
||||||
|
resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3-array: 3.2.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-delaunay@6.0.4:
|
||||||
|
resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
delaunator: 5.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-dispatch@3.0.1:
|
||||||
|
resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-drag@3.0.0:
|
||||||
|
resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3-dispatch: 3.0.1
|
||||||
|
d3-selection: 3.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-dsv@3.0.1:
|
||||||
|
resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
commander: 7.2.0
|
||||||
|
iconv-lite: 0.6.3
|
||||||
|
rw: 1.3.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-ease@3.0.1:
|
||||||
|
resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-fetch@3.0.1:
|
||||||
|
resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3-dsv: 3.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-force@3.0.0:
|
||||||
|
resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3-dispatch: 3.0.1
|
||||||
|
d3-quadtree: 3.0.1
|
||||||
|
d3-timer: 3.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-format@3.1.0:
|
||||||
|
resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-geo@3.1.0:
|
||||||
|
resolution: {integrity: sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3-array: 3.2.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-hierarchy@3.1.2:
|
||||||
|
resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-interpolate@3.0.1:
|
||||||
|
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3-color: 3.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-path@3.1.0:
|
||||||
|
resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-polygon@3.0.1:
|
||||||
|
resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-quadtree@3.0.1:
|
||||||
|
resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-random@3.0.1:
|
||||||
|
resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-scale-chromatic@3.0.0:
|
||||||
|
resolution: {integrity: sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3-color: 3.1.0
|
||||||
|
d3-interpolate: 3.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-scale@4.0.2:
|
||||||
|
resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3-array: 3.2.4
|
||||||
|
d3-format: 3.1.0
|
||||||
|
d3-interpolate: 3.0.1
|
||||||
|
d3-time: 3.1.0
|
||||||
|
d3-time-format: 4.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-selection@3.0.0:
|
||||||
|
resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-shape@3.2.0:
|
||||||
|
resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3-path: 3.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-time-format@4.1.0:
|
||||||
|
resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3-time: 3.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-time@3.1.0:
|
||||||
|
resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3-array: 3.2.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-timer@3.0.1:
|
||||||
|
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-transition@3.0.1(d3-selection@3.0.0):
|
||||||
|
resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
peerDependencies:
|
||||||
|
d3-selection: 2 - 3
|
||||||
|
dependencies:
|
||||||
|
d3-color: 3.1.0
|
||||||
|
d3-dispatch: 3.0.1
|
||||||
|
d3-ease: 3.0.1
|
||||||
|
d3-interpolate: 3.0.1
|
||||||
|
d3-selection: 3.0.0
|
||||||
|
d3-timer: 3.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3-zoom@3.0.0:
|
||||||
|
resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3-dispatch: 3.0.1
|
||||||
|
d3-drag: 3.0.0
|
||||||
|
d3-interpolate: 3.0.1
|
||||||
|
d3-selection: 3.0.0
|
||||||
|
d3-transition: 3.0.1(d3-selection@3.0.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/d3@7.8.5:
|
||||||
|
resolution: {integrity: sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
d3-array: 3.2.4
|
||||||
|
d3-axis: 3.0.0
|
||||||
|
d3-brush: 3.0.0
|
||||||
|
d3-chord: 3.0.1
|
||||||
|
d3-color: 3.1.0
|
||||||
|
d3-contour: 4.0.2
|
||||||
|
d3-delaunay: 6.0.4
|
||||||
|
d3-dispatch: 3.0.1
|
||||||
|
d3-drag: 3.0.0
|
||||||
|
d3-dsv: 3.0.1
|
||||||
|
d3-ease: 3.0.1
|
||||||
|
d3-fetch: 3.0.1
|
||||||
|
d3-force: 3.0.0
|
||||||
|
d3-format: 3.1.0
|
||||||
|
d3-geo: 3.1.0
|
||||||
|
d3-hierarchy: 3.1.2
|
||||||
|
d3-interpolate: 3.0.1
|
||||||
|
d3-path: 3.1.0
|
||||||
|
d3-polygon: 3.0.1
|
||||||
|
d3-quadtree: 3.0.1
|
||||||
|
d3-random: 3.0.1
|
||||||
|
d3-scale: 4.0.2
|
||||||
|
d3-scale-chromatic: 3.0.0
|
||||||
|
d3-selection: 3.0.0
|
||||||
|
d3-shape: 3.2.0
|
||||||
|
d3-time: 3.1.0
|
||||||
|
d3-time-format: 4.1.0
|
||||||
|
d3-timer: 3.0.1
|
||||||
|
d3-transition: 3.0.1(d3-selection@3.0.0)
|
||||||
|
d3-zoom: 3.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/damerau-levenshtein@1.0.8:
|
/damerau-levenshtein@1.0.8:
|
||||||
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
|
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -3561,18 +3870,6 @@ packages:
|
|||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/debug@4.3.4:
|
|
||||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
|
||||||
engines: {node: '>=6.0'}
|
|
||||||
peerDependencies:
|
|
||||||
supports-color: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
supports-color:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
ms: 2.1.2
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/debug@4.3.4(supports-color@5.5.0):
|
/debug@4.3.4(supports-color@5.5.0):
|
||||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||||
engines: {node: '>=6.0'}
|
engines: {node: '>=6.0'}
|
||||||
@ -3622,6 +3919,12 @@ packages:
|
|||||||
has-property-descriptors: 1.0.1
|
has-property-descriptors: 1.0.1
|
||||||
object-keys: 1.1.1
|
object-keys: 1.1.1
|
||||||
|
|
||||||
|
/delaunator@5.0.0:
|
||||||
|
resolution: {integrity: sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==}
|
||||||
|
dependencies:
|
||||||
|
robust-predicates: 3.0.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/delayed-stream@1.0.0:
|
/delayed-stream@1.0.0:
|
||||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
@ -3912,7 +4215,7 @@ packages:
|
|||||||
eslint: '*'
|
eslint: '*'
|
||||||
eslint-plugin-import: '*'
|
eslint-plugin-import: '*'
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.3.4
|
debug: 4.3.4(supports-color@5.5.0)
|
||||||
enhanced-resolve: 5.15.0
|
enhanced-resolve: 5.15.0
|
||||||
eslint: 8.56.0
|
eslint: 8.56.0
|
||||||
eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.18.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0)
|
eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.18.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0)
|
||||||
@ -4081,7 +4384,7 @@ packages:
|
|||||||
ajv: 6.12.6
|
ajv: 6.12.6
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
cross-spawn: 7.0.3
|
cross-spawn: 7.0.3
|
||||||
debug: 4.3.4
|
debug: 4.3.4(supports-color@5.5.0)
|
||||||
doctrine: 3.0.0
|
doctrine: 3.0.0
|
||||||
escape-string-regexp: 4.0.0
|
escape-string-regexp: 4.0.0
|
||||||
eslint-scope: 7.2.2
|
eslint-scope: 7.2.2
|
||||||
@ -4705,6 +5008,13 @@ packages:
|
|||||||
safer-buffer: 2.1.2
|
safer-buffer: 2.1.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/iconv-lite@0.6.3:
|
||||||
|
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dependencies:
|
||||||
|
safer-buffer: 2.1.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/ieee754@1.2.1:
|
/ieee754@1.2.1:
|
||||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -4758,6 +5068,17 @@ packages:
|
|||||||
side-channel: 1.0.4
|
side-channel: 1.0.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/internmap@2.0.3:
|
||||||
|
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/interval-tree-1d@1.0.4:
|
||||||
|
resolution: {integrity: sha512-wY8QJH+6wNI0uh4pDQzMvl+478Qh7Rl4qLmqiluxALlNvl+I+o5x38Pw3/z7mDPTPS1dQalZJXsmbvxx5gclhQ==}
|
||||||
|
dependencies:
|
||||||
|
binary-search-bounds: 2.0.5
|
||||||
|
dev: false
|
||||||
|
|
||||||
/ipaddr.js@1.9.1:
|
/ipaddr.js@1.9.1:
|
||||||
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
|
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
@ -4963,6 +5284,10 @@ packages:
|
|||||||
/isexe@2.0.0:
|
/isexe@2.0.0:
|
||||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||||
|
|
||||||
|
/isoformat@0.2.1:
|
||||||
|
resolution: {integrity: sha512-tFLRAygk9NqrRPhJSnNGh7g7oaVWDwR0wKh/GM2LgmPa50Eg4UfyaCO4I8k6EqJHl1/uh2RAD6g06n5ygEnrjQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/iterator.prototype@1.1.2:
|
/iterator.prototype@1.1.2:
|
||||||
resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==}
|
resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -5133,6 +5458,10 @@ packages:
|
|||||||
p-locate: 5.0.0
|
p-locate: 5.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/lodash-es@4.17.21:
|
||||||
|
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/lodash._baseiteratee@4.7.0:
|
/lodash._baseiteratee@4.7.0:
|
||||||
resolution: {integrity: sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==}
|
resolution: {integrity: sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -6188,12 +6517,20 @@ packages:
|
|||||||
glob: 7.2.3
|
glob: 7.2.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/robust-predicates@3.0.2:
|
||||||
|
resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/run-parallel@1.2.0:
|
/run-parallel@1.2.0:
|
||||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
queue-microtask: 1.2.3
|
queue-microtask: 1.2.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/rw@1.3.3:
|
||||||
|
resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/safe-array-concat@1.0.1:
|
/safe-array-concat@1.0.1:
|
||||||
resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==}
|
resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==}
|
||||||
engines: {node: '>=0.4'}
|
engines: {node: '>=0.4'}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user