Api tests added

This commit is contained in:
Fergal Moran
2023-07-02 13:37:36 +01:00
parent 8fae876184
commit 9f5a79bf6c
13 changed files with 145 additions and 7187 deletions

27
.env.testing Normal file
View File

@@ -0,0 +1,27 @@
#DATABASE_URL='mysql://79vu28u4opkh0ztv2qot:pscale_pw_LYNVUN5KF8RpUwQsIJnqCsMW2BeO9hTEfvpxZdq8289@aws.connect.psdb.cloud/mixyboos?sslaccept=strict'
DATABASE_URL='postgresql://postgres:hackme@localhost/mixyboosv2'
LIVE_HOST=https://live-mixyboos.dev.fergl.ie:9091
STREAM_HOST=rtmp://live-mixyboos.dev.fergl.ie:1935/live/
JWT_SECRET=W2XM83uLCMcxNnNaL0EQQ+rt+9Su+yAZ1DDQGlXwCL0=
GOOGLE_CLIENT_ID="760846434166-kpql57gbldqbbb0n7bp4lf62rtcok5bj.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET="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"
NEXTAUTH_SECRET="a/XTFzkdmVTfN8A9tOdHgFjUkZLxD/ju85E0Do2obCM="
_NEXTAUTH_URL="https://mixyboos.dev.fergl.ie:3000"
NEXTAUTH_URL="http://localhost:3000"
AZURE_ACCOUNT_NAME=mixyboos
AZURE_ACCOUNT_URL="https://mixyboos.blob.core.windows.net"
AZURE_ACCOUNT_KEY=Q65RK6QxP4nJuUt358RKbdPrMiz1QBT4wMz3/VfHFFvpZaK6d9ifJtzgcVEmY1qTVD8gdRQBF1i6+ASt9pkcJQ==
PUSHER_APPID="1600127"
PUSHER_KEY="5de20a9065b26377af84"
PUSHER_SECRET="7b2d304f71134aa358fe"
NEXT_PUBLIC_PUSHER_KEY=$PUSHER_KEY
NEXT_PUBLIC_LIVE_HOST=$LIVE_HOST
QUIRREL_BASE_URL=https://mixyboos.dev.fergl.ie:3000/
UPLOADTHING_SECRET=sk_live_ae93756f60a4ddbfe72d9425d610bf435f1ee0fda2472d294a39a9f856f0d83b
UPLOADTHING_APP_ID=n1karmceay

11
jest.setup.js Normal file
View File

@@ -0,0 +1,11 @@
import {loadEnvConfig} from '@next/env'
import next from 'next'
next({})
export default async () => {
const projectDir = process.cwd()
loadEnvConfig(projectDir)
import next from 'next'
next({})
}

View File

@@ -10,12 +10,13 @@
"quirrel": "quirrel",
"postinstall": "prisma generate",
"lint": "next lint",
"start": "next start"
"start": "next start",
"test": "export $(xargs < .env) && jest"
},
"dependencies": {
"@headlessui/react": "^1.7.15",
"@next-auth/prisma-adapter": "^1.0.7",
"@prisma/client": "^4.16.1",
"@prisma/client": "^4.16.2",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-toast": "^1.1.4",
@@ -30,6 +31,7 @@
"axios": "^1.4.0",
"classnames": "^2.3.2",
"http-status-codes": "^2.2.0",
"import-local": "^3.1.0",
"install": "^0.13.0",
"next": "13.4.7",
"next-auth": "^4.22.1",
@@ -48,6 +50,7 @@
"@faker-js/faker": "^8.0.2",
"@hookform/resolvers": "^3.1.1",
"@ianvs/prettier-plugin-sort-imports": "^4.0.2",
"@next/env": "^13.4.7",
"@radix-ui/react-avatar": "^1.0.3",
"@radix-ui/react-dropdown-menu": "^2.0.5",
"@radix-ui/react-label": "^2.0.2",
@@ -55,6 +58,8 @@
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.4",
"@types/eslint": "^8.40.2",
"@types/jest": "^29.5.2",
"@types/mime-types": "^2.1.1",
"@types/node": "^20.3.2",
"@types/prettier": "^2.7.3",
"@types/react": "^18.2.14",
@@ -71,12 +76,14 @@
"eslint": "^8.43.0",
"eslint-config-next": "^13.4.7",
"hls.js": "^1.4.6",
"jest": "^29.5.0",
"lucide-react": "^0.252.0",
"next-themes": "^0.2.1",
"node-mocks-http": "^1.12.2",
"postcss": "^8.4.24",
"prettier": "^2.8.8",
"prettier-plugin-tailwindcss": "^0.3.0",
"prisma": "4.16.1",
"prisma": "^4.16.2",
"pusher": "^5.1.3",
"pusher-js": "^8.2.0",
"react-copy-to-clipboard": "^5.1.0",
@@ -88,8 +95,11 @@
"tailwind-merge": "^1.13.2",
"tailwindcss": "^3.3.2",
"tailwindcss-animate": "^1.0.6",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",
"typescript": "^5.1.3",
"uuid": "^9.0.0"
"uuid": "^9.0.0",
"vitest": "^0.32.2"
},
"ct3aMetadata": {
"initVersion": "7.12.2"

7130
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

View File

@@ -13,7 +13,7 @@ import ImageUpload from "@/components/widgets/image-upload";
import { notice } from "@/lib/components/notifications/toast";
import { type UserModel } from "@/lib/models";
import { uploadFile } from "@/lib/services/azure/upload";
import { getFileExtension } from "@/lib/services/utils/fileUtils";
import { getFileExtension } from "@/lib/utils/fileUtils";
import { api } from "@/lib/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { useSession } from "next-auth/react";

View File

@@ -2,8 +2,7 @@ import { spawn } from "child_process";
import fs from "fs";
import os from "os";
import path from "path";
import { generateSasToken } from "@/lib/services/azure/sas-token";
import { uploadFile, uploadFolder } from "@/lib/services/azure/upload";
import { uploadFolder } from "@/lib/services/azure/serverUploader";
import { Queue } from "quirrel/next-app";
export const processMixQueue = Queue(
@@ -39,13 +38,13 @@ export const processMixQueue = Queue(
if (code !== 0) {
return;
}
generateSasToken("mixyboos", "audio")
.then((token) => {
console.log("upload/mix/route", "SAS TOKEN", token);
uploadFolder(outputDir, "audio", mixId, token);
uploadFolder(outputDir, "audio", path.join("mixes", mixId))
.then((r) => {
//probably need to tag the mix in some way here?
})
.catch((err) => {
console.log("route", "Error uploading folder", err);
console.log("route", "error uploading output folder", err);
});
});
}

View File

@@ -1,6 +1,7 @@
import { writeFile } from "fs/promises";
import { getFileExtension } from "@/lib/services/utils/fileUtils";
import { getFileExtension } from "@/lib/utils/fileUtils";
import { NextResponse, type NextRequest } from "next/server";
import { processMixQueue } from "../queues/upload/mix/route";
export async function POST(req: NextRequest) {
const data = await req.formData();
@@ -17,5 +18,6 @@ export async function POST(req: NextRequest) {
const path = `/tmp/${id}.${getFileExtension(file.name)}`;
await writeFile(path, buffer);
console.log(`open ${path} to see the uploaded file`);
await processMixQueue.enqueue({ filePath: path, mixId: id }, { delay: 1 });
return NextResponse.json({ success: true });
}

View File

@@ -1,4 +1,4 @@
import { getFileNameFromInput } from "@/lib/services/utils/fileUtils";
import { getFilename } from "@/lib/utils/fileUtils";
import axios, { type AxiosProgressEvent, type AxiosRequestConfig } from "axios";
import { StatusCodes } from "http-status-codes";
import React from "react";
@@ -33,7 +33,7 @@ const FileUpload = ({
onUploadProgress(progressEvent.total ?? 0, progressEvent.loaded);
},
};
onUploadStart(getFileNameFromInput(event.currentTarget.files[0].name));
onUploadStart(getFilename(event.currentTarget.files[0].name));
const result = await axios.post("/api/upload", formData, options);
if (result.status === StatusCodes.OK) {

View File

@@ -1,35 +1,55 @@
import { env } from "@/env.mjs";
import fs from "fs/promises";
import path from "path";
import * as Sentry from "@sentry/nextjs";
import {
BlockBlobClient,
BlobServiceClient,
StorageSharedKeyCredential,
} from "@azure/storage-blob";
import { StatusCodes } from "http-status-codes";
const uploadFile = (container: string) => {
const client = new BlockBlobClient(
`${env.AZURE_ACCOUNT_URL}/${container}`,
const uploadFile = async (
file: string,
container: string,
destination: string
): Promise<boolean> => {
const client = new BlobServiceClient(
`${process.env.AZURE_ACCOUNT_URL as string}`,
new StorageSharedKeyCredential(
env.AZURE_ACCOUNT_NAME,
env.AZURE_ACCOUNT_KEY
process.env.AZURE_ACCOUNT_NAME as string,
process.env.AZURE_ACCOUNT_KEY as string
)
);
const containerClient = client.getContainerClient(container);
const blockBlobClient = containerClient.getBlockBlobClient(destination);
const response = await blockBlobClient.uploadFile(file);
return response._response.status === StatusCodes.OK;
};
// const uploadFolder = (dir: string, container: string, subFolder: string) => {
// fs.readdir(dir, (err, files) => {
// console.log("upload", "uploadFolder", err, files);
const uploadFolder = async (
dir: string,
container: string,
subFolder: string
) => {
const files = await fs.readdir(dir);
console.log("upload", "uploadFolder", files);
for (const file of files) {
try {
const destinationFile = path.join(subFolder, file);
const r = await uploadFile(
path.join(dir, file),
container,
destinationFile
);
console.log("upload", "File uploaded", r);
} catch (err) {
Sentry.captureException(err);
console.log("upload", "Error uploading", err);
return false;
}
}
return true;
};
// files.forEach((file) => {
// const f = new File(file);
// uploadFile(f, containerName, `${subFolder}/${f.name}`)
// .then((r) => {
// console.log("upload", "File uploaded", r);
// })
// .catch((err) => {
// console.log("upload", "Error uploading", err);
// });
// });
// });
// };
export { uploadFolder };
export { uploadFolder, uploadFile };

View File

@@ -1,15 +0,0 @@
const getFileExtension = (fileName: string): string =>
fileName.split(".").pop() as string;
const getFileNameFromInput = (fullPath: string): string => {
const startIndex =
fullPath.indexOf("\\") >= 0
? fullPath.lastIndexOf("\\")
: fullPath.lastIndexOf("/");
let filename = fullPath.substring(startIndex);
if (filename.indexOf("\\") === 0 || filename.indexOf("/") === 0) {
filename = filename.substring(1);
}
return filename.replace(/\.[^/.]+$/, "");
};
export { getFileExtension, getFileNameFromInput };

View File

@@ -1,7 +1,24 @@
import { v4 as uuidv4 } from "uuid";
import {v4 as uuidv4} from "uuid";
import * as crypto from "crypto";
const generateSecretKey = (): string | undefined =>
crypto.createHmac("sha256", uuidv4()).digest("hex");
export { generateSecretKey };
const generateRandomBytes = async (size: number) => {
const buffer: crypto.BinaryLike = await new Promise((resolve, reject) => {
crypto.randomBytes(size, function (ex, buffer) {
if (ex) {
reject("error generating token");
}
resolve(buffer);
});
});
const token = crypto
.createHash("sha1")
.update(buffer)
.digest("hex");
return token;
}
export {generateSecretKey, generateRandomBytes};

View File

@@ -1,4 +1,9 @@
import { createTRPCRouter, publicProcedure } from "@/server/api/trpc";
import {
createTRPCRouter,
protectedProcedure,
publicProcedure,
} from "@/server/api/trpc";
import * as z from "zod";
export const mixRouter = createTRPCRouter({
getAll: publicProcedure.query(({ ctx }) => {
@@ -8,4 +13,13 @@ export const mixRouter = createTRPCRouter({
});
return mixes;
}),
createMix: protectedProcedure
.input(
z.object({
title: z.string(),
description: z.string(),
tags: z.array(z.string()).optional(),
})
)
.mutation(async ({ input: { title, description, tags }, ctx }) => {}),
});