enables schema saving in db, initial refactor of db schema for revisions
This commit is contained in:
parent
fd5e5bcc8b
commit
5b16cc60f7
@ -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}`);
|
|
||||||
};
|
|
@ -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
79
actions/Schemas/index.ts
Normal 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}`);
|
||||||
|
};
|
@ -1,18 +1,18 @@
|
|||||||
import { prisma } from "@/prisma/prismaClient";
|
import { prisma } from "@/prisma/prismaClient";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
export default async function GameSystem(
|
export default async function GameSystem({
|
||||||
{ params: { id } }: { params: { id: string } },
|
params: { id },
|
||||||
) {
|
}: {
|
||||||
|
params: { id: string };
|
||||||
|
}) {
|
||||||
if (!id) throw "HOW DID YOU GET HERE?";
|
if (!id) throw "HOW DID YOU GET HERE?";
|
||||||
|
|
||||||
const gameSystem = await prisma.gameSystem.findFirst({
|
const gameSystem = await prisma.gameSystem.findFirst({
|
||||||
where: {
|
where: {
|
||||||
id,
|
id,
|
||||||
},
|
},
|
||||||
select: {
|
include: {
|
||||||
id: true,
|
|
||||||
name: true,
|
|
||||||
schemas: {
|
schemas: {
|
||||||
select: {
|
select: {
|
||||||
name: true,
|
name: true,
|
||||||
@ -26,6 +26,10 @@ export default async function GameSystem(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// select: {
|
||||||
|
// id: true,
|
||||||
|
// name: true,
|
||||||
|
// },
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -30,7 +30,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth(async () => {
|
|||||||
let user = null;
|
let user = null;
|
||||||
|
|
||||||
const pwHash = await saltAndHashPassword(
|
const pwHash = await saltAndHashPassword(
|
||||||
credentials.password as string
|
credentials.password as string,
|
||||||
);
|
);
|
||||||
user = await prisma.user.findFirst({
|
user = await prisma.user.findFirst({
|
||||||
where: {
|
where: {
|
||||||
|
@ -1,30 +1,45 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { FC, useCallback, useState } from "react";
|
import { FC, useCallback, useEffect, useState } from "react";
|
||||||
import AnimatedPageContainer from "@/components/AnimatedPageContainer";
|
import AnimatedPageContainer from "@/components/AnimatedPageContainer";
|
||||||
import { TypeEditor } from "./type-editor";
|
import { TypeEditor } from "./type-editor";
|
||||||
import { useObjectStateWrapper } from "@/hooks/useObjectState";
|
import { useObjectStateWrapper } from "@/hooks/useObjectState";
|
||||||
import { useInput } from "../../hooks/useInput";
|
import { useInput } from "../../hooks/useInput";
|
||||||
import { useRecoilState, useResetRecoilState } from "recoil";
|
import { useRecoilState } from "recoil";
|
||||||
import { SchemaEditAtom } from "@/recoil/atoms/schema";
|
import { SchemaEditAtom } from "@/recoil/atoms/schema";
|
||||||
import { SchemaViewer } from "./schema-viewer";
|
import { SchemaViewer } from "./schema-viewer";
|
||||||
import { TemplateEditor } from "./template-editor";
|
import { TemplateEditor } from "./template-editor";
|
||||||
import { Icon } from "@/components/Icon";
|
import { Icon } from "@/components/Icon";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { FieldTypes } from "./fieldtypes";
|
import { FieldTypes } from "./fieldtypes";
|
||||||
import { prisma } from "@/prisma/prismaClient";
|
import { findSchema, saveSchemaDb } from "@/actions/Schemas/index";
|
||||||
|
import { useToast } from "../toast";
|
||||||
|
|
||||||
export const SchemaBuilder: FC = () => {
|
export const SchemaBuilder: FC = () => {
|
||||||
const [schema, setSchema] = useRecoilState(SchemaEditAtom);
|
const [schema, setSchema] = useRecoilState(SchemaEditAtom);
|
||||||
const resetSchema = useResetRecoilState(SchemaEditAtom);
|
// const resetSchema = useResetRecoilState(SchemaEditAtom);
|
||||||
|
const { createToast } = useToast();
|
||||||
const { update: updateSchema, bindProperty: bindSchemaProperty } =
|
const { update: updateSchema, bindProperty: bindSchemaProperty } =
|
||||||
useObjectStateWrapper<Schema>(schema, setSchema);
|
useObjectStateWrapper<Schema>(schema, setSchema);
|
||||||
|
|
||||||
const { schemaId, gameSystemId } = useParams<{
|
const { schemaId, id: gameSystemId } = useParams<{
|
||||||
schemaId: string;
|
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 {
|
const {
|
||||||
value: typeName,
|
value: typeName,
|
||||||
bind: bindTypeName,
|
bind: bindTypeName,
|
||||||
@ -33,7 +48,7 @@ export const SchemaBuilder: FC = () => {
|
|||||||
|
|
||||||
const [pageNumber, setPageNumber] = useState(0);
|
const [pageNumber, setPageNumber] = useState(0);
|
||||||
|
|
||||||
const [lastSaved, setLastSaved] = useState(schema);
|
const [lastSaved, _setLastSaved] = useState(schema);
|
||||||
|
|
||||||
const [selectedType, setSelectedType] = useState("");
|
const [selectedType, setSelectedType] = useState("");
|
||||||
|
|
||||||
@ -49,24 +64,13 @@ export const SchemaBuilder: FC = () => {
|
|||||||
setPageNumber(0);
|
setPageNumber(0);
|
||||||
setSelectedType("");
|
setSelectedType("");
|
||||||
},
|
},
|
||||||
[resetTypeName, updateSchema]
|
[resetTypeName, updateSchema],
|
||||||
);
|
);
|
||||||
|
|
||||||
const saveSchema = useCallback(async () => {
|
const saveSchema = useCallback(async () => {
|
||||||
// "use server";
|
createToast({ msg: "Saving Schema", fading: true });
|
||||||
// setLastSaved(schema);
|
await saveSchemaDb(schema);
|
||||||
// await prisma.schema.upsert({
|
}, [createToast, schema]);
|
||||||
// where: { id: schema.id },
|
|
||||||
// update: { ...schema },
|
|
||||||
// create: {
|
|
||||||
// name: schema.name,
|
|
||||||
// schema: schema.schema,
|
|
||||||
// types: schema.types,
|
|
||||||
// version: 0,
|
|
||||||
// gameSystemId,
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
}, [schema, gameSystemId]);
|
|
||||||
|
|
||||||
const selectTypeForEdit = useCallback((typeKey: string) => {
|
const selectTypeForEdit = useCallback((typeKey: string) => {
|
||||||
setSelectedType(typeKey);
|
setSelectedType(typeKey);
|
||||||
@ -82,10 +86,7 @@ export const SchemaBuilder: FC = () => {
|
|||||||
updateSchema((s) => ({
|
updateSchema((s) => ({
|
||||||
schema: {
|
schema: {
|
||||||
...s.schema,
|
...s.schema,
|
||||||
[schemaFieldName]: {
|
[schemaFieldName]: FieldTypes.any,
|
||||||
display: "",
|
|
||||||
type: FieldTypes.any,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
resetSchemaFieldName();
|
resetSchemaFieldName();
|
||||||
@ -100,7 +101,7 @@ export const SchemaBuilder: FC = () => {
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
[updateSchema]
|
[updateSchema],
|
||||||
);
|
);
|
||||||
|
|
||||||
const deleteType = useCallback(
|
const deleteType = useCallback(
|
||||||
@ -111,7 +112,7 @@ export const SchemaBuilder: FC = () => {
|
|||||||
return { types };
|
return { types };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[updateSchema]
|
[updateSchema],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -141,7 +142,7 @@ export const SchemaBuilder: FC = () => {
|
|||||||
template={schemaField}
|
template={schemaField}
|
||||||
update={updateSchemaField}
|
update={updateSchemaField}
|
||||||
/>
|
/>
|
||||||
)
|
),
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,9 +15,13 @@ export class DHSecretClient {
|
|||||||
* @param dhBaseUri uri for hosted Dragon's Hoard instance
|
* @param dhBaseUri uri for hosted Dragon's Hoard instance
|
||||||
* @param cacheDir path to cache dir
|
* @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";
|
this.cacheLocation = this.cacheDir.trim().replace(/\/^/, "") + "/.dh_cache";
|
||||||
mkdirSync(this.cacheDir, { recursive: true });
|
mkdirSync(this.cacheDir, { recursive: true });
|
||||||
|
|
||||||
this.readDiskCache();
|
this.readDiskCache();
|
||||||
this.token = this.fetchToken();
|
this.token = this.fetchToken();
|
||||||
}
|
}
|
||||||
@ -42,12 +46,13 @@ export class DHSecretClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readDiskCache() {
|
private readDiskCache() {
|
||||||
|
try {
|
||||||
const cache = readFileSync(this.cacheLocation, "utf-8");
|
const cache = readFileSync(this.cacheLocation, "utf-8");
|
||||||
|
this.cache = JSON.parse(cache || "{}");
|
||||||
if (!cache) {
|
} catch {
|
||||||
this.cache = {};
|
this.cache = {};
|
||||||
this.writeDiskCache();
|
this.writeDiskCache().then(this.readDiskCache);
|
||||||
} else this.cache = JSON.parse(cache || "{}");
|
}
|
||||||
}
|
}
|
||||||
private async writeDiskCache() {
|
private async writeDiskCache() {
|
||||||
await writeFile(this.cacheLocation, JSON.stringify(this.cache), "utf-8");
|
await writeFile(this.cacheLocation, JSON.stringify(this.cache), "utf-8");
|
||||||
|
@ -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 {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
}
|
}
|
||||||
@ -31,24 +25,53 @@ model Schema {
|
|||||||
author User @relation(fields: [authorId], references: [id])
|
author User @relation(fields: [authorId], references: [id])
|
||||||
authorId String
|
authorId String
|
||||||
|
|
||||||
originalId String?
|
|
||||||
name String
|
name String
|
||||||
|
|
||||||
|
SchemaRevision SchemaRevision[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model SchemaRevision {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
schemaId String
|
||||||
|
parentSchema Schema @relation(fields: [schemaId], references: [id])
|
||||||
|
|
||||||
schema Json
|
schema Json
|
||||||
types Json
|
types Json
|
||||||
version Int
|
version Int
|
||||||
|
Publication Publication[]
|
||||||
|
|
||||||
|
@@unique([schemaId, version])
|
||||||
}
|
}
|
||||||
|
|
||||||
model Publication {
|
model Publication {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
schema Schema @relation(fields: [schemaId], references: [id])
|
schema Schema @relation(fields: [schemaId], references: [id])
|
||||||
schemaId String
|
schemaId String
|
||||||
|
schemaVersion Int
|
||||||
|
schemaRevision SchemaRevision @relation(fields: [schemaVersion, schemaId], references: [version, schemaId])
|
||||||
tags Tag[]
|
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])
|
author User @relation(fields: [authorId], references: [id])
|
||||||
authorId String
|
authorId String
|
||||||
|
|
||||||
name String
|
@@unique([publicationId, version])
|
||||||
data Json
|
|
||||||
TagsOnPublications TagsOnPublications[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model TagsOnPublications {
|
model TagsOnPublications {
|
||||||
@ -83,7 +106,6 @@ model User {
|
|||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
schemas Schema[]
|
schemas Schema[]
|
||||||
gameSystems GameSystem[]
|
gameSystems GameSystem[]
|
||||||
publications Publication[]
|
|
||||||
|
|
||||||
name String?
|
name String?
|
||||||
username String? @unique
|
username String? @unique
|
||||||
@ -96,6 +118,7 @@ model User {
|
|||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
PublicationRevision PublicationRevision[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model Account {
|
model Account {
|
||||||
|
15
types.d.ts
vendored
15
types.d.ts
vendored
@ -31,7 +31,7 @@ type FrontMatter = Record<string, string>;
|
|||||||
type SearchFunction = (
|
type SearchFunction = (
|
||||||
s: string,
|
s: string,
|
||||||
start: number,
|
start: number,
|
||||||
end: number
|
end: number,
|
||||||
) => {
|
) => {
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
@ -53,7 +53,7 @@ type IdentifierRegistration = <N = Record<string, string>>(
|
|||||||
parseFunction: (s: string, rx: RegExp) => IdentifiedToken<N>,
|
parseFunction: (s: string, rx: RegExp) => IdentifiedToken<N>,
|
||||||
renderFunction: TokenRenderer<N>,
|
renderFunction: TokenRenderer<N>,
|
||||||
openTagRx?: RegExp,
|
openTagRx?: RegExp,
|
||||||
closeTagRx?: RegExp
|
closeTagRx?: RegExp,
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
// Schema
|
// Schema
|
||||||
@ -76,11 +76,16 @@ type Template = {
|
|||||||
display: string;
|
display: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type SchemaFields = Record<string, string>;
|
||||||
|
|
||||||
|
type SchemaTypes = Record<string, TypeType>;
|
||||||
|
|
||||||
type Schema = {
|
type Schema = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
schema: Record<string, Template>;
|
schema: SchemaFields;
|
||||||
types: Record<string, TypeType>;
|
types: SchemaTypes;
|
||||||
|
gameSystemId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Input Binder
|
// Input Binder
|
||||||
@ -88,7 +93,7 @@ type InputBinder = {
|
|||||||
name: string;
|
name: string;
|
||||||
value: string | number;
|
value: string | number;
|
||||||
onChange: (
|
onChange: (
|
||||||
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
|
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,
|
||||||
) => void;
|
) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user