diff --git a/README.md b/README.md index 06ba2e0..9d964d1 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,10 @@ Note: `DIRECT_URL` is only used for compatibility with pgBouncer in production, The hosted version of this project uses Supabase's PostgreSQL service as a database host, but does not use any additional features of Supabase. +### Seed Data Source + +The database seeding script used in development is set up to copy non-sensitive data on the current public website through the `/api/data-export` endpoint. Set the environment variable `SEED_DATA_SOURCE` to the URL of the website (as of writing, `https://algdb.vercel.app/api/data-export`) and the populated data will be copied to the local database. + ### NextAuth Set `NEXTAUTH_URL` to the canonical URL of the site. Use `http://localhost:3000` for development. diff --git a/app/api/data-export/route.ts b/app/api/data-export/route.ts new file mode 100644 index 0000000..e9858fc --- /dev/null +++ b/app/api/data-export/route.ts @@ -0,0 +1,70 @@ +import prisma from "@/prisma/global-prisma-client"; +import { Prisma } from "@prisma/client"; +import { NextResponse } from "next/server"; + +export type DataExport = { + algorithms: Prisma.AlgorithmCreateManyInput[]; + algorithmsForCase: Prisma.AlgorithmsForCaseCreateManyInput[]; + cases: Prisma.CaseCreateManyInput[]; + methods: Prisma.MethodCreateManyInput[]; + puzzles: Prisma.PuzzleCreateManyInput[]; + sets: { + id: string; + name: string; + slug: string; + puzzleId: string; + visualizationId: string; + cases: { + id: string; + name: string; + setup: string; + puzzleId: string; + }[]; + methods: { + id: string; + name: string; + slug: string; + puzzleId: string; + visualizationId: string; + }[]; + }[]; + visualizations: Prisma.VisualizationCreateManyInput[]; +}; + +export async function GET(request: Request) { + const algorithms = prisma.algorithm.findMany(); + const algorithmsForCase = prisma.algorithmsForCase.findMany(); + const cases = prisma.case.findMany(); + const methods = prisma.method.findMany(); + const puzzles = prisma.puzzle.findMany(); + const sets = prisma.set.findMany({ + include: { + cases: true, + methods: true, + }, + }); + const visualizations = prisma.visualization.findMany(); + + const data = await Promise.all([ + algorithms, + algorithmsForCase, + cases, + methods, + puzzles, + sets, + visualizations, + ]); + + return NextResponse.json( + { + algorithms: data[0], + algorithmsForCase: data[1], + cases: data[2], + methods: data[3], + puzzles: data[4], + sets: data[5], + visualizations: data[6], + }, + { status: 200 } + ); +} diff --git a/app/api/sets/route.ts b/app/api/sets/route.ts index e1a768c..bcf8be3 100644 --- a/app/api/sets/route.ts +++ b/app/api/sets/route.ts @@ -89,6 +89,7 @@ const Set: z.ZodType = z.object({ z.object({ name: z.string().min(1).max(255), setup: z.string().min(1).max(255), + puzzleId: z.string(), puzzle: z.object({ connect: z.optional( z.object({ @@ -101,6 +102,7 @@ const Set: z.ZodType = z.object({ .array( z.object({ moves: z.string(), + algorithmId: z.string(), }) ) .nonempty(), diff --git a/prisma/seed-data/algorithm.ts b/prisma/seed-data/algorithm.ts deleted file mode 100644 index 00af661..0000000 --- a/prisma/seed-data/algorithm.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Prisma } from "@prisma/client"; -import cuid from "cuid"; - -export const tPerm: Prisma.AlgorithmCreateInput = { - id: cuid(), - moves: "R U R' U' R' F R2 U' R' U' R U R' F'", -}; - -export const jPerm: Prisma.AlgorithmCreateInput = { - id: cuid(), - moves: "R U R' F' R U R' U' R' F R2 U' R'", -}; - -export const yPerm: Prisma.AlgorithmCreateInput = { - id: cuid(), - moves: "F R U' R' U' R U R' F' R U R' U' R' F R F'", -}; - -export const bars2x2_1: Prisma.AlgorithmCreateInput = { - id: cuid(), - moves: "R2 F2 R2", -}; - -export const bars2x2_2: Prisma.AlgorithmCreateInput = { - id: cuid(), - moves: "R2 B2 R2", -}; - -export const bars2x2_3: Prisma.AlgorithmCreateInput = { - id: cuid(), - moves: "x R2 U2 R2", -}; - -export const algorithms = [ - tPerm, - jPerm, - yPerm, - bars2x2_1, - bars2x2_2, - bars2x2_3, -]; diff --git a/prisma/seed-data/algorithms-for-case.ts b/prisma/seed-data/algorithms-for-case.ts deleted file mode 100644 index 8976781..0000000 --- a/prisma/seed-data/algorithms-for-case.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { Prisma } from "@prisma/client"; -import { - pbl2x2JPerm, - pbl2x2YPerm, - pbl2x2VerticalBars, - pbl2x2AdjAdj, - pbl2x2AdjOpp, -} from "./case"; -import { - tPerm, - jPerm, - yPerm, - bars2x2_1, - bars2x2_2, - bars2x2_3, -} from "./algorithm"; - -export const caseAlgorithmLinks: Prisma.AlgorithmsForCaseCreateInput[] = [ - { - case: { - connect: { - id: pbl2x2JPerm.id, - }, - }, - algorithm: { - connect: { - id: tPerm.id, - }, - }, - }, - { - case: { - connect: { - id: pbl2x2JPerm.id, - }, - }, - algorithm: { - connect: { - id: jPerm.id, - }, - }, - }, - { - case: { - connect: { - id: pbl2x2YPerm.id, - }, - }, - algorithm: { - connect: { - id: yPerm.id, - }, - }, - }, - { - case: { - connect: { - id: pbl2x2VerticalBars.id, - }, - }, - algorithm: { - connect: { - id: bars2x2_1.id, - }, - }, - }, - { - case: { - connect: { - id: pbl2x2VerticalBars.id, - }, - }, - algorithm: { - connect: { - id: bars2x2_2.id, - }, - }, - }, - { - case: { - connect: { - id: pbl2x2VerticalBars.id, - }, - }, - algorithm: { - connect: { - id: bars2x2_3.id, - }, - }, - }, - { - case: { - connect: { - id: pbl2x2AdjAdj.id, - }, - }, - algorithm: { - create: { - moves: "R2 U' B2 U2 R2 U' R2", - }, - }, - }, - { - case: { - connect: { - id: pbl2x2AdjOpp.id, - }, - }, - algorithm: { - create: { - moves: "R U' R F2 R' U R'", - }, - }, - }, -]; diff --git a/prisma/seed-data/case.ts b/prisma/seed-data/case.ts deleted file mode 100644 index 0e9231d..0000000 --- a/prisma/seed-data/case.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Prisma } from "@prisma/client"; -import { twoByTwo } from "./puzzle"; -import cuid from "cuid"; - -export const pbl2x2JPerm: Prisma.CaseCreateInput = { - id: cuid(), - name: "J Perm / T Perm", - puzzle: { - connect: { - id: twoByTwo.id, - }, - }, - setup: "R U R' U' R' F R2 U' R' U' R U R' F' U'", -}; - -export const pbl2x2YPerm: Prisma.CaseCreateInput = { - id: cuid(), - name: "Y Perm", - puzzle: { - connect: { - id: twoByTwo.id, - }, - }, - setup: "F R U' R' U' R U R' F' R U R' U' R' F R F'", -}; - -export const pbl2x2VerticalBars: Prisma.CaseCreateInput = { - id: cuid(), - name: "Vertical Bars", - puzzle: { - connect: { - id: twoByTwo.id, - }, - }, - setup: "R2 F2 R2", -}; - -export const pbl2x2AdjAdj: Prisma.CaseCreateInput = { - id: cuid(), - name: "Adj-Adj", - puzzle: { - connect: { - id: twoByTwo.id, - }, - }, - setup: "R2 U' B2 U2 R2 U' R2", -}; - -export const pbl2x2AdjOpp: Prisma.CaseCreateInput = { - id: cuid(), - name: "Adj-Opp", - puzzle: { - connect: { - id: twoByTwo.id, - }, - }, - setup: "R U' R F2 R' U R'", -}; - -export const pbl2x2Cases: Prisma.CaseCreateInput[] = [ - pbl2x2JPerm, - pbl2x2YPerm, - pbl2x2VerticalBars, - pbl2x2AdjAdj, - pbl2x2AdjOpp, -]; diff --git a/prisma/seed-data/method.ts b/prisma/seed-data/method.ts deleted file mode 100644 index 338807e..0000000 --- a/prisma/seed-data/method.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Prisma } from "@prisma/client"; -import { twoByTwo } from "./puzzle"; -import { visualization2x2Ortega } from "./visualization"; -import { pbl2x2 } from "./set"; - -export const ortega: Prisma.MethodCreateInput = { - name: "Ortega", - slug: "ortega", - puzzle: { - connect: { - id: twoByTwo.id, - }, - }, - visualization: { - create: visualization2x2Ortega, - }, - sets: { - connect: { - id: pbl2x2.id, - }, - }, -}; - -export const methods = [ortega]; diff --git a/prisma/seed-data/puzzle.ts b/prisma/seed-data/puzzle.ts deleted file mode 100644 index b93f2f3..0000000 --- a/prisma/seed-data/puzzle.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Prisma } from "@prisma/client"; - -import { - visualization2x2, - visualization3x3, - visualization4x4, -} from "./visualization"; - -export const threeByThree: Prisma.PuzzleCreateInput = { - id: "333", - name: "3x3", - slug: "333", - rank: 0, - visualization: { - create: visualization3x3, - }, -}; - -export const twoByTwo: Prisma.PuzzleCreateInput = { - id: "222", - name: "2x2", - slug: "222", - rank: 10, - visualization: { - create: visualization2x2, - }, -}; - -export const fourByFour: Prisma.PuzzleCreateInput = { - id: "444", - name: "4x4", - slug: "444", - rank: 20, - visualization: { - create: visualization4x4, - }, -}; - -export const puzzles: Prisma.PuzzleCreateInput[] = [ - threeByThree, - twoByTwo, - fourByFour, -]; diff --git a/prisma/seed-data/set.ts b/prisma/seed-data/set.ts deleted file mode 100644 index d874616..0000000 --- a/prisma/seed-data/set.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Prisma } from "@prisma/client"; - -import { threeByThree, twoByTwo } from "./puzzle"; -import { - visualization2x2Pbl, - visualization3x3Oll, - visualization3x3Pll, -} from "./visualization"; -import { pbl2x2Cases } from "./case"; - -export const pll3x3: Prisma.SetCreateInput = { - name: "PLL", - slug: "pll", - puzzle: { - connect: { - id: threeByThree.id, - }, - }, - visualization: { - create: visualization3x3Pll, - }, -}; - -export const oll3x3: Prisma.SetCreateInput = { - name: "OLL", - slug: "oll", - puzzle: { - connect: { - id: threeByThree.id, - }, - }, - visualization: { - create: visualization3x3Oll, - }, -}; - -export const pbl2x2: Prisma.SetCreateInput = { - id: "cllyliite000008lbd2wt371e", - name: "PBL", - slug: "pbl", - puzzle: { - connect: { - id: twoByTwo.id, - }, - }, - visualization: { - create: visualization2x2Pbl, - }, - cases: { - create: pbl2x2Cases, - }, -}; - -export const sets: Prisma.SetCreateInput[] = [pll3x3, oll3x3, pbl2x2]; diff --git a/prisma/seed-data/visualization.ts b/prisma/seed-data/visualization.ts deleted file mode 100644 index 088702e..0000000 --- a/prisma/seed-data/visualization.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Prisma } from "@prisma/client"; - -export const visualization3x3: Prisma.VisualizationCreateManyInput = { - type: "cube", - options: { - puzzle: { - size: 3, - }, - }, -}; - -export const visualization3x3Pll: Prisma.VisualizationCreateManyInput = { - type: "cube-top", - options: { - puzzle: { - size: 3, - alg: "R2 u' R U' R U R' u R2 y R U' R'", - mask: { - F: [3, 4, 5, 6, 7, 8], - B: [3, 4, 5, 6, 7, 8], - R: [3, 4, 5, 6, 7, 8], - L: [3, 4, 5, 6, 7, 8], - D: [0, 1, 2, 3, 4, 5, 6, 7, 8], - }, - }, - }, -}; - -export const visualization3x3Oll: Prisma.VisualizationCreateManyInput = { - type: "cube-top", - options: { - puzzle: { - size: 3, - alg: "r U r' R U R' U' r U' r'", - mask: { - R: [0, 1, 2, 3, 4, 5, 6, 7, 8], - F: [0, 1, 2, 3, 4, 5, 6, 7, 8], - D: [0, 1, 2, 3, 4, 5, 6, 7, 8], - L: [0, 1, 2, 3, 4, 5, 6, 7, 8], - B: [0, 1, 2, 3, 4, 5, 6, 7, 8], - }, - }, - }, -}; - -export const visualization4x4: Prisma.VisualizationCreateManyInput = { - type: "cube", - options: { - puzzle: { - size: 4, - }, - }, -}; - -export const visualization2x2: Prisma.VisualizationCreateManyInput = { - type: "cube", - options: { - puzzle: { - size: 2, - }, - }, -}; - -export const visualization2x2Pbl: Prisma.VisualizationCreateManyInput = { - type: "cube", - options: { - puzzle: { - size: 2, - alg: "R U' R F2 R' U R'", - }, - }, -}; - -export const visualization2x2Ortega: Prisma.VisualizationCreateManyInput = { - type: "cube", - options: { - puzzle: { - size: 2, - alg: "D2 U R U' R F2 R' U R'", - }, - }, -}; - -export const visualizations: Prisma.VisualizationCreateManyInput[] = [ - visualization3x3, - visualization3x3Oll, - visualization3x3Pll, - visualization4x4, - visualization2x2, - visualization2x2Pbl, - visualization2x2Ortega, -]; diff --git a/prisma/seed.ts b/prisma/seed.ts index c00d7d7..4aee3dc 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -1,36 +1,67 @@ import { PrismaClient } from "@prisma/client"; -import { puzzles } from "./seed-data/puzzle"; -import { sets } from "./seed-data/set"; -import { methods } from "./seed-data/method"; -import { algorithms } from "./seed-data/algorithm"; -import { caseAlgorithmLinks } from "./seed-data/algorithms-for-case"; +import { DataExport } from "@/app/api/data-export/route"; const prisma = new PrismaClient(); -async function main() { - const puzzlePromises = puzzles.map(async (puzzle) => { - await prisma.puzzle.create({ data: puzzle }); +/** + * Inserts the data from the DataExport object into the database. + * @param data + */ +async function loadData(data: DataExport) { + await prisma.visualization.createMany({ + data: data.visualizations, + }); + + await prisma.puzzle.createMany({ + data: data.puzzles, }); - await Promise.all(puzzlePromises); - const setPromises = sets.map(async (set) => { - await prisma.set.create({ data: set }); + await prisma.method.createMany({ + data: data.methods, }); - await Promise.all(setPromises); - const methodPromises = methods.map(async (method) => { - await prisma.method.create({ data: method }); + await prisma.algorithm.createMany({ + data: data.algorithms, }); - await Promise.all(methodPromises); - const algorithmPromises = algorithms.map(async (algorithm) => { - await prisma.algorithm.create({ data: algorithm }); + await prisma.case.createMany({ + data: data.cases, }); - await Promise.all(algorithmPromises); - const caseAlgorithmLinkPromises = caseAlgorithmLinks.map(async (link) => { - await prisma.algorithmsForCase.create({ data: link }); + await prisma.algorithmsForCase.createMany({ + data: data.algorithmsForCase, }); - await Promise.all(caseAlgorithmLinkPromises); + + // Set data cannot be created with createMany due to Prisma limitations + await Promise.all( + data.sets.map(async (set) => { + await prisma.set.create({ + data: { + ...set, + cases: { + connect: set.cases, + }, + methods: { + connect: set.methods, + }, + }, + }); + }) + ); +} + +/** + * Retrieves data from the endpoint declared in the environment variable SEED_DATA_SOURCE + * and returns it as a DataExport object. + */ +async function fetchData(): Promise { + const response = await fetch(process.env.SEED_DATA_SOURCE as string); + const data = await response.json(); + return data as DataExport; +} + +async function main() { + const data = await fetchData(); + await loadData(data); } main()