mirror of
https://github.com/barthuijgen/factorio-sites.git
synced 2024-11-24 08:52:36 +02:00
update fbe-editor, migrate from sequelize to prisma
This commit is contained in:
parent
fe51aa2376
commit
a42927f336
4
.gitignore
vendored
4
.gitignore
vendored
@ -43,5 +43,5 @@ Thumbs.db
|
|||||||
/credentials
|
/credentials
|
||||||
.env.local
|
.env.local
|
||||||
.local.env
|
.local.env
|
||||||
/.yalc
|
local.readme.md
|
||||||
local.readme.md
|
.yalc
|
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
}
|
"[prisma]": {
|
||||||
|
"editor.defaultFormatter": "Prisma.prisma"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- The migration will add a unique constraint covering the columns `[blueprint_hash]` on the table `blueprint_book`. If there are existing duplicate values, the migration will fail.
|
||||||
|
- The migration will add a unique constraint covering the columns `[factorioprints_id]` on the table `blueprint_page`. If there are existing duplicate values, the migration will fail.
|
||||||
|
- The migration will add a unique constraint covering the columns `[session_token]` on the table `session`. If there are existing duplicate values, the migration will fail.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "blueprint" ALTER COLUMN "created_at" SET DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "updated_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "blueprint_book" ALTER COLUMN "created_at" SET DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "updated_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "blueprint_page" ALTER COLUMN "created_at" SET DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "updated_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "session" ALTER COLUMN "last_used" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "created_at" SET DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "updated_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "user" ALTER COLUMN "password_reset_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "last_password_change" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "last_login_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "created_at" SET DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "updated_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "user_favorites" ALTER COLUMN "created_at" SET DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "updated_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "blueprint_book.blueprint_hash_unique" ON "blueprint_book"("blueprint_hash");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "blueprint_page.factorioprints_id_unique" ON "blueprint_page"("factorioprints_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "session.session_token_unique" ON "session"("session_token");
|
3
apps/blueprints/prisma/migrations/migration_lock.toml
Normal file
3
apps/blueprints/prisma/migrations/migration_lock.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (i.e. Git)
|
||||||
|
provider = "postgresql"
|
113
apps/blueprints/prisma/schema.prisma
Normal file
113
apps/blueprints/prisma/schema.prisma
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "postgresql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
enum enum_user_role {
|
||||||
|
user
|
||||||
|
moderator
|
||||||
|
admin
|
||||||
|
}
|
||||||
|
|
||||||
|
model blueprint {
|
||||||
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
label String? @db.VarChar(255)
|
||||||
|
description String?
|
||||||
|
game_version String? @db.VarChar(255)
|
||||||
|
blueprint_hash String @unique @db.VarChar(40)
|
||||||
|
image_hash String @db.VarChar(40)
|
||||||
|
image_version Int @default(1)
|
||||||
|
tags String[] @db.VarChar(255)
|
||||||
|
created_at DateTime @default(now())
|
||||||
|
updated_at DateTime @updatedAt
|
||||||
|
blueprint_page blueprint_page?
|
||||||
|
}
|
||||||
|
|
||||||
|
model blueprint_book {
|
||||||
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
label String? @db.VarChar(255)
|
||||||
|
description String?
|
||||||
|
child_tree Json @db.Json
|
||||||
|
blueprint_hash String @unique @db.VarChar(40)
|
||||||
|
is_modded Boolean
|
||||||
|
created_at DateTime @default(now())
|
||||||
|
updated_at DateTime @updatedAt
|
||||||
|
blueprint_page blueprint_page?
|
||||||
|
}
|
||||||
|
|
||||||
|
model blueprint_book_blueprints {
|
||||||
|
blueprint_book_id String @db.Uuid
|
||||||
|
blueprint_id String @db.Uuid
|
||||||
|
|
||||||
|
@@id([blueprint_book_id, blueprint_id])
|
||||||
|
}
|
||||||
|
|
||||||
|
model blueprint_book_books {
|
||||||
|
blueprint_book_1_id String @db.Uuid
|
||||||
|
blueprint_book_2_id String @db.Uuid
|
||||||
|
|
||||||
|
@@id([blueprint_book_1_id, blueprint_book_2_id])
|
||||||
|
}
|
||||||
|
|
||||||
|
model blueprint_page {
|
||||||
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
user_id String? @db.Uuid
|
||||||
|
blueprint_id String? @unique @db.Uuid
|
||||||
|
blueprint_book_id String? @unique @db.Uuid
|
||||||
|
title String @db.VarChar(255)
|
||||||
|
description_markdown String?
|
||||||
|
tags String[] @db.VarChar(255)
|
||||||
|
factorioprints_id String? @unique @db.VarChar(255)
|
||||||
|
created_at DateTime @default(now())
|
||||||
|
updated_at DateTime @updatedAt
|
||||||
|
blueprint_book blueprint_book? @relation(fields: [blueprint_book_id], references: [id])
|
||||||
|
blueprint blueprint? @relation(fields: [blueprint_id], references: [id])
|
||||||
|
user_favorites user_favorites[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model session {
|
||||||
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
user_id String @db.Uuid
|
||||||
|
session_token String @unique @db.VarChar(255)
|
||||||
|
useragent String @db.VarChar(255)
|
||||||
|
ip String @db.VarChar(255)
|
||||||
|
last_used DateTime
|
||||||
|
created_at DateTime @default(now())
|
||||||
|
updated_at DateTime @updatedAt
|
||||||
|
user user @relation(fields: [user_id], references: [id])
|
||||||
|
}
|
||||||
|
|
||||||
|
model user {
|
||||||
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
email String? @unique @db.VarChar(255)
|
||||||
|
username String @unique @db.VarChar(255)
|
||||||
|
role enum_user_role? @default(user)
|
||||||
|
steam_id String? @unique @db.VarChar(255)
|
||||||
|
password String? @db.VarChar(255)
|
||||||
|
password_reset_token String? @db.Uuid
|
||||||
|
password_reset_at DateTime?
|
||||||
|
last_password_change DateTime?
|
||||||
|
last_login_at DateTime?
|
||||||
|
last_login_ip String @db.VarChar(255)
|
||||||
|
email_validated Boolean @default(false)
|
||||||
|
email_validate_token String? @db.Uuid
|
||||||
|
created_at DateTime @default(now())
|
||||||
|
updated_at DateTime @updatedAt
|
||||||
|
session session[]
|
||||||
|
user_favorites user_favorites[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model user_favorites {
|
||||||
|
created_at DateTime @default(now())
|
||||||
|
updated_at DateTime @updatedAt
|
||||||
|
user_id String @db.Uuid
|
||||||
|
blueprint_page_id String @db.Uuid
|
||||||
|
blueprint_page blueprint_page @relation(fields: [blueprint_page_id], references: [id])
|
||||||
|
user user @relation(fields: [user_id], references: [id])
|
||||||
|
|
||||||
|
@@id([user_id, blueprint_page_id])
|
||||||
|
}
|
51
apps/blueprints/src/components/BlueprintLink.tsx
Normal file
51
apps/blueprints/src/components/BlueprintLink.tsx
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import { css } from "@emotion/react";
|
||||||
|
import { BlueprintPage } from "@factorio-sites/database";
|
||||||
|
import { Box, Text } from "@chakra-ui/react";
|
||||||
|
import { MdFavorite } from "react-icons/md";
|
||||||
|
|
||||||
|
const linkStyles = css`
|
||||||
|
width: 100%;
|
||||||
|
margin: 5px 0;
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
padding: 5px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const formatDate = (datenum: number) => {
|
||||||
|
const date = new Date(datenum * 1000);
|
||||||
|
return date.toLocaleString();
|
||||||
|
};
|
||||||
|
|
||||||
|
interface BlueprintLinkProps {
|
||||||
|
blueprint: BlueprintPage;
|
||||||
|
editLink?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BlueprintLink: React.FC<BlueprintLinkProps> = ({ blueprint, editLink }) => (
|
||||||
|
<div css={linkStyles}>
|
||||||
|
<Link
|
||||||
|
href={editLink ? `/user/blueprint/${blueprint.id}` : `/blueprint/${blueprint.id}`}
|
||||||
|
passHref
|
||||||
|
>
|
||||||
|
<a>
|
||||||
|
<Box css={{ display: "flex", justifyContent: "space-between" }}>
|
||||||
|
<Text>{blueprint.title}</Text>
|
||||||
|
<Box css={{ display: "flex" }}>
|
||||||
|
<Text css={{ display: "flex", alignItems: "center", marginRight: "2rem" }}>
|
||||||
|
<MdFavorite css={{ marginRight: "0.5rem" }} />
|
||||||
|
{blueprint.favorite_count}
|
||||||
|
</Text>
|
||||||
|
<Text>{formatDate(blueprint.updated_at)}</Text>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
@ -5,10 +5,10 @@ const handler = apiHandler(async (_, res, { session }) => {
|
|||||||
if (session) {
|
if (session) {
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
auth: {
|
auth: {
|
||||||
user_id: session.user.get("id"),
|
user_id: session.user.id,
|
||||||
username: session.user.get("username"),
|
username: session.user.username,
|
||||||
email: session.user.get("email"),
|
email: session.user.email,
|
||||||
steam_id: session.user.get("steam_id"),
|
steam_id: session.user.steam_id,
|
||||||
} as AuthContextProps,
|
} as AuthContextProps,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { isBlueprintPageUserFavorite, createUserFavorite } from "@factorio-sites/database";
|
import { isBlueprintPageUserFavorite, createUserFavorite, prisma } from "@factorio-sites/database";
|
||||||
import { apiHandler } from "../../../utils/api-handler";
|
import { apiHandler } from "../../../utils/api-handler";
|
||||||
|
|
||||||
const handler = apiHandler(async (req, res, { session }) => {
|
const handler = apiHandler(async (req, res, { session }) => {
|
||||||
@ -12,7 +12,14 @@ const handler = apiHandler(async (req, res, { session }) => {
|
|||||||
const existing = await isBlueprintPageUserFavorite(user.id, blueprint_page_id);
|
const existing = await isBlueprintPageUserFavorite(user.id, blueprint_page_id);
|
||||||
|
|
||||||
if (existing) {
|
if (existing) {
|
||||||
await existing.destroy();
|
await prisma().user_favorites.delete({
|
||||||
|
where: {
|
||||||
|
user_id_blueprint_page_id: {
|
||||||
|
user_id: existing.user_id,
|
||||||
|
blueprint_page_id: existing.blueprint_page_id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
await createUserFavorite(user.id, blueprint_page_id);
|
await createUserFavorite(user.id, blueprint_page_id);
|
||||||
}
|
}
|
||||||
|
@ -1,52 +1,12 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { NextPage, NextPageContext } from "next";
|
import { NextPage, NextPageContext } from "next";
|
||||||
import Link from "next/link";
|
|
||||||
import { css } from "@emotion/react";
|
|
||||||
import { BlueprintPage, getMostRecentBlueprintPages, init } from "@factorio-sites/database";
|
import { BlueprintPage, getMostRecentBlueprintPages, init } from "@factorio-sites/database";
|
||||||
import { SimpleGrid, Box, RadioGroup, Stack, Radio, Text } from "@chakra-ui/react";
|
import { SimpleGrid, Box, RadioGroup, Stack, Radio } from "@chakra-ui/react";
|
||||||
import { Panel } from "../components/Panel";
|
import { Panel } from "../components/Panel";
|
||||||
import { Pagination } from "../components/Pagination";
|
import { Pagination } from "../components/Pagination";
|
||||||
import { useRouterQueryToHref } from "../hooks/query.hook";
|
import { useRouterQueryToHref } from "../hooks/query.hook";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { MdFavorite } from "react-icons/md";
|
import { BlueprintLink } from "../components/BlueprintLink";
|
||||||
|
|
||||||
const linkStyles = css`
|
|
||||||
width: 100%;
|
|
||||||
margin: 5px 0;
|
|
||||||
a {
|
|
||||||
display: block;
|
|
||||||
padding: 5px;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background: #ccc;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const formatDate = (datenum: number) => {
|
|
||||||
const date = new Date(datenum * 1000);
|
|
||||||
return date.toLocaleString();
|
|
||||||
};
|
|
||||||
|
|
||||||
const BlueprintComponent: React.FC<{ blueprint: BlueprintPage }> = ({ blueprint }) => (
|
|
||||||
<div css={linkStyles}>
|
|
||||||
<Link href={`/blueprint/${blueprint.id}`} passHref>
|
|
||||||
<a>
|
|
||||||
<Box css={{ display: "flex", justifyContent: "space-between" }}>
|
|
||||||
<Text>{blueprint.title}</Text>
|
|
||||||
<Box css={{ display: "flex" }}>
|
|
||||||
<Text css={{ display: "flex", alignItems: "center", marginRight: "2rem" }}>
|
|
||||||
<MdFavorite css={{ marginRight: "0.5rem" }} />
|
|
||||||
{blueprint.favorite_count}
|
|
||||||
</Text>
|
|
||||||
<Text>{formatDate(blueprint.updated_at)}</Text>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
interface IndexProps {
|
interface IndexProps {
|
||||||
totalItems: number;
|
totalItems: number;
|
||||||
@ -88,7 +48,7 @@ export const Index: NextPage<IndexProps> = ({
|
|||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
{blueprints.map((bp) => (
|
{blueprints.map((bp) => (
|
||||||
<BlueprintComponent key={bp.id} blueprint={bp} />
|
<BlueprintLink key={bp.id} blueprint={bp} />
|
||||||
))}
|
))}
|
||||||
<Pagination page={currentPage} totalPages={totalPages} totalItems={totalItems} />
|
<Pagination page={currentPage} totalPages={totalPages} totalItems={totalItems} />
|
||||||
</Box>
|
</Box>
|
||||||
|
229
apps/blueprints/src/pages/user/blueprint/[blueprintId].tsx
Normal file
229
apps/blueprints/src/pages/user/blueprint/[blueprintId].tsx
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { NextPage } from "next";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { Formik, Field } from "formik";
|
||||||
|
import { css } from "@emotion/react";
|
||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
FormLabel,
|
||||||
|
FormErrorMessage,
|
||||||
|
Input,
|
||||||
|
SimpleGrid,
|
||||||
|
Button,
|
||||||
|
Box,
|
||||||
|
Text,
|
||||||
|
Textarea,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import MultiSelect from "react-multi-select-component";
|
||||||
|
import { chakraResponsive } from "@factorio-sites/web-utils";
|
||||||
|
import {
|
||||||
|
Blueprint,
|
||||||
|
BlueprintBook,
|
||||||
|
BlueprintPage,
|
||||||
|
getBlueprintBookById,
|
||||||
|
getBlueprintById,
|
||||||
|
getBlueprintPageById,
|
||||||
|
getBlueprintStringByHash,
|
||||||
|
} from "@factorio-sites/database";
|
||||||
|
import { pageHandler } from "../../../utils/page-handler";
|
||||||
|
import { Panel } from "../../../components/Panel";
|
||||||
|
import { validateCreateBlueprintForm } from "../../../utils/validate";
|
||||||
|
import { useAuth } from "../../../providers/auth";
|
||||||
|
import { ImageEditor } from "../../../components/ImageEditor";
|
||||||
|
|
||||||
|
const FieldStyle = css`
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TAGS = [
|
||||||
|
{ value: "foo", label: "foo" },
|
||||||
|
{ value: "bar", label: "bar" },
|
||||||
|
{ value: "x", label: "x" },
|
||||||
|
{ value: "y", label: "y" },
|
||||||
|
];
|
||||||
|
|
||||||
|
type Selected =
|
||||||
|
| { type: "blueprint"; data: Pick<Blueprint, "id" | "blueprint_hash">; string: string }
|
||||||
|
| { type: "blueprint_book"; data: Pick<BlueprintBook, "id" | "blueprint_hash">; string: string };
|
||||||
|
|
||||||
|
interface UserBlueprintProps {
|
||||||
|
blueprintPage: BlueprintPage;
|
||||||
|
selected: Selected;
|
||||||
|
}
|
||||||
|
export const UserBlueprint: NextPage<UserBlueprintProps> = ({ blueprintPage, selected }) => {
|
||||||
|
const auth = useAuth();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
if (!auth) {
|
||||||
|
router.push("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!blueprintPage) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div css={{ margin: "0.7rem" }}>
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
title: blueprintPage.title,
|
||||||
|
description: blueprintPage.description_markdown,
|
||||||
|
string: selected.string,
|
||||||
|
tags: [],
|
||||||
|
}}
|
||||||
|
validate={validateCreateBlueprintForm}
|
||||||
|
onSubmit={async (values, { setSubmitting, setErrors, setStatus }) => {
|
||||||
|
setStatus("");
|
||||||
|
|
||||||
|
const result = await fetch("/api/blueprint/create", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "content-type": "application/json" },
|
||||||
|
body: JSON.stringify(values),
|
||||||
|
}).then((res) => res.json());
|
||||||
|
|
||||||
|
if (result.status) {
|
||||||
|
setSubmitting(false);
|
||||||
|
setStatus(result.status);
|
||||||
|
} else if (result.errors) {
|
||||||
|
setSubmitting(false);
|
||||||
|
setErrors(result.errors);
|
||||||
|
} else if (result.success) {
|
||||||
|
router.push(`/blueprint/${result.id}`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ isSubmitting, handleSubmit, status, values, errors, setFieldValue }) => (
|
||||||
|
<SimpleGrid
|
||||||
|
columns={2}
|
||||||
|
gap={6}
|
||||||
|
templateColumns={chakraResponsive({ mobile: "1fr", desktop: "1fr 1fr" })}
|
||||||
|
>
|
||||||
|
<Panel title="Create new blueprint">
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<Field name="title">
|
||||||
|
{({ field, meta }: any) => (
|
||||||
|
<FormControl
|
||||||
|
id="title"
|
||||||
|
isRequired
|
||||||
|
isInvalid={meta.touched && meta.error}
|
||||||
|
css={FieldStyle}
|
||||||
|
>
|
||||||
|
<FormLabel>Title</FormLabel>
|
||||||
|
<Input type="text" {...field} />
|
||||||
|
<FormErrorMessage>{meta.error}</FormErrorMessage>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field name="description">
|
||||||
|
{({ field, meta }: any) => (
|
||||||
|
<FormControl
|
||||||
|
id="description"
|
||||||
|
isRequired
|
||||||
|
isInvalid={meta.touched && meta.error}
|
||||||
|
css={FieldStyle}
|
||||||
|
>
|
||||||
|
<FormLabel>Description</FormLabel>
|
||||||
|
<Textarea {...field} />
|
||||||
|
<FormErrorMessage>{meta.error}</FormErrorMessage>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field name="tags">
|
||||||
|
{({ field, meta }: any) => (
|
||||||
|
<FormControl id="tags" isInvalid={meta.touched && meta.error} css={FieldStyle}>
|
||||||
|
<FormLabel>Tags</FormLabel>
|
||||||
|
<MultiSelect
|
||||||
|
css={{ color: "black" }}
|
||||||
|
options={TAGS}
|
||||||
|
value={field.value}
|
||||||
|
onChange={(value: any) => setFieldValue("tags", value)}
|
||||||
|
labelledBy="Select"
|
||||||
|
hasSelectAll={false}
|
||||||
|
/>
|
||||||
|
<FormErrorMessage>{meta.error}</FormErrorMessage>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field name="string">
|
||||||
|
{({ field, meta }: any) => (
|
||||||
|
<FormControl
|
||||||
|
id="string"
|
||||||
|
isRequired
|
||||||
|
isInvalid={meta.touched && meta.error}
|
||||||
|
css={FieldStyle}
|
||||||
|
>
|
||||||
|
<FormLabel>Blueprint string</FormLabel>
|
||||||
|
<Input type="text" {...field} />
|
||||||
|
<FormErrorMessage>{meta.error}</FormErrorMessage>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Box css={{ display: "flex", alignItems: "center" }}>
|
||||||
|
<Button type="submit" colorScheme="green" disabled={isSubmitting}>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
{status && <Text css={{ marginLeft: "1rem", color: "red" }}>{status}</Text>}
|
||||||
|
</Box>
|
||||||
|
</form>
|
||||||
|
</Panel>
|
||||||
|
<Panel title="Preview">
|
||||||
|
<Box>
|
||||||
|
{values.string && !errors.string && (
|
||||||
|
<ImageEditor string={values.string}></ImageEditor>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Panel>
|
||||||
|
</SimpleGrid>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps = pageHandler(async (context, { session }) => {
|
||||||
|
const blueprintId = context.query.blueprintId ? (context.query.blueprintId as string) : null;
|
||||||
|
|
||||||
|
if (!session || !blueprintId) {
|
||||||
|
return { props: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
const blueprintPage = await getBlueprintPageById(blueprintId);
|
||||||
|
let selected!: UserBlueprintProps["selected"];
|
||||||
|
|
||||||
|
if (blueprintPage?.blueprint_id) {
|
||||||
|
const blueprint = await getBlueprintById(blueprintPage.blueprint_id);
|
||||||
|
if (!blueprint) return;
|
||||||
|
|
||||||
|
selected = {
|
||||||
|
type: "blueprint",
|
||||||
|
data: {
|
||||||
|
id: blueprintPage.blueprint_id,
|
||||||
|
blueprint_hash: blueprint.blueprint_hash,
|
||||||
|
},
|
||||||
|
string: (await getBlueprintStringByHash(blueprint.blueprint_hash)) as string,
|
||||||
|
};
|
||||||
|
} else if (blueprintPage?.blueprint_book_id) {
|
||||||
|
const blueprintBook = await getBlueprintBookById(blueprintPage.blueprint_book_id);
|
||||||
|
if (!blueprintBook) return;
|
||||||
|
|
||||||
|
selected = {
|
||||||
|
type: "blueprint_book",
|
||||||
|
data: {
|
||||||
|
id: blueprintPage.blueprint_book_id,
|
||||||
|
blueprint_hash: blueprintBook.blueprint_hash,
|
||||||
|
},
|
||||||
|
string: (await getBlueprintStringByHash(blueprintBook.blueprint_hash)) as string,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
blueprintPage: blueprintPage,
|
||||||
|
selected,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export default UserBlueprint;
|
@ -3,22 +3,55 @@ import { NextPage } from "next";
|
|||||||
import { Button, SimpleGrid, Box } from "@chakra-ui/react";
|
import { Button, SimpleGrid, Box } from "@chakra-ui/react";
|
||||||
import { Panel } from "../../components/Panel";
|
import { Panel } from "../../components/Panel";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { pageHandler } from "../../utils/page-handler";
|
||||||
|
import { BlueprintPage, getBlueprintPageByUserId } from "@factorio-sites/database";
|
||||||
|
import { BlueprintLink } from "../../components/BlueprintLink";
|
||||||
|
|
||||||
export const UserBlueprints: NextPage = () => {
|
interface UserBlueprintsProps {
|
||||||
|
blueprints: BlueprintPage[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UserBlueprints: NextPage<UserBlueprintsProps> = ({ blueprints }) => {
|
||||||
return (
|
return (
|
||||||
<div css={{ margin: "0.7rem" }}>
|
<div css={{ margin: "0.7rem" }}>
|
||||||
<SimpleGrid columns={1} margin="0 auto" maxWidth="800px">
|
<SimpleGrid columns={1} margin="0 auto" maxWidth="800px">
|
||||||
<Panel title="Blueprints">
|
<Panel title="Blueprints">
|
||||||
<Box>user blueprints</Box>
|
<Box
|
||||||
<Link href="/user/blueprint-create">
|
css={{
|
||||||
<a>
|
display: "flex",
|
||||||
<Button colorScheme="green">create blueprint</Button>
|
borderBottom: "1px solid #b7b7b7",
|
||||||
</a>
|
paddingBottom: "0.3rem",
|
||||||
</Link>
|
}}
|
||||||
|
>
|
||||||
|
<Link href="/user/blueprint-create">
|
||||||
|
<a>
|
||||||
|
<Button colorScheme="green">create blueprint</Button>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
{blueprints.map((bp) => (
|
||||||
|
<BlueprintLink key={bp.id} blueprint={bp} editLink />
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
</Panel>
|
</Panel>
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps = pageHandler(async (context, { session }) => {
|
||||||
|
if (!session) {
|
||||||
|
return { props: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
const blueprints = await getBlueprintPageByUserId(session.user.id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
blueprints,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
export default UserBlueprints;
|
export default UserBlueprints;
|
||||||
|
Binary file not shown.
@ -1,14 +1,13 @@
|
|||||||
import { BlueprintData, getBlueprintContentForImageHash } from "@factorio-sites/common-utils";
|
import { BlueprintData, getBlueprintContentForImageHash } from "@factorio-sites/common-utils";
|
||||||
import { encodeBlueprint, hashString } from "@factorio-sites/node-utils";
|
import { encodeBlueprint, hashString } from "@factorio-sites/node-utils";
|
||||||
// import { getBlueprintImageRequestTopic } from "../gcp-pubsub";
|
import { blueprint as BlueprintModel } from "@prisma/client";
|
||||||
import { saveBlueprintString } from "../gcp-storage";
|
import { saveBlueprintString } from "../gcp-storage";
|
||||||
import { BlueprintModel } from "../postgres/database";
|
import { prisma } from "../postgres/database";
|
||||||
import { BlueprintInstance } from "../postgres/models/Blueprint";
|
|
||||||
import { Blueprint } from "../types";
|
import { Blueprint } from "../types";
|
||||||
|
|
||||||
// const blueprintImageRequestTopic = getBlueprintImageRequestTopic();
|
// const blueprintImageRequestTopic = getBlueprintImageRequestTopic();
|
||||||
|
|
||||||
const mapBlueprintInstanceToEntry = (entity: BlueprintInstance): Blueprint => ({
|
const mapBlueprintInstanceToEntry = (entity: BlueprintModel): Blueprint => ({
|
||||||
id: entity.id,
|
id: entity.id,
|
||||||
blueprint_hash: entity.blueprint_hash,
|
blueprint_hash: entity.blueprint_hash,
|
||||||
image_hash: entity.image_hash,
|
image_hash: entity.image_hash,
|
||||||
@ -21,18 +20,12 @@ const mapBlueprintInstanceToEntry = (entity: BlueprintInstance): Blueprint => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export async function getBlueprintById(id: string): Promise<Blueprint | null> {
|
export async function getBlueprintById(id: string): Promise<Blueprint | null> {
|
||||||
const result = await BlueprintModel()
|
const result = await prisma().blueprint.findUnique({ where: { id } });
|
||||||
.findByPk(id)
|
|
||||||
.catch(() => null);
|
|
||||||
return result ? mapBlueprintInstanceToEntry(result) : null;
|
return result ? mapBlueprintInstanceToEntry(result) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getBlueprintByHash(hash: string): Promise<Blueprint | null> {
|
export async function getBlueprintByHash(hash: string): Promise<Blueprint | null> {
|
||||||
const result = await BlueprintModel()
|
const result = await prisma().blueprint.findUnique({ where: { blueprint_hash: hash } });
|
||||||
.findOne({
|
|
||||||
where: { blueprint_hash: hash },
|
|
||||||
})
|
|
||||||
.catch(() => null);
|
|
||||||
return result ? mapBlueprintInstanceToEntry(result) : null;
|
return result ? mapBlueprintInstanceToEntry(result) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,16 +50,19 @@ export async function createBlueprint(
|
|||||||
await saveBlueprintString(blueprint_hash, string);
|
await saveBlueprintString(blueprint_hash, string);
|
||||||
|
|
||||||
// Write blueprint details to datastore
|
// Write blueprint details to datastore
|
||||||
const result = await BlueprintModel().create({
|
|
||||||
label: blueprint.label,
|
const result = await prisma().blueprint.create({
|
||||||
description: blueprint.description,
|
data: {
|
||||||
blueprint_hash: blueprint_hash,
|
label: blueprint.label,
|
||||||
image_hash: image_hash,
|
description: blueprint.description,
|
||||||
tags: extraInfo.tags,
|
blueprint_hash: blueprint_hash,
|
||||||
game_version: `${blueprint.version}`,
|
image_hash: image_hash,
|
||||||
image_version: 1,
|
tags: extraInfo.tags,
|
||||||
updated_at: extraInfo.updated_at ? new Date(extraInfo.updated_at * 1000) : undefined,
|
game_version: `${blueprint.version}`,
|
||||||
created_at: extraInfo.created_at ? new Date(extraInfo.created_at * 1000) : undefined,
|
image_version: 1,
|
||||||
|
updated_at: extraInfo.updated_at ? new Date(extraInfo.updated_at * 1000) : new Date(),
|
||||||
|
created_at: extraInfo.created_at ? new Date(extraInfo.created_at * 1000) : new Date(),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Created Blueprint ${result.id}`);
|
console.log(`Created Blueprint ${result.id}`);
|
||||||
|
@ -1,35 +1,29 @@
|
|||||||
import { BlueprintBookData } from "@factorio-sites/common-utils";
|
import { BlueprintBookData } from "@factorio-sites/common-utils";
|
||||||
import { encodeBlueprint, hashString } from "@factorio-sites/node-utils";
|
import { encodeBlueprint, hashString } from "@factorio-sites/node-utils";
|
||||||
|
import { blueprint_book } from "@prisma/client";
|
||||||
import { saveBlueprintString } from "../gcp-storage";
|
import { saveBlueprintString } from "../gcp-storage";
|
||||||
import { BlueprintBookModel } from "../postgres/database";
|
import { prisma } from "../postgres/database";
|
||||||
import { BlueprintBookInstance } from "../postgres/models/BlueprintBook";
|
|
||||||
import { BlueprintBook, ChildTree } from "../types";
|
import { BlueprintBook, ChildTree } from "../types";
|
||||||
import { createBlueprint } from "./blueprint";
|
import { createBlueprint } from "./blueprint";
|
||||||
|
|
||||||
const mapBlueprintBookEntityToObject = (entity: BlueprintBookInstance): BlueprintBook => ({
|
const mapBlueprintBookEntityToObject = (entity: blueprint_book): BlueprintBook => ({
|
||||||
id: entity.id,
|
id: entity.id,
|
||||||
child_tree: entity.child_tree ? entity.child_tree : [],
|
child_tree: entity.child_tree ? (entity.child_tree as any) : [],
|
||||||
blueprint_hash: entity.blueprint_hash,
|
blueprint_hash: entity.blueprint_hash,
|
||||||
label: entity.label,
|
label: entity.label || "",
|
||||||
description: entity.description,
|
description: entity.description || "",
|
||||||
created_at: entity.created_at && entity.created_at.getTime() / 1000,
|
created_at: entity.created_at && entity.created_at.getTime() / 1000,
|
||||||
updated_at: entity.updated_at && entity.updated_at.getTime() / 1000,
|
updated_at: entity.updated_at && entity.updated_at.getTime() / 1000,
|
||||||
is_modded: entity.is_modded || false,
|
is_modded: entity.is_modded || false,
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function getBlueprintBookById(id: string): Promise<BlueprintBook | null> {
|
export async function getBlueprintBookById(id: string): Promise<BlueprintBook | null> {
|
||||||
const result = await BlueprintBookModel()
|
const result = await prisma().blueprint_book.findUnique({ where: { id } });
|
||||||
.findByPk(id)
|
|
||||||
.catch(() => null);
|
|
||||||
return result ? mapBlueprintBookEntityToObject(result) : null;
|
return result ? mapBlueprintBookEntityToObject(result) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getBlueprintBookByHash(hash: string): Promise<BlueprintBook | null> {
|
export async function getBlueprintBookByHash(hash: string): Promise<BlueprintBook | null> {
|
||||||
const result = await BlueprintBookModel()
|
const result = await prisma().blueprint_book.findUnique({ where: { blueprint_hash: hash } });
|
||||||
.findOne({
|
|
||||||
where: { blueprint_hash: hash },
|
|
||||||
})
|
|
||||||
.catch(() => null);
|
|
||||||
return result ? mapBlueprintBookEntityToObject(result) : null;
|
return result ? mapBlueprintBookEntityToObject(result) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,14 +75,16 @@ export async function createBlueprintBook(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await BlueprintBookModel().create({
|
const result = await prisma().blueprint_book.create({
|
||||||
label: blueprintBook.label,
|
data: {
|
||||||
description: blueprintBook.description,
|
label: blueprintBook.label,
|
||||||
blueprint_hash: blueprint_hash,
|
description: blueprintBook.description,
|
||||||
is_modded: false,
|
blueprint_hash: blueprint_hash,
|
||||||
child_tree,
|
is_modded: false,
|
||||||
updated_at: extraInfo.updated_at ? new Date(extraInfo.updated_at * 1000) : undefined,
|
child_tree: child_tree as any,
|
||||||
created_at: extraInfo.created_at ? new Date(extraInfo.created_at * 1000) : undefined,
|
updated_at: extraInfo.updated_at ? new Date(extraInfo.updated_at * 1000) : new Date(),
|
||||||
|
created_at: extraInfo.created_at ? new Date(extraInfo.created_at * 1000) : new Date(),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Created Blueprint book ${result.id}`);
|
console.log(`Created Blueprint book ${result.id}`);
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
import { Sequelize } from "sequelize";
|
import { blueprint_page } from "@prisma/client";
|
||||||
import { Op } from "sequelize";
|
import { prisma } from "../postgres/database";
|
||||||
import { BlueprintPageModel } from "../postgres/database";
|
|
||||||
import { BlueprintPageInstance } from "../postgres/models/BlueprintPage";
|
|
||||||
import { BlueprintPage } from "../types";
|
import { BlueprintPage } from "../types";
|
||||||
|
|
||||||
const mapBlueprintPageEntityToObject = (entity: BlueprintPageInstance): BlueprintPage => ({
|
const mapBlueprintPageEntityToObject = (entity: blueprint_page): BlueprintPage => ({
|
||||||
id: entity.id,
|
id: entity.id,
|
||||||
blueprint_id: entity.blueprint_id ?? null,
|
blueprint_id: entity.blueprint_id ?? null,
|
||||||
blueprint_book_id: entity.blueprint_book_id ?? null,
|
blueprint_book_id: entity.blueprint_book_id ?? null,
|
||||||
title: entity.title,
|
title: entity.title,
|
||||||
description_markdown: entity.description_markdown,
|
description_markdown: entity.description_markdown || "",
|
||||||
created_at: entity.created_at && entity.created_at.getTime() / 1000,
|
created_at: entity.created_at && entity.created_at.getTime() / 1000,
|
||||||
updated_at: entity.updated_at && entity.updated_at.getTime() / 1000,
|
updated_at: entity.updated_at && entity.updated_at.getTime() / 1000,
|
||||||
factorioprints_id: entity.factorioprints_id ?? null,
|
factorioprints_id: entity.factorioprints_id ?? null,
|
||||||
@ -17,20 +15,19 @@ const mapBlueprintPageEntityToObject = (entity: BlueprintPageInstance): Blueprin
|
|||||||
});
|
});
|
||||||
|
|
||||||
export async function getBlueprintPageById(id: string): Promise<BlueprintPage | null> {
|
export async function getBlueprintPageById(id: string): Promise<BlueprintPage | null> {
|
||||||
const result = await BlueprintPageModel()
|
const result = await prisma().blueprint_page.findUnique({ where: { id } });
|
||||||
.findByPk(id)
|
|
||||||
.catch(() => null);
|
|
||||||
return result ? mapBlueprintPageEntityToObject(result) : null;
|
return result ? mapBlueprintPageEntityToObject(result) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getBlueprintPageByUserId(user_id: string): Promise<BlueprintPage[] | null> {
|
||||||
|
const results = await prisma().blueprint_page.findMany({ where: { user_id } });
|
||||||
|
return results ? results.map((result) => mapBlueprintPageEntityToObject(result)) : null;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getBlueprintPageByFactorioprintsId(
|
export async function getBlueprintPageByFactorioprintsId(
|
||||||
id: string
|
id: string
|
||||||
): Promise<BlueprintPage | null> {
|
): Promise<BlueprintPage | null> {
|
||||||
const result = await BlueprintPageModel()
|
const result = await prisma().blueprint_page.findFirst({ where: { factorioprints_id: id } });
|
||||||
.findOne({
|
|
||||||
where: { factorioprints_id: id },
|
|
||||||
})
|
|
||||||
.catch(() => null);
|
|
||||||
return result ? mapBlueprintPageEntityToObject(result) : null;
|
return result ? mapBlueprintPageEntityToObject(result) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,37 +42,30 @@ export async function getMostRecentBlueprintPages({
|
|||||||
query?: string;
|
query?: string;
|
||||||
order: "date" | "favorites" | string;
|
order: "date" | "favorites" | string;
|
||||||
}): Promise<{ count: number; rows: BlueprintPage[] }> {
|
}): Promise<{ count: number; rows: BlueprintPage[] }> {
|
||||||
const orderMap: any = {
|
const orderMap: Record<string, string> = {
|
||||||
date: {
|
date: "updated_at",
|
||||||
order: [["updated_at", "DESC"]],
|
favorites: "favorite_count",
|
||||||
},
|
|
||||||
favorites: {
|
|
||||||
order: [[Sequelize.literal(`"favorite_count"`), "DESC"]],
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
const orderArgs = orderMap[order] || orderMap.date;
|
const result = (
|
||||||
const result = await BlueprintPageModel()
|
await prisma().$queryRaw<(blueprint_page & { favorite_count: number })[]>(
|
||||||
.findAndCountAll({
|
`SELECT *, (SELECT COUNT(*) FROM user_favorites where user_favorites.blueprint_page_id = blueprint_page.id) AS favorite_count
|
||||||
where: query ? { title: { [Op.iLike]: `%${query}%` } } : undefined,
|
FROM public.blueprint_page
|
||||||
limit: perPage,
|
WHERE blueprint_page.title ILIKE $1
|
||||||
offset: (page - 1) * perPage,
|
ORDER BY ${orderMap[order] || orderMap.date} DESC
|
||||||
raw: true,
|
LIMIT $2 OFFSET $3`,
|
||||||
attributes: [
|
query ? `%${query}%` : "%",
|
||||||
"blueprint_page.*",
|
perPage,
|
||||||
[
|
(page - 1) * perPage
|
||||||
Sequelize.literal(
|
)
|
||||||
"(SELECT COUNT(*) FROM user_favorites WHERE user_favorites.blueprint_page_id = blueprint_page.id)"
|
).map((blueprintPage) => ({
|
||||||
),
|
...blueprintPage,
|
||||||
"favorite_count",
|
created_at: new Date(blueprintPage.created_at),
|
||||||
],
|
updated_at: new Date(blueprintPage.updated_at),
|
||||||
],
|
}));
|
||||||
...orderArgs,
|
|
||||||
})
|
|
||||||
.catch(() => null);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
count: result?.count ?? 0,
|
count: result.length,
|
||||||
rows: result?.rows.map(mapBlueprintPageEntityToObject) ?? [],
|
rows: result.map(mapBlueprintPageEntityToObject),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,16 +82,18 @@ export async function createBlueprintPage(
|
|||||||
factorioprints_id?: string;
|
factorioprints_id?: string;
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
const page = await BlueprintPageModel().create({
|
const page = await prisma().blueprint_page.create({
|
||||||
user_id: extraInfo.user_id,
|
data: {
|
||||||
title: extraInfo.title,
|
user_id: extraInfo.user_id,
|
||||||
description_markdown: extraInfo.description_markdown,
|
title: extraInfo.title,
|
||||||
factorioprints_id: extraInfo.factorioprints_id,
|
description_markdown: extraInfo.description_markdown,
|
||||||
blueprint_id: type === "blueprint" ? targetId : undefined,
|
factorioprints_id: extraInfo.factorioprints_id,
|
||||||
blueprint_book_id: type === "blueprint_book" ? targetId : undefined,
|
blueprint_id: type === "blueprint" ? targetId : undefined,
|
||||||
tags: extraInfo.tags ? extraInfo.tags : [],
|
blueprint_book_id: type === "blueprint_book" ? targetId : undefined,
|
||||||
updated_at: extraInfo.updated_at ? new Date(extraInfo.updated_at * 1000) : undefined,
|
tags: extraInfo.tags ? extraInfo.tags : [],
|
||||||
created_at: extraInfo.created_at ? new Date(extraInfo.created_at * 1000) : undefined,
|
updated_at: extraInfo.updated_at ? new Date(extraInfo.updated_at * 1000) : new Date(),
|
||||||
|
created_at: extraInfo.created_at ? new Date(extraInfo.created_at * 1000) : new Date(),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Created Blueprint Page`);
|
console.log(`Created Blueprint Page`);
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import * as bcrypt from "bcrypt";
|
import * as bcrypt from "bcrypt";
|
||||||
import { sequelize, SessionModel, UserModel } from "../postgres/database";
|
import { prisma } from "../postgres/database";
|
||||||
import { SessionInstance } from "../postgres/models/Session";
|
import { SessionInstance } from "../postgres/models/Session";
|
||||||
import { UserInstance } from "../postgres/models/User";
|
import { UserInstance } from "../postgres/models/User";
|
||||||
|
|
||||||
export const getUserById = async (id: string) => {
|
export const getUserById = async (id: string) => {
|
||||||
const user = await UserModel().findByPk(id, { raw: true });
|
const user = await prisma().user.findUnique({ where: { id } });
|
||||||
return user ? user : null;
|
return user ? user : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -15,20 +15,24 @@ export const createUserWithEmail = async (
|
|||||||
ip: string
|
ip: string
|
||||||
) => {
|
) => {
|
||||||
const hash = await bcrypt.hash(password, 10);
|
const hash = await bcrypt.hash(password, 10);
|
||||||
return UserModel().create({
|
return prisma().user.create({
|
||||||
email,
|
data: {
|
||||||
username,
|
email,
|
||||||
password: hash,
|
username,
|
||||||
last_login_ip: ip,
|
password: hash,
|
||||||
last_password_change: new Date(),
|
last_login_ip: ip,
|
||||||
|
last_password_change: new Date(),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createUserWithSteam = async (steam_id: string, username: string, ip: string) => {
|
export const createUserWithSteam = async (steam_id: string, username: string, ip: string) => {
|
||||||
return UserModel().create({
|
return prisma().user.create({
|
||||||
steam_id,
|
data: {
|
||||||
username,
|
steam_id,
|
||||||
last_login_ip: ip,
|
username,
|
||||||
|
last_login_ip: ip,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,7 +47,7 @@ export const loginUserWithEmail = async ({
|
|||||||
useragent: string;
|
useragent: string;
|
||||||
ip: string;
|
ip: string;
|
||||||
}): Promise<(UserInstance & { session: SessionInstance }) | null> => {
|
}): Promise<(UserInstance & { session: SessionInstance }) | null> => {
|
||||||
const user = await UserModel().findOne({ where: { email } });
|
const user = await prisma().user.findUnique({ where: { email } });
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -61,45 +65,50 @@ export const loginUserWithEmail = async ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const loginUserWithSteam = async (steam_id: string, ip: string) => {
|
export const loginUserWithSteam = async (steam_id: string, ip: string) => {
|
||||||
const user = await UserModel().findOne({ where: { steam_id } });
|
const user = await prisma().user.findUnique({ where: { steam_id } });
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
await user.update({
|
await prisma().user.update({
|
||||||
last_login_ip: ip,
|
where: { steam_id },
|
||||||
last_login_at: new Date(),
|
data: {
|
||||||
|
last_login_ip: ip,
|
||||||
|
last_login_at: new Date(),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
return user;
|
return user;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createSession = async (user: UserInstance, useragent: string, ip: string) => {
|
export const createSession = async (user: UserInstance, useragent: string, ip: string) => {
|
||||||
return SessionModel().create({
|
return prisma().session.create({
|
||||||
user_id: user.id,
|
datta: {
|
||||||
useragent,
|
user_id: user.id,
|
||||||
ip,
|
useragent,
|
||||||
|
ip,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSessionByToken = async (
|
export const getSessionByToken = async (token: string) => {
|
||||||
token: string
|
return await prisma().session.findUnique({
|
||||||
): Promise<(SessionInstance & { user: UserInstance }) | null> => {
|
|
||||||
return SessionModel().findOne<any>({
|
|
||||||
where: { session_token: token },
|
where: { session_token: token },
|
||||||
include: [UserModel()],
|
include: {
|
||||||
|
user: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isBlueprintPageUserFavorite = async (user_id: string, blueprint_page_id: string) => {
|
export const isBlueprintPageUserFavorite = async (user_id: string, blueprint_page_id: string) => {
|
||||||
const UserFavorites = sequelize().models.user_favorites;
|
const { user_favorites } = prisma();
|
||||||
return UserFavorites.findOne({
|
return user_favorites.findFirst({ where: { user_id, blueprint_page_id } });
|
||||||
where: { user_id, blueprint_page_id },
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createUserFavorite = async (user_id: string, blueprint_page_id: string) => {
|
export const createUserFavorite = async (user_id: string, blueprint_page_id: string) => {
|
||||||
const UserFavorites = sequelize().models.user_favorites;
|
const { user_favorites } = prisma();
|
||||||
return UserFavorites.create({
|
return user_favorites.create({
|
||||||
user_id,
|
data: {
|
||||||
blueprint_page_id,
|
user_id,
|
||||||
|
blueprint_page_id,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,438 +0,0 @@
|
|||||||
// import { Datastore } from "@google-cloud/datastore";
|
|
||||||
// import { encodeBlueprint, hashString, parseBlueprintString } from "@factorio-sites/node-utils";
|
|
||||||
// import {
|
|
||||||
// BlueprintStringData,
|
|
||||||
// BlueprintBookData,
|
|
||||||
// getBlueprintContentForImageHash,
|
|
||||||
// } from "@factorio-sites/common-utils";
|
|
||||||
// import { getBlueprintImageRequestTopic } from "./gcp-pubsub";
|
|
||||||
// import { saveBlueprintString } from "./gcp-storage";
|
|
||||||
// import { BlueprintBook, Blueprint, BlueprintPage } from "./types";
|
|
||||||
|
|
||||||
// const datastore = new Datastore();
|
|
||||||
// const BlueprintEntity = "Blueprint";
|
|
||||||
// const BlueprintBookEntity = "BlueprintBook";
|
|
||||||
// const BlueprintPageEntity = "BlueprintPage";
|
|
||||||
// const BlueprintStringEntity = "BlueprintString";
|
|
||||||
// const blueprintImageRequestTopic = getBlueprintImageRequestTopic();
|
|
||||||
|
|
||||||
// class DatastoreExistsError extends Error {
|
|
||||||
// constructor(public existingId: string, message: string) {
|
|
||||||
// super(message);
|
|
||||||
// this.name = this.constructor.name;
|
|
||||||
// Error.captureStackTrace(this, this.constructor);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /*
|
|
||||||
// * BlueprintBook
|
|
||||||
// */
|
|
||||||
|
|
||||||
// const mapBlueprintBookEntityToObject = (entity: any): BlueprintBook => ({
|
|
||||||
// id: entity[datastore.KEY].id,
|
|
||||||
// // blueprint_ids: entity.blueprint_ids.map((key: any) => key.id),
|
|
||||||
// // blueprint_book_ids: entity.blueprint_book_ids.map((key: any) => key.id),
|
|
||||||
// child_tree: entity.child_tree ? entity.child_tree : [],
|
|
||||||
// blueprint_hash: entity.blueprint_hash,
|
|
||||||
// label: entity.label,
|
|
||||||
// description: entity.description,
|
|
||||||
// created_at: entity.created_at && entity.created_at.getTime() / 1000,
|
|
||||||
// updated_at: entity.updated_at && entity.updated_at.getTime() / 1000,
|
|
||||||
// is_modded: entity.is_modded || false,
|
|
||||||
// factorioprints_id: entity.factorioprints_id || null,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// export async function getBlueprintBookById(id: string): Promise<BlueprintBook | null> {
|
|
||||||
// const result = await datastore.get(datastore.key([BlueprintBookEntity, Number(id)]));
|
|
||||||
// return result[0] ? mapBlueprintBookEntityToObject(result[0]) : null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export async function getBlueprintBookByHash(hash: string): Promise<BlueprintBook | null> {
|
|
||||||
// const query = datastore
|
|
||||||
// .createQuery(BlueprintBookEntity)
|
|
||||||
// .filter("blueprint_hash", "=", hash)
|
|
||||||
// .limit(1);
|
|
||||||
// const [result] = await datastore.runQuery(query);
|
|
||||||
// return result[0] ? mapBlueprintBookEntityToObject(result[0]) : null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export async function createBlueprintBook(
|
|
||||||
// blueprintBook: BlueprintBookData,
|
|
||||||
// extraInfo: { tags: string[]; created_at: number; updated_at: number; factorioprints_id: string }
|
|
||||||
// ): Promise<{ insertedId: string; child_tree: BlueprintBook["child_tree"] }> {
|
|
||||||
// const string = await encodeBlueprint({ blueprint_book: blueprintBook });
|
|
||||||
// const blueprint_hash = hashString(string);
|
|
||||||
|
|
||||||
// const exists = await getBlueprintBookByHash(blueprint_hash);
|
|
||||||
// if (exists) {
|
|
||||||
// const book = await getBlueprintBookById(exists.id);
|
|
||||||
// if (!book) throw Error("this is impossible, just pleasing typescript");
|
|
||||||
// return { insertedId: exists.id, child_tree: book.child_tree };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Write string to google storage
|
|
||||||
// await saveBlueprintString(blueprint_hash, string);
|
|
||||||
|
|
||||||
// const blueprint_ids = [];
|
|
||||||
// const blueprint_book_ids = [];
|
|
||||||
// const child_tree: BlueprintBook["child_tree"] = [];
|
|
||||||
|
|
||||||
// // Create the book's child objects
|
|
||||||
// for (let i = 0; i < blueprintBook.blueprints.length; i++) {
|
|
||||||
// const blueprint = blueprintBook.blueprints[i];
|
|
||||||
// if (blueprint.blueprint) {
|
|
||||||
// const result = await createBlueprint(blueprint.blueprint, extraInfo);
|
|
||||||
// child_tree.push({
|
|
||||||
// type: "blueprint",
|
|
||||||
// id: result.insertedId,
|
|
||||||
// name: blueprint.blueprint.label,
|
|
||||||
// });
|
|
||||||
// blueprint_ids.push(result.insertedId);
|
|
||||||
// } else if (blueprint.blueprint_book) {
|
|
||||||
// const result = await createBlueprintBook(blueprint.blueprint_book, extraInfo);
|
|
||||||
// child_tree.push({
|
|
||||||
// type: "blueprint_book",
|
|
||||||
// id: result.insertedId,
|
|
||||||
// name: blueprint.blueprint_book.label,
|
|
||||||
// children: result.child_tree,
|
|
||||||
// });
|
|
||||||
// blueprint_book_ids.push(result.insertedId);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Write blueprint details to datastore
|
|
||||||
// const [result] = await datastore.insert({
|
|
||||||
// key: datastore.key([BlueprintBookEntity]),
|
|
||||||
// data: [
|
|
||||||
// {
|
|
||||||
// name: "blueprint_ids",
|
|
||||||
// value: blueprint_ids.map((id) => datastore.key([BlueprintEntity, datastore.int(id)])),
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// name: "blueprint_book_ids",
|
|
||||||
// value: blueprint_book_ids.map((id) =>
|
|
||||||
// datastore.key([BlueprintBookEntity, datastore.int(id)])
|
|
||||||
// ),
|
|
||||||
// },
|
|
||||||
// { name: "child_tree", value: child_tree, excludeFromIndexes: true },
|
|
||||||
// { name: "label", value: blueprintBook.label },
|
|
||||||
// {
|
|
||||||
// name: "description",
|
|
||||||
// value: blueprintBook.description || null,
|
|
||||||
// excludeFromIndexes: true,
|
|
||||||
// },
|
|
||||||
// { name: "blueprint_hash", value: blueprint_hash },
|
|
||||||
// {
|
|
||||||
// name: "created_at",
|
|
||||||
// value: extraInfo.created_at ? new Date(extraInfo.created_at * 1000) : null,
|
|
||||||
// excludeFromIndexes: true,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// name: "updated_at",
|
|
||||||
// value: extraInfo.updated_at ? new Date(extraInfo.updated_at * 1000) : null,
|
|
||||||
// excludeFromIndexes: true,
|
|
||||||
// },
|
|
||||||
// { name: "factorioprints_id", value: extraInfo.factorioprints_id || null },
|
|
||||||
// ],
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const insertedId = String(result.mutationResults?.[0]?.key?.path?.[0]?.id) as string;
|
|
||||||
// if (!insertedId) throw Error("Something went wrong inserting entity");
|
|
||||||
|
|
||||||
// console.log(`Created Blueprint book ${insertedId}`);
|
|
||||||
|
|
||||||
// return { insertedId, child_tree };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /*
|
|
||||||
// * Blueprint
|
|
||||||
// */
|
|
||||||
|
|
||||||
// const mapBlueprintEntityToObject = (entity: any): Blueprint => ({
|
|
||||||
// id: entity[datastore.KEY].id,
|
|
||||||
// blueprint_hash: entity.blueprint_hash,
|
|
||||||
// image_hash: entity.image_hash,
|
|
||||||
// label: entity.label,
|
|
||||||
// description: entity.description,
|
|
||||||
// tags: entity.tags,
|
|
||||||
// created_at: entity.created_at && entity.created_at.getTime() / 1000,
|
|
||||||
// updated_at: entity.updated_at && entity.updated_at.getTime() / 1000,
|
|
||||||
// factorioprints_id: entity.factorioprints_id || null,
|
|
||||||
// game_version: entity.game_version,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const mapBlueprintObjectToEntity = (object: Omit<Blueprint, "id"> & { id?: string }): any => ({
|
|
||||||
// key: datastore.key(object.id ? [BlueprintEntity, datastore.int(object.id)] : [BlueprintEntity]),
|
|
||||||
// data: [
|
|
||||||
// { name: "label", value: object.label },
|
|
||||||
// {
|
|
||||||
// name: "description",
|
|
||||||
// value: object.description || null,
|
|
||||||
// excludeFromIndexes: true,
|
|
||||||
// },
|
|
||||||
// { name: "blueprint_hash", value: object.blueprint_hash },
|
|
||||||
// { name: "image_hash", value: object.image_hash },
|
|
||||||
// { name: "tags", value: object.tags || [] },
|
|
||||||
// {
|
|
||||||
// name: "created_at",
|
|
||||||
// value: object.created_at ? new Date(object.created_at * 1000) : new Date(),
|
|
||||||
// excludeFromIndexes: true,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// name: "updated_at",
|
|
||||||
// value: object.updated_at ? new Date(object.updated_at * 1000) : new Date(),
|
|
||||||
// excludeFromIndexes: true,
|
|
||||||
// },
|
|
||||||
// { name: "game_version", value: object.game_version, excludeFromIndexes: true },
|
|
||||||
// { name: "factorioprints_id", value: object.factorioprints_id || null },
|
|
||||||
// ],
|
|
||||||
// });
|
|
||||||
|
|
||||||
// export async function getBlueprintById(id: string): Promise<Blueprint | null> {
|
|
||||||
// const result = await datastore.get(datastore.key([BlueprintEntity, datastore.int(id)]));
|
|
||||||
// return result[0] ? mapBlueprintEntityToObject(result[0]) : null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export async function getBlueprintByHash(hash: string): Promise<Blueprint | null> {
|
|
||||||
// const query = datastore.createQuery(BlueprintEntity).filter("blueprint_hash", "=", hash).limit(1);
|
|
||||||
// const [result] = await datastore.runQuery(query);
|
|
||||||
// return result[0] ? mapBlueprintEntityToObject(result[0]) : null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export async function getBlueprintByImageHash(hash: string): Promise<Blueprint | null> {
|
|
||||||
// const query = datastore.createQuery(BlueprintEntity).filter("image_hash", "=", hash).limit(1);
|
|
||||||
// const [result] = await datastore.runQuery(query);
|
|
||||||
// return result[0] ? mapBlueprintEntityToObject(result[0]) : null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export async function createBlueprint(
|
|
||||||
// blueprint: BlueprintStringData,
|
|
||||||
// extraInfo: { tags: string[]; created_at: number; updated_at: number; factorioprints_id: string }
|
|
||||||
// ) {
|
|
||||||
// const string = await encodeBlueprint({ blueprint });
|
|
||||||
// const blueprint_hash = hashString(string);
|
|
||||||
// const image_hash = hashString(getBlueprintContentForImageHash(blueprint));
|
|
||||||
|
|
||||||
// const exists = await getBlueprintByHash(blueprint_hash);
|
|
||||||
// if (exists) {
|
|
||||||
// return { insertedId: exists.id };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Write string to google storage
|
|
||||||
// await saveBlueprintString(blueprint_hash, string);
|
|
||||||
|
|
||||||
// // Write blueprint details to datastore
|
|
||||||
// const [result] = await datastore.insert({
|
|
||||||
// key: datastore.key([BlueprintEntity]),
|
|
||||||
// data: [
|
|
||||||
// { name: "label", value: blueprint.label },
|
|
||||||
// {
|
|
||||||
// name: "description",
|
|
||||||
// value: blueprint.description || null,
|
|
||||||
// excludeFromIndexes: true,
|
|
||||||
// },
|
|
||||||
// { name: "blueprint_hash", value: blueprint_hash },
|
|
||||||
// { name: "image_hash", value: image_hash },
|
|
||||||
// { name: "tags", value: extraInfo.tags ? extraInfo.tags : [] },
|
|
||||||
// {
|
|
||||||
// name: "created_at",
|
|
||||||
// value: extraInfo.created_at ? new Date(extraInfo.created_at * 1000) : new Date(),
|
|
||||||
// excludeFromIndexes: true,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// name: "updated_at",
|
|
||||||
// value: extraInfo.updated_at ? new Date(extraInfo.updated_at * 1000) : new Date(),
|
|
||||||
// excludeFromIndexes: true,
|
|
||||||
// },
|
|
||||||
// { name: "game_version", value: blueprint.version, excludeFromIndexes: true },
|
|
||||||
// { name: "factorioprints_id", value: extraInfo.factorioprints_id || null },
|
|
||||||
// ],
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const insertedId = String(result.mutationResults?.[0]?.key?.path?.[0]?.id);
|
|
||||||
// if (!insertedId) throw Error("Something went wrong inserting entity");
|
|
||||||
|
|
||||||
// console.log(`Created Blueprint ${insertedId}`);
|
|
||||||
|
|
||||||
// blueprintImageRequestTopic.publishJSON({
|
|
||||||
// blueprintId: insertedId,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// await datastore.insert({
|
|
||||||
// key: datastore.key([BlueprintEntity, datastore.int(insertedId), BlueprintStringEntity]),
|
|
||||||
// data: [
|
|
||||||
// { name: "blueprint_hash", value: blueprint_hash },
|
|
||||||
// { name: "image_hash", value: image_hash },
|
|
||||||
// { name: "version", value: 1 },
|
|
||||||
// { name: "changes_markdown", value: null },
|
|
||||||
// {
|
|
||||||
// name: "created_at",
|
|
||||||
// value: extraInfo.created_at ? new Date(extraInfo.created_at * 1000) : new Date(),
|
|
||||||
// excludeFromIndexes: true,
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// });
|
|
||||||
|
|
||||||
// return { insertedId };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export async function updateBlueprint(blueprint: Blueprint) {
|
|
||||||
// datastore.save(mapBlueprintObjectToEntity(blueprint));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /*
|
|
||||||
// * BlueprintPage
|
|
||||||
// */
|
|
||||||
// const mapBlueprintPageEntityToObject = (entity: any): BlueprintPage => ({
|
|
||||||
// id: entity[datastore.KEY].id,
|
|
||||||
// blueprint_id: entity.blueprint_id ? entity.blueprint_id.id : null,
|
|
||||||
// blueprint_book_id: entity.blueprint_book_id ? entity.blueprint_book_id.id : null,
|
|
||||||
// title: entity.title,
|
|
||||||
// description_markdown: entity.description_markdown,
|
|
||||||
// created_at: entity.created_at && entity.created_at.getTime() / 1000,
|
|
||||||
// updated_at: entity.updated_at && entity.updated_at.getTime() / 1000,
|
|
||||||
// factorioprints_id: entity.factorioprints_id || null,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// export async function getBlueprintPageById(id: string): Promise<BlueprintPage | null> {
|
|
||||||
// const result = await datastore.get(datastore.key([BlueprintPageEntity, datastore.int(id)]));
|
|
||||||
// return result[0] ? mapBlueprintPageEntityToObject(result[0]) : null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export async function getBlueprintPageByFactorioprintsId(
|
|
||||||
// id: string
|
|
||||||
// ): Promise<BlueprintPage | null> {
|
|
||||||
// const query = datastore
|
|
||||||
// .createQuery(BlueprintPageEntity)
|
|
||||||
// .filter("factorioprints_id", "=", id)
|
|
||||||
// .limit(1);
|
|
||||||
// const [result] = await datastore.runQuery(query);
|
|
||||||
// return result[0] ? mapBlueprintPageEntityToObject(result[0]) : null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async function createBlueprintPage(
|
|
||||||
// type: "blueprint" | "blueprint_book",
|
|
||||||
// targetId: string,
|
|
||||||
// extraInfo: {
|
|
||||||
// title: string;
|
|
||||||
// description_markdown: string;
|
|
||||||
// created_at: number;
|
|
||||||
// updated_at: number;
|
|
||||||
// factorioprints_id?: string;
|
|
||||||
// }
|
|
||||||
// ) {
|
|
||||||
// const insertData: any = [
|
|
||||||
// { name: "title", value: extraInfo.title },
|
|
||||||
// {
|
|
||||||
// name: "description_markdown",
|
|
||||||
// value: extraInfo.description_markdown,
|
|
||||||
// excludeFromIndexes: true,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// name: "created_at",
|
|
||||||
// value: extraInfo.created_at ? new Date(extraInfo.created_at * 1000) : new Date(),
|
|
||||||
// excludeFromIndexes: true,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// name: "updated_at",
|
|
||||||
// value: extraInfo.updated_at ? new Date(extraInfo.updated_at * 1000) : new Date(),
|
|
||||||
// },
|
|
||||||
// { name: "factorioprints_id", value: extraInfo.factorioprints_id || null },
|
|
||||||
// ];
|
|
||||||
|
|
||||||
// if (type === "blueprint") {
|
|
||||||
// insertData.push({
|
|
||||||
// name: "blueprint_id",
|
|
||||||
// value: datastore.key([BlueprintEntity, datastore.int(targetId)]),
|
|
||||||
// });
|
|
||||||
// } else if (type === "blueprint_book") {
|
|
||||||
// insertData.push({
|
|
||||||
// name: "blueprint_book_id",
|
|
||||||
// value: datastore.key([BlueprintBookEntity, datastore.int(targetId)]),
|
|
||||||
// });
|
|
||||||
// } else {
|
|
||||||
// throw Error("Invalid type given");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// await datastore.insert({
|
|
||||||
// key: datastore.key([BlueprintPageEntity]),
|
|
||||||
// data: insertData,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// console.log(`Created Blueprint Page`);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /*
|
|
||||||
// * Other
|
|
||||||
// */
|
|
||||||
|
|
||||||
// interface BlueprintDataFromFactorioprints {
|
|
||||||
// description_markdown: string;
|
|
||||||
// title: string;
|
|
||||||
// updated_at: number;
|
|
||||||
// created_at: number;
|
|
||||||
// tags: string[];
|
|
||||||
// factorioprints_id: string;
|
|
||||||
// }
|
|
||||||
// export async function saveBlueprintFromFactorioprints(
|
|
||||||
// factorioprintData: BlueprintDataFromFactorioprints,
|
|
||||||
// blueprintString: string
|
|
||||||
// ) {
|
|
||||||
// const parsed = await parseBlueprintString(blueprintString);
|
|
||||||
|
|
||||||
// // not needed for inserting, just printing
|
|
||||||
// // const { blueprints, books } = flattenBlueprintData(parsed.data);
|
|
||||||
// // console.log(`string has ${books.length} books with ${blueprints.length} blueprints`);
|
|
||||||
|
|
||||||
// const extraInfo = {
|
|
||||||
// created_at: factorioprintData.created_at,
|
|
||||||
// updated_at: factorioprintData.updated_at,
|
|
||||||
// tags: factorioprintData.tags,
|
|
||||||
// factorioprints_id: factorioprintData.factorioprints_id,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const extraInfoPage = {
|
|
||||||
// title: factorioprintData.title,
|
|
||||||
// description_markdown: factorioprintData.description_markdown,
|
|
||||||
// created_at: factorioprintData.created_at,
|
|
||||||
// updated_at: factorioprintData.updated_at,
|
|
||||||
// factorioprints_id: factorioprintData.factorioprints_id,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// if (parsed.data.blueprint) {
|
|
||||||
// console.log("string has one blueprint...");
|
|
||||||
// const { insertedId } = await createBlueprint(parsed.data.blueprint, extraInfo).catch(
|
|
||||||
// (error) => {
|
|
||||||
// if (error instanceof DatastoreExistsError) {
|
|
||||||
// console.log(`Blueprint already exists with id ${error.existingId}`);
|
|
||||||
// return { insertedId: error.existingId };
|
|
||||||
// } else throw error;
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
// await createBlueprintPage("blueprint", insertedId, extraInfoPage);
|
|
||||||
// } else if (parsed.data.blueprint_book) {
|
|
||||||
// console.log("string has a blueprint book...");
|
|
||||||
// const { insertedId } = await createBlueprintBook(parsed.data.blueprint_book, extraInfo);
|
|
||||||
// await createBlueprintPage("blueprint_book", insertedId, extraInfoPage);
|
|
||||||
// }
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export async function getMostRecentBlueprintPages(page = 1): Promise<BlueprintPage[]> {
|
|
||||||
// const perPage = 10;
|
|
||||||
// const query = datastore
|
|
||||||
// .createQuery(BlueprintPageEntity)
|
|
||||||
// .limit(perPage)
|
|
||||||
// .offset((page - 1) * perPage);
|
|
||||||
// const [result] = await datastore.runQuery(query);
|
|
||||||
// return result.map(mapBlueprintPageEntityToObject);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export async function getPaginatedBlueprints(page = 1, perPage = 10): Promise<Blueprint[]> {
|
|
||||||
// const query = datastore
|
|
||||||
// .createQuery(BlueprintEntity)
|
|
||||||
// .limit(perPage)
|
|
||||||
// .offset((page - 1) * perPage);
|
|
||||||
// const [result] = await datastore.runQuery(query);
|
|
||||||
// return result.map(mapBlueprintEntityToObject);
|
|
||||||
// }
|
|
@ -1,123 +1,40 @@
|
|||||||
import { Sequelize } from "sequelize";
|
import { PrismaClient } from "@prisma/client";
|
||||||
import { getBlueprintModel } from "./models/Blueprint";
|
import { getEnvOrThrow, getSecretOrThrow } from "../env.util";
|
||||||
import { getBlueprintBookModel } from "./models/BlueprintBook";
|
|
||||||
import { getBlueprintPageModel } from "./models/BlueprintPage";
|
|
||||||
import { getUsertModel } from "./models/User";
|
|
||||||
import { getSessionModel } from "./models/Session";
|
|
||||||
import { getEnv, getEnvOrThrow, getSecretOrThrow } from "../env.util";
|
|
||||||
|
|
||||||
const _init = async () => {
|
const _init = async () => {
|
||||||
|
if (_prisma) return _prisma;
|
||||||
|
|
||||||
const databasePassword = getEnvOrThrow("POSTGRES_PASSWORD");
|
const databasePassword = getEnvOrThrow("POSTGRES_PASSWORD");
|
||||||
|
|
||||||
const sequelize = new Sequelize({
|
const prisma = new PrismaClient({
|
||||||
dialect: "postgres",
|
log: ["query", "info", "warn", "error"],
|
||||||
host: getEnvOrThrow("POSTGRES_HOST"),
|
datasources: {
|
||||||
port: 5432,
|
db: {
|
||||||
database: getEnvOrThrow("POSTGRES_DB"),
|
url: `postgresql://${getEnvOrThrow("POSTGRES_USER")}:${await getSecretOrThrow(
|
||||||
username: getEnvOrThrow("POSTGRES_USER"),
|
databasePassword
|
||||||
password: databasePassword.startsWith("projects/")
|
)}@${getEnvOrThrow("POSTGRES_HOST")}:5432/${getEnvOrThrow("POSTGRES_DB")}`,
|
||||||
? await getSecretOrThrow(databasePassword)
|
},
|
||||||
: databasePassword,
|
|
||||||
define: {
|
|
||||||
underscored: true,
|
|
||||||
freezeTableName: true,
|
|
||||||
updatedAt: "updated_at",
|
|
||||||
createdAt: "created_at",
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const UserModel = getUsertModel(sequelize);
|
await prisma.$connect();
|
||||||
const SessionModel = getSessionModel(sequelize);
|
|
||||||
const BlueprintModel = getBlueprintModel(sequelize);
|
|
||||||
const BlueprintBookModel = getBlueprintBookModel(sequelize);
|
|
||||||
const BlueprintPageModel = getBlueprintPageModel(sequelize);
|
|
||||||
|
|
||||||
UserModel.hasMany(SessionModel, { foreignKey: "user_id" });
|
return prisma;
|
||||||
|
|
||||||
SessionModel.belongsTo(UserModel, { foreignKey: "user_id" });
|
|
||||||
|
|
||||||
BlueprintModel.hasMany(BlueprintPageModel, { foreignKey: "blueprint_id" });
|
|
||||||
|
|
||||||
BlueprintPageModel.belongsTo(BlueprintModel, { foreignKey: "blueprint_id" });
|
|
||||||
BlueprintPageModel.belongsTo(BlueprintBookModel, { foreignKey: "blueprint_book_id" });
|
|
||||||
|
|
||||||
BlueprintBookModel.hasMany(BlueprintPageModel, { foreignKey: "blueprint_book_id" });
|
|
||||||
BlueprintBookModel.belongsToMany(BlueprintBookModel, {
|
|
||||||
through: "blueprint_book_books",
|
|
||||||
as: "blueprint_books",
|
|
||||||
foreignKey: "blueprint_book_1_id",
|
|
||||||
otherKey: "blueprint_book_2_id",
|
|
||||||
timestamps: false,
|
|
||||||
});
|
|
||||||
BlueprintBookModel.belongsToMany(BlueprintModel, {
|
|
||||||
through: "blueprint_book_blueprints",
|
|
||||||
as: "blueprints",
|
|
||||||
foreignKey: "blueprint_book_id",
|
|
||||||
otherKey: "blueprint_id",
|
|
||||||
timestamps: false,
|
|
||||||
});
|
|
||||||
UserModel.belongsToMany(BlueprintPageModel, {
|
|
||||||
through: "user_favorites",
|
|
||||||
as: "favorites",
|
|
||||||
foreignKey: "user_id",
|
|
||||||
otherKey: "blueprint_page_id",
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test database connection, if it fails the method will throw
|
|
||||||
if (process.env.NODE_ENV === "development") {
|
|
||||||
console.log(`[DB] connecting to database ${sequelize.config.database}`);
|
|
||||||
await sequelize.authenticate();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getEnv("SYNCHRONIZE_DB")) {
|
|
||||||
const sync_tables = getEnv("SYNCHRONIZE_DB")?.split(",") || [];
|
|
||||||
console.log(`[SYNCHRONIZE_DB] syncing database`, { sync_tables });
|
|
||||||
|
|
||||||
for (const key in sequelize.models) {
|
|
||||||
if (sync_tables.includes(key) || sync_tables.includes("*")) {
|
|
||||||
await sequelize.models[key].sync({ force: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
sequelize,
|
|
||||||
UserModel,
|
|
||||||
SessionModel,
|
|
||||||
BlueprintModel,
|
|
||||||
BlueprintBookModel,
|
|
||||||
BlueprintPageModel,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const promise = _init()
|
const promise = _init()
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
_sequelize = result.sequelize;
|
_prisma = result;
|
||||||
_UserModel = result.UserModel;
|
|
||||||
_SessionModel = result.SessionModel;
|
|
||||||
_BlueprintModel = result.BlueprintModel;
|
|
||||||
_BlueprintBookModel = result.BlueprintBookModel;
|
|
||||||
_BlueprintPageModel = result.BlueprintPageModel;
|
|
||||||
return result;
|
return result;
|
||||||
})
|
})
|
||||||
.catch((reason) => {
|
.catch((reason) => {
|
||||||
console.log("Database failed to init!", reason);
|
console.log("Database failed to init!", reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
let _sequelize: Sequelize;
|
let _prisma: PrismaClient;
|
||||||
let _UserModel: ReturnType<typeof getUsertModel>;
|
|
||||||
let _SessionModel: ReturnType<typeof getSessionModel>;
|
|
||||||
let _BlueprintModel: ReturnType<typeof getBlueprintModel>;
|
|
||||||
let _BlueprintBookModel: ReturnType<typeof getBlueprintBookModel>;
|
|
||||||
let _BlueprintPageModel: ReturnType<typeof getBlueprintPageModel>;
|
|
||||||
|
|
||||||
export const init = async () => {
|
export const init = async () => {
|
||||||
return promise;
|
return promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sequelize = () => _sequelize;
|
export const prisma = () => _prisma;
|
||||||
export const UserModel = () => _UserModel;
|
|
||||||
export const SessionModel = () => _SessionModel;
|
|
||||||
export const BlueprintModel = () => _BlueprintModel;
|
|
||||||
export const BlueprintBookModel = () => _BlueprintBookModel;
|
|
||||||
export const BlueprintPageModel = () => _BlueprintPageModel;
|
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
import { QueryInterface, Sequelize } from "sequelize";
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
async up(queryInterface: QueryInterface, Sequelize: Sequelize) {
|
|
||||||
queryInterface.createTable("user", {});
|
|
||||||
},
|
|
||||||
async down(queryInterface: QueryInterface, Sequelize: Sequelize) {
|
|
||||||
//
|
|
||||||
},
|
|
||||||
};
|
|
86
package.json
86
package.json
@ -24,76 +24,79 @@
|
|||||||
"update": "nx migrate latest",
|
"update": "nx migrate latest",
|
||||||
"workspace-schematic": "nx workspace-schematic",
|
"workspace-schematic": "nx workspace-schematic",
|
||||||
"dep-graph": "nx dep-graph",
|
"dep-graph": "nx dep-graph",
|
||||||
"help": "nx help"
|
"help": "nx help",
|
||||||
|
"db-gen": "npx prisma generate --schema=./apps/blueprints/prisma/schema.prisma",
|
||||||
|
"preinstall": "npx yalc add @fbe/editor"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chakra-ui/react": "1.1.2",
|
"@chakra-ui/react": "1.3.3",
|
||||||
"@emotion/react": "11.1.4",
|
"@emotion/react": "11.1.5",
|
||||||
"@emotion/server": "11.0.0",
|
"@emotion/server": "11.0.0",
|
||||||
"@emotion/styled": "11.0.0",
|
"@emotion/styled": "11.1.5",
|
||||||
"@fbe/editor": "./fbe-editor-v1.0.0.tgz",
|
"@fbe/editor": "file:.yalc/@fbe/editor",
|
||||||
"@google-cloud/datastore": "6.3.1",
|
"@google-cloud/datastore": "6.3.1",
|
||||||
"@google-cloud/pubsub": "2.7.0",
|
"@google-cloud/pubsub": "2.9.0",
|
||||||
"@google-cloud/secret-manager": "3.2.3",
|
"@google-cloud/secret-manager": "3.4.0",
|
||||||
"@google-cloud/storage": "5.7.0",
|
"@google-cloud/storage": "5.7.4",
|
||||||
"@hookstate/core": "3.0.3",
|
"@hookstate/core": "3.0.5",
|
||||||
|
"@prisma/client": "2.18.0",
|
||||||
"bbcode-to-react": "0.2.9",
|
"bbcode-to-react": "0.2.9",
|
||||||
"bcrypt": "5.0.0",
|
"bcrypt": "5.0.0",
|
||||||
"cookie": "0.4.1",
|
"cookie": "0.4.1",
|
||||||
"document-register-element": "1.14.10",
|
"document-register-element": "1.14.10",
|
||||||
"formik": "2.2.6",
|
"formik": "2.2.6",
|
||||||
"framer-motion": "3.1.4",
|
"framer-motion": "3.3.0",
|
||||||
"next": "10.0.5",
|
"next": "10.0.7",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"openid": "2.0.7",
|
"openid": "2.0.7",
|
||||||
"pako": "1.0.11",
|
"pako": "1.0.11",
|
||||||
"pg": "8.5.1",
|
"pg": "8.5.1",
|
||||||
"phin": "3.5.1",
|
"phin": "3.5.1",
|
||||||
"puppeteer": "5.5.0",
|
"puppeteer": "7.1.0",
|
||||||
"react": "17.0.1",
|
"react": "17.0.1",
|
||||||
"react-dom": "17.0.1",
|
"react-dom": "17.0.1",
|
||||||
"react-icons": "4.1.0",
|
"react-icons": "4.2.0",
|
||||||
"react-map-interaction": "2.0.0",
|
"react-map-interaction": "2.0.0",
|
||||||
"react-markdown": "5.0.3",
|
"react-markdown": "5.0.3",
|
||||||
"sequelize": "6.3.5",
|
"react-multi-select-component": "3.1.3",
|
||||||
"sharp": "0.27.0",
|
"sharp": "0.27.1",
|
||||||
"ws": "7.4.2"
|
"ws": "7.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.12.10",
|
"@babel/core": "7.12.16",
|
||||||
"@babel/preset-env": "7.12.11",
|
"@babel/preset-env": "7.12.16",
|
||||||
"@babel/preset-react": "7.12.10",
|
"@babel/preset-react": "7.12.13",
|
||||||
"@babel/preset-typescript": "7.12.7",
|
"@babel/preset-typescript": "7.12.16",
|
||||||
"@nrwl/cli": "11.0.20",
|
"@nrwl/cli": "11.2.12",
|
||||||
"@nrwl/cypress": "11.0.20",
|
"@nrwl/cypress": "11.2.12",
|
||||||
"@nrwl/eslint-plugin-nx": "11.0.20",
|
"@nrwl/eslint-plugin-nx": "11.2.12",
|
||||||
"@nrwl/jest": "11.0.20",
|
"@nrwl/jest": "11.2.12",
|
||||||
"@nrwl/next": "11.0.20",
|
"@nrwl/next": "11.2.12",
|
||||||
"@nrwl/node": "11.0.20",
|
"@nrwl/node": "11.2.12",
|
||||||
"@nrwl/react": "11.0.20",
|
"@nrwl/react": "11.2.12",
|
||||||
"@nrwl/web": "11.0.20",
|
"@nrwl/web": "11.2.12",
|
||||||
"@nrwl/workspace": "11.0.20",
|
"@nrwl/workspace": "11.2.12",
|
||||||
"@testing-library/react": "11.2.2",
|
"@testing-library/react": "11.2.5",
|
||||||
"@types/bbcode-to-react": "0.2.0",
|
"@types/bbcode-to-react": "0.2.0",
|
||||||
"@types/bcrypt": "3.0.0",
|
"@types/bcrypt": "3.0.0",
|
||||||
"@types/cookie": "0.4.0",
|
"@types/cookie": "0.4.0",
|
||||||
"@types/jest": "26.0.20",
|
"@types/jest": "26.0.20",
|
||||||
"@types/node": "14.14.14",
|
"@types/node": "14.14.28",
|
||||||
"@types/nprogress": "0.2.0",
|
"@types/nprogress": "0.2.0",
|
||||||
"@types/openid": "2.0.1",
|
"@types/openid": "2.0.1",
|
||||||
"@types/pako": "1.0.1",
|
"@types/pako": "1.0.1",
|
||||||
"@types/puppeteer": "5.4.2",
|
"@types/puppeteer": "5.4.3",
|
||||||
"@types/react": "17.0.0",
|
"@types/react": "17.0.2",
|
||||||
"@types/react-dom": "17.0.0",
|
"@types/react-dom": "17.0.1",
|
||||||
"@types/sharp": "0.27.1",
|
"@types/sharp": "0.27.1",
|
||||||
"@types/ws": "7.4.0",
|
"@types/ws": "7.4.0",
|
||||||
"@typescript-eslint/eslint-plugin": "4.12.0",
|
"@typescript-eslint/eslint-plugin": "4.15.1",
|
||||||
"@typescript-eslint/parser": "4.12.0",
|
"@typescript-eslint/parser": "4.15.1",
|
||||||
"babel-jest": "26.6.3",
|
"babel-jest": "26.6.3",
|
||||||
"cypress": "6.2.1",
|
"cypress": "6.4.0",
|
||||||
"dotenv": "8.2.0",
|
"dotenv": "8.2.0",
|
||||||
"eslint": "7.17.0",
|
"eslint": "7.20.0",
|
||||||
"eslint-config-prettier": "7.1.0",
|
"eslint-config-prettier": "7.2.0",
|
||||||
"eslint-plugin-cypress": "2.11.2",
|
"eslint-plugin-cypress": "2.11.2",
|
||||||
"eslint-plugin-import": "2.22.1",
|
"eslint-plugin-import": "2.22.1",
|
||||||
"eslint-plugin-jsx-a11y": "6.4.1",
|
"eslint-plugin-jsx-a11y": "6.4.1",
|
||||||
@ -101,10 +104,11 @@
|
|||||||
"eslint-plugin-react-hooks": "4.2.0",
|
"eslint-plugin-react-hooks": "4.2.0",
|
||||||
"jest": "26.6.3",
|
"jest": "26.6.3",
|
||||||
"prettier": "2.2.1",
|
"prettier": "2.2.1",
|
||||||
"ts-jest": "26.4.4",
|
"prisma": "2.18.0",
|
||||||
|
"ts-jest": "26.5.1",
|
||||||
"ts-node": "9.1.1",
|
"ts-node": "9.1.1",
|
||||||
"tslint": "6.1.3",
|
"tslint": "6.1.3",
|
||||||
"typescript": "4.1.3",
|
"typescript": "4.1.5",
|
||||||
"wasm-loader": "1.3.0"
|
"wasm-loader": "1.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,9 @@
|
|||||||
"version": "v1",
|
"version": "v1",
|
||||||
"packages": {
|
"packages": {
|
||||||
"@fbe/editor": {
|
"@fbe/editor": {
|
||||||
"signature": "fcd9eadabd31e2c2000a5ea3e4b000f8",
|
"signature": "4394b199f59f3db8ce4aa6b1c9801472",
|
||||||
"file": true
|
"file": true,
|
||||||
|
"replaced": "./fbe-editor-v1.0.0.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user