Compare commits
No commits in common. "fd5e5bcc8bc7a319cda5708fc59a9b08f9febee3" and "729aba68ceb055b5ebb84108f40cd4a19c4617a5" have entirely different histories.
fd5e5bcc8b
...
729aba68ce
@ -1,19 +1,11 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import { auth } from "@/auth";
|
|
||||||
import { prisma } from "@/prisma/prismaClient";
|
import { prisma } from "@/prisma/prismaClient";
|
||||||
import { isEmailVerified } from "@/util/isEmailVerified";
|
|
||||||
|
|
||||||
export const createGameSystem = async (name: string) => {
|
export const createGameSystem = async (name: string) => {
|
||||||
const session = await auth();
|
|
||||||
if (!session?.user?.id) return null;
|
|
||||||
|
|
||||||
if (!isEmailVerified(session.user.id)) return null;
|
|
||||||
|
|
||||||
const { id } = await prisma.gameSystem.create({
|
const { id } = await prisma.gameSystem.create({
|
||||||
data: {
|
data: {
|
||||||
name,
|
name,
|
||||||
authorId: session.user.id,
|
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
|
@ -1,18 +1,13 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import { auth } from "@/auth";
|
|
||||||
import { prisma } from "@/prisma/prismaClient";
|
import { prisma } from "@/prisma/prismaClient";
|
||||||
import { isEmailVerified } from "@/util/isEmailVerified";
|
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
export const createSchema = async (form: FormData) => {
|
export const createSchema = async (form: FormData) => {
|
||||||
const name = form.get("name")?.toString();
|
const name = form.get("name")?.toString();
|
||||||
const gsId = form.get("gsId")?.toString();
|
const gsId = form.get("gsId")?.toString();
|
||||||
|
|
||||||
const session = await auth();
|
if (!name || !gsId) return;
|
||||||
|
|
||||||
if (!name || !gsId || !session?.user?.id || !isEmailVerified(session.user.id))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const { id } = await prisma.schema.create({
|
const { id } = await prisma.schema.create({
|
||||||
data: {
|
data: {
|
||||||
@ -21,7 +16,6 @@ export const createSchema = async (form: FormData) => {
|
|||||||
types: "{}",
|
types: "{}",
|
||||||
version: 0,
|
version: 0,
|
||||||
gameSystemId: gsId,
|
gameSystemId: gsId,
|
||||||
authorId: session.user.id,
|
|
||||||
},
|
},
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
});
|
});
|
||||||
|
@ -1,22 +1,20 @@
|
|||||||
"use client";
|
import { prisma } from "@/prisma/prismaClient";
|
||||||
import { createGameSystem } from "@/actions/GameSystems/create";
|
|
||||||
import { useToast } from "@/components/toast";
|
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
export default function CreateGameSystem() {
|
export default function CreateGameSystem() {
|
||||||
const { createToast } = useToast();
|
|
||||||
async function create(form: FormData) {
|
async function create(form: FormData) {
|
||||||
|
"use server";
|
||||||
|
|
||||||
const name = form.get("name")?.toString();
|
const name = form.get("name")?.toString();
|
||||||
if (!name)
|
if (!name) return;
|
||||||
return createToast({ msg: "Please provide a name", fading: true });
|
const { id } = await prisma.gameSystem.create({
|
||||||
createToast({ msg: "Creating Game System", fading: true });
|
data: {
|
||||||
const id = await createGameSystem(name);
|
name,
|
||||||
if (!id)
|
},
|
||||||
return createToast({
|
select: {
|
||||||
msg: "Issue creating game system. Is your email verified?",
|
id: true,
|
||||||
fading: true,
|
},
|
||||||
type: "error",
|
});
|
||||||
});
|
|
||||||
redirect(`/game-systems/${id}`);
|
redirect(`/game-systems/${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,6 +22,7 @@ export default function CreateGameSystem() {
|
|||||||
<form action={create}>
|
<form action={create}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
// {...bind}
|
||||||
name="name"
|
name="name"
|
||||||
placeholder="Create a new game system..."
|
placeholder="Create a new game system..."
|
||||||
className="w-min"
|
className="w-min"
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { MDEditor } from "@/components/mdeditor";
|
|
||||||
|
|
||||||
export default function Testing() {
|
|
||||||
return <MDEditor />;
|
|
||||||
}
|
|
@ -5,36 +5,45 @@ import Credentials from "next-auth/providers/credentials";
|
|||||||
import Discord from "next-auth/providers/discord";
|
import Discord from "next-auth/providers/discord";
|
||||||
|
|
||||||
import bcrypt from "bcryptjs";
|
import bcrypt from "bcryptjs";
|
||||||
import { SecretClient } from "@/lib/secret/init";
|
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
export const { handlers, signIn, signOut, auth } = NextAuth(async () => {
|
export const { handlers, signIn, signOut, auth } = NextAuth({
|
||||||
const sClient = SecretClient();
|
providers: [
|
||||||
|
Discord({
|
||||||
|
clientId: process.env.DISCORD_CLIENT_ID,
|
||||||
|
clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
||||||
|
}),
|
||||||
|
Credentials({
|
||||||
|
credentials: {
|
||||||
|
email: {},
|
||||||
|
password: {},
|
||||||
|
},
|
||||||
|
authorize: async (credentials) => {
|
||||||
|
let user = null;
|
||||||
|
|
||||||
const clientId = await sClient.fetchSecret("discord_client_id");
|
const pwHash = await saltAndHashPassword(
|
||||||
const clientSecret = await sClient.fetchSecret("discord_client_secret");
|
credentials.password as string
|
||||||
|
);
|
||||||
|
user = await prisma.user.findFirst({
|
||||||
|
where: {
|
||||||
|
email: credentials.email as string,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
image: true,
|
||||||
|
email: true,
|
||||||
|
emailVerified: true,
|
||||||
|
username: true,
|
||||||
|
passwordHash: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
if (!user) {
|
||||||
providers: [
|
user = await prisma.user.create({
|
||||||
Discord({
|
data: {
|
||||||
clientId,
|
|
||||||
clientSecret,
|
|
||||||
}),
|
|
||||||
Credentials({
|
|
||||||
credentials: {
|
|
||||||
email: {},
|
|
||||||
password: {},
|
|
||||||
},
|
|
||||||
authorize: async (credentials) => {
|
|
||||||
let user = null;
|
|
||||||
|
|
||||||
const pwHash = await saltAndHashPassword(
|
|
||||||
credentials.password as string
|
|
||||||
);
|
|
||||||
user = await prisma.user.findFirst({
|
|
||||||
where: {
|
|
||||||
email: credentials.email as string,
|
email: credentials.email as string,
|
||||||
|
passwordHash: pwHash,
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
name: true,
|
name: true,
|
||||||
@ -42,35 +51,18 @@ export const { handlers, signIn, signOut, auth } = NextAuth(async () => {
|
|||||||
email: true,
|
email: true,
|
||||||
emailVerified: true,
|
emailVerified: true,
|
||||||
username: true,
|
username: true,
|
||||||
passwordHash: true,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
user = await prisma.user.create({
|
|
||||||
data: {
|
|
||||||
email: credentials.email as string,
|
|
||||||
passwordHash: pwHash,
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
name: true,
|
|
||||||
image: true,
|
|
||||||
email: true,
|
|
||||||
emailVerified: true,
|
|
||||||
username: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
user.passwordHash = null;
|
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
},
|
}
|
||||||
}),
|
|
||||||
],
|
user.passwordHash = null;
|
||||||
adapter: PrismaAdapter(prisma),
|
|
||||||
};
|
return user;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
adapter: PrismaAdapter(prisma),
|
||||||
});
|
});
|
||||||
async function saltAndHashPassword(password: string) {
|
async function saltAndHashPassword(password: string) {
|
||||||
const hash = await bcrypt.hash(password, 10);
|
const hash = await bcrypt.hash(password, 10);
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
import CodeMirror from "@uiw/react-codemirror";
|
|
||||||
import { markdown } from "@codemirror/lang-markdown";
|
|
||||||
import { duotoneDark } from "@uiw/codemirror-theme-duotone";
|
|
||||||
|
|
||||||
interface TextEditorProps {
|
|
||||||
value: string;
|
|
||||||
onChange: (value: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TextEditor: React.FC<TextEditorProps> = ({ value, onChange }) => {
|
|
||||||
return (
|
|
||||||
<CodeMirror
|
|
||||||
value={value}
|
|
||||||
extensions={[markdown({ extensions: [] })]}
|
|
||||||
onChange={(value, _viewUpdate) => {
|
|
||||||
onChange(value);
|
|
||||||
}}
|
|
||||||
theme={duotoneDark}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,19 +0,0 @@
|
|||||||
"use client";
|
|
||||||
import { useDeferredValue, useState } from "react";
|
|
||||||
import { TextEditor } from "./TextEditor";
|
|
||||||
import { TTCMD } from "../ttcmd";
|
|
||||||
|
|
||||||
export const MDEditor: React.FC = () => {
|
|
||||||
const [text, setText] = useState("??<<2d6,$0.distribution>>");
|
|
||||||
const body = useDeferredValue(text);
|
|
||||||
return (
|
|
||||||
<div className="flex gap-8">
|
|
||||||
<div className="w-1/2">
|
|
||||||
<TextEditor value={text} onChange={setText} />
|
|
||||||
</div>
|
|
||||||
<div className="w-1/2">
|
|
||||||
<TTCMD body={body} parserId="preview" title="preview" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
74
lib/dice.ts
74
lib/dice.ts
@ -1,17 +1,9 @@
|
|||||||
import { sum } from "./utils/sum";
|
|
||||||
|
|
||||||
export class Dice {
|
export class Dice {
|
||||||
private count!: number;
|
private count!: number;
|
||||||
private sides!: number;
|
private sides!: number;
|
||||||
private diceString: string;
|
|
||||||
|
|
||||||
toString() {
|
|
||||||
return this.diceString;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(dice: string) {
|
constructor(dice: string) {
|
||||||
this.parseDice(dice);
|
this.parseDice(dice);
|
||||||
this.diceString = dice;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseDice(dice: string) {
|
private parseDice(dice: string) {
|
||||||
@ -21,23 +13,11 @@ export class Dice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public roll() {
|
public roll() {
|
||||||
let results = [];
|
let total = 0;
|
||||||
for (let i = 0; i < this.count; i++) {
|
for (let i = 0; i < this.count; i++) {
|
||||||
results.push(this.rollSingle());
|
total += this.rollSingle();
|
||||||
}
|
}
|
||||||
return {
|
return total;
|
||||||
total: sum(...results),
|
|
||||||
max: Math.max(...results),
|
|
||||||
min: Math.min(...results),
|
|
||||||
results,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public rollMax() {
|
|
||||||
return this.roll().max;
|
|
||||||
}
|
|
||||||
public rollMin() {
|
|
||||||
return this.roll().min;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private rollSingle() {
|
private rollSingle() {
|
||||||
@ -45,13 +25,13 @@ export class Dice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public rollAvg() {
|
public rollAvg() {
|
||||||
return this.roll().total / this.count;
|
return this.roll() / this.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public rollTimes(times: number) {
|
public rollTimes(times: number) {
|
||||||
let total = 0;
|
let total = 0;
|
||||||
for (let i = 0; i < times; i++) {
|
for (let i = 0; i < times; i++) {
|
||||||
total += this.roll().total;
|
total += this.roll();
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
@ -78,36 +58,32 @@ export class Dice {
|
|||||||
return this.computeDistribution();
|
return this.computeDistribution();
|
||||||
}
|
}
|
||||||
|
|
||||||
public computeDistribution(): Record<number, number> {
|
private computeDistribution(): Record<number, number> {
|
||||||
const maxSum = this.count * this.sides;
|
|
||||||
const dp: number[][] = Array.from({ length: this.count + 1 }, () =>
|
|
||||||
Array(maxSum + 1).fill(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
dp[0][0] = 1;
|
|
||||||
|
|
||||||
for (let dice = 1; dice <= this.count; dice++) {
|
|
||||||
for (let sum = 0; sum <= maxSum; sum++) {
|
|
||||||
for (let face = 1; face <= this.sides; face++) {
|
|
||||||
if (sum >= face) {
|
|
||||||
dp[dice][sum] += dp[dice - 1][sum - face];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const distribution: Record<number, number> = {};
|
const distribution: Record<number, number> = {};
|
||||||
for (let sum = this.count; sum <= maxSum; sum++) {
|
|
||||||
distribution[sum] = dp[this.count][sum];
|
// Helper function to compute the sum distribution for given number of dice
|
||||||
}
|
const computeSumDistribution = (
|
||||||
|
dice: number,
|
||||||
|
sides: number,
|
||||||
|
currentSum: number,
|
||||||
|
currentDice: number
|
||||||
|
): void => {
|
||||||
|
if (currentDice === dice) {
|
||||||
|
distribution[currentSum] = (distribution[currentSum] || 0) + 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let i = 1; i <= sides; i++) {
|
||||||
|
computeSumDistribution(dice, sides, currentSum + i, currentDice + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compute distribution
|
||||||
|
computeSumDistribution(this.count, this.sides, 0, 0);
|
||||||
|
|
||||||
return distribution;
|
return distribution;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STATIC
|
// STATIC
|
||||||
static isDice(d: string) {
|
static isDice(d: string) {
|
||||||
return /\d+[dD]\d+/.test(d);
|
return /\d+[dD]\d+/.test(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// globalThis.Dice = Dice;
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
// import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
// import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
||||||
import { writeFile } from "fs/promises";
|
// import { writeFile } from "fs/promises";
|
||||||
|
|
||||||
import { mkdirSync, readFileSync } from "fs";
|
|
||||||
|
|
||||||
export class DHSecretClient {
|
export class DHSecretClient {
|
||||||
private token!: Promise<string>; //Set by init
|
private token!: Promise<string>; //Set by init
|
||||||
@ -15,10 +13,14 @@ 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();
|
// writeFileSync(this.cacheLocation, "{}", { encoding: "utf-8", flag: "wx" });
|
||||||
|
// this.readDiskCache();
|
||||||
this.token = this.fetchToken();
|
this.token = this.fetchToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,22 +43,19 @@ export class DHSecretClient {
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readDiskCache() {
|
// private readDiskCache() {
|
||||||
const cache = readFileSync(this.cacheLocation, "utf-8");
|
// const cache = readFileSync(this.cacheLocation, "utf-8");
|
||||||
|
|
||||||
if (!cache) {
|
// this.cache = JSON.parse(cache || "{}");
|
||||||
this.cache = {};
|
// }
|
||||||
this.writeDiskCache();
|
// private async writeDiskCache() {
|
||||||
} else this.cache = JSON.parse(cache || "{}");
|
// await writeFile(this.cacheLocation, JSON.stringify(this.cache), "utf-8");
|
||||||
}
|
// }
|
||||||
private async writeDiskCache() {
|
|
||||||
await writeFile(this.cacheLocation, JSON.stringify(this.cache), "utf-8");
|
|
||||||
}
|
|
||||||
|
|
||||||
private writeCache(key: string, value: string, expires?: number) {
|
private writeCache(key: string, value: string, expires?: number) {
|
||||||
this.cache[key] = { value, expires };
|
this.cache[key] = { value, expires };
|
||||||
|
|
||||||
this.writeDiskCache();
|
// this.writeDiskCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
private readCache(key: string) {
|
private readCache(key: string) {
|
||||||
@ -74,10 +73,7 @@ export class DHSecretClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fetchSecret(secret_name: string, environment?: string) {
|
async fetchSecret(secret_name: string, environment?: string) {
|
||||||
const uri =
|
const uri = this.dhBaseUri + "/api/keys/" + secret_name +
|
||||||
this.dhBaseUri +
|
|
||||||
"/api/keys/" +
|
|
||||||
secret_name +
|
|
||||||
(environment ? "?env=" + environment : "");
|
(environment ? "?env=" + environment : "");
|
||||||
|
|
||||||
const cached = this.readCache(secret_name);
|
const cached = this.readCache(secret_name);
|
||||||
|
@ -5,8 +5,8 @@ if (!globalThis.Secrets) {
|
|||||||
"https://dragonshoard.cyborggrizzly.com",
|
"https://dragonshoard.cyborggrizzly.com",
|
||||||
process.env.NODE_ENV === "development"
|
process.env.NODE_ENV === "development"
|
||||||
? "./.dragonshoard"
|
? "./.dragonshoard"
|
||||||
: "/.dragonshoard"
|
: "/.dragonshoard",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SecretClient = (): DHSecretClient => globalThis.Secrets;
|
export const SecretClient = () => globalThis.Secrets;
|
||||||
|
@ -1,27 +1,18 @@
|
|||||||
import { PublicationAtom } from "@/recoil/atoms/publication";
|
import { PublicationAtom } from "@/recoil/atoms/publication";
|
||||||
import { useState, useEffect, useCallback, useRef, ReactNode } from "react";
|
import { useState, useEffect, useCallback, useRef } from "react";
|
||||||
import { useRecoilValue } from "recoil";
|
import { useRecoilValue } from "recoil";
|
||||||
import { TTCQueryResolver } from "../ttcQuery/TTCResolvers";
|
import { TTCQueryResolver } from "../ttcQuery/TTCResolvers";
|
||||||
|
|
||||||
export function Resolver({ resolver }: { resolver: string }) {
|
export function Resolver({ resolver }: { resolver: string }) {
|
||||||
const parser = useRecoilValue(PublicationAtom);
|
const parser = useRecoilValue(PublicationAtom);
|
||||||
const [res] = useState(new TTCQueryResolver(parser));
|
const [res] = useState(new TTCQueryResolver(parser));
|
||||||
const [content, setContent] = useState<ReactNode>("");
|
const [content, setContent] = useState("");
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let resolved = res.resolve(resolver);
|
setContent(res.resolve(resolver));
|
||||||
|
|
||||||
setContent(
|
|
||||||
typeof resolved?.display === "function" ? (
|
|
||||||
<resolved.display />
|
|
||||||
) : (
|
|
||||||
resolved?.display
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}, [resolver, res]);
|
}, [resolver, res]);
|
||||||
return <span>{content}</span>;
|
return <span>{content}</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultTemplate = "$x";
|
|
||||||
export function OnDemandResolver({
|
export function OnDemandResolver({
|
||||||
resolver,
|
resolver,
|
||||||
template,
|
template,
|
||||||
@ -33,27 +24,17 @@ export function OnDemandResolver({
|
|||||||
}) {
|
}) {
|
||||||
const parser = useRecoilValue(PublicationAtom);
|
const parser = useRecoilValue(PublicationAtom);
|
||||||
const res = useRef(new TTCQueryResolver(parser));
|
const res = useRef(new TTCQueryResolver(parser));
|
||||||
const [content, setContent] = useState<ReactNode>("");
|
const [content, setContent] = useState("");
|
||||||
const generateContent = useCallback(() => {
|
const generateContent = useCallback(() => {
|
||||||
setContent(() => {
|
let content = template;
|
||||||
let content = template || defaultTemplate;
|
const stackIdxs = Array.from(new Set(template.match(/\$\d/g)));
|
||||||
|
for (const idx of stackIdxs) {
|
||||||
const stackIdxs = Array.from(new Set(content.match(/\$(?:\d+|x)/g)));
|
let thing = res.current.getFromStack(idx);
|
||||||
for (const idx of stackIdxs) {
|
if (Array.isArray(thing)) thing = thing.at(0);
|
||||||
let thing = res.current.getFromStack(idx);
|
if (typeof thing === "function") thing = thing();
|
||||||
if (Array.isArray(thing)) thing = thing.at(0);
|
content = content.replaceAll(idx, thing as string);
|
||||||
console.log(thing);
|
}
|
||||||
if (typeof thing.display === "function") {
|
setContent(content);
|
||||||
const disp = thing.display();
|
|
||||||
if (typeof disp === "string" || typeof disp === "number")
|
|
||||||
content = content.replaceAll(idx, disp.toString());
|
|
||||||
else return disp as ReactNode;
|
|
||||||
}
|
|
||||||
// else if (idx === defaultTemplate && )
|
|
||||||
else content = content.replaceAll(idx, thing.display as string);
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [res, template]);
|
}, [res, template]);
|
||||||
|
|
||||||
const resolve = useCallback(() => {
|
const resolve = useCallback(() => {
|
||||||
@ -62,16 +43,12 @@ export function OnDemandResolver({
|
|||||||
}, [res, resolver, generateContent]);
|
}, [res, resolver, generateContent]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-2 rounded-md p-1 bg-black/20 flex flex-col">
|
<>
|
||||||
<button
|
<button onMouseDown={() => setContent("")} onClick={resolve}>
|
||||||
className="text-primary-600"
|
|
||||||
onMouseDown={() => setContent("")}
|
|
||||||
onClick={resolve}
|
|
||||||
>
|
|
||||||
{title ?? "Resolve"}
|
{title ?? "Resolve"}
|
||||||
</button>
|
</button>
|
||||||
<br />
|
<br />
|
||||||
{!!content && <span>{content}</span>}
|
{!!content && <span>{content}</span>}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -549,7 +549,6 @@ export const buildOnlyDefaultElements = () => {
|
|||||||
// paragraph
|
// paragraph
|
||||||
registerIdentifier(
|
registerIdentifier(
|
||||||
"p",
|
"p",
|
||||||
// /(?<=\n\n|^)([\s\S]*?)(?=\n\n|$)/g,
|
|
||||||
/(?<=\n\n)([\s\S]*?)(?=\n\n)/g,
|
/(?<=\n\n)([\s\S]*?)(?=\n\n)/g,
|
||||||
(s) => {
|
(s) => {
|
||||||
return {
|
return {
|
||||||
@ -803,11 +802,11 @@ export const buildOnlyDefaultElements = () => {
|
|||||||
// on-demand resolver
|
// on-demand resolver
|
||||||
registerIdentifier(
|
registerIdentifier(
|
||||||
"on-demand resolver",
|
"on-demand resolver",
|
||||||
/\?\?\[.*?\](\(.*?\))<<(.*?)>>/g,
|
/\?\?\[.*?\](\(.*?\))?<<(.*?)>>/g,
|
||||||
(s) => {
|
(s) => {
|
||||||
const inp = s.match(/(?<=<<)(.*?)(?=>>)/)![0];
|
const inp = s.match(/(?<=<<)(.*?)(?=>>)/)![0];
|
||||||
const title = s.match(/(?<=\?\?\[)(.*?)(?=\])/)![0];
|
const template = s.match(/(?<=\?\?\[)(.*?)(?=\])/)![0];
|
||||||
const template = s.match(/(?<=\]\()(.*?)(?=\))/)![0];
|
const title = s.match(/(?<=\]\()(.*?)(?=\))/)?.at(0);
|
||||||
if (inp == undefined)
|
if (inp == undefined)
|
||||||
return {
|
return {
|
||||||
content: "Error parsing resolver: " + s,
|
content: "Error parsing resolver: " + s,
|
||||||
@ -932,7 +931,7 @@ function search(
|
|||||||
function generateId(t: string, usedIds: string[]) {
|
function generateId(t: string, usedIds: string[]) {
|
||||||
let id = t
|
let id = t
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.replace(/[^a-z\s-\d]/gi, "")
|
.replace(/[^a-z\s]/gi, "")
|
||||||
.trim()
|
.trim()
|
||||||
.replaceAll(" ", "-");
|
.replaceAll(" ", "-");
|
||||||
let idNum = 1;
|
let idNum = 1;
|
||||||
|
@ -11,10 +11,7 @@ export const createElements = (body: string): Token[] => {
|
|||||||
const tokenize = (body: string) => {
|
const tokenize = (body: string) => {
|
||||||
const tokenizedBody: TokenMarker[] = [];
|
const tokenizedBody: TokenMarker[] = [];
|
||||||
|
|
||||||
body = body
|
body = body.replaceAll(/[ \t]+\n/g, "\n").replaceAll(/\n{3,}/g, "\n\n");
|
||||||
.trim()
|
|
||||||
.replaceAll(/[ \t]+\n/g, "\n")
|
|
||||||
.replaceAll(/\n{3,}/g, "\n\n");
|
|
||||||
|
|
||||||
const addToken = (thing: TokenMarker) => {
|
const addToken = (thing: TokenMarker) => {
|
||||||
tokenizedBody.push(thing);
|
tokenizedBody.push(thing);
|
||||||
@ -26,7 +23,6 @@ const tokenize = (body: string) => {
|
|||||||
const rx = new RegExp(token.rx);
|
const rx = new RegExp(token.rx);
|
||||||
let match;
|
let match;
|
||||||
while ((match = rx.exec(body)) !== null) {
|
while ((match = rx.exec(body)) !== null) {
|
||||||
if (type === "p") debugger;
|
|
||||||
const start = match.index;
|
const start = match.index;
|
||||||
const end = rx.lastIndex;
|
const end = rx.lastIndex;
|
||||||
|
|
||||||
@ -273,10 +269,7 @@ export function extractFrontMatter(body: string): [FrontMatter, string] {
|
|||||||
const rx = /^---([\s\S]*?)---/;
|
const rx = /^---([\s\S]*?)---/;
|
||||||
const [_, frontmatterString] = body.match(rx) || ["", ""];
|
const [_, frontmatterString] = body.match(rx) || ["", ""];
|
||||||
|
|
||||||
console.log(body.replace(rx, ""));
|
body = body.replace(rx, "");
|
||||||
|
|
||||||
body = body.replace(rx, "").trim();
|
|
||||||
console.log(body);
|
|
||||||
|
|
||||||
const frontMatter: FrontMatter = {};
|
const frontMatter: FrontMatter = {};
|
||||||
|
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
import { FC } from "react";
|
|
||||||
|
|
||||||
export const DiceChart: FC<{ dice: Record<string, number> }> = ({ dice }) => {
|
|
||||||
const data = Object.entries(dice).sort((a, b) => Number(a[0]) - Number(b[0]));
|
|
||||||
|
|
||||||
const max = Math.max(...data.map((d) => d[1]));
|
|
||||||
const _min = Math.min(...data.map((d) => d[1]));
|
|
||||||
|
|
||||||
const uniqueValues = Array.from(new Set(data.map((d) => d[1])));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-8 bg-black/30 rounded-md max-w-[35rem]">
|
|
||||||
<div className="relative flex px-2 gap-1 justify-around items-end h-48 border-b border-l">
|
|
||||||
<div className="absolute inset-0 flex flex-col-reverse">
|
|
||||||
{uniqueValues.map((_, i) => (
|
|
||||||
<div
|
|
||||||
key={"dicechartline" + i}
|
|
||||||
className="relative w-full border-t border-mixed-300/50 flex-grow"
|
|
||||||
>
|
|
||||||
{/* <div className="absolute right-full text-xs flex items-center -translate-y-1/2">
|
|
||||||
<span>
|
|
||||||
{Math.round(max / uniqueValues.length) * (i + 1)} {}
|
|
||||||
</span>
|
|
||||||
<div className="border-b w-1 ml-1"></div>
|
|
||||||
</div> */}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{data.map((d) => (
|
|
||||||
<div
|
|
||||||
key={"dice" + d[0]}
|
|
||||||
title={d[0] + ": " + d[1]}
|
|
||||||
className="flex-grow px-[1px] border hover:border-mixed-300/50 border-transparent h-full flex transition-colors rounded-md @container"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="bg-purple-800 relative rounded-t-sm w-full mt-auto"
|
|
||||||
style={{ height: (d[1] / max) * 100 + "%" }}
|
|
||||||
>
|
|
||||||
<span className="absolute top-full left-1/2 -translate-x-1/2 text-xs @[.75rem]:inline hidden">
|
|
||||||
{d[0]}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,20 +1,11 @@
|
|||||||
/* eslint-disable react/display-name */
|
|
||||||
import { ReactNode } from "react";
|
|
||||||
import { Dice } from "../dice";
|
import { Dice } from "../dice";
|
||||||
import { sum } from "../utils/sum";
|
|
||||||
import { DiceChart } from "./DiceChart";
|
|
||||||
import { TTCQueryParser } from "./TTCQueryParser";
|
import { TTCQueryParser } from "./TTCQueryParser";
|
||||||
|
|
||||||
interface StackItem {
|
|
||||||
value: any;
|
|
||||||
display: ReactNode | (() => ReactNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TTCQueryResolver {
|
export class TTCQueryResolver {
|
||||||
private parser: TTCQueryParser | null;
|
private parser: TTCQueryParser | null;
|
||||||
private context: QueryableObject | null = null;
|
private context: QueryableObject | null = null;
|
||||||
|
|
||||||
private stack: StackItem[] = [];
|
private stack: any[] = [];
|
||||||
|
|
||||||
constructor(parser?: TTCQueryParser) {
|
constructor(parser?: TTCQueryParser) {
|
||||||
this.parser = parser || null;
|
this.parser = parser || null;
|
||||||
@ -28,35 +19,21 @@ export class TTCQueryResolver {
|
|||||||
this.context = obj;
|
this.context = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public resolve(resolver: string, onDemand?: boolean): StackItem | undefined {
|
public resolve(resolver: string, onDemand?: boolean) {
|
||||||
try {
|
const resList = resolver.split(",");
|
||||||
const resList = resolver.split(",");
|
for (const res of resList) {
|
||||||
for (const res of resList) {
|
this.stack.push(this.parseResolver(res));
|
||||||
this.stack.push(this.parseResolver(res));
|
|
||||||
}
|
|
||||||
const last = this.stack.at(-1);
|
|
||||||
if (typeof last?.display === "function" && !onDemand) {
|
|
||||||
last.display = last.display();
|
|
||||||
}
|
|
||||||
|
|
||||||
return last;
|
|
||||||
} catch (e) {
|
|
||||||
return { value: e?.toString(), display: e?.toString() };
|
|
||||||
}
|
}
|
||||||
|
const last = this.stack.at(-1);
|
||||||
|
if (typeof last === "function" && !onDemand) return last();
|
||||||
|
|
||||||
|
return last;
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseResolver(resolver: string): StackItem {
|
private parseResolver(resolver: string) {
|
||||||
if (this.isArithmetic(resolver)) return this.solveArithmetic(resolver);
|
if (this.isArithmetic(resolver)) return this.solveArithmetic(resolver);
|
||||||
if (this.isQuery(resolver)) return this.runQuery(resolver);
|
if (this.isQuery(resolver)) return this.runQuery(resolver);
|
||||||
if (Dice.isDice(resolver)) {
|
return resolver;
|
||||||
const value = new Dice(resolver);
|
|
||||||
const dice: StackItem = {
|
|
||||||
value,
|
|
||||||
display: () => value.toString(),
|
|
||||||
};
|
|
||||||
return dice;
|
|
||||||
}
|
|
||||||
return { value: resolver, display: resolver };
|
|
||||||
}
|
}
|
||||||
private isQuery(resolver: string) {
|
private isQuery(resolver: string) {
|
||||||
return (
|
return (
|
||||||
@ -65,141 +42,76 @@ export class TTCQueryResolver {
|
|||||||
/^\$\d\./.test(resolver)
|
/^\$\d\./.test(resolver)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
private runQuery(query: string): StackItem {
|
private runQuery(query: string) {
|
||||||
if (!this.parser) throw "Parser not defined in query resolver";
|
if (!this.parser) throw "Parser not defined in query resolver";
|
||||||
if (query.startsWith("$")) {
|
if (query.startsWith("$")) {
|
||||||
const [_, idx, q] = query.match(/^(\$\d+)\.(.*)/) || [];
|
const [_, idx, q] = query.match(/^(\$\d+)\.(.*)/) || [];
|
||||||
if (!_) throw "Detected stack query but did not match the regex";
|
if (!_) throw "Detected stack query but did not match the regex";
|
||||||
const stackItem = this.getFromStack(idx);
|
const stackItem = this.getFromStack(idx);
|
||||||
|
if (typeof stackItem === "string" && Dice.isDice(stackItem)) {
|
||||||
if (stackItem.value instanceof Dice) {
|
return this.handleDice(stackItem, q);
|
||||||
return {
|
|
||||||
value: query,
|
|
||||||
display: this.handleDice(stackItem.value, q),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const [res] = this.parser.search(q, stackItem.value as QueryableObject);
|
return this.parser.search(q, stackItem as QueryableObject);
|
||||||
debugger;
|
|
||||||
if (Dice.isDice(res)) {
|
|
||||||
const value = new Dice(res);
|
|
||||||
return {
|
|
||||||
value,
|
|
||||||
display: () => value.toString(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
value: res,
|
|
||||||
display() {
|
|
||||||
return (
|
|
||||||
this.value.render ?? this.value ?? "Error resolving query: " + query
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (query.startsWith("?") || query.startsWith("_"))
|
// if (query.startsWith("?") || query.startsWith("_"))
|
||||||
const res =
|
return query.startsWith("_") && this.context
|
||||||
query.startsWith("_") && this.context
|
? this.parser.search(query.replace("_", "^"), this.context).at(0)
|
||||||
? this.parser.search(query.replace("_", "^"), this.context).at(0)
|
: this.parser.search(query.replace(/^[?_].?/, "")).at(0);
|
||||||
: this.parser.search(query.replace(/^[?_].?/, "")).at(0);
|
}
|
||||||
if (Dice.isDice(res)) {
|
|
||||||
const value = new Dice(res);
|
private handleDice(dice: string, query: string) {
|
||||||
return {
|
const d = new Dice(dice);
|
||||||
value,
|
const [method, n] = query.split(":");
|
||||||
display: () => value.toString(),
|
let num = Number(n);
|
||||||
};
|
if (n && n.startsWith("$")) num = this.getFromStack(n);
|
||||||
|
switch (method) {
|
||||||
|
case "roll":
|
||||||
|
return () => d.roll();
|
||||||
|
case "rollAvg":
|
||||||
|
return () => d.rollAvg();
|
||||||
|
case "rollTimes":
|
||||||
|
return () => d.rollTimes(num);
|
||||||
|
case "rollTimesAvg":
|
||||||
|
return () => d.rollTimesAvg(num);
|
||||||
|
default:
|
||||||
|
return "No valid method provided for dice";
|
||||||
}
|
}
|
||||||
return {
|
|
||||||
value: res,
|
|
||||||
display() {
|
|
||||||
return (
|
|
||||||
this.value.render ?? this.value ?? "Error resolving query: " + query
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private isArithmetic(resolver: string) {
|
private isArithmetic(resolver: string) {
|
||||||
return resolver.split(/\+|\/|-|\*|\^/).filter((e) => !!e).length > 1;
|
return resolver.split(/\+|\/|-|\*|\^/).filter((e) => !!e).length > 1;
|
||||||
}
|
}
|
||||||
private solveArithmetic(resolver: string): StackItem {
|
private solveArithmetic(resolver: string) {
|
||||||
const [n1, op, n2] = resolver
|
const [n1, op, n2] = resolver
|
||||||
.match(/(\$?\d+)([+\-*\/^])(\$?\d+)/)
|
.match(/(\$?\d+)([+\-*\/^])(\$?\d+)/)
|
||||||
?.slice(1) || ["", "+", ""];
|
?.slice(1) || ["", "+", ""];
|
||||||
let num1: number = Number(n1),
|
let num1 = Number(n1),
|
||||||
num2: number = Number(n2);
|
num2 = Number(n2);
|
||||||
|
|
||||||
if (n1.startsWith("$")) {
|
if (n1.startsWith("$")) num1 = this.getFromStack<number>(n1);
|
||||||
const result = this.getFromStack(n1).value;
|
if (n2.startsWith("$")) num2 = this.getFromStack<number>(n2);
|
||||||
num1 = result instanceof Dice ? result.roll().total : Number(result);
|
|
||||||
|
switch (op) {
|
||||||
|
case "+":
|
||||||
|
return num1 + num2;
|
||||||
|
case "-":
|
||||||
|
return num1 - num2;
|
||||||
|
case "*":
|
||||||
|
return num1 * num2;
|
||||||
|
case "/":
|
||||||
|
return num1 / num2;
|
||||||
|
case "^":
|
||||||
|
return num1 ^ num2;
|
||||||
|
default:
|
||||||
|
throw "Arithmetic detected but no proper operator assigned";
|
||||||
}
|
}
|
||||||
if (n2.startsWith("$")) {
|
|
||||||
const result = this.getFromStack(n1).value;
|
|
||||||
num2 = result instanceof Dice ? result.roll().total : Number(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
const thing: StackItem = {
|
|
||||||
value: () => {
|
|
||||||
switch (op) {
|
|
||||||
case "+":
|
|
||||||
return num1 + num2;
|
|
||||||
case "-":
|
|
||||||
return num1 - num2;
|
|
||||||
case "*":
|
|
||||||
return num1 * num2;
|
|
||||||
case "/":
|
|
||||||
return num1 / num2;
|
|
||||||
case "^":
|
|
||||||
return num1 ^ num2;
|
|
||||||
default:
|
|
||||||
throw "Arithmetic detected but no proper operator assigned";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
display() {
|
|
||||||
return typeof this.value === "function" ? this.value() : this.value;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return thing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getFromStack(stackIndex: string): StackItem {
|
public getFromStack<T>(stackIndex: string): T {
|
||||||
if (stackIndex === "$x") return this.stack.at(-1)!;
|
|
||||||
const i = Number(stackIndex.replace("$", ""));
|
const i = Number(stackIndex.replace("$", ""));
|
||||||
const val = this.stack[i];
|
const val = this.stack[i] as T;
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
private handleDice(dice: Dice, query: string) {
|
|
||||||
const [method, n] = query.split(":");
|
|
||||||
let num = Number(n);
|
|
||||||
// if (n && n.startsWith("$")) num = this.getFromStack(n);
|
|
||||||
switch (method) {
|
|
||||||
case "roll":
|
|
||||||
return () => dice.roll.apply(dice).total;
|
|
||||||
case "rollAvg":
|
|
||||||
return () => dice.rollAvg.apply(dice);
|
|
||||||
case "rollTimes":
|
|
||||||
return () => dice.rollTimes.apply(dice, [num]);
|
|
||||||
case "rollTimesAvg":
|
|
||||||
return () => dice.rollTimesAvg.apply(dice, [num]);
|
|
||||||
case "rollLowest":
|
|
||||||
return () => dice.rollMin.apply(dice);
|
|
||||||
case "rollHighest":
|
|
||||||
return () => dice.rollMax.apply(dice);
|
|
||||||
case "rollDropHighest":
|
|
||||||
return () =>
|
|
||||||
sum(...dice.roll.apply(dice).results.toSorted().toSpliced(-1, 1));
|
|
||||||
case "rollDropLowest":
|
|
||||||
return () =>
|
|
||||||
sum(...dice.roll.apply(dice).results.toSorted().toSpliced(0, 1));
|
|
||||||
case "distribution":
|
|
||||||
return () => DiceChart({ dice: dice.getRollDistribution.apply(dice) });
|
|
||||||
case "distribution.normalized":
|
|
||||||
return () =>
|
|
||||||
DiceChart({ dice: dice.getNormalizedRollDistribution.apply(dice) });
|
|
||||||
default:
|
|
||||||
return () => "No valid method provided for dice";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export const sum = (...args: number[]) => {
|
|
||||||
return args.reduce((a, b) => a + b, 0);
|
|
||||||
};
|
|
@ -2,7 +2,7 @@
|
|||||||
title: How to use ttcMD
|
title: How to use ttcMD
|
||||||
author: Emmaline Autumn
|
author: Emmaline Autumn
|
||||||
date: March 14th, 2024
|
date: March 14th, 2024
|
||||||
updated: August 21st, 2024
|
updated: March 14th, 2024
|
||||||
---
|
---
|
||||||
|
|
||||||
# Table of Contents
|
# Table of Contents
|
||||||
@ -368,17 +368,17 @@ Let's say you want to get the the result of rolling a dice field. You would simp
|
|||||||
|
|
||||||
This works very similarly to the normal resolver, but when it renders it will have a button that you can press. When you press it, it recalculates its value. This is very useful for dice and decks of cards. It has not been fully implemented yet
|
This works very similarly to the normal resolver, but when it renders it will have a button that you can press. When you press it, it recalculates its value. This is very useful for dice and decks of cards. It has not been fully implemented yet
|
||||||
|
|
||||||
Here's the syntax: `??[Text](Template)<<List::QueryValue>>`. Template is a basic string that has access to all values of the resolver using the `$#` variables. If template is left blank, the value that the resolver finally reaches will be rendered. In lieu of a template, you can also have it render the display of a field if it has one. Text is the text that will be rendered in the button. If not provided, the button will simply same "Resolve."
|
Here's the syntax: `??[Template](Text?)<<List::QueryValue>>`. Template is a basic string that has access to all values of the resolver using the `$#` variables. If template is left blank, the value that the resolver finally reaches will be rendered. In lieu of a template, you can also have it render the display of a field if it has one. Text is the text that will be rendered in the button. If not provided, the button will simply same "Resolve."
|
||||||
|
|
||||||
[][]
|
[][]
|
||||||
[[
|
[[
|
||||||
To use the dice as an example again, here's how you would do that: `??[Roll 2d6](Rolling $0, you got: $1)<<_.path.to.dice,$0.roll>>`
|
To use the dice as an example again, here's how you would do that: `??[Rolling $0, you got: $1](Roll 2d6)<<_.path.to.dice,$0.roll>>`
|
||||||
|
|
||||||
??[Roll 2d6](Rolling $0, you got: $1)<<_.path.to.dice,$0.roll>>
|
??[Rolling $0, you got: $1](Roll 2d6)<<_.path.to.dice,$0.roll>>
|
||||||
]]
|
]]
|
||||||
|
|
||||||
[[
|
[[
|
||||||
For drawing a card and having it show the card's display: `??[]()<<_.path.to.deck,$0.draw,$1.display>>`
|
For drawing a card and having it show the card's display: `??[]<<_.path.to.deck,$0.draw,$1.display>>`
|
||||||
]]
|
]]
|
||||||
/[]
|
/[]
|
||||||
|
|
||||||
|
@ -12,9 +12,4 @@
|
|||||||
3. my
|
3. my
|
||||||
4. name
|
4. name
|
||||||
5. is
|
5. is
|
||||||
6. welcome
|
6. welcome
|
||||||
|
|
||||||
- [-\_hello1234!@#$%^\&\*()-](#-_hello1234-)
|
|
||||||
|
|
||||||
|
|
||||||
# -_hello1234!@#$%^&*()-
|
|
472
package-lock.json
generated
472
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,13 +11,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@auth/prisma-adapter": "^2.4.2",
|
"@auth/prisma-adapter": "^2.4.2",
|
||||||
"@codemirror/lang-markdown": "^6.2.5",
|
|
||||||
"@heroicons/react": "^2.1.1",
|
"@heroicons/react": "^2.1.1",
|
||||||
"@prisma/client": "^5.18.0",
|
"@prisma/client": "^5.18.0",
|
||||||
"@tailwindcss/container-queries": "^0.1.1",
|
|
||||||
"@types/bcryptjs": "^2.4.6",
|
"@types/bcryptjs": "^2.4.6",
|
||||||
"@uiw/codemirror-theme-duotone": "^4.23.0",
|
|
||||||
"@uiw/react-codemirror": "^4.23.0",
|
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"isomorphic-dompurify": "^2.4.0",
|
"isomorphic-dompurify": "^2.4.0",
|
||||||
"jotai": "^2.9.3",
|
"jotai": "^2.9.3",
|
||||||
|
46
prisma/migrations/20240317161445_init/migration.sql
Normal file
46
prisma/migrations/20240317161445_init/migration.sql
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `GameSystem` (
|
||||||
|
`id` VARCHAR(191) NOT NULL,
|
||||||
|
`name` VARCHAR(191) NOT NULL,
|
||||||
|
`created` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Schema` (
|
||||||
|
`id` VARCHAR(191) NOT NULL,
|
||||||
|
`gameSystemId` VARCHAR(191) NOT NULL,
|
||||||
|
`name` VARCHAR(191) NOT NULL,
|
||||||
|
`schema` JSON NOT NULL,
|
||||||
|
`version` INTEGER NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Publication` (
|
||||||
|
`id` VARCHAR(191) NOT NULL,
|
||||||
|
`schemaId` VARCHAR(191) NOT NULL,
|
||||||
|
`name` VARCHAR(191) NOT NULL,
|
||||||
|
`data` JSON NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Tag` (
|
||||||
|
`id` VARCHAR(191) NOT NULL,
|
||||||
|
`publicationId` VARCHAR(191) NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Schema` ADD CONSTRAINT `Schema_gameSystemId_fkey` FOREIGN KEY (`gameSystemId`) REFERENCES `GameSystem`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Publication` ADD CONSTRAINT `Publication_schemaId_fkey` FOREIGN KEY (`schemaId`) REFERENCES `Schema`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Tag` ADD CONSTRAINT `Tag_publicationId_fkey` FOREIGN KEY (`publicationId`) REFERENCES `Publication`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
8
prisma/migrations/20240317164602_update/migration.sql
Normal file
8
prisma/migrations/20240317164602_update/migration.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- A unique constraint covering the columns `[name]` on the table `GameSystem` will be added. If there are existing duplicate values, this will fail.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX `GameSystem_name_key` ON `GameSystem`(`name`);
|
@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Added the required column `types` to the `Schema` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Schema` ADD COLUMN `types` JSON NOT NULL;
|
83
prisma/migrations/20240815100640_retagging/migration.sql
Normal file
83
prisma/migrations/20240815100640_retagging/migration.sql
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Added the required column `authorId` to the `GameSystem` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `authorId` to the `Publication` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `authorId` to the `Schema` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `originalId` to the `Schema` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `name` to the `Tag` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE `Schema` DROP FOREIGN KEY `Schema_gameSystemId_fkey`;
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE `Tag` DROP FOREIGN KEY `Tag_publicationId_fkey`;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `GameSystem` ADD COLUMN `authorId` VARCHAR(191) NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Publication` ADD COLUMN `authorId` VARCHAR(191) NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Schema` ADD COLUMN `authorId` VARCHAR(191) NOT NULL,
|
||||||
|
ADD COLUMN `originalId` VARCHAR(191) NOT NULL,
|
||||||
|
MODIFY `gameSystemId` VARCHAR(191) NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Tag` ADD COLUMN `name` VARCHAR(191) NOT NULL,
|
||||||
|
MODIFY `publicationId` VARCHAR(191) NULL;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `TagsOnPublications` (
|
||||||
|
`publicationId` VARCHAR(191) NOT NULL,
|
||||||
|
`tagId` VARCHAR(191) NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`publicationId`, `tagId`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `TagsOnTags` (
|
||||||
|
`parentTagId` VARCHAR(191) NOT NULL,
|
||||||
|
`childTagId` VARCHAR(191) NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`parentTagId`, `childTagId`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `User` (
|
||||||
|
`id` VARCHAR(191) NOT NULL,
|
||||||
|
`username` VARCHAR(191) NOT NULL,
|
||||||
|
`email` VARCHAR(191) NOT NULL,
|
||||||
|
|
||||||
|
UNIQUE INDEX `User_email_key`(`email`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `GameSystem` ADD CONSTRAINT `GameSystem_authorId_fkey` FOREIGN KEY (`authorId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Schema` ADD CONSTRAINT `Schema_gameSystemId_fkey` FOREIGN KEY (`gameSystemId`) REFERENCES `GameSystem`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Schema` ADD CONSTRAINT `Schema_authorId_fkey` FOREIGN KEY (`authorId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Publication` ADD CONSTRAINT `Publication_authorId_fkey` FOREIGN KEY (`authorId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `TagsOnPublications` ADD CONSTRAINT `TagsOnPublications_publicationId_fkey` FOREIGN KEY (`publicationId`) REFERENCES `Publication`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `TagsOnPublications` ADD CONSTRAINT `TagsOnPublications_tagId_fkey` FOREIGN KEY (`tagId`) REFERENCES `Tag`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Tag` ADD CONSTRAINT `Tag_publicationId_fkey` FOREIGN KEY (`publicationId`) REFERENCES `Publication`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `TagsOnTags` ADD CONSTRAINT `TagsOnTags_parentTagId_fkey` FOREIGN KEY (`parentTagId`) REFERENCES `Tag`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `TagsOnTags` ADD CONSTRAINT `TagsOnTags_childTagId_fkey` FOREIGN KEY (`childTagId`) REFERENCES `Tag`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
71
prisma/migrations/20240818144724_auth/migration.sql
Normal file
71
prisma/migrations/20240818144724_auth/migration.sql
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- A unique constraint covering the columns `[username]` on the table `User` will be added. If there are existing duplicate values, this will fail.
|
||||||
|
- Added the required column `updatedAt` to the `User` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `User` ADD COLUMN `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
ADD COLUMN `emailVerified` DATETIME(3) NULL,
|
||||||
|
ADD COLUMN `image` VARCHAR(191) NULL,
|
||||||
|
ADD COLUMN `name` VARCHAR(191) NULL,
|
||||||
|
ADD COLUMN `updatedAt` DATETIME(3) NOT NULL,
|
||||||
|
MODIFY `username` VARCHAR(191) NULL,
|
||||||
|
MODIFY `email` VARCHAR(191) NULL;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Account` (
|
||||||
|
`id` VARCHAR(191) NOT NULL,
|
||||||
|
`userId` VARCHAR(191) NOT NULL,
|
||||||
|
`type` VARCHAR(191) NOT NULL,
|
||||||
|
`provider` VARCHAR(191) NOT NULL,
|
||||||
|
`providerAccountId` VARCHAR(191) NOT NULL,
|
||||||
|
`refresh_token` TEXT NULL,
|
||||||
|
`access_token` TEXT NULL,
|
||||||
|
`expires_at` INTEGER NULL,
|
||||||
|
`token_type` VARCHAR(191) NULL,
|
||||||
|
`scope` VARCHAR(191) NULL,
|
||||||
|
`id_token` TEXT NULL,
|
||||||
|
`session_state` VARCHAR(191) NULL,
|
||||||
|
`refresh_token_expires_in` INTEGER NULL,
|
||||||
|
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
`updatedAt` DATETIME(3) NOT NULL,
|
||||||
|
|
||||||
|
UNIQUE INDEX `Account_userId_key`(`userId`),
|
||||||
|
INDEX `Account_userId_idx`(`userId`),
|
||||||
|
UNIQUE INDEX `Account_provider_providerAccountId_key`(`provider`, `providerAccountId`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Session` (
|
||||||
|
`id` VARCHAR(191) NOT NULL,
|
||||||
|
`sessionToken` VARCHAR(191) NOT NULL,
|
||||||
|
`userId` VARCHAR(191) NOT NULL,
|
||||||
|
`expires` DATETIME(3) NOT NULL,
|
||||||
|
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
`updatedAt` DATETIME(3) NOT NULL,
|
||||||
|
|
||||||
|
UNIQUE INDEX `Session_sessionToken_key`(`sessionToken`),
|
||||||
|
INDEX `Session_userId_idx`(`userId`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `VerificationToken` (
|
||||||
|
`identifier` VARCHAR(191) NOT NULL,
|
||||||
|
`token` VARCHAR(191) NOT NULL,
|
||||||
|
`expires` DATETIME(3) NOT NULL,
|
||||||
|
|
||||||
|
UNIQUE INDEX `VerificationToken_identifier_token_key`(`identifier`, `token`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX `User_username_key` ON `User`(`username`);
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Account` ADD CONSTRAINT `Account_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Session` ADD CONSTRAINT `Session_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
2
prisma/migrations/20240818160027_pw/migration.sql
Normal file
2
prisma/migrations/20240818160027_pw/migration.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `User` ADD COLUMN `passwordHash` VARCHAR(191) NULL;
|
@ -1,177 +0,0 @@
|
|||||||
-- CreateTable
|
|
||||||
CREATE TABLE "GameSystem" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"authorId" TEXT NOT NULL,
|
|
||||||
"name" TEXT NOT NULL,
|
|
||||||
"created" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
CONSTRAINT "GameSystem_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "Schema" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"gameSystemId" TEXT,
|
|
||||||
"authorId" TEXT NOT NULL,
|
|
||||||
"originalId" TEXT NOT NULL,
|
|
||||||
"name" TEXT NOT NULL,
|
|
||||||
"schema" JSONB NOT NULL,
|
|
||||||
"types" JSONB NOT NULL,
|
|
||||||
"version" INTEGER NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "Schema_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "Publication" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"schemaId" TEXT NOT NULL,
|
|
||||||
"authorId" TEXT NOT NULL,
|
|
||||||
"name" TEXT NOT NULL,
|
|
||||||
"data" JSONB NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "Publication_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "TagsOnPublications" (
|
|
||||||
"publicationId" TEXT NOT NULL,
|
|
||||||
"tagId" TEXT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "TagsOnPublications_pkey" PRIMARY KEY ("publicationId","tagId")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "Tag" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"name" TEXT NOT NULL,
|
|
||||||
"publicationId" TEXT,
|
|
||||||
|
|
||||||
CONSTRAINT "Tag_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "TagsOnTags" (
|
|
||||||
"parentTagId" TEXT NOT NULL,
|
|
||||||
"childTagId" TEXT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "TagsOnTags_pkey" PRIMARY KEY ("parentTagId","childTagId")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "User" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"name" TEXT,
|
|
||||||
"username" TEXT,
|
|
||||||
"email" TEXT,
|
|
||||||
"emailVerified" TIMESTAMP(3),
|
|
||||||
"passwordHash" TEXT,
|
|
||||||
"image" TEXT,
|
|
||||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "Account" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"userId" TEXT NOT NULL,
|
|
||||||
"type" TEXT NOT NULL,
|
|
||||||
"provider" TEXT NOT NULL,
|
|
||||||
"providerAccountId" TEXT NOT NULL,
|
|
||||||
"refresh_token" TEXT,
|
|
||||||
"access_token" TEXT,
|
|
||||||
"expires_at" INTEGER,
|
|
||||||
"token_type" TEXT,
|
|
||||||
"scope" TEXT,
|
|
||||||
"id_token" TEXT,
|
|
||||||
"session_state" TEXT,
|
|
||||||
"refresh_token_expires_in" INTEGER,
|
|
||||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "Account_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "Session" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"sessionToken" TEXT NOT NULL,
|
|
||||||
"userId" TEXT NOT NULL,
|
|
||||||
"expires" TIMESTAMP(3) NOT NULL,
|
|
||||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "VerificationToken" (
|
|
||||||
"identifier" TEXT NOT NULL,
|
|
||||||
"token" TEXT NOT NULL,
|
|
||||||
"expires" TIMESTAMP(3) NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "GameSystem_name_key" ON "GameSystem"("name");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "Account_userId_key" ON "Account"("userId");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE INDEX "Account_userId_idx" ON "Account"("userId");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE INDEX "Session_userId_idx" ON "Session"("userId");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token");
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "GameSystem" ADD CONSTRAINT "GameSystem_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "Schema" ADD CONSTRAINT "Schema_gameSystemId_fkey" FOREIGN KEY ("gameSystemId") REFERENCES "GameSystem"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "Schema" ADD CONSTRAINT "Schema_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "Publication" ADD CONSTRAINT "Publication_schemaId_fkey" FOREIGN KEY ("schemaId") REFERENCES "Schema"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "Publication" ADD CONSTRAINT "Publication_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "TagsOnPublications" ADD CONSTRAINT "TagsOnPublications_publicationId_fkey" FOREIGN KEY ("publicationId") REFERENCES "Publication"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "TagsOnPublications" ADD CONSTRAINT "TagsOnPublications_tagId_fkey" FOREIGN KEY ("tagId") REFERENCES "Tag"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "Tag" ADD CONSTRAINT "Tag_publicationId_fkey" FOREIGN KEY ("publicationId") REFERENCES "Publication"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "TagsOnTags" ADD CONSTRAINT "TagsOnTags_parentTagId_fkey" FOREIGN KEY ("parentTagId") REFERENCES "Tag"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "TagsOnTags" ADD CONSTRAINT "TagsOnTags_childTagId_fkey" FOREIGN KEY ("childTagId") REFERENCES "Tag"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
@ -1,2 +0,0 @@
|
|||||||
-- AlterTable
|
|
||||||
ALTER TABLE "Schema" ALTER COLUMN "originalId" DROP NOT NULL;
|
|
@ -1,3 +1,3 @@
|
|||||||
# Please do not edit this file manually
|
# Please do not edit this file manually
|
||||||
# It should be added in your version-control system (i.e. Git)
|
# It should be added in your version-control system (i.e. Git)
|
||||||
provider = "postgresql"
|
provider = "mysql"
|
@ -9,7 +9,7 @@ generator client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
provider = "postgresql"
|
provider = "mysql"
|
||||||
url = env("DATABASE_URL")
|
url = env("DATABASE_URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ model Schema {
|
|||||||
author User @relation(fields: [authorId], references: [id])
|
author User @relation(fields: [authorId], references: [id])
|
||||||
authorId String
|
authorId String
|
||||||
|
|
||||||
originalId String?
|
originalId String
|
||||||
name String
|
name String
|
||||||
schema Json
|
schema Json
|
||||||
types Json
|
types Json
|
||||||
|
@ -51,6 +51,6 @@ const config: Config = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [require("@tailwindcss/container-queries")],
|
plugins: [],
|
||||||
};
|
};
|
||||||
export default config;
|
export default config;
|
||||||
|
@ -38,7 +38,6 @@
|
|||||||
"node_modules"
|
"node_modules"
|
||||||
],
|
],
|
||||||
"files": [
|
"files": [
|
||||||
"global.d.ts",
|
"global.d.ts"
|
||||||
"./components/mdeditor/TextEditor.tsx"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -1,54 +0,0 @@
|
|||||||
import { prisma } from "@/prisma/prismaClient";
|
|
||||||
|
|
||||||
export async function isEmailVerified(id: string) {
|
|
||||||
const user = await prisma.user.findUnique({
|
|
||||||
where: { id },
|
|
||||||
select: {
|
|
||||||
emailVerified: true,
|
|
||||||
email: true,
|
|
||||||
accounts: { select: { provider: true, access_token: true } },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (!user) return false;
|
|
||||||
|
|
||||||
if (user?.emailVerified) return true;
|
|
||||||
|
|
||||||
const discordAccount = user.accounts.find((a) => a.provider === "discord");
|
|
||||||
if (user && discordAccount?.access_token) {
|
|
||||||
const dcUser = await getDiscordUserInfo(discordAccount.access_token);
|
|
||||||
if (!dcUser.verified) return false;
|
|
||||||
prisma.user.update({ where: { id }, data: { emailVerified: new Date() } });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getDiscordUserInfo(accessToken: string): Promise<{
|
|
||||||
id: string;
|
|
||||||
username: string;
|
|
||||||
discriminator: string;
|
|
||||||
avatar: string;
|
|
||||||
verified: boolean;
|
|
||||||
email: string;
|
|
||||||
flags: number;
|
|
||||||
banner: string;
|
|
||||||
accent_color: number;
|
|
||||||
premium_type: number;
|
|
||||||
public_flags: number;
|
|
||||||
avatar_decoration_data: {
|
|
||||||
sku_id: string;
|
|
||||||
asset: string;
|
|
||||||
};
|
|
||||||
}> {
|
|
||||||
try {
|
|
||||||
const response = await fetch("https://discord.com/api/users/@me", {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${accessToken}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return await response.json();
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching user info from Discord:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user