progress
ci / build (push) Has been cancelled Details

This commit is contained in:
CJ_Clippy 2024-07-10 14:11:18 -08:00
parent 358e484a12
commit 93b90c5586
93 changed files with 518 additions and 415 deletions

View File

@ -19,9 +19,6 @@
**/charts/**/charts
**/.mocharc.json
**/.env
**/node_modules
packages/strapi/.tmp/
packages/strapi/.cache/
packages/strapi/.git/

View File

@ -48,19 +48,6 @@ dotenv(fn='.env.development')
# )
## this method results in the following error. Build Failed: Internal error occurred: failed calling webhook "webhook.cert-manager.io": failed to call webhook: Post "https://cert-manager-webhook.cert-manager.svc:443/validate?timeout=30s": service "cert-manager-webhook" not found
# helm_remote(
# 'cert-manager',
# repo_url='https://charts.jetstack.io',
# repo_name='cert-manager',
# namespace='cert-manager',
# version='1.15.1',
# set=[
# 'crds.enabled=true'
# ]
# )
helm_remote(
'traefik',
@ -125,8 +112,8 @@ k8s_yaml(helm(
docker_build(
'fp/strapi',
'.',
only=['./packages/strapi', './packages/types'],
dockerfile='./d.strapi.dev.dockerfile',
dockerfile='./d.packages.dockerfile',
target='strapi',
live_update=[
sync('./packages/strapi', '/app')
]
@ -135,7 +122,6 @@ docker_build(
docker_build(
'fp/bot',
'.',
only=['./pnpm-lock.yaml', './package.json', './packages/types', './packages/bot'],
dockerfile='./d.packages.dockerfile',
target='bot-dev',
live_update=[
@ -193,8 +179,7 @@ cmd_button('temporal-web:namespace',
docker_build(
'fp/next',
'.',
# only=['./pnpm-lock.yaml', './package.json', './packages/next', './packages/types', './ca/letsencrypt-stg-root-x1.pem'],
dockerfile='d.next.dockerfile',
dockerfile='d.packages.dockerfile',
target='next',
build_args={
'NEXT_PUBLIC_STRAPI_URL': 'https://strapi.fp.sbtp.xyz'
@ -208,7 +193,6 @@ docker_build(
docker_build(
'fp/scout-manager',
'.',
only=['./pnpm-lock.yaml', './package.json', './packages/scout', './packages/types', './ca/letsencrypt-stg-root-x1.pem'],
dockerfile='d.packages.dockerfile',
target='scout-manager',
live_update=[
@ -223,7 +207,6 @@ docker_build(
docker_build(
'fp/boop',
'.',
# only=['./pnpm-lock.yaml', './package.json', './packages/scout', './packages/types', './ca/letsencrypt-stg-root-x1.pem'],
dockerfile='d.packages.dockerfile',
target='boop',
live_update=[
@ -239,7 +222,6 @@ docker_build(
docker_build(
'fp/scout-worker',
'.',
only=['./pnpm-lock.yaml', './package.json', './packages/scout', './packages/types', './ca/letsencrypt-stg-root-x1.pem'],
# ignore=['./packages/next'], # I wish I could use this ignore to ignore file changes in this dir, but that's not how it works
dockerfile='d.packages.dockerfile',
target='scout-worker',

View File

@ -1,45 +0,0 @@
## Important! Build context is the ROOT of the project.
## this keeps the door open for future possibility of shared code between pnpm workspace packages
FROM node:20-slim AS base
FROM base AS deps
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /app
FROM deps AS install
ARG NEXT_PUBLIC_SITE_URL=https://futureporn.net
ARG NEXT_PUBLIC_STRAPI_URL=https://portal.futureporn.net
ARG NEXT_PUBLIC_UPPY_COMPANION_URL=https://uppy.futureporn.net
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}
ENV NEXT_TELEMETRY_DISABLED 1
ENV NODE_EXTRA_CA_CERTS "/app/letsencrypt-stg-root-x1.pem"
COPY pnpm-lock.yaml ./
RUN pnpm fetch
COPY ./packages/next /app
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm install
FROM install AS dev
CMD ["pnpm", "run", "dev"]
FROM install AS build
RUN pnpm run build
FROM deps AS next
RUN apt-get update && apt-get install -y -qq --no-install-recommends dumb-init
COPY --chown=node:node --from=build /app/package.json /app/pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
COPY --chown=node:node --from=build /app/public ./public
COPY --chown=node:node --from=build /app/.next/standalone ./
COPY --chown=node:node --from=build /app/.next/static ./.next/static
ENV TZ=UTC
ENV NODE_ENV=production
ENV HOSTNAME="0.0.0.0"
CMD [ "dumb-init", "node", "server.js" ]

View File

@ -21,19 +21,37 @@ ENTRYPOINT ["pnpm"]
FROM base AS build
ENV NODE_ENV=production
ARG NEXT_PUBLIC_SITE_URL=https://futureporn.net
ARG NEXT_PUBLIC_STRAPI_URL=https://portal.futureporn.net
ARG NEXT_PUBLIC_UPPY_COMPANION_URL=https://uppy.futureporn.net
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}
ENV NEXT_TELEMETRY_DISABLED 1
COPY . /usr/src/app
WORKDIR /usr/src/app
RUN mkdir -p /prod/scout
RUN mkdir -p /prod
RUN pnpm fetch
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile --offline
## strapi needs node-gyp
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install -g node-gyp
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
ENV NODE_ENV production
RUN pnpm --recursive build
RUN pnpm deploy --filter=boop /prod/boop
# RUN pnpm deploy --filter=scout --prod /prod/scout
# RUN pnpm deploy --filter=bot --prod /prod/bot
# RUN pnpm deploy --filter=temporal-worker --prod /prod/temporal-worker
# RUN pnpm deploy --filter=next /prod/next
# RUN pnpm deploy --filter=next /prod/next-dev
## we use pnpm deploy to create bundled, isolated node packages for docker containers. See https://pnpm.io/cli/deploy
## we can deploy most packages this way, but with some projects such as next it's not necessary as next's build step packages all dependencies.
RUN pnpm deploy --filter=boop --prod /prod/boop
RUN pnpm deploy --filter=scout --prod /prod/scout
FROM base AS strapi
COPY --from=build /usr/src/app/packages/strapi .
CMD ["run", "develop"]
# NODE_ENV=development is important for the fp/next build.
## @todo remove the --filter
FROM base AS boop
COPY --from=build /prod/boop /app
@ -60,19 +78,25 @@ CMD ["start"]
# RUN pnpm fetch
# RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm install
FROM base AS next-build
COPY --from=build /prod/next /app
# RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm install
# RUN ls -la
RUN pnpm run build
# FROM base AS next-build
# COPY --from=build /prod/next /app
# # RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm install
# # RUN ls -la
# RUN pnpm run build
FROM base as next
COPY --from=next-build /app /app
RUN apt-get update && apt-get install -y -qq --no-install-recommends dumb-init
COPY --chown=node:node --from=build /usr/src/app/packages/next/public ./public
COPY --chown=node:node --from=build /usr/src/app/packages/next/.next/standalone ./
COPY --chown=node:node --from=build /usr/src/app/packages/next/.next/static ./.next/static
ENV TZ=UTC
ENV NODE_ENV=production
ENV HOSTNAME="0.0.0.0"
ENTRYPOINT [ "dumb-init", "node", "server.js" ]
# FROM base AS next-pre
# COPY --from=build /prod/next /app
# ENV NODE_EXTRA_CA_CERTS "/app/letsencrypt-stg-root-x1.pem"

View File

@ -1,10 +0,0 @@
FROM node:18 AS base
WORKDIR /usr/src/app/
RUN corepack enable
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
COPY ./packages/strapi/package.json ./packages/strapi/pnpm-lock.yaml .
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
COPY ./packages/strapi/ .
RUN ["pnpm", "run", "build"]
CMD ["pnpm", "run", "develop"]

View File

@ -15,7 +15,7 @@
"noImplicitOverride": true,
// Transpile our TypeScript code to JavaScript
"module": "NodeNext",
"outDir": "lib",
"outDir": "dist",
"lib": [
"es2022"
]

View File

@ -6,7 +6,8 @@
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js"
"start": "node index.js",
"build": "tsc --build"
},
"dependencies": {
"taco": "workspace:*",
@ -14,5 +15,8 @@
},
"keywords": [],
"author": "",
"license": "ISC"
"license": "Unlicense",
"devDependencies": {
"typescript": "^5.5.3"
}
}

View File

@ -14,3 +14,18 @@ importers:
types:
specifier: workspace:*
version: link:../types
devDependencies:
typescript:
specifier: ^5.5.3
version: 5.5.3
packages:
typescript@5.5.3:
resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==}
engines: {node: '>=14.17'}
hasBin: true
snapshots:
typescript@5.5.3: {}

View File

@ -0,0 +1,31 @@
{
"compilerOptions": {
// Base Options recommended for all projects
"esModuleInterop": true,
"skipLibCheck": true,
"target": "es2022",
"allowJs": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"isolatedModules": true,
// Enable strict type checking so you can catch bugs early
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
// Transpile our TypeScript code to JavaScript
"module": "NodeNext",
"outDir": "dist",
"lib": [
"es2022",
"dom"
]
},
// Include the necessary files for your project
"include": [
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
}

View File

@ -14,7 +14,7 @@
"noImplicitOverride": true,
// Transpile our TypeScript code to JavaScript
"module": "NodeNext",
"outDir": "lib",
"outDir": "dist",
"lib": [
"es2022"
]

View File

@ -1,2 +1,3 @@
# next
HTML5 frontend for https://futureporn.net

View File

@ -1,7 +1,7 @@
import { getVodTitle } from '@/components/vod-page';
import { getUrl, getAllVods } from "@/lib/vods"
import { IVod } from "@/lib/vods"
import { getVodTitle } from '@/app/components/vod-page';
import { getUrl, getAllVods } from "@/app/lib/vods"
import { IVod } from "@/app/lib/vods"
/*

View File

@ -1,6 +1,6 @@
import StreamPage from '@/components/stream-page';
import { getStreamByCuid } from '@/lib/streams';
import StreamPage from '@/app/components/stream-page';
import { getStreamByCuid } from '@/app/lib/streams';
interface IPageParams {

View File

@ -1,8 +1,8 @@
import Pager from "@/components/pager";
import StreamsList from "@/components/streams-list";
import StreamsTable from '@/components/streams-table';
import { getAllStreams, getStreamsForVtuber } from "@/lib/streams";
// import { getAllVtubers } from "@/lib/vtubers";
import Pager from "@/app/components/pager";
import StreamsList from "@/app/components/streams-list";
import StreamsTable from '@/app/components/streams-table';
import { getAllStreams, getStreamsForVtuber } from "@/app/lib/streams";
// import { getAllVtubers } from "@/app/lib/vtubers";
import { notFound } from "next/navigation";

View File

@ -1,6 +1,6 @@
import { getAllStreamsForVtuber, getStreamCountForVtuber } from "@/lib/streams";
import { getVodsForVtuber } from "@/lib/vods";
import { IVtuber } from "@/lib/vtubers";
import { getAllStreamsForVtuber, getStreamCountForVtuber } from "@/app/lib/streams";
import { getVodsForVtuber } from "@/app/lib/vods";
import { IVtuber } from "types";
export interface IArchiveProgressProps {
vtuber: IVtuber;

View File

@ -7,7 +7,7 @@ import { faPatreon } from '@fortawesome/free-brands-svg-icons';
import { useLocalStorageValue } from '@react-hookz/web';
import { faRightFromBracket } from '@fortawesome/free-solid-svg-icons';
import Skeleton from 'react-loading-skeleton';
import { strapiUrl } from '@/lib/constants';
import { strapiUrl } from '@/app/lib/constants';
// import NextAuth from 'next-auth'; // this is (pipedream) wishlist
// import Providers from 'next-auth/providers';

View File

@ -1,6 +1,6 @@
import { getCampaign } from "@/lib/patreon";
import { getGoals, IGoals } from '@/lib/pm'
import { getCampaign } from "@/app/lib/patreon";
import { getGoals, IGoals } from '@/app/lib/pm'
import Image from "next/legacy/image";
import React from 'react';
import Link from 'next/link'

View File

@ -5,7 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons";
import { faUser, faUpload } from "@fortawesome/free-solid-svg-icons";
import Link from 'next/link'
import { LoginButton, useAuth } from '@/components/auth'
import { LoginButton, useAuth } from '@/app/components/auth'
export default function Navbar() {

View File

@ -1,4 +1,4 @@
import { IStream } from "@/lib/streams";
import { IStream } from "types";
import Link from "next/link"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCalendar } from "@fortawesome/free-solid-svg-icons";

View File

@ -1,8 +1,8 @@
'use client';
import { IStream } from "@/lib/streams";
import { IStream } from "types";
// import NotFound from "app/streams/[cuid]/not-found";
import { IVod } from "@/lib/vods";
import { IVod } from "@/app/lib/vods";
import Link from "next/link";
import Image from "next/legacy/image";
import { LocalizedDate } from "./localized-date";
@ -37,7 +37,7 @@ function determineStatus(stream: IStream): Status {
if (stream.attributes.vods.data.length < 1) {
return 'missing'
} else {
if (stream.attributes.vods.data.some(vod => !hasNote(vod))) {
if (stream.attributes.vods.data.some((vod: IVod) => !hasNote(vod))) {
return 'good';
} else {
return 'issue';

View File

@ -1,9 +1,9 @@
import { IStream } from "@/lib/streams";
import NotFound from "app/vt/[slug]/not-found";
import { IStream } from "types";
import NotFound from "@/app/vt/[slug]/not-found";
import { LocalizedDate } from "./localized-date";
import Link from "next/link";
import ChaturbateIcon from "@/components/icons/chaturbate";
import FanslyIcon from "@/components/icons/fansly";
import ChaturbateIcon from "@/app/components/icons/chaturbate";
import FanslyIcon from "@/app/components/icons/fansly";
import Image from "next/legacy/image";
export interface IStreamProps {

View File

@ -1,9 +1,10 @@
import React from 'react'
import Link from 'next/link';
import { IVtuber } from '@/lib/vtubers';
import { IVtuber } from 'types';
import { notFound } from 'next/navigation';
import { IStream, getAllStreams } from '@/lib/streams';
import { StreamSummary } from '@/components/stream';
import { getAllStreams } from '@/app/lib/streams';
import { IStream } from 'types';
import { StreamSummary } from '@/app/components/stream';
interface IStreamsListProps {
vtubers: IVtuber[];

View File

@ -19,7 +19,8 @@ import {
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSpinner } from '@fortawesome/free-solid-svg-icons'
import { fetchStreamData, IStream } from '@/lib/streams'
import { fetchStreamData } from '@/app/lib/streams'
import { IStream } from 'types'
const queryClient = new QueryClient()

View File

@ -1,13 +1,13 @@
'use client';
import { ITagVodRelation, ITagVodRelationsResponse } from "@/lib/tag-vod-relations"
import { ITagVodRelation, ITagVodRelationsResponse } from "@/app/lib/tag-vod-relations"
import { isWithinInterval, subHours } from "date-fns";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { AuthContext, IUseAuth } from "./auth";
import { useContext, useEffect, useState } from "react";
import { useRouter } from 'next/navigation';
import { strapiUrl } from "@/lib/constants";
import { strapiUrl } from "@/app/lib/constants";
export interface ITagParams {
tvr: ITagVodRelation;

View File

@ -1,18 +1,18 @@
'use client';
import { useState, useCallback, useEffect, useContext } from 'react';
import { IVod } from '@/lib/vods';
import { IVod } from '@/app/lib/vods';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPlus, faX, faTags } from "@fortawesome/free-solid-svg-icons";
import { formatTimestamp } from '@/lib/dates';
import { readOrCreateTagVodRelation } from '@/lib/tag-vod-relations';
import { readOrCreateTag } from '@/lib/tags';
import { formatTimestamp } from '@/app/lib/dates';
import { readOrCreateTagVodRelation } from '@/app/lib/tag-vod-relations';
import { readOrCreateTag } from '@/app/lib/tags';
import { useAuth } from './auth';
import { debounce } from 'lodash';
import { strapiUrl } from '@/lib/constants';
import { strapiUrl } from '@/app/lib/constants';
import { VideoContext } from './video-context';
import { useForm } from "react-hook-form";
import { ITimestamp, createTimestamp } from '@/lib/timestamps';
import { ITimestamp, createTimestamp } from '@/app/lib/timestamps';
import { useRouter } from 'next/navigation';
import styles from '@/assets/styles/fp.module.css'
import qs from 'qs';

View File

@ -1,13 +1,13 @@
import React, { useContext, useState, useEffect } from "react";
import { IVod } from "@/lib/vods";
import { IVod } from "@/app/lib/vods";
import {
ITimestamp,
deleteTimestamp
} from "@/lib/timestamps";
} from "@/app/lib/timestamps";
import {
formatTimestamp,
formatUrlTimestamp,
} from "@/lib/dates";
} from "@/app/lib/dates";
import Link from 'next/link';
import { faClock, faLink, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";

View File

@ -1,6 +1,6 @@
import React from 'react';
import { IToy, IToysResponse } from '@/lib/toys';
import { IVtuber } from '@/lib/vtubers';
import { IToy, IToysResponse } from '@/app/lib/toys';
import { IVtuber } from 'types';
import Link from 'next/link';
import Image from "next/legacy/image";

View File

@ -1,28 +1,26 @@
'use client';
import { IVtuber } from "@/lib/vtubers";
import { IStream } from "@/lib/streams";
import { IVtuber } from "types";
import { useSearchParams } from 'next/navigation';
import React, { useContext, useState, useEffect } from 'react';
import { UppyContext } from 'app/uppy';
import React from 'react';
import AwsS3 from '@uppy/aws-s3';
import RemoteSources from '@uppy/remote-sources';
import { LoginButton, useAuth } from '@/components/auth';
import { LoginButton, useAuth } from '@/app/components/auth';
import { Dashboard } from '@uppy/react';
import styles from '@/assets/styles/fp.module.css'
import { projektMelodyEpoch } from "@/lib/constants";
import { projektMelodyEpoch } from "@/app/lib/constants";
import add from "date-fns/add";
import sub from "date-fns/sub";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCheckCircle, faEraser, faPaperPlane, faSpinner, faX, faXmark } from "@fortawesome/free-solid-svg-icons";
import { useForm, useFieldArray, ValidationMode } from 'react-hook-form';
import { faEraser, faPaperPlane, faSpinner, faX, faXmark } from "@fortawesome/free-solid-svg-icons";
import { useForm, ValidationMode } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as Yup from 'yup';
import qs from 'qs';
import { toast } from "react-toastify";
import { ErrorMessage } from "@hookform/error-message"
import Uppy from '@uppy/core';
import { companionUrl } from '@/lib/constants';
import { companionUrl } from '@/app/lib/constants';

View File

@ -1,7 +1,6 @@
import VideoApiElement from "@mux/mux-player/dist/types/video-api";
import { MutableRefObject, createContext, useState } from "react";
import { ITagVodRelation } from "@/lib/tag-vod-relations";
import { createContext } from "react";
import { ITagVodRelation } from "@/app/lib/tag-vod-relations";
export interface IVideoContextValue {
timeStamp: number;

View File

@ -1,17 +1,17 @@
'use client';
import { IVod } from "@/lib/vods";
import { IVod } from "@/app/lib/vods";
import { useRef, useState, useEffect, useCallback } from "react";
import { VideoPlayer } from "./video-player";
import { Tagger } from './tagger';
import { ITimestamp, getTimestampsForVod } from "@/lib/timestamps";
import { ITimestamp, getTimestampsForVod } from "@/app/lib/timestamps";
import { TimestampsList } from "./timestamps-list";
import { ITagVodRelation } from "@/lib/tag-vod-relations";
import { ITagVodRelation } from "@/app/lib/tag-vod-relations";
import { VideoContext } from "./video-context";
import { getVodTitle } from "./vod-page";
import { useSearchParams } from 'next/navigation';
import VideoApiElement from "@mux/mux-player/dist/types/video-api";
import { parseUrlTimestamp } from "@/lib/dates";
import type VideoApiElement from "@mux/mux-player";
import { parseUrlTimestamp } from "@/app/lib/dates";
import { faTags, faNoteSticky, faClock } from "@fortawesome/free-solid-svg-icons";
import { Tag } from './tag';
import VodNav from './vod-nav';

View File

@ -1,16 +1,16 @@
'use client';
import { useEffect, useState, forwardRef, useContext, Ref } from 'react';
import { IVod } from '@/lib/vods';
import { useAuth } from '@/components/auth';
import { IVod } from '@/app/lib/vods';
import { useAuth } from '@/app/components/auth';
import { getVodTitle } from './vod-page';
import { VideoSourceSelector } from '@/components/video-source-selector'
import { buildIpfsUrl } from '@/lib/ipfs';
import { strapiUrl } from '@/lib/constants';
import { VideoSourceSelector } from '@/app/components/video-source-selector'
import { buildIpfsUrl } from '@/app/lib/ipfs';
import { strapiUrl } from '@/app/lib/constants';
import MuxPlayer from '@mux/mux-player-react/lazy';
import { VideoContext } from './video-context';
import MuxPlayerElement from '@mux/mux-player';
import VideoApiElement from "@mux/mux-player/dist/types/video-api";
import type VideoApiElement from "@mux/mux-player";
interface IPlayerProps {
vod: IVod;

View File

@ -1,8 +1,8 @@
import Link from "next/link";
import { getSafeDate } from '@/lib/dates';
import { IVtuber } from '@/lib/vtubers';
import { getSafeDate } from '@/app/lib/dates';
import { IVtuber } from 'types';
import Image from "next/legacy/image"
import { LocalizedDate } from '@/components/localized-date'
import { LocalizedDate } from '@/app/components/localized-date'
interface IVodCardProps {
id: number;

View File

@ -5,10 +5,10 @@ import { faXTwitter } from '@fortawesome/free-brands-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import Image from "next/legacy/image";
import Link from 'next/link';
import { IVod } from '@/lib/vods';
import { buildIpfsUrl } from '@/lib/ipfs';
import { getSafeDate } from "@/lib/dates";
import { StreamButton } from '@/components/stream-button';
import { IVod } from '@/app/lib/vods';
import { buildIpfsUrl } from '@/app/lib/ipfs';
import { getSafeDate } from "@/app/lib/dates";
import { StreamButton } from '@/app/components/stream-button';
import VtuberButton from "./vtuber-button";
import { LocalizedDate } from "./localized-date";

View File

@ -1,5 +1,5 @@
import { getUrl, getNextVod, getPreviousVod, getLocalizedDate } from '@/lib/vods';
import { IVod } from '@/lib/vods';
import { getUrl, getNextVod, getPreviousVod, getLocalizedDate } from '@/app/lib/vods';
import { IVod } from '@/app/lib/vods';
import Link from 'next/link';
import { VideoInteractive } from './video-interactive';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

View File

@ -1,8 +1,8 @@
import React from 'react'
import Link from 'next/link';
import VodCard from './vod-card';
import { IVtuber, IVtuberResponse } from '@/lib/vtubers';
import { IVodsResponse, IVod } from '@/lib/vods';
import { IVtuber } from 'types';
import { IVod } from '@/app/lib/vods';
import { getVodTitle } from './vod-page';
import { notFound } from 'next/navigation';

View File

@ -1,8 +1,8 @@
import Link from "next/link";
import type { IVtuber } from '@/lib/vtubers';
import { getVodsForVtuber } from "@/lib/vods";
import type { IVtuber } from 'types';
import { getVodsForVtuber } from "@/app/lib/vods";
import Image from "next/legacy/image"
import NotFound from "app/vt/[slug]/not-found";
import NotFound from "@/app/vt/[slug]/not-found";
import ArchiveProgress from "./archive-progress";
export default async function VTuberCard(vtuber: IVtuber) {

View File

@ -3,9 +3,9 @@
import { useSearchParams, useRouter } from 'next/navigation'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { strapiUrl } from '@/lib/constants'
import { useAuth, IAuthData, IUser, IJWT } from '@/components/auth'
import { DangerNotification } from '@/components/notifications'
import { strapiUrl } from '@/app/lib/constants'
import { useAuth, IAuthData, IUser, IJWT } from '@/app/components/auth'
import { DangerNotification } from '@/app/components/notifications'
export type AccessToken = string | null;

View File

@ -3,8 +3,8 @@ import { getVtuberBySlug } from '../lib/vtubers'
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons";
import { faLink } from '@fortawesome/free-solid-svg-icons';
import { projektMelodyEpoch } from '@/lib/constants';
import LinkableHeading from '@/components/linkable-heading';
import { projektMelodyEpoch } from '@/app/lib/constants';
import LinkableHeading from '@/app/components/linkable-heading';
export default async function Page() {
return (

View File

@ -1,4 +1,4 @@
import { generateFeeds } from "@/lib/rss"
import { generateFeeds } from "@/app/lib/rss"
export async function GET() {
const feeds = await generateFeeds()

View File

@ -1,4 +1,4 @@
import { generateFeeds } from "@/lib/rss"
import { generateFeeds } from "@/app/lib/rss"
export async function GET() {
const { atom1 } = await generateFeeds()

View File

@ -1,4 +1,4 @@
import { generateFeeds } from "@/lib/rss"
import { generateFeeds } from "@/app/lib/rss"
export async function GET() {
const { rss2 } = await generateFeeds()

View File

@ -1,5 +1,5 @@
import { getGoals } from "@/lib/pm";
import { getCampaign } from "@/lib/patreon";
import { getGoals } from "@/app/lib/pm";
import { getCampaign } from "@/app/lib/patreon";
interface IFundingStatusBadgeProps {
completedPercentage: number;

View File

@ -1,6 +1,6 @@
import VodsList from '@/components/vods-list';
import { getVods } from '@/lib/vods';
import Pager from '@/components/pager';
import VodsList from '@/app/components/vods-list';
import { getVods } from '@/app/lib/vods';
import Pager from '@/app/components/pager';
interface IPageParams {
params: {

View File

@ -1,8 +1,8 @@
import VodsList from '@/components/vods-list';
import { IVodsResponse } from '@/lib/vods';
import Pager from '@/components/pager';
import { getVods } from '@/lib/vods';
import VodsList from '@/app/components/vods-list';
import { IVodsResponse } from '@/app/lib/vods';
import Pager from '@/app/components/pager';
import { getVods } from '@/app/lib/vods';
interface IPageParams {

View File

@ -1,5 +1,5 @@
import { strapiUrl, patreonVideoAccessBenefitId, giteaUrl } from './constants'
import { IAuthData } from '@/components/auth';
import { IAuthData } from '@/app/components/auth';
export interface IPatron {
username: string;

View File

@ -1,7 +1,7 @@
import { authorName, authorEmail, siteUrl, title, description, siteImage, favicon, authorLink } from './constants'
import { Feed } from "feed";
import { getVods, getUrl, IVod } from '@/lib/vods'
import { ITagVodRelation } from '@/lib/tag-vod-relations';
import { getVods, getUrl, IVod } from '@/app/lib/vods'
import { ITagVodRelation } from '@/app/lib/tag-vod-relations';
export async function generateFeeds() {
const feedOptions = {

View File

@ -2,8 +2,8 @@
import { siteUrl, strapiUrl } from './constants';
import { getSafeDate } from './dates';
import qs from 'qs';
import { IStream } from 'types';
import { IStreamsResponse } from 'types';

View File

@ -10,7 +10,7 @@ import qs from 'qs';
import { strapiUrl } from './constants'
import { ITagResponse, IToyTagResponse } from './tags';
import { IVod, IVodResponse } from './vods';
import { IAuthData } from '@/components/auth';
import { IAuthData } from '@/app/components/auth';
import { IMeta } from 'types';
export interface ITagVodRelation {

View File

@ -3,7 +3,7 @@ import { fetchPaginatedData } from './fetchers';
import { IVod } from './vods';
import slugify from 'slugify';
import { IToy } from './toys';
import { IAuthData } from '@/components/auth';
import { IAuthData } from '@/app/components/auth';
import qs from 'qs';
import { IMeta } from 'types';

View File

@ -2,7 +2,7 @@
import qs from 'qs';
import { strapiUrl } from './constants'
import { IAuthData } from '@/components/auth';
import { IAuthData } from '@/app/components/auth';
import { ITagsResponse, ITag, ITagResponse } from './tags';
import { IMeta } from 'types';

View File

@ -1,5 +1,5 @@
import { ITag, ITagResponse, ITagsResponse } from '@/lib/tags'
import { ITag, ITagResponse, ITagsResponse } from '@/app/lib/tags'
import { IMeta } from 'types';

View File

@ -1,4 +1,4 @@
import { IVtuberResponse } from "./vtubers";
import { IVtuberResponse } from "types";
import { IMeta } from "types";
export interface ITweet {

View File

@ -1,13 +1,12 @@
import { strapiUrl, siteUrl } from './constants';
import { getDateFromSafeDate, getSafeDate } from './dates';
import { IVtuber, IVtuberResponse } from './vtubers';
import { IStream, IStreamResponse } from './streams';
import { IVtuber, IVtuberResponse, IStream, IStreamResponse } from 'types';
import qs from 'qs';
import { ITagVodRelationsResponse } from './tag-vod-relations';
import { ITimestampsResponse } from './timestamps';
import { IMeta, IMuxAsset, IMuxAssetResponse } from 'types';
import { IB2File, IB2FileResponse } from '@/lib/b2File';
import { IB2File, IB2FileResponse } from '@/app/lib/b2File';
import fetchAPI from './fetch-api';
import { IUserResponse } from './users';

View File

@ -3,7 +3,7 @@
import { IVod } from './vods'
import { strapiUrl, siteUrl } from './constants';
import qs from 'qs';
import { IMeta } from 'types';
import { IMeta, IVtuber, IVtubersResponse } from 'types';
const fetchVtubersOptions = {

View File

@ -1,24 +1,25 @@
import FundingGoal from "@/components/funding-goal";
import VodCard from "@/components/vod-card";
import { getVodTitle } from "@/components/vod-page";
import { getVods } from '@/lib/vods';
import { IVod } from "@/lib/vods";
import { getVtubers, IVtuber } from "./lib/vtubers";
import FundingGoal from "@/app/components/funding-goal";
import VodCard from "@/app/components/vod-card";
import { getVodTitle } from "@/app/components/vod-page";
import { getVods } from '@/app/lib/vods';
import { IVod } from "@/app/lib/vods";
import { IVtuber } from 'types';
import { getVtubers } from "./lib/vtubers";
import VTuberCard from "./components/vtuber-card";
import Link from 'next/link';
import { notFound } from "next/navigation";
export default async function Page() {
const vods = await getVods(1, 9, true);
console.log('vods as follows')
console.log(JSON.stringify(vods, null, 2))
// console.log('vods as follows')
// console.log(JSON.stringify(vods, null, 2))
const vtubers = await getVtubers();
if (!vtubers) notFound();
console.log(`vtubers as follows`)
console.log(JSON.stringify(vtubers, null, 2))
// console.log(`vtubers as follows`)
// console.log(JSON.stringify(vtubers, null, 2))

View File

@ -1,7 +1,7 @@
import { getVodsForTag, IVod } from '@/lib/vods'
import VodCard from '@/components/vod-card'
import { getVodsForTag, IVod } from '@/app/lib/vods'
import VodCard from '@/app/components/vod-card'
import Link from 'next/link'
import { getVodTitle } from '@/components/vod-page'
import { getVodTitle } from '@/app/components/vod-page'
import { notFound } from 'next/navigation'
export default async function Page({ params }: { params: { slug: string }}) {

View File

@ -1,11 +1,11 @@
import { getAllVtubers } from '@/lib/vtubers';
import UploadForm from '@/components/upload-form';
import { getAllVtubers } from '@/app/lib/vtubers';
import UploadForm from '@/app/components/upload-form';
import { Suspense } from 'react';
import '@uppy/core/dist/style.min.css';
import '@uppy/dashboard/dist/style.min.css';
import { getStreamByCuid } from '@/lib/streams';
import { getStreamByCuid } from '@/app/lib/streams';
export default async function Page() {

View File

@ -5,7 +5,7 @@ import Uppy from '@uppy/core';
import AwsS3 from '@uppy/aws-s3';
import RemoteSources from '@uppy/remote-sources';
import { useAuth } from './components/auth';
import { companionUrl } from '@/lib/constants';
import { companionUrl } from '@/app/lib/constants';
// Uppy is a challenging react integration. Following are some references

View File

@ -1,6 +1,6 @@
import VodPage from '@/components/vod-page';
import { IVodPageProps, getVodFromSafeDateOrCuid } from '@/lib/vods';
import VodPage from '@/app/components/vod-page';
import { IVodPageProps, getVodFromSafeDateOrCuid } from '@/app/lib/vods';
import { notFound } from 'next/navigation';

View File

@ -1,24 +1,24 @@
import VodsList from '@/components/vods-list';
import VodsList from '@/app/components/vods-list';
import Link from 'next/link';
import { getVtuberBySlug } from '@/lib/vtubers'
import { getVtuberBySlug } from '@/app/lib/vtubers'
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faExternalLinkAlt, faBagShopping } from "@fortawesome/free-solid-svg-icons";
import { faFacebook, faInstagram, faPatreon, faYoutube, faTwitch, faTiktok, faXTwitter, faReddit, faDiscord } from "@fortawesome/free-brands-svg-icons";
import Image from "next/legacy/image";
import OnlyfansIcon from "@/components/icons/onlyfans";
import PornhubIcon from '@/components/icons/pornhub';
import ThroneIcon from '@/components/icons/throne';
import LinktreeIcon from '@/components/icons/linktree';
import FanslyIcon from '@/components/icons/fansly';
import ChaturbateIcon from '@/components/icons/chaturbate';
import CarrdIcon from '@/components/icons/carrd';
import OnlyfansIcon from "@/app/components/icons/onlyfans";
import PornhubIcon from '@/app/components/icons/pornhub';
import ThroneIcon from '@/app/components/icons/throne';
import LinktreeIcon from '@/app/components/icons/linktree';
import FanslyIcon from '@/app/components/icons/fansly';
import ChaturbateIcon from '@/app/components/icons/chaturbate';
import CarrdIcon from '@/app/components/icons/carrd';
import styles from '@/assets/styles/icon.module.css';
import { getVodsForVtuber } from '@/lib/vods';
import { getVodsForVtuber } from '@/app/lib/vods';
import { notFound } from 'next/navigation';
import ArchiveProgress from '@/components/archive-progress';
import { getAllStreamsForVtuber, getStreamsForVtuber } from '@/lib/streams';
import LinkableHeading from '@/components/linkable-heading';
import ArchiveProgress from '@/app/components/archive-progress';
import { getAllStreamsForVtuber, getStreamsForVtuber } from '@/app/lib/streams';
import LinkableHeading from '@/app/components/linkable-heading';

View File

@ -1,7 +1,7 @@
import { Stream } from '@/components/stream';
import { IStream, getStreamForVtuber } from '@/lib/streams';
import { getVtuberBySlug } from '@/lib/vtubers';
import { Stream } from '@/app/components/stream';
import { getStreamForVtuber } from '@/app/lib/streams';
import { getVtuberBySlug } from '@/app/lib/vtubers';
import NotFound from '../../not-found';
interface IPageProps {

View File

@ -1,7 +1,7 @@
import { getVtuberBySlug } from '@/lib/vtubers';
import { getStreamsForVtuber } from '@/lib/streams';
import Pager from '@/components/pager';
import { getVtuberBySlug } from '@/app/lib/vtubers';
import { getStreamsForVtuber } from '@/app/lib/streams';
import Pager from '@/app/components/pager';
import { notFound } from 'next/navigation';
interface IPageParams {

View File

@ -1,9 +1,9 @@
// import VodsList, { VodsListHeading } from '@/components/vods-list'
// import { getVtuberBySlug } from '@/lib/vtubers'
// // import { IToys, getToysForVtuber } from '@/lib/toys'
// import { ToysList, ToysListHeading } from '@/components/toys'
// import Pager from '@/components/pager'
// import VodsList, { VodsListHeading } from '@/app/components/vods-list'
// import { getVtuberBySlug } from '@/app/lib/vtubers'
// // import { IToys, getToysForVtuber } from '@/app/lib/toys'
// import { ToysList, ToysListHeading } from '@/app/components/toys'
// import Pager from '@/app/components/pager'
// interface IPageParams {
// params: {

View File

@ -1,9 +1,9 @@
// import VodsList, { VodsListHeading } from '@/components/vods-list'
// import { getVtuberBySlug } from '@/lib/vtubers'
// // import { IToys, getToysForVtuber } from '@/lib/toys'
// import { ToysList } from '@/components/toys'
// import Pager from '@/components/pager'
// import VodsList, { VodsListHeading } from '@/app/components/vods-list'
// import { getVtuberBySlug } from '@/app/lib/vtubers'
// // import { IToys, getToysForVtuber } from '@/app/lib/toys'
// import { ToysList } from '@/app/components/toys'
// import Pager from '@/app/components/pager'
interface IPageParams {
params: {

View File

@ -1,6 +1,6 @@
import VodPage from '@/components/vod-page'
import { IVodPageProps, getVodFromSafeDateOrCuid } from '@/lib/vods'
import VodPage from '@/app/components/vod-page'
import { IVodPageProps, getVodFromSafeDateOrCuid } from '@/app/lib/vods'
import { notFound } from 'next/navigation';

View File

@ -1,7 +1,7 @@
import VodsList, { VodsListHeading } from '@/components/vods-list';
import { getVtuberBySlug, getUrl } from '@/lib/vtubers';
import { IVodsResponse, getVodsForVtuber } from '@/lib/vods';
import Pager from '@/components/pager';
import VodsList, { VodsListHeading } from '@/app/components/vods-list';
import { getVtuberBySlug, getUrl } from '@/app/lib/vtubers';
import { IVodsResponse, getVodsForVtuber } from '@/app/lib/vods';
import Pager from '@/app/components/pager';
import { notFound } from 'next/navigation';

View File

@ -1,8 +1,8 @@
import VodsList, { VodsListHeading } from '@/components/vods-list'
import { getVtuberBySlug, getUrl } from '@/lib/vtubers'
import { IVodsResponse, getVodsForVtuber, getPaginatedUrl } from '@/lib/vods'
import Pager from '@/components/pager'
import VodsList, { VodsListHeading } from '@/app/components/vods-list'
import { getVtuberBySlug, getUrl } from '@/app/lib/vtubers'
import { IVodsResponse, getVodsForVtuber, getPaginatedUrl } from '@/app/lib/vods'
import Pager from '@/app/components/pager'
import { notFound } from 'next/navigation'
interface IPageParams {

View File

@ -1,6 +1,7 @@
import { notFound } from 'next/navigation'
import VTuberCard from '../components/vtuber-card'
import { getVtubers, IVtuber } from '../lib/vtubers'
import { getVtubers } from '../lib/vtubers'
import { IVtuber } from 'types'
export default async function Page() {

View File

@ -1,49 +1,27 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/components/*": [
"app/components/*"
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"@/lib/*": [
"app/lib/*"
],
"@/assets/*": [
"assets/*"
]
},
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
"paths": {
"@/*": ["./*"]
}
]
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
"dist/types/**/*.ts",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

View File

@ -0,0 +1,51 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/components/*": [
"app/components/*"
],
"@/lib/*": [
"app/lib/*"
],
"@/assets/*": [
"assets/*"
]
},
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
]
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
"dist/types/**/*.ts",
".next/types/**/*.ts",
"./app",
"./assets"
],
"exclude": [
"node_modules"
]
}

View File

@ -47,6 +47,7 @@
"slugify": "^1.6.6",
"ts-node": "^10.9.2",
"tsx": "^4.7.2",
"types": "workspace:^",
"typescript": "^5.4.5",
"xpath": "^0.0.34"
},

View File

@ -95,6 +95,9 @@ importers:
tsx:
specifier: ^4.7.2
version: 4.15.1
types:
specifier: workspace:^
version: link:../types
typescript:
specifier: ^5.4.5
version: 5.4.5

View File

@ -1,43 +0,0 @@
import dotenv from 'dotenv'
dotenv.config({
path: '../../.env'
})
import twitter from './twitter.js'
import fansly from './fansly.js'
/**
* Acquire a vtuber image from the www
*
* Sources preference
* 1. Twitter
* 2. Fansly
*
* Our task is to download an avatar image of the vtuber.
* A slug is good for pulling a record from the database. From there, we can see any social medias such as Twitter or Fansly.
* Twitter is preferred.
*
* We depend on one of these social media URLs. If there is neither Twitter or fansly listed, we throw an error.
*
* @param {Object} vtuber -- vtuber instance from strapi
* @returns {String} filePath -- path on disk where the image was saved
*/
export async function getImage(vtuber) {
if (!vtuber) throw new Error('first arg must be vtuber instance');
const { twitter: twitterUrl, fanslyId: fanslyId } = vtuber.attributes
const twitterUsername = twitterUrl && twitter.regex.username.exec(twitterUrl).at(1)
let img;
if (twitterUrl) {
img = await twitter.data.image(twitterUsername)
} else if (fanslyId) {
img = await fansly.data.image(fanslyId)
} else {
const msg = ` while attempting to get vtuber image, there was neither twitterUrl nor fanslyId listed. One of these must exist for us to download an image. \nvtuber=${JSON.stringify(vtuber, null, 2)}`
console.error(msg)
throw new Error(msg)
}
return img
}

View File

@ -0,0 +1,43 @@
import dotenv from 'dotenv';
dotenv.config({
path: '../../.env',
});
import { type IVtuber } from 'types';
import twitter from './twitter.js';
import fansly from './fansly.js';
/**
* Acquire a vtuber image from the www
*
* Sources preference
* 1. Twitter
* 2. Fansly
*
* Our task is to download an avatar image of the vtuber.
* A slug is good for pulling a record from the database. From there, we can see any social medias such as Twitter or Fansly.
* Twitter is preferred.
*
* We depend on one of these social media URLs. If there is neither Twitter nor Fansly listed, we throw an error.
*
* @param {IVtuber} vtuber -- vtuber instance from Strapi
* @returns {string} filePath -- path on disk where the image was saved
*/
export async function getImage(vtuber: IVtuber): Promise<string> {
if (!vtuber) throw new Error('first arg must be vtuber instance');
const { twitter: twitterUrl, fanslyId: fanslyId } = vtuber.attributes;
const twitterUsername = twitterUrl && twitterUrl.match(/@(\w+)/)?.[1];
let img: string;
if (twitterUrl) {
img = await twitter.data.image(twitterUsername);
} else if (fanslyId) {
img = await fansly.data.image(fanslyId);
} else {
const msg = `while attempting to get vtuber image, there was neither twitterUrl nor fanslyId listed. One of these must exist for us to download an image.\nvtuber=${JSON.stringify(vtuber, null, 2)}`;
console.error(msg);
throw new Error(msg);
}
return img;
}

View File

@ -1,30 +1,31 @@
// {
// "compilerOptions": {
// "outDir": "./built",
// "allowJs": true,
// "target": "ES2017",
// "module": "NodeNext"
// },
// "include": ["./src/**/*"]
// }
{
"extends": "@tsconfig/node20/tsconfig.json",
"version": "4.4.2",
"compilerOptions": {
"target": "es2017",
"module": "esnext",
"moduleResolution": "node",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["**/*.ts"],
"ts-node": {
"experimentalSpecifierResolution": "node",
"transpileOnly": true,
"esm": true,
// Base Options recommended for all projects
"esModuleInterop": true,
"skipLibCheck": true,
"target": "es2022",
"allowJs": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"isolatedModules": true,
"verbatimModuleSyntax": true,
// Enable strict type checking so you can catch bugs early
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
// Transpile our TypeScript code to JavaScript
"module": "NodeNext",
"outDir": "dist",
"lib": [
"es2022"
]
},
// Include the necessary files for your project
"include": [
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
}

View File

@ -14,7 +14,7 @@
"noImplicitOverride": true,
// Transpile our TypeScript code to JavaScript
"module": "NodeNext",
"outDir": "lib",
"outDir": "dist",
"lib": [
"es2022"
]

View File

@ -1,5 +1,6 @@
{
"name": "temporal-worker",
"type": "module",
"version": "0.0.0",
"private": true,
"scripts": {

View File

@ -5,7 +5,7 @@
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./lib"
"outDir": "dist"
},
"include": ["**/*.ts"]
}

View File

@ -1,5 +1,5 @@
import { Worker } from '@temporalio/worker';
import * as activities from '../temporal-workflows/src/all-activities';
import * as activities from '../temporal-workflows/src/all-activities.js';
async function run() {
// Step 1: Register Workflows and Activities with the Worker and connect to

View File

@ -11,6 +11,7 @@
"dependencies": {
"@temporalio/activity": "^1.9.0",
"@temporalio/workflow": "^1.9.0",
"@types/qs": "^6.9.15",
"date-fns": "^3.6.0",
"image": "workspace:*",
"qs": "^6.12.3",

View File

@ -14,6 +14,9 @@ importers:
'@temporalio/workflow':
specifier: ^1.9.0
version: 1.10.1
'@types/qs':
specifier: ^6.9.15
version: 6.9.15
date-fns:
specifier: ^3.6.0
version: 3.6.0
@ -151,6 +154,9 @@ packages:
'@types/node@20.14.10':
resolution: {integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==}
'@types/qs@6.9.15':
resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==}
'@types/semver@7.5.8':
resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
@ -959,6 +965,8 @@ snapshots:
dependencies:
undici-types: 5.26.5
'@types/qs@6.9.15': {}
'@types/semver@7.5.8': {}
'@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@4.9.5))(eslint@7.32.0)(typescript@4.9.5)':

View File

@ -1,4 +1,4 @@
import { IStreamResponse } from 'types';
import { IStreamResponse, IStreamsResponse } from 'types';
import { subMinutes, addMinutes } from 'date-fns';
import qs from 'qs'
@ -53,22 +53,24 @@ export async function upsertStream({
'Content-Type': 'application/json'
}
})
const findStreamData = await findStreamRes.json() as IStreamResponse
const findStreamData = await findStreamRes.json() as IStreamsResponse
if (findStreamData?.data && findStreamData.data.length > 0) {
console.log('>> we found a findStreamData json. (there is an existing stream for this e-mail/notification)')
console.log(JSON.stringify(findStreamData, null, 2))
streamId = findStreamData.data[0].id
const stream = findStreamData.data[0]
if (!stream) throw new Error('stream was undefined');
streamId = stream.id
// Before we're done here, we need to do something extra. We need to populate isChaturbateStream and/or isFanslyStream.
// We know which of these booleans to set based on the stream's related platformNotifications
// We go through each pNotif and look at it's platform
let isFanslyStream = false
let isChaturbateStream = false
if (findStreamData.data[0].attributes.platformNotifications) {
for (const pn of findStreamData.data[0].attributes.platformNotifications) {
if (pn.platform === 'fansly') {
if (stream.attributes.platformNotifications) {
for (const pn of stream.attributes.platformNotifications) {
if (pn.attributes.platform === 'fansly') {
isFanslyStream = true
} else if (pn.platform === 'chaturbate') {
} else if (pn.attributes.platform === 'chaturbate') {
isChaturbateStream = true
}
}
@ -131,7 +133,7 @@ export async function upsertStream({
console.error(JSON.stringify(createStreamJson.error, null, 2))
throw new Error('Failed to create stream in DB due to an error. (see above)')
}
streamId = createStreamJson.id
streamId = createStreamJson.data.id
}
if (!streamId) throw new Error('failed to get streamId')

View File

@ -1,4 +1,4 @@
import { NotificationData, IVtubersResponse, IVtuberResponse } from 'types';
import { NotificationData, IVtubersResponse, IVtuberResponse, IVtuber } from 'types';
import { fpSlugify } from 'utils';
import qs from 'qs';
import { getProminentColor } from 'image';
@ -8,7 +8,7 @@ import { uploadFile } from 'storage/s3.js';
/**
* find or create vtuber in Strapi
*/
export async function upsertVtuber({ platform, userId, url, channel }: NotificationData): Promise<number> {
export async function upsertVtuber({ platform, userId, url, channel }: { platform: string, userId: string | null, url: string, channel: string }): Promise<number> {
let vtuberId
console.log('>> # Step 1, upsertVtuber')
@ -74,10 +74,16 @@ export async function upsertVtuber({ platform, userId, url, channel }: Notificat
// download image from platform
// vtuber.getImage expects a vtuber object, which we don't have yet, so we create a dummy one
const dummyVtuber = {
const dummyVtuber: IVtuber = {
id: 69,
attributes: {
slug: fpSlugify(channel),
fanslyId: (platform === 'fansly') ? userId : null
displayName: 'example',
vods: [],
description1: ' ',
image: ' ',
themeColor: ' ',
fanslyId: (platform === 'fansly') ? (userId ? userId : undefined) : undefined
}
}
const imageFile = await getImage(dummyVtuber)

View File

@ -1,8 +1,9 @@
import { proxyActivities, sleep, log } from '@temporalio/workflow';
import { NotificationData } from 'types';
// Only import the activity types
import type * as upsertVtuberType from './activities/upsertVtuber';
import type * as upsertStreamType from './activities/upsertStream';
import type * as upsertPlatformNotificationType from './activities/upsertPlatformNotification';
import type * as upsertVtuberType from './activities/upsertVtuber.js';
import type * as upsertStreamType from './activities/upsertStream.js';
import type * as upsertPlatformNotificationType from './activities/upsertPlatformNotification.js';
const { upsertVtuber } = proxyActivities<typeof upsertVtuberType>({
startToCloseTimeout: '1 minute',
@ -14,10 +15,10 @@ const { upsertPlatformNotification } = proxyActivities<typeof upsertPlatformNoti
startToCloseTimeout: '1 minute',
});
export async function WorkflowA(name: string): Promise<string> {
log.info('Hello from processNotifEmail');
const res1 = await upsertVtuber(name);
const res2 = await upsertStream(name);
const res3 = await upsertPlatformNotification(name);
return `A: ${res1} | B: ${res2} | C: ${res3}`;
export async function processNotifEmail({ url, platform, channel, displayName, date, userId, avatar }: NotificationData): Promise<string> {
log.info('Hello from processNotifEmail workflow');
const vtuberId = await upsertVtuber({ platform, userId, url, channel });
const pNotifId = await upsertPlatformNotification({ source: 'email', date, platform, vtuberId });
const streamId = await upsertStream({ date, vtuberId, platform, pNotifId });
return `vtuberId: ${vtuberId} | pNotifId: ${pNotifId} | streamId: ${streamId}`;
}

View File

@ -1,7 +1,7 @@
import { proxyActivities, sleep, log } from '@temporalio/workflow';
// Only import the activity types
import type * as activitiesA from './activities/activitiesA';
import type * as activitiesB from './activities/activitiesB';
import type * as activitiesA from './activities/activitiesA.js';
import type * as activitiesB from './activities/activitiesB.js';
const { activityA } = proxyActivities<typeof activitiesA>({
startToCloseTimeout: '1 minute',

View File

@ -14,7 +14,7 @@
"noImplicitOverride": true,
// Transpile our TypeScript code to JavaScript
"module": "NodeNext",
"outDir": "lib",
"outDir": "dist",
"lib": [
"es2022"
]

View File

@ -57,6 +57,7 @@ export interface IStream {
tweet: ITweetResponse;
isChaturbateStream: boolean;
isFanslyStream: boolean;
platformNotifications: IPlatformNotification[];
}
}

View File

@ -6,7 +6,7 @@
"declarationMap": true,
"sourceMap": true,
"rootDir": "./src",
"outDir": "./lib"
"outDir": "dist"
},
"files": ["index.d.ts"]
}

View File

@ -1,4 +1,4 @@
import * as slugify from 'slugify';
import slugify from './src/slugifyFix.js';
import { basename, join } from 'path';
import os from 'node:os';
import fs from 'node:fs';
@ -42,11 +42,12 @@ export async function download({ url, filePath }: { url: string; filePath?: stri
});
const { body } = response;
if (!body) throw new Error('body was null');
await finished(Readable.fromWeb(body).pipe(stream));
// Abort retrying if the resource doesn't exist
if (response.status === 404) {
throw new AbortError(response.statusText);
throw new Error(response.statusText);
}
return;

View File

@ -11,7 +11,9 @@
"author": "",
"license": "Unlicense",
"dependencies": {
"@paralleldrive/cuid2": "^2.2.2",
"@types/node": "^20.14.9",
"p-retry": "^5.1.2",
"scout": "workspace:^",
"slugify": "^1.6.6"
},

View File

@ -8,9 +8,15 @@ importers:
.:
dependencies:
'@paralleldrive/cuid2':
specifier: ^2.2.2
version: 2.2.2
'@types/node':
specifier: ^20.14.9
version: 20.14.10
p-retry:
specifier: ^5.1.2
version: 5.1.2
scout:
specifier: workspace:^
version: link:../scout
@ -50,6 +56,13 @@ packages:
'@jridgewell/trace-mapping@0.3.9':
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
'@noble/hashes@1.4.0':
resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==}
engines: {node: '>= 16'}
'@paralleldrive/cuid2@2.2.2':
resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==}
'@tsconfig/node10@1.0.11':
resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
@ -71,6 +84,9 @@ packages:
'@types/node@20.14.10':
resolution: {integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==}
'@types/retry@0.12.1':
resolution: {integrity: sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==}
acorn-walk@8.3.3:
resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==}
engines: {node: '>=0.4.0'}
@ -319,6 +335,10 @@ packages:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'}
p-retry@5.1.2:
resolution: {integrity: sha512-couX95waDu98NfNZV+i/iLt+fdVxmI7CbrrdC2uDWfPdUAApyxT4wmDlyOtR5KtTDmkDO0zDScDjDou9YHhd9g==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@ -342,6 +362,10 @@ packages:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
retry@0.13.1:
resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
engines: {node: '>= 4'}
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
@ -450,6 +474,12 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
'@noble/hashes@1.4.0': {}
'@paralleldrive/cuid2@2.2.2':
dependencies:
'@noble/hashes': 1.4.0
'@tsconfig/node10@1.0.11': {}
'@tsconfig/node12@1.0.11': {}
@ -466,6 +496,8 @@ snapshots:
dependencies:
undici-types: 5.26.5
'@types/retry@0.12.1': {}
acorn-walk@8.3.3:
dependencies:
acorn: 8.12.1
@ -693,6 +725,11 @@ snapshots:
dependencies:
p-limit: 3.1.0
p-retry@5.1.2:
dependencies:
'@types/retry': 0.12.1
retry: 0.13.1
path-exists@4.0.0: {}
pathval@2.0.0: {}
@ -709,6 +746,8 @@ snapshots:
require-directory@2.1.1: {}
retry@0.13.1: {}
safe-buffer@5.2.1: {}
serialize-javascript@6.0.2:

View File

@ -0,0 +1,8 @@
/**
* Hack to make 'slugify' import work with "type": "module".
* @see https://github.com/simov/slugify/issues/173
*/
import slugify from 'slugify'
export default slugify as unknown as typeof slugify.default

View File

@ -14,7 +14,7 @@
"noImplicitOverride": true,
// Transpile our TypeScript code to JavaScript
"module": "NodeNext",
"outDir": "lib",
"outDir": "dist",
"lib": [
"es2022"
]