Move next js project to archive (#207)

This commit is contained in:
Aaron Po
2026-04-20 02:30:25 -04:00
committed by GitHub
parent 92ec16ce93
commit d47e3ed7f0
347 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
import { PrismaClient } from '@prisma/client';
import { NODE_ENV } from '../config/env';
const globalForPrisma = global as unknown as { prisma: PrismaClient };
const DBClient = {
instance:
globalForPrisma.prisma ||
new PrismaClient({
log: ['info', 'warn'],
}),
};
if (NODE_ENV !== 'production') {
globalForPrisma.prisma = DBClient.instance;
}
export default DBClient;

View File

@@ -0,0 +1,187 @@
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"username" TEXT NOT NULL,
"firstName" TEXT NOT NULL,
"lastName" TEXT NOT NULL,
"hash" TEXT NOT NULL,
"email" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
"isAccountVerified" BOOLEAN NOT NULL DEFAULT false,
"dateOfBirth" TIMESTAMP(3) NOT NULL,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "BeerPost" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"ibu" DOUBLE PRECISION NOT NULL,
"abv" DOUBLE PRECISION NOT NULL,
"description" TEXT NOT NULL,
"postedById" TEXT NOT NULL,
"breweryId" TEXT NOT NULL,
"typeId" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
CONSTRAINT "BeerPost_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "BeerPostLike" (
"id" TEXT NOT NULL,
"beerPostId" TEXT NOT NULL,
"likedById" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
CONSTRAINT "BeerPostLike_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "BreweryPostLike" (
"id" TEXT NOT NULL,
"breweryPostId" TEXT NOT NULL,
"likedById" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
CONSTRAINT "BreweryPostLike_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "BeerComment" (
"id" TEXT NOT NULL,
"rating" INTEGER NOT NULL,
"beerPostId" TEXT NOT NULL,
"postedById" TEXT NOT NULL,
"content" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
CONSTRAINT "BeerComment_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "BeerType" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
"postedById" TEXT NOT NULL,
CONSTRAINT "BeerType_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Location" (
"id" TEXT NOT NULL,
"city" TEXT NOT NULL,
"stateOrProvince" TEXT,
"country" TEXT,
"coordinates" DOUBLE PRECISION [],
"address" TEXT NOT NULL,
"postedById" TEXT NOT NULL,
CONSTRAINT "Location_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "BreweryPost" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"locationId" TEXT NOT NULL,
"description" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
"postedById" TEXT NOT NULL,
"dateEstablished" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "BreweryPost_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "BreweryComment" (
"id" TEXT NOT NULL,
"rating" INTEGER NOT NULL,
"breweryPostId" TEXT NOT NULL,
"postedById" TEXT NOT NULL,
"content" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
CONSTRAINT "BreweryComment_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "BeerImage" (
"id" TEXT NOT NULL,
"beerPostId" TEXT NOT NULL,
"path" TEXT NOT NULL,
"alt" TEXT NOT NULL,
"caption" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
"postedById" TEXT NOT NULL,
CONSTRAINT "BeerImage_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "BreweryImage" (
"id" TEXT NOT NULL,
"breweryPostId" TEXT NOT NULL,
"path" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
"caption" TEXT NOT NULL,
"alt" TEXT NOT NULL,
"postedById" TEXT NOT NULL,
CONSTRAINT "BreweryImage_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
-- CreateIndex
CREATE UNIQUE INDEX "BreweryPost_locationId_key" ON "BreweryPost"("locationId");
-- AddForeignKey
ALTER TABLE "BeerPost"
ADD CONSTRAINT "BeerPost_postedById_fkey" FOREIGN KEY ("postedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BeerPost"
ADD CONSTRAINT "BeerPost_breweryId_fkey" FOREIGN KEY ("breweryId") REFERENCES "BreweryPost"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BeerPost"
ADD CONSTRAINT "BeerPost_typeId_fkey" FOREIGN KEY ("typeId") REFERENCES "BeerType"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BeerPostLike"
ADD CONSTRAINT "BeerPostLike_beerPostId_fkey" FOREIGN KEY ("beerPostId") REFERENCES "BeerPost"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BeerPostLike"
ADD CONSTRAINT "BeerPostLike_likedById_fkey" FOREIGN KEY ("likedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BreweryPostLike"
ADD CONSTRAINT "BreweryPostLike_breweryPostId_fkey" FOREIGN KEY ("breweryPostId") REFERENCES "BreweryPost"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BreweryPostLike"
ADD CONSTRAINT "BreweryPostLike_likedById_fkey" FOREIGN KEY ("likedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BeerComment"
ADD CONSTRAINT "BeerComment_beerPostId_fkey" FOREIGN KEY ("beerPostId") REFERENCES "BeerPost"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BeerComment"
ADD CONSTRAINT "BeerComment_postedById_fkey" FOREIGN KEY ("postedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BeerType"
ADD CONSTRAINT "BeerType_postedById_fkey" FOREIGN KEY ("postedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Location"
ADD CONSTRAINT "Location_postedById_fkey" FOREIGN KEY ("postedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BreweryPost"
ADD CONSTRAINT "BreweryPost_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "Location"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BreweryPost"
ADD CONSTRAINT "BreweryPost_postedById_fkey" FOREIGN KEY ("postedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BreweryComment"
ADD CONSTRAINT "BreweryComment_breweryPostId_fkey" FOREIGN KEY ("breweryPostId") REFERENCES "BreweryPost"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BreweryComment"
ADD CONSTRAINT "BreweryComment_postedById_fkey" FOREIGN KEY ("postedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BeerImage"
ADD CONSTRAINT "BeerImage_beerPostId_fkey" FOREIGN KEY ("beerPostId") REFERENCES "BeerPost"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BeerImage"
ADD CONSTRAINT "BeerImage_postedById_fkey" FOREIGN KEY ("postedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BreweryImage"
ADD CONSTRAINT "BreweryImage_breweryPostId_fkey" FOREIGN KEY ("breweryPostId") REFERENCES "BreweryPost"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BreweryImage"
ADD CONSTRAINT "BreweryImage_postedById_fkey" FOREIGN KEY ("postedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,9 @@
/*
Warnings:
- You are about to drop the column `isAccountVerified` on the `User` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "User" DROP COLUMN "isAccountVerified",
ADD COLUMN "accountIsVerified" BOOLEAN NOT NULL DEFAULT false;

View File

@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "Location" ADD COLUMN "createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
ADD COLUMN "updatedAt" TIMESTAMPTZ(3);

View File

@@ -0,0 +1,38 @@
/*
Warnings:
- You are about to drop the column `typeId` on the `BeerPost` table. All the data in the column will be lost.
- You are about to drop the `BeerType` table. If the table is not empty, all the data it contains will be lost.
- Added the required column `styleId` to the `BeerPost` table without a default value. This is not possible if the table is not empty.
*/
-- DropForeignKey
ALTER TABLE "BeerPost" DROP CONSTRAINT "BeerPost_typeId_fkey";
-- DropForeignKey
ALTER TABLE "BeerType" DROP CONSTRAINT "BeerType_postedById_fkey";
-- AlterTable
ALTER TABLE "BeerPost" DROP COLUMN "typeId",
ADD COLUMN "styleId" TEXT NOT NULL;
-- DropTable
DROP TABLE "BeerType";
-- CreateTable
CREATE TABLE "BeerStyle" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
"postedById" TEXT NOT NULL,
CONSTRAINT "BeerStyle_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "BeerPost" ADD CONSTRAINT "BeerPost_styleId_fkey" FOREIGN KEY ("styleId") REFERENCES "BeerStyle"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BeerStyle" ADD CONSTRAINT "BeerStyle_postedById_fkey" FOREIGN KEY ("postedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,28 @@
/*
Warnings:
- Added the required column `glasswareId` to the `BeerStyle` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "BeerStyle" ADD COLUMN "abvRange" DOUBLE PRECISION[],
ADD COLUMN "glasswareId" TEXT NOT NULL,
ADD COLUMN "ibuRange" DOUBLE PRECISION[];
-- CreateTable
CREATE TABLE "Glassware" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
"postedById" TEXT NOT NULL,
CONSTRAINT "Glassware_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "BeerStyle" ADD CONSTRAINT "BeerStyle_glasswareId_fkey" FOREIGN KEY ("glasswareId") REFERENCES "Glassware"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Glassware" ADD CONSTRAINT "Glassware_postedById_fkey" FOREIGN KEY ("postedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,5 @@
-- CreateEnum
CREATE TYPE "Role" AS ENUM ('USER', 'ADMIN');
-- AlterTable
ALTER TABLE "User" ADD COLUMN "role" "Role" NOT NULL DEFAULT 'USER';

View File

@@ -0,0 +1,35 @@
-- CreateTable
CREATE TABLE "BeerStyleLike" (
"id" TEXT NOT NULL,
"beerStyleId" TEXT NOT NULL,
"likedById" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
CONSTRAINT "BeerStyleLike_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "BeerStyleComment" (
"id" TEXT NOT NULL,
"rating" INTEGER NOT NULL,
"beerStyleId" TEXT NOT NULL,
"postedById" TEXT NOT NULL,
"content" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
CONSTRAINT "BeerStyleComment_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "BeerStyleLike" ADD CONSTRAINT "BeerStyleLike_beerStyleId_fkey" FOREIGN KEY ("beerStyleId") REFERENCES "BeerStyle"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BeerStyleLike" ADD CONSTRAINT "BeerStyleLike_likedById_fkey" FOREIGN KEY ("likedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BeerStyleComment" ADD CONSTRAINT "BeerStyleComment_beerStyleId_fkey" FOREIGN KEY ("beerStyleId") REFERENCES "BeerStyle"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BeerStyleComment" ADD CONSTRAINT "BeerStyleComment_postedById_fkey" FOREIGN KEY ("postedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,57 @@
/*
Warnings:
- You are about to drop the `Location` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE "BreweryPost" DROP CONSTRAINT "BreweryPost_locationId_fkey";
-- DropForeignKey
ALTER TABLE "Location" DROP CONSTRAINT "Location_postedById_fkey";
-- AlterTable
ALTER TABLE "User" ADD COLUMN "bio" TEXT;
-- DropTable
DROP TABLE "Location";
-- CreateTable
CREATE TABLE "UserAvatar" (
"id" TEXT NOT NULL,
"path" TEXT NOT NULL,
"alt" TEXT NOT NULL,
"caption" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
CONSTRAINT "UserAvatar_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "BreweryLocation" (
"id" TEXT NOT NULL,
"city" TEXT NOT NULL,
"stateOrProvince" TEXT,
"country" TEXT,
"coordinates" DOUBLE PRECISION[],
"address" TEXT NOT NULL,
"postedById" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3),
CONSTRAINT "BreweryLocation_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "UserAvatar_userId_key" ON "UserAvatar"("userId");
-- AddForeignKey
ALTER TABLE "UserAvatar" ADD CONSTRAINT "UserAvatar_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BreweryLocation" ADD CONSTRAINT "BreweryLocation_postedById_fkey" FOREIGN KEY ("postedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BreweryPost" ADD CONSTRAINT "BreweryPost_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "BreweryLocation"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@@ -0,0 +1,14 @@
-- CreateTable
CREATE TABLE "UserFollow" (
"followerId" TEXT NOT NULL,
"followingId" TEXT NOT NULL,
"followedAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "UserFollow_pkey" PRIMARY KEY ("followerId","followingId")
);
-- AddForeignKey
ALTER TABLE "UserFollow" ADD CONSTRAINT "UserFollow_followerId_fkey" FOREIGN KEY ("followerId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "UserFollow" ADD CONSTRAINT "UserFollow_followingId_fkey" FOREIGN KEY ("followingId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"

View File

@@ -0,0 +1,240 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("POSTGRES_PRISMA_URL") // uses connection pooling
directUrl = env("POSTGRES_URL_NON_POOLING") // uses a direct connection
shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
}
enum Role {
USER
ADMIN
}
model User {
id String @id @default(cuid())
username String @unique
firstName String
lastName String
hash String
email String @unique
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime? @updatedAt @db.Timestamptz(3)
accountIsVerified Boolean @default(false)
dateOfBirth DateTime
role Role @default(USER)
bio String?
beerPosts BeerPost[]
beerStyles BeerStyle[]
breweryPosts BreweryPost[]
beerComments BeerComment[]
breweryComments BreweryComment[]
beerPostLikes BeerPostLike[]
beerImages BeerImage[]
breweryImages BreweryImage[]
breweryPostLikes BreweryPostLike[]
locations BreweryLocation[]
glasswares Glassware[]
beerStyleLikes BeerStyleLike[]
beerStyleComments BeerStyleComment[]
userAvatar UserAvatar?
followedBy UserFollow[] @relation("following")
following UserFollow[] @relation("follower")
}
model UserFollow {
follower User @relation("follower", fields: [followerId], references: [id])
followerId String
following User @relation("following", fields: [followingId], references: [id])
followingId String
followedAt DateTime @default(now()) @db.Timestamptz(3)
@@id([followerId, followingId])
}
model UserAvatar {
id String @id @default(cuid())
path String
alt String
caption String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String @unique
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime? @updatedAt @db.Timestamptz(3)
}
model BeerPost {
id String @id @default(cuid())
name String
ibu Float
abv Float
description String
postedBy User @relation(fields: [postedById], references: [id], onDelete: Cascade)
postedById String
brewery BreweryPost @relation(fields: [breweryId], references: [id], onDelete: Cascade)
breweryId String
style BeerStyle @relation(fields: [styleId], references: [id], onDelete: Cascade)
styleId String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime? @updatedAt @db.Timestamptz(3)
beerComments BeerComment[]
beerImages BeerImage[]
beerPostLikes BeerPostLike[]
}
model BeerPostLike {
id String @id @default(cuid())
beerPost BeerPost @relation(fields: [beerPostId], references: [id], onDelete: Cascade)
beerPostId String
likedBy User @relation(fields: [likedById], references: [id], onDelete: Cascade)
likedById String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime? @updatedAt @db.Timestamptz(3)
}
model BreweryPostLike {
id String @id @default(cuid())
breweryPost BreweryPost @relation(fields: [breweryPostId], references: [id], onDelete: Cascade)
breweryPostId String
likedBy User @relation(fields: [likedById], references: [id], onDelete: Cascade)
likedById String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime? @updatedAt @db.Timestamptz(3)
}
model BeerComment {
id String @id @default(cuid())
rating Int
beerPost BeerPost @relation(fields: [beerPostId], references: [id], onDelete: Cascade)
beerPostId String
postedBy User @relation(fields: [postedById], references: [id], onDelete: Cascade)
postedById String
content String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime? @updatedAt @db.Timestamptz(3)
}
model BeerStyle {
id String @id @default(cuid())
name String
description String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime? @updatedAt @db.Timestamptz(3)
postedBy User @relation(fields: [postedById], references: [id], onDelete: Cascade)
glassware Glassware @relation(fields: [glasswareId], references: [id], onDelete: Cascade)
glasswareId String
postedById String
abvRange Float[]
ibuRange Float[]
beerPosts BeerPost[]
beerStyleLike BeerStyleLike[]
beerStyleComment BeerStyleComment[]
}
model BeerStyleLike {
id String @id @default(cuid())
beerStyle BeerStyle @relation(fields: [beerStyleId], references: [id], onDelete: Cascade)
beerStyleId String
likedBy User @relation(fields: [likedById], references: [id], onDelete: Cascade)
likedById String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime? @updatedAt @db.Timestamptz(3)
}
model BeerStyleComment {
id String @id @default(cuid())
rating Int
beerStyle BeerStyle @relation(fields: [beerStyleId], references: [id], onDelete: Cascade)
beerStyleId String
postedBy User @relation(fields: [postedById], references: [id], onDelete: Cascade)
postedById String
content String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime? @updatedAt @db.Timestamptz(3)
}
model Glassware {
id String @id @default(cuid())
name String
description String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime? @updatedAt @db.Timestamptz(3)
postedBy User @relation(fields: [postedById], references: [id], onDelete: Cascade)
postedById String
beerStyle BeerStyle[]
}
model BreweryLocation {
id String @id @default(cuid())
city String
stateOrProvince String?
country String?
coordinates Float[]
address String
postedBy User @relation(fields: [postedById], references: [id], onDelete: Cascade)
postedById String
breweryPost BreweryPost?
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime? @updatedAt @db.Timestamptz(3)
}
model BreweryPost {
id String @id @default(cuid())
name String
location BreweryLocation @relation(fields: [locationId], references: [id])
locationId String @unique
beers BeerPost[]
description String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime? @updatedAt @db.Timestamptz(3)
postedBy User @relation(fields: [postedById], references: [id], onDelete: Cascade)
postedById String
breweryComments BreweryComment[]
breweryImages BreweryImage[]
breweryPostLike BreweryPostLike[]
dateEstablished DateTime @default(now()) @db.Timestamptz(3)
}
model BreweryComment {
id String @id @default(cuid())
rating Int
breweryPost BreweryPost @relation(fields: [breweryPostId], references: [id], onDelete: Cascade)
breweryPostId String
postedBy User @relation(fields: [postedById], references: [id], onDelete: Cascade)
postedById String
content String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime? @updatedAt @db.Timestamptz(3)
}
model BeerImage {
id String @id @default(cuid())
beerPost BeerPost @relation(fields: [beerPostId], references: [id], onDelete: Cascade)
beerPostId String
path String
alt String
caption String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime? @updatedAt @db.Timestamptz(3)
postedBy User @relation(fields: [postedById], references: [id], onDelete: Cascade)
postedById String
}
model BreweryImage {
id String @id @default(cuid())
breweryPost BreweryPost @relation(fields: [breweryPostId], references: [id], onDelete: Cascade)
breweryPostId String
path String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime? @updatedAt @db.Timestamptz(3)
caption String
alt String
postedBy User @relation(fields: [postedById], references: [id], onDelete: Cascade)
postedById String
}

View File

@@ -0,0 +1,7 @@
import { cloudinary } from '../../../config/cloudinary';
const clearCloudinaryStorage = async () => {
await cloudinary.api.delete_resources_by_prefix('biergarten-dev/');
};
export default clearCloudinaryStorage;

View File

@@ -0,0 +1,30 @@
import DBClient from '../../DBClient';
const clearDatabase = async () => {
const prisma = DBClient.instance;
/**
* Truncate all tables in the database.
*
* Declares a cursor called statements that contains all table names in the public
* schema then loops through each table and truncates it.
*/
await prisma.$executeRaw`
DO $$
DECLARE
statements CURSOR FOR
SELECT tablename FROM pg_tables
WHERE schemaname = 'public'
AND tablename != '_prisma_migrations';
BEGIN
FOR statement IN statements LOOP
EXECUTE 'TRUNCATE TABLE ' || quote_ident(statement.tablename) || ' CASCADE;';
END LOOP;
END;
$$ LANGUAGE plpgsql;
`;
await prisma.$disconnect();
};
export default clearDatabase;

View File

@@ -0,0 +1,16 @@
import logger from '../../../config/pino/logger';
import clearCloudinaryStorage from './clearCloudinaryStorage';
import clearDatabase from './clearDatabase';
(async () => {
await clearDatabase();
await clearCloudinaryStorage();
})()
.then(() => {
logger.info('Successfully cleared database and cloudinary storage.');
process.exit(0);
})
.catch((err) => {
logger.error(err);
process.exit(1);
});

View File

@@ -0,0 +1,49 @@
import { z } from 'zod';
import { hashPassword } from '../../../config/auth/passwordFns';
import { ADMIN_PASSWORD } from '../../../config/env';
import DBClient from '../../DBClient';
import GetUserSchema from '../../../services/users/auth/schema/GetUserSchema';
import imageUrls from '../util/imageUrls';
const createAdminUser = async () => {
const hash = await hashPassword(ADMIN_PASSWORD);
const adminUser: z.infer<typeof GetUserSchema> = await DBClient.instance.user.create({
data: {
username: 'admin',
email: 'admin@example.com',
firstName: 'Admin',
lastName: 'User',
dateOfBirth: new Date('1990-01-01'),
role: 'ADMIN',
hash,
userAvatar: {
create: {
path: imageUrls[Math.floor(Math.random() * imageUrls.length)],
alt: 'Admin User',
caption: 'Admin User',
createdAt: new Date(),
},
},
},
select: {
id: true,
username: true,
email: true,
firstName: true,
lastName: true,
dateOfBirth: true,
createdAt: true,
accountIsVerified: true,
updatedAt: true,
role: true,
bio: true,
userAvatar: true,
},
});
return adminUser;
};
export default createAdminUser;

View File

@@ -0,0 +1,51 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { faker } from '@faker-js/faker';
import { BeerPost, User } from '@prisma/client';
import DBClient from '../../DBClient';
import imageUrls from '../util/imageUrls';
interface CreateNewBeerImagesArgs {
numberOfImages: number;
joinData: { beerPosts: BeerPost[]; users: User[] };
}
interface BeerImageData {
path: string;
alt: string;
caption: string;
beerPostId: string;
postedById: string;
createdAt: Date;
}
const createNewBeerImages = async ({
numberOfImages,
joinData: { beerPosts, users },
}: CreateNewBeerImagesArgs) => {
const prisma = DBClient.instance;
const beerImageData: BeerImageData[] = [];
// eslint-disable-next-line no-plusplus
for (let i = 0; i < numberOfImages; i++) {
const beerPost = beerPosts[Math.floor(Math.random() * beerPosts.length)];
const user = users[Math.floor(Math.random() * users.length)];
const caption = faker.lorem.sentence();
const alt = faker.lorem.sentence();
const path = imageUrls[Math.floor(Math.random() * imageUrls.length)];
const createdAt = faker.date.past({ years: 1 });
beerImageData.push({
path,
alt,
caption,
beerPostId: beerPost.id,
postedById: user.id,
createdAt,
});
}
await prisma.beerImage.createMany({ data: beerImageData });
return prisma.beerImage.findMany();
};
export default createNewBeerImages;

View File

@@ -0,0 +1,56 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { faker } from '@faker-js/faker';
import { BeerPost, User } from '@prisma/client';
import DBClient from '../../DBClient';
interface CreateNewBeerCommentsArgs {
numberOfComments: number;
joinData: {
beerPosts: BeerPost[];
users: User[];
};
}
interface BeerCommentData {
content: string;
postedById: string;
beerPostId: string;
rating: number;
createdAt: Date;
}
const createNewBeerComments = async ({
numberOfComments,
joinData,
}: CreateNewBeerCommentsArgs) => {
const { beerPosts, users } = joinData;
const prisma = DBClient.instance;
const beerCommentData: BeerCommentData[] = [];
// eslint-disable-next-line no-plusplus
for (let i = 0; i < numberOfComments; i++) {
const content = faker.lorem.lines(5);
const user = users[Math.floor(Math.random() * users.length)];
const beerPost = beerPosts[Math.floor(Math.random() * beerPosts.length)];
const createdAt = faker.date.past({ years: 1 });
const rating = Math.floor(Math.random() * 5) + 1;
beerCommentData.push({
content,
postedById: user.id,
beerPostId: beerPost.id,
createdAt,
rating,
});
}
await prisma.beerComment.createMany({
data: beerCommentData,
});
return prisma.beerComment.findMany();
};
export default createNewBeerComments;

View File

@@ -0,0 +1,40 @@
import type { BeerPost, User } from '@prisma/client';
// eslint-disable-next-line import/no-extraneous-dependencies
import { faker } from '@faker-js/faker';
import DBClient from '../../DBClient';
interface BeerPostLikeData {
beerPostId: string;
likedById: string;
createdAt: Date;
}
const createNewBeerPostLikes = async ({
joinData: { beerPosts, users },
numberOfLikes,
}: {
joinData: { beerPosts: BeerPost[]; users: User[] };
numberOfLikes: number;
}) => {
const beerPostLikeData: BeerPostLikeData[] = [];
// eslint-disable-next-line no-plusplus
for (let i = 0; i < numberOfLikes; i++) {
const beerPost = beerPosts[Math.floor(Math.random() * beerPosts.length)];
const user = users[Math.floor(Math.random() * users.length)];
const createdAt = faker.date.past({ years: 1 });
beerPostLikeData.push({
beerPostId: beerPost.id,
likedById: user.id,
createdAt,
});
}
await DBClient.instance.beerPostLike.createMany({
data: beerPostLikeData,
});
return DBClient.instance.beerPostLike.findMany();
};
export default createNewBeerPostLikes;

View File

@@ -0,0 +1,66 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { faker } from '@faker-js/faker';
import { User, BeerStyle, BreweryPost } from '@prisma/client';
import DBClient from '../../DBClient';
interface CreateNewBeerPostsArgs {
numberOfPosts: number;
joinData: {
users: User[];
breweryPosts: BreweryPost[];
beerStyles: BeerStyle[];
};
}
interface BeerPostData {
abv: number;
ibu: number;
name: string;
description: string;
createdAt: Date;
breweryId: string;
postedById: string;
styleId: string;
}
const createNewBeerPosts = async ({
numberOfPosts,
joinData,
}: CreateNewBeerPostsArgs) => {
const { users, breweryPosts, beerStyles } = joinData;
const prisma = DBClient.instance;
const beerPostData: BeerPostData[] = [];
// eslint-disable-next-line no-plusplus
for (let i = 0; i < numberOfPosts; i++) {
const user = users[Math.floor(Math.random() * users.length)];
const beerStyle = beerStyles[Math.floor(Math.random() * beerStyles.length)];
const breweryPost = breweryPosts[Math.floor(Math.random() * breweryPosts.length)];
const createdAt = faker.date.past({ years: 1 });
const [minABV, maxABV] = beerStyle.abvRange;
const [minIBU, maxIBU] = beerStyle.ibuRange;
const abv = parseFloat((Math.random() * (maxABV - minABV) + minABV).toFixed(1));
const ibu = Math.floor(Math.random() * (maxIBU - minIBU) + minIBU);
const name = faker.commerce.productName();
const description = faker.lorem.lines(20).replace(/(\r\n|\n|\r)/gm, ' ');
beerPostData.push({
postedById: user.id,
styleId: beerStyle.id,
breweryId: breweryPost.id,
createdAt,
abv,
ibu,
name,
description,
});
}
await prisma.beerPost.createMany({ data: beerPostData });
return prisma.beerPost.findMany();
};
export default createNewBeerPosts;

View File

@@ -0,0 +1,56 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { faker } from '@faker-js/faker';
import { BeerStyle, User } from '@prisma/client';
import DBClient from '../../DBClient';
interface CreateNewBeerCommentsArgs {
numberOfComments: number;
joinData: {
beerStyles: BeerStyle[];
users: User[];
};
}
interface BeerStyleComment {
content: string;
postedById: string;
beerStyleId: string;
rating: number;
createdAt: Date;
}
const createNewBeerStyleComments = async ({
numberOfComments,
joinData,
}: CreateNewBeerCommentsArgs) => {
const { beerStyles, users } = joinData;
const prisma = DBClient.instance;
const beerStyleCommentData: BeerStyleComment[] = [];
// eslint-disable-next-line no-plusplus
for (let i = 0; i < numberOfComments; i++) {
const content = faker.lorem.lines(5);
const user = users[Math.floor(Math.random() * users.length)];
const beerStyle = beerStyles[Math.floor(Math.random() * beerStyles.length)];
const createdAt = faker.date.past({ years: 1 });
const rating = Math.floor(Math.random() * 5) + 1;
beerStyleCommentData.push({
content,
postedById: user.id,
beerStyleId: beerStyle.id,
createdAt,
rating,
});
}
await prisma.beerStyleComment.createMany({
data: beerStyleCommentData,
});
return prisma.beerStyleComment.findMany();
};
export default createNewBeerStyleComments;

View File

@@ -0,0 +1,44 @@
import type { BeerStyle, User } from '@prisma/client';
// eslint-disable-next-line import/no-extraneous-dependencies
import { faker } from '@faker-js/faker';
import DBClient from '../../DBClient';
interface BeerPostLikeData {
beerStyleId: string;
likedById: string;
createdAt: Date;
}
interface CreateNewBeerStyleLikesArgs {
joinData: {
beerStyles: BeerStyle[];
users: User[];
};
numberOfLikes: number;
}
const createNewBeerStyleLikes = async ({
joinData: { beerStyles, users },
numberOfLikes,
}: CreateNewBeerStyleLikesArgs) => {
const beerStyleLikeData: BeerPostLikeData[] = [];
// eslint-disable-next-line no-plusplus
for (let i = 0; i < numberOfLikes; i++) {
const beerStyle = beerStyles[Math.floor(Math.random() * beerStyles.length)];
const user = users[Math.floor(Math.random() * users.length)];
const createdAt = faker.date.past({ years: 1 });
beerStyleLikeData.push({
beerStyleId: beerStyle.id,
likedById: user.id,
createdAt,
});
}
await DBClient.instance.beerStyleLike.createMany({
data: beerStyleLikeData,
});
return DBClient.instance.beerStyleLike.findMany();
};
export default createNewBeerStyleLikes;

View File

@@ -0,0 +1,75 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { faker } from '@faker-js/faker';
import { User } from '@prisma/client';
import uniq from 'lodash/uniq';
import DBClient from '../../DBClient';
import beerStyles from '../util/beerStyles';
interface CreateNewBeerStylesArgs {
joinData: { users: User[] };
}
interface BeerStyleData {
name: string;
postedById: string;
description: string;
createdAt: Date;
glasswareId: string;
abvRange: [number, number];
ibuRange: [number, number];
}
const createNewBeerStyles = async ({ joinData }: CreateNewBeerStylesArgs) => {
const { users } = joinData;
const prisma = DBClient.instance;
const glasswarePromises: Promise<{
id: string;
description: string;
name: string;
}>[] = [];
const glasswareData = uniq(beerStyles.map((beerStyle) => beerStyle.glassware));
glasswareData.forEach((glassware) => {
const createdAt = faker.date.past({ years: 1 });
const user = users[Math.floor(Math.random() * users.length)];
const query = prisma.glassware.create({
data: {
createdAt,
description: faker.lorem.paragraph(),
name: glassware,
postedById: user.id,
},
select: { id: true, description: true, name: true },
});
glasswarePromises.push(query);
});
const glassware = await Promise.all(glasswarePromises);
const beerStyleData: BeerStyleData[] = [];
beerStyles.forEach((beerStyle) => {
const { description, name, ibuRange, abvRange } = beerStyle;
const glasswareId = glassware.find((glass) => glass.name === beerStyle.glassware)!.id;
const createdAt = faker.date.past({ years: 1 });
const user = users[Math.floor(Math.random() * users.length)];
beerStyleData.push({
createdAt,
description,
name,
postedById: user.id,
glasswareId,
ibuRange,
abvRange,
});
});
await prisma.beerStyle.createMany({ data: beerStyleData, skipDuplicates: true });
return prisma.beerStyle.findMany();
};
export default createNewBeerStyles;

View File

@@ -0,0 +1,54 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { faker } from '@faker-js/faker';
import { BreweryPost, User } from '@prisma/client';
import DBClient from '../../DBClient';
import imageUrls from '../util/imageUrls';
interface CreateBreweryImagesArgs {
numberOfImages: number;
joinData: {
breweryPosts: BreweryPost[];
users: User[];
};
}
interface BreweryImageData {
path: string;
alt: string;
caption: string;
breweryPostId: string;
postedById: string;
createdAt: Date;
}
const createNewBreweryImages = async ({
numberOfImages,
joinData: { breweryPosts, users },
}: CreateBreweryImagesArgs) => {
const prisma = DBClient.instance;
const createdAt = faker.date.past({ years: 1 });
const breweryImageData: BreweryImageData[] = [];
// eslint-disable-next-line no-plusplus
for (let i = 0; i < numberOfImages; i++) {
const breweryPost = breweryPosts[Math.floor(Math.random() * breweryPosts.length)];
const user = users[Math.floor(Math.random() * users.length)];
const path = imageUrls[Math.floor(Math.random() * imageUrls.length)];
breweryImageData.push({
path,
alt: 'Placeholder brewery image.',
caption: 'Placeholder brewery image caption.',
breweryPostId: breweryPost.id,
postedById: user.id,
createdAt,
});
}
await prisma.breweryImage.createMany({
data: breweryImageData,
});
return prisma.breweryImage.findMany();
};
export default createNewBreweryImages;

View File

@@ -0,0 +1,51 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { faker } from '@faker-js/faker';
import { BreweryPost, User } from '@prisma/client';
import DBClient from '../../DBClient';
interface CreateNewBreweryPostCommentsArgs {
numberOfComments: number;
joinData: {
breweryPosts: BreweryPost[];
users: User[];
};
}
interface BreweryPostCommentData {
content: string;
postedById: string;
breweryPostId: string;
rating: number;
createdAt: Date;
}
const createNewBreweryPostComments = async ({
numberOfComments,
joinData,
}: CreateNewBreweryPostCommentsArgs) => {
const { breweryPosts, users } = joinData;
const prisma = DBClient.instance;
const breweryPostCommentData: BreweryPostCommentData[] = [];
// eslint-disable-next-line no-plusplus
for (let i = 0; i < numberOfComments; i++) {
const content = faker.lorem.lines(3).replace(/\n/g, ' ');
const user = users[Math.floor(Math.random() * users.length)];
const breweryPost = breweryPosts[Math.floor(Math.random() * breweryPosts.length)];
const createdAt = faker.date.past({ years: 1 });
const rating = Math.floor(Math.random() * 5) + 1;
breweryPostCommentData.push({
content,
createdAt,
rating,
postedById: user.id,
breweryPostId: breweryPost.id,
});
}
await prisma.breweryComment.createMany({ data: breweryPostCommentData });
return prisma.breweryComment.findMany();
};
export default createNewBreweryPostComments;

View File

@@ -0,0 +1,42 @@
import type { BreweryPost, User } from '@prisma/client';
// eslint-disable-next-line import/no-extraneous-dependencies
import { faker } from '@faker-js/faker';
import DBClient from '../../DBClient';
interface BreweryPostLikeData {
breweryPostId: string;
likedById: string;
createdAt: Date;
}
const createNewBreweryPostLikes = async ({
joinData: { breweryPosts, users },
numberOfLikes,
}: {
joinData: {
breweryPosts: BreweryPost[];
users: User[];
};
numberOfLikes: number;
}) => {
const breweryPostLikeData: BreweryPostLikeData[] = [];
// eslint-disable-next-line no-plusplus
for (let i = 0; i < numberOfLikes; i++) {
const breweryPost = breweryPosts[Math.floor(Math.random() * breweryPosts.length)];
const user = users[Math.floor(Math.random() * users.length)];
const createdAt = faker.date.past({ years: 1 });
breweryPostLikeData.push({
breweryPostId: breweryPost.id,
likedById: user.id,
createdAt,
});
}
await DBClient.instance.breweryPostLike.createMany({
data: breweryPostLikeData,
});
return DBClient.instance.breweryPostLike.findMany();
};
export default createNewBreweryPostLikes;

View File

@@ -0,0 +1,56 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { faker } from '@faker-js/faker';
import { BreweryLocation, User } from '@prisma/client';
import DBClient from '../../DBClient';
interface CreateNewBreweryPostsArgs {
numberOfPosts: number;
joinData: {
users: User[];
locations: BreweryLocation[];
};
}
interface BreweryData {
name: string;
locationId: string;
description: string;
postedById: string;
createdAt: Date;
dateEstablished: Date;
}
const createNewBreweryPosts = async ({
numberOfPosts,
joinData,
}: CreateNewBreweryPostsArgs) => {
const { users, locations } = joinData;
const prisma = DBClient.instance;
const breweryData: BreweryData[] = [];
// eslint-disable-next-line no-plusplus
for (let i = 0; i < numberOfPosts; i++) {
const name = `${faker.commerce.productName()} Brewing Company`;
const locationIndex = Math.floor(Math.random() * locations.length);
const location = locations[locationIndex];
locations.splice(locationIndex, 1); // Remove the location from the array
const description = faker.lorem.lines(20).replace(/(\r\n|\n|\r)/gm, ' ');
const user = users[Math.floor(Math.random() * users.length)];
const createdAt = faker.date.past({ years: 1 });
const dateEstablished = faker.date.past({ years: 40 });
breweryData.push({
name,
locationId: location.id,
description,
postedById: user.id,
createdAt,
dateEstablished,
});
}
await prisma.breweryPost.createMany({ data: breweryData, skipDuplicates: true });
return prisma.breweryPost.findMany();
};
export default createNewBreweryPosts;

View File

@@ -0,0 +1,55 @@
/* eslint-disable import/no-extraneous-dependencies */
import { faker } from '@faker-js/faker';
import { User } from '@prisma/client';
import DBClient from '../../DBClient';
import canadianCities from '../util/canadianCities';
interface CreateNewLocationsArgs {
numberOfLocations: number;
joinData: {
users: User[];
};
}
interface LocationData {
city: string;
stateOrProvince?: string;
country?: string;
coordinates: number[];
address: string;
postedById: string;
createdAt: Date;
}
const createNewLocations = async ({
numberOfLocations,
joinData,
}: CreateNewLocationsArgs) => {
const prisma = DBClient.instance;
const locationData: LocationData[] = [];
// eslint-disable-next-line no-plusplus
for (let i = 0; i < numberOfLocations; i++) {
const randomIndex = Math.floor(Math.random() * canadianCities.length);
const randomCity = canadianCities[randomIndex];
const randomUser = joinData.users[Math.floor(Math.random() * joinData.users.length)];
canadianCities.splice(randomIndex, 1);
locationData.push({
address: randomCity.city,
city: randomCity.city,
coordinates: [randomCity.longitude, randomCity.latitude],
createdAt: faker.date.past({ years: 1 }),
postedById: randomUser.id,
stateOrProvince: randomCity.province,
country: 'Canada',
});
}
await prisma.breweryLocation.createMany({ data: locationData, skipDuplicates: true });
return prisma.breweryLocation.findMany();
};
export default createNewLocations;

View File

@@ -0,0 +1,39 @@
import { User } from '@prisma/client';
import DBClient from '../../DBClient';
import imageUrls from '../util/imageUrls';
interface CreateNewUserAvatarsArgs {
joinData: { users: User[] };
}
interface UserAvatarData {
path: string;
alt: string;
caption: string;
userId: string;
createdAt: Date;
}
const createNewUserAvatars = async ({
joinData: { users },
}: CreateNewUserAvatarsArgs) => {
const userAvatars: UserAvatarData[] = [];
users.forEach((user) => {
const path = imageUrls[Math.floor(Math.random() * imageUrls.length)];
userAvatars.push({
path,
alt: `${user.firstName} ${user.lastName}`,
caption: `${user.firstName} ${user.lastName}`,
userId: user.id,
createdAt: new Date(),
});
});
await DBClient.instance.userAvatar.createMany({ data: userAvatars });
return DBClient.instance.userAvatar.findMany({
where: { user: { role: { not: 'ADMIN' } } },
});
};
export default createNewUserAvatars;

View File

@@ -0,0 +1,49 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { faker } from '@faker-js/faker';
import type { User } from '@prisma/client';
import DBClient from '../../DBClient';
interface CreateNewUserFollowsArgs {
joinData: { users: User[] };
}
interface UserFollowData {
followerId: string;
followingId: string;
followedAt: Date;
}
const createNewUserFollows = async ({
joinData: { users },
}: CreateNewUserFollowsArgs) => {
const userFollows: UserFollowData[] = [];
users.forEach((user) => {
// Get 20 random users to follow.
const randomUsers = users
.filter((randomUser) => randomUser.id !== user.id)
.sort(() => Math.random() - Math.random())
.slice(0, 100);
// Get the user to follow the random users
const data = randomUsers.flatMap((randomUser) => [
{
followerId: user.id,
followingId: randomUser.id,
followedAt: faker.date.between({ from: user.createdAt, to: new Date() }),
},
]);
userFollows.push(...data);
});
await DBClient.instance.userFollow.createMany({
data: userFollows,
skipDuplicates: true,
});
return DBClient.instance.userFollow.findMany();
};
export default createNewUserFollows;

View File

@@ -0,0 +1,102 @@
/* eslint-disable import/no-extraneous-dependencies */
import { faker } from '@faker-js/faker';
import generator from 'generate-password';
import crypto from 'crypto';
import DBClient from '../../DBClient';
import { hashPassword } from '../../../config/auth/passwordFns';
import logger from '../../../config/pino/logger';
interface CreateNewUsersArgs {
numberOfUsers: number;
}
interface UserData {
firstName: string;
lastName: string;
email: string;
username: string;
dateOfBirth: Date;
createdAt: Date;
hash: string;
accountIsVerified: boolean;
role: 'USER' | 'ADMIN';
bio: string;
}
const createNewUsers = async ({ numberOfUsers }: CreateNewUsersArgs) => {
const prisma = DBClient.instance;
await DBClient.instance.$disconnect();
const passwords = Array.from({ length: numberOfUsers }, () =>
generator.generate({
length: 20,
symbols: true,
numbers: true,
uppercase: true,
strict: true,
}),
);
logger.info('Hashing passwords. This may take a while...');
const hashedPasswords = await Promise.all(
passwords.map((password) => hashPassword(password)),
);
logger.info('Creating new users. This may take a while...');
const data: UserData[] = [];
const takenUsernames: string[] = [];
const takenEmails: string[] = [];
// eslint-disable-next-line no-plusplus
for (let i = 0; i < numberOfUsers; i++) {
const randomValue = crypto.randomBytes(1).toString('hex');
const firstName = faker.person.firstName();
const lastName = faker.person.lastName();
const username = `${firstName[0]}.${lastName}.${randomValue}`.toLowerCase();
const email = faker.internet
.email({ firstName, lastName, provider: 'example.com' })
.toLowerCase();
const hash = hashedPasswords[i];
const userAvailable =
!takenUsernames.includes(username) && !takenEmails.includes(email);
if (!userAvailable) {
i -= 1;
// eslint-disable-next-line no-continue
continue;
}
takenUsernames.push(username);
takenEmails.push(email);
const dateOfBirth = faker.date.birthdate({ mode: 'age', min: 19 });
const createdAt = faker.date.past({ years: 4 });
const bio = faker.lorem.paragraphs(1).replace(/\n/g, ' ');
const user: UserData = {
firstName,
lastName,
email,
username,
dateOfBirth,
createdAt,
hash,
bio,
accountIsVerified: true,
role: 'USER',
};
data.push(user);
}
await prisma.user.createMany({ data, skipDuplicates: true });
return prisma.user.findMany({
where: { role: { not: 'ADMIN' } },
});
};
export default createNewUsers;

View File

@@ -0,0 +1,138 @@
import { performance } from 'perf_hooks';
import { exit } from 'process';
import clearDatabase from './clear/clearDatabase';
import createNewBeerImages from './create/createNewBeerImages';
import createNewBeerPostComments from './create/createNewBeerPostComments';
import createNewBeerPostLikes from './create/createNewBeerPostLikes';
import createNewBeerPosts from './create/createNewBeerPosts';
import createNewBeerStyles from './create/createNewBeerStyles';
import createNewBreweryImages from './create/createNewBreweryImages';
import createNewBreweryPostComments from './create/createNewBreweryPostComments';
import createNewBreweryPosts from './create/createNewBreweryPosts';
import createNewUsers from './create/createNewUsers';
import createNewBreweryPostLikes from './create/createNewBreweryPostLikes';
import createNewLocations from './create/createNewLocations';
import logger from '../../config/pino/logger';
import createAdminUser from './create/createAdminUser';
import createNewBeerStyleComments from './create/createNewBeerStyleComments';
import createNewBeerStyleLikes from './create/createNewBeerStyleLikes';
import createNewUserAvatars from './create/createNewUserAvatars';
import createNewUserFollows from './create/createNewUserFollows';
import clearCloudinaryStorage from './clear/clearCloudinaryStorage';
(async () => {
try {
const start = performance.now();
logger.info('Clearing database.');
await clearDatabase();
await clearCloudinaryStorage();
logger.info('Database cleared successfully, preparing to seed.');
await createAdminUser();
logger.info('Admin user created successfully.');
const users = await createNewUsers({ numberOfUsers: 1000 });
logger.info('Users created successfully.');
const userAvatars = await createNewUserAvatars({ joinData: { users } });
logger.info('User avatars created successfully.');
const userFollows = await createNewUserFollows({ joinData: { users } });
logger.info('User follows created successfully.');
const locations = await createNewLocations({
numberOfLocations: 500,
joinData: { users },
});
logger.info('Locations created successfully.');
const [breweryPosts, beerStyles] = await Promise.all([
createNewBreweryPosts({ numberOfPosts: 450, joinData: { users, locations } }),
createNewBeerStyles({ joinData: { users } }),
]);
logger.info('Brewery posts and beer types created successfully.');
const beerPosts = await createNewBeerPosts({
numberOfPosts: 3000,
joinData: { breweryPosts, beerStyles, users },
});
logger.info('Beer posts created successfully.');
const [beerPostComments, beerStyleComments, breweryPostComments] = await Promise.all([
createNewBeerPostComments({
numberOfComments: 100000,
joinData: { beerPosts, users },
}),
createNewBeerStyleComments({
numberOfComments: 5000,
joinData: { beerStyles, users },
}),
createNewBreweryPostComments({
numberOfComments: 50000,
joinData: { breweryPosts, users },
}),
]);
logger.info('Created beer post comments and brewery post comments.');
const [beerPostLikes, beerStyleLikes, breweryPostLikes] = await Promise.all([
createNewBeerPostLikes({
numberOfLikes: 500000,
joinData: { beerPosts, users },
}),
createNewBeerStyleLikes({
numberOfLikes: 50000,
joinData: { beerStyles, users },
}),
createNewBreweryPostLikes({
numberOfLikes: 100000,
joinData: { breweryPosts, users },
}),
]);
logger.info('Created beer post likes, and brewery post likes.');
const [beerImages, breweryImages] = await Promise.all([
createNewBeerImages({
numberOfImages: 20000,
joinData: { beerPosts, users },
}),
createNewBreweryImages({
numberOfImages: 5000,
joinData: { breweryPosts, users },
}),
]);
logger.info('Created beer images and brewery images.');
const end = performance.now();
const timeElapsed = (end - start) / 1000;
logger.info('Database seeded successfully.');
logger.info({
numberOfUsers: users.length,
numberOfUserAvatars: userAvatars.length,
numberOfUserFollows: userFollows.length,
numberOfBreweryPosts: breweryPosts.length,
numberOfBeerPosts: beerPosts.length,
numberOfBeerStyles: beerStyles.length,
numberOfBeerStyleLikes: beerStyleLikes.length,
numberOfBeerStyleComments: beerStyleComments.length,
numberOfBeerPostLikes: beerPostLikes.length,
numberOfBreweryPostLikes: breweryPostLikes.length,
numberOfBeerPostComments: beerPostComments.length,
numberOfBreweryPostComments: breweryPostComments.length,
numberOfBeerImages: beerImages.length,
numberOfBreweryImages: breweryImages.length,
});
logger.info(`Database seeded in ${timeElapsed.toFixed(2)} seconds.`);
exit(0);
} catch (error) {
logger.error('Error seeding database.');
logger.error(error);
exit(1);
}
})();

View File

@@ -0,0 +1,92 @@
interface BeerStyle {
name: string;
description: string;
glassware: string;
abvRange: [number, number];
ibuRange: [number, number];
}
const beerStyles: BeerStyle[] = [
{
name: 'IPA',
description:
'India Pale Ale (IPA) is a hop-forward beer known for its distinctively bitter and citrusy flavor. It often features floral, piney, and fruity notes, with a moderate to high alcohol content. IPAs are usually served in a pint glass, and their alcohol by volume (ABV) typically falls in the range of 6.0% to 7.5%. The International Bitterness Units (IBU) range from 40 to 70, giving it a pleasantly bitter profile.',
glassware: 'Pint Glass',
abvRange: [6.0, 7.5],
ibuRange: [40, 70],
},
{
name: 'Stout',
description:
'Stout is a dark, rich beer characterized by its roasted malt and coffee flavors. It has a creamy texture and can often include hints of chocolate, caramel, and even oatmeal. Typically served in a pint glass, stouts have an ABV range of 4.0% to 7.0% and an IBU range of 30 to 40, providing a balanced bitterness.',
glassware: 'Pint Glass',
abvRange: [4.0, 7.0],
ibuRange: [30, 40],
},
{
name: 'Pilsner',
description:
'Pilsner is a pale lager celebrated for its crisp and refreshing taste. It offers a clean, balanced flavor profile with a slightly bitter finish. Pilsners are commonly served in tall, slender pilsner glasses and have an ABV range of 4.0% to 6.0%. The IBU typically ranges from 25 to 45, providing a mild hop presence that complements the malt sweetness.',
glassware: 'Pilsner Glass',
abvRange: [4.0, 6.0],
ibuRange: [25, 45],
},
{
name: 'Wheat Beer',
description:
"Wheat beer is a light and cloudy ale with a slightly fruity flavor and often a touch of spice. It's typically served in weizen glasses. The ABV range for wheat beers falls between 4.0% and 5.5%, with an IBU range of 10 to 20, making it a refreshing and approachable choice, perfect for warm days.",
glassware: 'Weizen Glass',
abvRange: [4.0, 5.5],
ibuRange: [10, 20],
},
{
name: 'Porter',
description:
'Porter is a dark, full-bodied beer known for its rich malt character with notes of chocolate, caramel, and roasted coffee. This beer is typically served in pint glasses. Porters have an ABV range of 4.0% to 6.5% and an IBU range of 20 to 40, providing a harmonious balance between malt sweetness and hop bitterness.',
glassware: 'Pint Glass',
abvRange: [4.0, 6.5],
ibuRange: [20, 40],
},
{
name: 'Belgian Tripel',
description:
"Belgian Tripel is a strong and complex ale with a fruity and spicy character. It is served in chalices to enhance its aroma. The ABV range is relatively high, typically ranging from 7.5% to 10.5%, while the IBU falls between 20 and 40, providing a subdued bitterness. The beer's strength and flavor make it a sipping choice.",
glassware: 'Chalice',
abvRange: [7.5, 10.5],
ibuRange: [20, 40],
},
{
name: 'Saison',
description:
'Saison is a farmhouse ale known for its fruity, spicy, and often slightly tart character. Served in tulip glasses, saisons have an ABV range of 5.0% to 7.0% and an IBU range of 20 to 35. This style offers a refreshing and unique flavor profile, making it a versatile choice for different occasions.',
glassware: 'Tulip Glass',
abvRange: [5.0, 7.0],
ibuRange: [20, 35],
},
{
name: 'Amber Ale',
description:
'Amber Ale is a well-balanced beer with a pleasant blend of caramel malt sweetness and hop bitterness. Typically served in pint glasses, amber ales have an ABV range of 4.5% to 6.2% and an IBU range of 20 to 40, creating a harmonious and approachable beer with a rich amber hue.',
glassware: 'Pint Glass',
abvRange: [4.5, 6.2],
ibuRange: [20, 40],
},
{
name: 'Barleywine',
description:
'Barleywine is a robust ale with a high alcohol content and a complex malt character. It is typically served in snifter glasses to concentrate its aroma. The ABV for barleywines ranges from 8.0% to 12.0%, and the IBU can be notably high, falling between 50 and 100, resulting in a strong, bold, and flavorful beer.',
glassware: 'Snifter Glass',
abvRange: [8.0, 12.0],
ibuRange: [50, 100],
},
{
name: 'Hefeweizen',
description:
'Hefeweizen is a wheat beer with a hazy appearance and distinctive banana-clove flavors. Served in weizen glasses, hefeweizens have an ABV range of 4.0% to 5.6% and a mild IBU range of 8 to 15. The combination of yeast and wheat imparts a refreshing and slightly spicy character.',
glassware: 'Weizen Glass',
abvRange: [4.0, 5.6],
ibuRange: [8, 15],
},
];
export default beerStyles;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
const imageUrls = [
'https://res.cloudinary.com/dxie9b7na/image/upload/v1701056802/cloudinary-images/pexels-brett-sayles-1551944_era4te.jpg',
'https://res.cloudinary.com/dxie9b7na/image/upload/v1701056801/cloudinary-images/pexels-ketut-subiyanto-5055809_fwkfoj.jpg',
'https://res.cloudinary.com/dxie9b7na/image/upload/v1701056801/cloudinary-images/pexels-cottonbro-studio-5537954_zsxs7n.jpg',
'https://res.cloudinary.com/dxie9b7na/image/upload/v1701056800/cloudinary-images/pexels-tembela-bohle-1089931_f7jcer.jpg',
'https://res.cloudinary.com/dxie9b7na/image/upload/v1701056798/cloudinary-images/pexels-tembela-bohle-1089930_yzfjlv.jpg',
'https://res.cloudinary.com/dxie9b7na/image/upload/v1701056797/cloudinary-images/pexels-cottonbro-studio-5529918_e0jlle.jpg',
'https://res.cloudinary.com/dxie9b7na/image/upload/v1701056797/cloudinary-images/pexels-casalfilmsestudio-2076748_xymlox.jpg',
'https://res.cloudinary.com/dxie9b7na/image/upload/v1701056795/cloudinary-images/pexels-elevate-3009770_simouh.jpg',
'https://res.cloudinary.com/dxie9b7na/image/upload/v1701056793/cloudinary-images/pexels-elevate-1267700_jrno3s.jpg',
] as const;
export default imageUrls;