enables schema saving in db, initial refactor of db schema for revisions

This commit is contained in:
Emmaline Autumn 2024-09-02 03:16:10 -06:00
parent fd5e5bcc8b
commit 5b16cc60f7
9 changed files with 190 additions and 122 deletions

View File

@ -1,29 +0,0 @@
"use server";
import { auth } from "@/auth";
import { prisma } from "@/prisma/prismaClient";
import { isEmailVerified } from "@/util/isEmailVerified";
import { redirect } from "next/navigation";
export const createSchema = async (form: FormData) => {
const name = form.get("name")?.toString();
const gsId = form.get("gsId")?.toString();
const session = await auth();
if (!name || !gsId || !session?.user?.id || !isEmailVerified(session.user.id))
return;
const { id } = await prisma.schema.create({
data: {
name,
schema: "{}",
types: "{}",
version: 0,
gameSystemId: gsId,
authorId: session.user.id,
},
select: { id: true },
});
redirect(`/game-systems/${gsId}/schema/${id}`);
};

View File

@ -1,20 +0,0 @@
"use server";
import { prisma } from "@/prisma/prismaClient";
export const findSchema = async (id: string) => {
const schema = await prisma.schema.findFirst({
where: {
id,
},
include: {
gameSystem: {
select: {
id: true,
name: true,
},
},
},
});
return schema;
};

79
actions/Schemas/index.ts Normal file
View File

@ -0,0 +1,79 @@
"use server";
import { auth } from "@/auth";
import { isEmailVerified } from "@/util/isEmailVerified";
import { redirect } from "next/navigation";
import { prisma } from "@/prisma/prismaClient";
export const saveSchemaDb = async (s: Schema) => {
const sesh = await auth();
if (!sesh?.user?.id) return;
const { id } = await prisma.schema.upsert({
// data: {
// ...s,
// },
create: {
...s,
version: 0,
authorId: sesh.user.id,
id: undefined,
},
update: s,
where: {
id: s.id,
},
select: {
id: true,
},
});
redirect(`/game-systems/${s.gameSystemId}/schema/${id}`);
};
export const findSchema = async (id: string): Promise<Schema | null> => {
const schema = await prisma.schema.findFirst({
where: {
id,
},
// include: {
// gameSystem: {
// select: {
// id: true,
// name: true,
// },
// },
// },
select: {
id: true,
name: true,
schema: true,
types: true,
},
});
if (!schema) return null;
return schema as Schema;
};
export const createSchema = async (form: FormData) => {
const name = form.get("name")?.toString();
const gsId = form.get("gsId")?.toString();
const session = await auth();
if (!name || !gsId || !session?.user?.id || !isEmailVerified(session.user.id))
return;
const { id } = await prisma.schema.create({
data: {
name,
schema: "{}",
types: "{}",
version: 0,
gameSystemId: gsId,
authorId: session.user.id,
},
select: { id: true },
});
redirect(`/game-systems/${gsId}/schema/${id}`);
};

View File

@ -1,18 +1,18 @@
import { prisma } from "@/prisma/prismaClient";
import Link from "next/link";
export default async function GameSystem(
{ params: { id } }: { params: { id: string } },
) {
export default async function GameSystem({
params: { id },
}: {
params: { id: string };
}) {
if (!id) throw "HOW DID YOU GET HERE?";
const gameSystem = await prisma.gameSystem.findFirst({
where: {
id,
},
select: {
id: true,
name: true,
include: {
schemas: {
select: {
name: true,
@ -26,6 +26,10 @@ export default async function GameSystem(
},
},
},
// select: {
// id: true,
// name: true,
// },
});
return (

View File

@ -30,7 +30,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth(async () => {
let user = null;
const pwHash = await saltAndHashPassword(
credentials.password as string
credentials.password as string,
);
user = await prisma.user.findFirst({
where: {

View File

@ -1,30 +1,45 @@
"use client";
import { FC, useCallback, useState } from "react";
import { FC, useCallback, useEffect, useState } from "react";
import AnimatedPageContainer from "@/components/AnimatedPageContainer";
import { TypeEditor } from "./type-editor";
import { useObjectStateWrapper } from "@/hooks/useObjectState";
import { useInput } from "../../hooks/useInput";
import { useRecoilState, useResetRecoilState } from "recoil";
import { useRecoilState } from "recoil";
import { SchemaEditAtom } from "@/recoil/atoms/schema";
import { SchemaViewer } from "./schema-viewer";
import { TemplateEditor } from "./template-editor";
import { Icon } from "@/components/Icon";
import { useParams } from "next/navigation";
import { FieldTypes } from "./fieldtypes";
import { prisma } from "@/prisma/prismaClient";
import { findSchema, saveSchemaDb } from "@/actions/Schemas/index";
import { useToast } from "../toast";
export const SchemaBuilder: FC = () => {
const [schema, setSchema] = useRecoilState(SchemaEditAtom);
const resetSchema = useResetRecoilState(SchemaEditAtom);
// const resetSchema = useResetRecoilState(SchemaEditAtom);
const { createToast } = useToast();
const { update: updateSchema, bindProperty: bindSchemaProperty } =
useObjectStateWrapper<Schema>(schema, setSchema);
const { schemaId, gameSystemId } = useParams<{
const { schemaId, id: gameSystemId } = useParams<{
schemaId: string;
gameSystemId?: string;
id: string;
}>();
useEffect(() => {
if (schemaId !== "create" && schemaId !== schema.id)
findSchema(schemaId).then((sc) => {
if (!sc) return;
setSchema(sc);
});
}, [schema.id, schemaId, setSchema]);
useEffect(() => {
if (gameSystemId && !schema.gameSystemId)
setSchema((sc) => ({ ...sc, gameSystemId }));
}, [gameSystemId, schema.gameSystemId, setSchema]);
const {
value: typeName,
bind: bindTypeName,
@ -33,7 +48,7 @@ export const SchemaBuilder: FC = () => {
const [pageNumber, setPageNumber] = useState(0);
const [lastSaved, setLastSaved] = useState(schema);
const [lastSaved, _setLastSaved] = useState(schema);
const [selectedType, setSelectedType] = useState("");
@ -49,24 +64,13 @@ export const SchemaBuilder: FC = () => {
setPageNumber(0);
setSelectedType("");
},
[resetTypeName, updateSchema]
[resetTypeName, updateSchema],
);
const saveSchema = useCallback(async () => {
// "use server";
// setLastSaved(schema);
// await prisma.schema.upsert({
// where: { id: schema.id },
// update: { ...schema },
// create: {
// name: schema.name,
// schema: schema.schema,
// types: schema.types,
// version: 0,
// gameSystemId,
// },
// });
}, [schema, gameSystemId]);
createToast({ msg: "Saving Schema", fading: true });
await saveSchemaDb(schema);
}, [createToast, schema]);
const selectTypeForEdit = useCallback((typeKey: string) => {
setSelectedType(typeKey);
@ -82,10 +86,7 @@ export const SchemaBuilder: FC = () => {
updateSchema((s) => ({
schema: {
...s.schema,
[schemaFieldName]: {
display: "",
type: FieldTypes.any,
},
[schemaFieldName]: FieldTypes.any,
},
}));
resetSchemaFieldName();
@ -100,7 +101,7 @@ export const SchemaBuilder: FC = () => {
},
}));
},
[updateSchema]
[updateSchema],
);
const deleteType = useCallback(
@ -111,7 +112,7 @@ export const SchemaBuilder: FC = () => {
return { types };
});
},
[updateSchema]
[updateSchema],
);
return (
@ -141,7 +142,7 @@ export const SchemaBuilder: FC = () => {
template={schemaField}
update={updateSchemaField}
/>
)
),
)}
</ul>
</div>

View File

@ -15,9 +15,13 @@ export class DHSecretClient {
* @param dhBaseUri uri for hosted Dragon's Hoard instance
* @param cacheDir path to cache dir
*/
constructor(private dhBaseUri: string, private cacheDir: string) {
constructor(
private dhBaseUri: string,
private cacheDir: string,
) {
this.cacheLocation = this.cacheDir.trim().replace(/\/^/, "") + "/.dh_cache";
mkdirSync(this.cacheDir, { recursive: true });
this.readDiskCache();
this.token = this.fetchToken();
}
@ -42,12 +46,13 @@ export class DHSecretClient {
}
private readDiskCache() {
try {
const cache = readFileSync(this.cacheLocation, "utf-8");
if (!cache) {
this.cache = JSON.parse(cache || "{}");
} catch {
this.cache = {};
this.writeDiskCache();
} else this.cache = JSON.parse(cache || "{}");
this.writeDiskCache().then(this.readDiskCache);
}
}
private async writeDiskCache() {
await writeFile(this.cacheLocation, JSON.stringify(this.cache), "utf-8");

View File

@ -1,9 +1,3 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
}
@ -31,24 +25,53 @@ model Schema {
author User @relation(fields: [authorId], references: [id])
authorId String
originalId String?
name String
SchemaRevision SchemaRevision[]
}
model SchemaRevision {
id String @id @default(cuid())
schemaId String
parentSchema Schema @relation(fields: [schemaId], references: [id])
schema Json
types Json
version Int
Publication Publication[]
@@unique([schemaId, version])
}
model Publication {
id String @id @default(cuid())
schema Schema @relation(fields: [schemaId], references: [id])
schemaId String
schemaVersion Int
schemaRevision SchemaRevision @relation(fields: [schemaVersion, schemaId], references: [version, schemaId])
tags Tag[]
name String
TagsOnPublications TagsOnPublications[]
PublicationRevision PublicationRevision[]
}
model PublicationRevision {
id String @id @default(cuid())
version String
isFinal Boolean
data Json
previousId String?
publicationId String
publication Publication @relation(fields: [publicationId], references: [id])
previousRevision PublicationRevision? @relation(name: "downlineRevisions", fields: [previousId], references: [id])
downlineRevisions PublicationRevision[] @relation("downlineRevisions")
author User @relation(fields: [authorId], references: [id])
authorId String
name String
data Json
TagsOnPublications TagsOnPublications[]
@@unique([publicationId, version])
}
model TagsOnPublications {
@ -83,7 +106,6 @@ model User {
id String @id @default(cuid())
schemas Schema[]
gameSystems GameSystem[]
publications Publication[]
name String?
username String? @unique
@ -96,6 +118,7 @@ model User {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
PublicationRevision PublicationRevision[]
}
model Account {

15
types.d.ts vendored
View File

@ -31,7 +31,7 @@ type FrontMatter = Record<string, string>;
type SearchFunction = (
s: string,
start: number,
end: number
end: number,
) => {
start: number;
end: number;
@ -53,7 +53,7 @@ type IdentifierRegistration = <N = Record<string, string>>(
parseFunction: (s: string, rx: RegExp) => IdentifiedToken<N>,
renderFunction: TokenRenderer<N>,
openTagRx?: RegExp,
closeTagRx?: RegExp
closeTagRx?: RegExp,
) => void;
// Schema
@ -76,11 +76,16 @@ type Template = {
display: string;
};
type SchemaFields = Record<string, string>;
type SchemaTypes = Record<string, TypeType>;
type Schema = {
id: string;
name: string;
schema: Record<string, Template>;
types: Record<string, TypeType>;
schema: SchemaFields;
types: SchemaTypes;
gameSystemId?: string;
};
// Input Binder
@ -88,7 +93,7 @@ type InputBinder = {
name: string;
value: string | number;
onChange: (
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,
) => void;
};