1
0
mirror of https://github.com/barthuijgen/factorio-sites.git synced 2024-11-21 18:16:33 +02:00

Merge pull request #262 from barthuijgen/develop

Develop to main
This commit is contained in:
Bart 2021-12-07 23:05:10 +01:00 committed by GitHub
commit 468c40ce44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 7478 additions and 6983 deletions

View File

@ -9,3 +9,8 @@ updates:
target-branch: develop
ignore:
- dependency-name: "@fbe/editor"
- dependency-name: "factorio-wasm"
- dependency-name: "react-map-interaction"
- dependency-name: "pako" # v2 does not support required use case
- dependency-name: "*"
update-types: ["version-update:semver-patch"]

View File

@ -25,7 +25,6 @@ jobs:
restore-keys: |
${{ runner.os }}-yarn-
- run: yarn
- run: yarn db-gen
- run: yarn nx build blueprints
env:
CF_WEB_ANALYTICS: 6c563c1e5db141129a5fc95d5c459722

View File

@ -25,7 +25,6 @@ jobs:
restore-keys: |
${{ runner.os }}-yarn-
- run: yarn
- run: yarn db-gen
- run: yarn nx build blueprints
env:
PUBLIC_URL: https://factorio-blueprints-assets.storage.googleapis.com/public

View File

@ -21,7 +21,6 @@ jobs:
restore-keys: |
${{ runner.os }}-yarn-
- run: yarn --prefer-offline
- run: yarn db-gen
- run: yarn nx run-many --all --target=lint
- run: yarn nx run-many --all --target=test "--ci"
- run: yarn nx build blueprints

3
.gitignore vendored
View File

@ -33,6 +33,7 @@ npm-debug.log
yarn-error.log
testem.log
/typings
*.tsbuildinfo
# System Files
.DS_Store
@ -45,3 +46,5 @@ Thumbs.db
.local.env
.env
local.readme.md
/bin/

View File

@ -1,20 +0,0 @@
{
"name": "blueprint-image-function",
"version": "0.0.1",
"scripts": {
"postinstall": "yarn prisma generate"
},
"dependencies": {
"puppeteer": "7.1.0",
"tslib": "2.1.0",
"sharp": "0.27.2",
"prisma": "2.18.0",
"@prisma/client": "2.18.0",
"@google-cloud/pubsub": "2.9.0",
"@google-cloud/secret-manager": "3.4.0",
"@google-cloud/storage": "5.7.4",
"cookie": "0.4.1",
"pako": "1.0.11",
"bcrypt": "5.0.0"
}
}

View File

@ -11,4 +11,5 @@ module.exports = {
},
moduleFileExtensions: ["ts", "js", "html"],
coverageDirectory: "../../coverage/apps/blueprint-image-function",
testEnvironment: "node",
};

View File

@ -0,0 +1,24 @@
console.log("POST BUILD SCRIPT", __dirname);
/*
yarn nx build blueprint-image-function
1. Add to package.json
"scripts": {
"postinstall": "yarn prisma generate"
},
2. Add to dependencies
"prisma": "3.6.0",
2. Replace prisma generate header
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "debian-openssl-1.1.x"]
}
*/

View File

@ -0,0 +1,83 @@
import * as phin from "phin";
import {
getBlueprintById,
hasBlueprintImage,
saveBlueprintImage,
init,
} from "@factorio-sites/database";
import { jsonReplaceErrors } from "@factorio-sites/node-utils";
import { optimise } from "./image-optimiser";
// {"blueprintId":"ee9b98eb-313a-4401-8aee-d6e970b76aad"}
// ^ image_hash: 6f78c0a93c20fe99076e8defe4e396923f42753b
/** message body for pubsub triggered function */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Message = Record<string, any>;
/** context for pubsub triggered function */
interface Context {
eventId: string;
eventType: string;
timestamp: string;
resource: { service: string; name: string };
}
type PubSubHandler = (
message: Message,
context: Context,
callback: (error?: string) => void
) => void;
export const functionPubSubHandler: PubSubHandler = async (message, _context, callback) => {
const error = (message: string, data: Record<string, unknown> = {}) => {
console.error(JSON.stringify({ message, ...data }, jsonReplaceErrors));
callback(message);
};
const log = (message: string, data: Record<string, unknown> = {}) => {
console.log(JSON.stringify({ message, ...data }));
};
try {
const data = message.data
? JSON.parse(Buffer.from(message.data, "base64").toString())
: message;
const blueprintId = data.blueprintId;
if (!blueprintId) {
return error("No blueprintId in body");
}
await init();
const blueprint = await getBlueprintById(blueprintId);
if (!blueprint) {
return error(`Blueprint ${blueprintId} not found`);
}
if (await hasBlueprintImage(blueprint.image_hash, "300")) {
log(`Image already exists ${blueprint.image_hash}`);
return callback();
}
console.log(`Fetching https://fbsr.factorio.workers.dev/${blueprint.blueprint_hash}?size=300`);
const response = await phin(
`https://fbsr.factorio.workers.dev/${blueprint.blueprint_hash}?size=300`
);
const image = response.body;
console.log("Image fetched");
// Make thumbnail, max size 300px
const min_image = await optimise(image, 300);
await saveBlueprintImage(blueprint.image_hash, min_image, "300");
log(`Saved image with image hash ${blueprint.image_hash}`);
// await saveBlueprintImage(blueprint.image_hash, image, "original");
callback();
} catch (reason) {
error(`Error rendering image ${reason}`, { error: reason });
}
};

View File

@ -58,7 +58,7 @@ export const functionHttpHandler: Handler = async (req, res) => {
}
if (await hasBlueprintImage(blueprint.image_hash, "300")) {
return res.status(200).send("Image already exists");
return res.status(200).send(`Image already exists ${blueprint.image_hash}`);
}
const blueprint_string = await getBlueprintStringByHash(blueprint.blueprint_hash);
@ -75,7 +75,8 @@ export const functionHttpHandler: Handler = async (req, res) => {
await saveBlueprintImage(blueprint.image_hash, min_image, "300");
res.status(200).send("Done");
} catch (reason) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (reason: any) {
res.status(500).send(`Error rendering image ${reason.stack || reason}`);
}
};
@ -100,13 +101,11 @@ export const functionPubSubHandler: PubSubHandler = async (message, _context, ca
return error("No blueprintId in body");
}
log(`generating image for ${blueprintId}`);
await init();
const blueprint = await getBlueprintById(blueprintId);
if (!blueprint) {
return error("Blueprint not found");
return error(`Blueprint ${blueprintId} not found`);
}
if (await hasBlueprintImage(blueprint.image_hash, "300")) {
@ -114,6 +113,8 @@ export const functionPubSubHandler: PubSubHandler = async (message, _context, ca
return callback();
}
log(`generating image for ${blueprintId}`);
const blueprint_string = await getBlueprintStringByHash(blueprint.blueprint_hash);
if (!blueprint_string) {
return error("Blueprint string not found");

View File

@ -1,11 +1,8 @@
import { functionHttpHandler, functionPubSubHandler } from "./function-handler";
// import { local_test } from "./local-test";
// import { functionHttpHandler, functionPubSubHandler } from "./function-handler";
import { functionPubSubHandler } from "./fetch-handlers";
// import { rePublishAllBlueprints } from "./republish-pubsub";
// import { subscribeToPubSub } from "./pubsub-render";
// subscribeToPubSub().catch((reason) => console.error("Fatal error:", reason));
// rePublishAllBlueprints().catch((reason) => console.error("Fatal error:", reason));
exports.renderImageHttp = functionHttpHandler;
// exports.renderImageHttp = functionHttpHandler;
exports.renderImagePubSub = functionPubSubHandler;
// local_test("8737437e-f15b-459c-8c1d-d0074f3a89ca");
// rePublishAllBlueprints();

View File

@ -60,7 +60,8 @@ export async function subscribeToPubSub() {
await saveBlueprintImage(blueprint.image_hash, min_image, "300");
return ack("[pubsub] image saved", true);
} catch (reason) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (reason: any) {
return ack(`[pubsub:error] ${reason.stack || reason}`, false);
}
};

View File

@ -1,22 +1,70 @@
// import { getBlueprintImageRequestTopic, getPaginatedBlueprints } from "@factorio-sites/database";
import {
getBlueprintBookById,
getBlueprintById,
getBlueprintImageRequestTopic,
hasBlueprintImage,
init,
searchBlueprintPages,
} from "@factorio-sites/database";
import { getFirstBlueprintFromChildTree } from "@factorio-sites/node-utils";
import { BlueprintPage } from "@factorio-sites/types";
// export async function rePublishAllBlueprints() {
// const topic = getBlueprintImageRequestTopic();
// const fetchPage = async (page = 1) => {
// const blueprints = await getPaginatedBlueprints(page);
// if (blueprints.length === 0) {
// return console.log("No more blueprints found");
// }
// console.log(`Publishing page ${page} with ${blueprints.length} blueprints`);
const perPage = 10;
const pageLimit = 99;
// await Promise.all(
// blueprints.map((blueprint) => {
// return topic.publishJSON({ blueprintId: blueprint.id });
// })
// );
// fetchPage(page + 1);
// };
// await fetchPage();
// }
async function getBlueprintPageFirstBlueprint(page: BlueprintPage) {
if (page.blueprint_id) {
return getBlueprintById(page.blueprint_id);
} else if (page.blueprint_book_id) {
const book = await getBlueprintBookById(page.blueprint_book_id);
if (!book) return null;
const blueprint = getFirstBlueprintFromChildTree(book.child_tree);
return getBlueprintById(blueprint);
}
return null;
}
export {};
export async function rePublishAllBlueprints() {
const topic = getBlueprintImageRequestTopic();
if (!topic) throw Error("Topic not found");
let exists = 0;
let published = 0;
await init();
const fetchPage = async (page = 1) => {
const blueprintPages = await searchBlueprintPages({
page,
perPage,
mode: "AND",
order: "date",
});
if (blueprintPages.rows.length === 0) {
return console.log("No more blueprints found");
}
console.log(`Publishing page ${page} with ${blueprintPages.rows.length} blueprints`);
await Promise.all(
blueprintPages.rows.map(async (blueprintPage) => {
const blueprint = await getBlueprintPageFirstBlueprint(blueprintPage);
if (!blueprint) {
console.log(`Error: blueprint not found for page ${blueprintPage.id}`);
return;
}
if (await hasBlueprintImage(blueprint.image_hash, "300")) {
exists++;
return;
}
published++;
return topic.publishMessage({ json: { blueprintId: blueprint.id } });
})
);
if (page < pageLimit) await fetchPage(page + 1);
};
await fetchPage();
console.log(`done fetching, ${exists} already existed, ${published} published`);
}

View File

@ -5,6 +5,6 @@
"target": "ES2019",
"types": ["node"]
},
"exclude": ["**/*.spec.ts"],
"exclude": ["**/*.spec.ts", "**/*.test.ts"],
"include": ["**/*.ts"]
}

View File

@ -5,5 +5,5 @@
"module": "commonjs",
"types": ["jest", "node"]
},
"include": ["**/*.spec.ts", "**/*.d.ts"]
"include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts"]
}

View File

@ -11,12 +11,10 @@
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
const { preprocessTypescript } = require('@nrwl/cypress/plugins/preprocessor');
const { preprocessTypescript } = require("@nrwl/cypress/plugins/preprocessor");
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
// Preprocess Typescript file using Nx helper
on('file:preprocessor', preprocessTypescript(config));
};

View File

@ -1,14 +0,0 @@
{
"presets": [
[
"next/babel",
{
"preset-react": {
"runtime": "automatic",
"importSource": "@emotion/react"
}
}
]
],
"plugins": ["@emotion/babel-plugin"]
}

View File

@ -1,5 +1,9 @@
{
"extends": ["../../.eslintrc.json", "plugin:@next/next/recommended"],
"extends": [
"plugin:@nrwl/nx/react-typescript",
"../../.eslintrc.json",
"plugin:@next/next/recommended"
],
"ignorePatterns": ["!**/*"],
"env": {
"browser": true,
@ -281,7 +285,9 @@
"parserOptions": {
"project": ["apps/blueprints/tsconfig(.*)?.json"]
},
"rules": {}
"rules": {
"@next/next/no-html-link-for-pages": ["error", "apps/blueprints/src/pages"]
}
},
{
"files": ["*.ts", "*.tsx"],

View File

@ -9,6 +9,6 @@
}
],
"@babel/preset-typescript",
"@babel/preset-react"
["@babel/preset-react", { "runtime": "automatic" }]
]
}

View File

@ -7,5 +7,4 @@ module.exports = {
},
moduleFileExtensions: ["ts", "tsx", "js", "jsx"],
coverageDirectory: "../../coverage/apps/blueprints",
snapshotSerializers: ["@emotion/jest/serializer"],
};

View File

@ -1,3 +1,6 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
/// <reference types="@emotion/react/types/css-prop" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@ -20,6 +20,11 @@ module.exports = {
config.plugins.push(new ForkTsCheckerWebpackPlugin());
}
if (!config.experiments) {
config.experiments = {};
}
config.experiments.asyncWebAssembly = true;
return config;
},
async headers() {

View File

@ -3,7 +3,6 @@ jest.mock("next/config", () => () => ({
publicRuntimeConfig: {},
}));
import React from "react";
import { render } from "@testing-library/react";
import Index from "../src/pages/index";
import * as nextRouter from "next/router";

View File

@ -109,7 +109,7 @@ export const BlueprintTile: React.FC<BlueprintTileProps> = ({
};
return (
<div css={linkStyles}>
<Box css={linkStyles}>
<Link
href={editLink ? `/user/blueprint/${blueprint.id}` : `/blueprint/${blueprint.id}`}
passHref
@ -127,6 +127,7 @@ export const BlueprintTile: React.FC<BlueprintTileProps> = ({
objectFit="contain"
alt={blueprint.title}
onError={onImageError}
unoptimized
/>
)}
</div>
@ -150,6 +151,6 @@ export const BlueprintTile: React.FC<BlueprintTileProps> = ({
</Box>
</a>
</Link>
</div>
</Box>
);
};

View File

@ -1,6 +1,7 @@
import { memo } from "react";
import { css } from "@emotion/react";
import Link, { LinkProps } from "next/link";
import { Box } from "@chakra-ui/react";
import { FactorioIcon } from "./FactorioIcon";
import { FactorioCode } from "./FactorioCode";
import { BlueprintBookData, BlueprintStringData, ChildTree, Icon } from "@factorio-sites/types";
@ -132,11 +133,11 @@ const InnerBookChildTree: React.FC<BookChildTreeProps> = ({ book_item, base_url,
export const BookChildTree: React.FC<BookChildTreeProps> = memo(
({ book_item, base_url, selected_id }) => {
return (
<div css={componentStyles}>
<Box css={componentStyles}>
<div className="child-tree-wrapper ">
<InnerBookChildTree book_item={book_item} base_url={base_url} selected_id={selected_id} />
</div>
</div>
</Box>
);
}
);

View File

@ -1,4 +1,4 @@
import { Box } from "@chakra-ui/layout";
import { Box, Text } from "@chakra-ui/layout";
import styled from "@emotion/styled";
import { IconSignalTypes } from "@factorio-sites/types";
import { ReactNode } from "react";
@ -49,7 +49,7 @@ const parseFactorioCode = (string: string): ReactNode => {
if (match.start > lastHandledIndex) {
let content = string.substr(lastHandledIndex, match.start - lastHandledIndex);
content = content.replace(/ /g, "\u00A0");
result.push(<span key={lastHandledIndex}>{content}</span>);
result.push(<Text key={lastHandledIndex}>{content}</Text>);
}
if (match.groups.color && match.groups.content) {
@ -57,12 +57,12 @@ const parseFactorioCode = (string: string): ReactNode => {
match.groups.color = `rgb(${match.groups.color})`;
}
result.push(
<span
<Text
key={match.start}
css={{ color: match.groups.color, display: "inline-flex", alignItems: "center" }}
>
{parseFactorioCode(match.groups.content)}
</span>
</Text>
);
} else if (match.groups.type && match.groups.icon) {
if (match.groups.type === "img") {
@ -88,7 +88,7 @@ const parseFactorioCode = (string: string): ReactNode => {
});
if (lastHandledIndex < string.length) {
result.push(<span key={lastHandledIndex}>{string.substr(lastHandledIndex)}</span>);
result.push(<Text key={lastHandledIndex}>{string.substr(lastHandledIndex)}</Text>);
}
return result;

View File

@ -1,3 +1,4 @@
import { Box } from "@chakra-ui/layout";
import { IconSignalTypes } from "@factorio-sites/types";
interface FactorioIconProps {
@ -40,7 +41,7 @@ export const FactorioIcon: React.FC<FactorioIconProps> = ({ type, icon, size })
return <span css={{ color: "#ffa700" }}>[{icon}]</span>;
}
return (
<div
<Box
css={{
display: "inline-block",
width: `${size}px`,

View File

@ -71,7 +71,7 @@ export const FullscreenImage: React.FC<FullscreenImageProps> = ({ alt, src }) =>
/>
</StyledImage>
{open && (
<div
<Box
css={elementStyle}
onClick={(e) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -89,7 +89,7 @@ export const FullscreenImage: React.FC<FullscreenImageProps> = ({ alt, src }) =>
<MapInteractionCSS value={state} onChange={setState}>
<img alt={alt} src={src} />
</MapInteractionCSS>
</div>
</Box>
)}
</>
);

View File

@ -2,6 +2,7 @@ import { css } from "@emotion/react";
import { parseBlueprintStringClient } from "@factorio-sites/web-utils";
import { useEffect, useRef, useState } from "react";
import { PUBLIC_URL } from "../utils/env";
import { Box } from "@chakra-ui/react";
type FBE = typeof import("@fbe/editor");
type Editor = InstanceType<FBE["Editor"]>;
@ -89,7 +90,7 @@ export const ImageEditor: React.FC<ImageEditorProps> = ({ string, onError }) =>
// const picture = await editor.getPicture();
// setImage(URL.createObjectURL(picture));
} catch (reason) {
} catch (reason: any) {
setRenderError(true);
if (onError) onError();
if (Array.isArray(reason.errors)) {
@ -101,7 +102,7 @@ export const ImageEditor: React.FC<ImageEditorProps> = ({ string, onError }) =>
}, [string, editorLoaded, onError]);
return (
<div css={editorCss}>
<Box css={editorCss}>
{renderError && (
<div className="error">
<h3>Failed to render blueprint</h3>
@ -113,6 +114,6 @@ export const ImageEditor: React.FC<ImageEditorProps> = ({ string, onError }) =>
)}
<canvas id="pbe" ref={canvasRef} style={{ width: "100%", height: "auto" }} />
{/* <img src={image} alt="blueprint" style={{ width: "500px" }}></img> */}
</div>
</Box>
);
};

View File

@ -1,9 +1,12 @@
import React from "react";
import styled from "@emotion/styled";
import SimpleMDE, { SimpleMDEEditorProps } from "react-simplemde-editor";
import React, { Suspense } from "react";
import { css } from "@emotion/react";
import { Box } from "@chakra-ui/react";
import type { SimpleMDEReactProps } from "react-simplemde-editor";
import "easymde/dist/easymde.min.css";
const StyledSimpleMDE = styled(SimpleMDE)`
const SimpleMDE = React.lazy(() => import("react-simplemde-editor"));
const styles = css`
.editor-toolbar {
button.active,
button:hover {
@ -34,8 +37,12 @@ const StyledSimpleMDE = styled(SimpleMDE)`
}
`;
export const MDEditor: React.FC<SimpleMDEEditorProps> = (props) => {
export const MDEditor: React.FC<SimpleMDEReactProps> = (props) => {
return (
<StyledSimpleMDE options={{ spellChecker: false, sideBySideFullscreen: false }} {...props} />
<Suspense fallback={null}>
<Box css={styles}>
<SimpleMDE options={{ spellChecker: false, sideBySideFullscreen: false }} {...props} />
</Box>
</Suspense>
);
};

View File

@ -2,6 +2,7 @@ import { css } from "@emotion/react";
import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";
import rehypeSanitize from "rehype-sanitize";
import { Box } from "@chakra-ui/react";
const markdownStyle = css`
overflow: auto;
@ -19,7 +20,7 @@ const markdownStyle = css`
`;
export const Markdown: React.FC<{ children: string }> = ({ children, ...props }) => (
<div css={markdownStyle} {...props}>
<Box css={markdownStyle} {...props}>
<ReactMarkdown rehypePlugins={[rehypeRaw, rehypeSanitize]}>{children}</ReactMarkdown>
</div>
</Box>
);

View File

@ -1,6 +1,7 @@
import MultiSelect from "react-multi-select-component";
import { MultiSelect } from "react-multi-select-component";
import styled from "@emotion/styled";
import { ISelectProps } from "react-multi-select-component/dist/lib/interfaces";
type ISelectProps = Parameters<typeof MultiSelect>[0];
interface Tag {
value: string;

View File

@ -1,5 +1,10 @@
import MultiSelect from "react-multi-select-component";
import { MultiSelect } from "react-multi-select-component";
import { useFbeData } from "../hooks/fbe.hook";
import styled from "@emotion/styled";
const MultiSelectStyled = styled(MultiSelect)`
color: black;
`;
interface Tag {
value: string;
@ -25,8 +30,7 @@ export const TagsSelect: React.FC<TagsSelectProps> = ({ value, onChange, classNa
});
return (
<MultiSelect
css={{ color: "black" }}
<MultiSelectStyled
className={className}
options={TAGS}
value={value.map((value) => ({ value, label: value.replace(/[_-]/g, " ") }))}

View File

@ -1,10 +1,9 @@
/* eslint-disable no-irregular-whitespace */
import React from "react";
import { render } from "@testing-library/react";
import { FactorioCode } from "../FactorioCode";
import { createSerializer } from "@emotion/jest";
expect.addSnapshotSerializer(createSerializer({ DOMElements: false }));
expect.addSnapshotSerializer(createSerializer());
const cleanUpElement = (element: Element) => {
const el = element.querySelector("div > div");
@ -30,7 +29,7 @@ describe("FactorioCode", () => {
const { baseElement } = render(<FactorioCode code="Blueprint [color=red]red[/color]" />);
expect(cleanUpElement(baseElement)).toMatchInlineSnapshot(`
.emotion-0 {
.emotion-1 {
color: red;
display: -webkit-inline-box;
display: -webkit-inline-flex;
@ -45,14 +44,16 @@ describe("FactorioCode", () => {
<div
class=""
>
<span>
<p
class="chakra-text emotion-0"
>
Blueprint 
</span>
<span
class="emotion-0"
</p>
<p
class="chakra-text emotion-1"
>
red
</span>
</p>
</div>
`);
});
@ -61,46 +62,6 @@ describe("FactorioCode", () => {
const { baseElement } = render(<FactorioCode code="Blueprint [item=iron-ore]" />);
expect(cleanUpElement(baseElement)).toMatchInlineSnapshot(`
.emotion-0 {
display: inline-block;
width: 20px;
height: 20px;
background-image: url('https://storage.googleapis.com/factorio-blueprints-assets/factorio/graphics/icons/iron-ore.png');
-webkit-background-size: 38px;
background-size: 38px;
}
<div
class=""
>
<span>
Blueprint 
</span>
<div
class="emotion-0"
/>
</div>
`);
});
it("should render icons in colors", () => {
const { baseElement } = render(
<FactorioCode code="Blueprint [color=white]hello [item=iron-ore][/color]" />
);
expect(cleanUpElement(baseElement)).toMatchInlineSnapshot(`
.emotion-0 {
color: white;
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.emotion-1 {
display: inline-block;
width: 20px;
@ -113,19 +74,65 @@ describe("FactorioCode", () => {
<div
class=""
>
<span>
Blueprint 
</span>
<span
class="emotion-0"
<p
class="chakra-text emotion-0"
>
<span>
Blueprint 
</p>
<div
class="emotion-1"
/>
</div>
`);
});
it("should render icons in colors", () => {
const { baseElement } = render(
<FactorioCode code="Blueprint [color=white]hello [item=iron-ore][/color]" />
);
expect(cleanUpElement(baseElement)).toMatchInlineSnapshot(`
.emotion-1 {
color: white;
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.emotion-3 {
display: inline-block;
width: 20px;
height: 20px;
background-image: url('https://storage.googleapis.com/factorio-blueprints-assets/factorio/graphics/icons/iron-ore.png');
-webkit-background-size: 38px;
background-size: 38px;
}
<div
class=""
>
<p
class="chakra-text emotion-0"
>
Blueprint 
</p>
<p
class="chakra-text emotion-1"
>
<p
class="chakra-text emotion-0"
>
hello 
</span>
</p>
<div
class="emotion-1"
class="emotion-3"
/>
</span>
</p>
</div>
`);
});

View File

@ -102,7 +102,7 @@ export const BlueprintSubPage: React.FC<BlueprintProps> = ({
<img
src={`${PUBLIC_URL}/fbe.svg`}
alt="Factorio blueprint editor"
css={{ display: "inline-block", height: "24px", marginLeft: "10px" }}
style={{ display: "inline-block", height: "24px", marginLeft: "10px" }}
/>
)}
<Box css={{ display: "inline-block", flexGrow: 1, textAlign: "right" }}>

View File

@ -175,7 +175,7 @@ export const BlueprintBookSubPage: React.FC<BlueprintBookSubPageProps> = ({
<img
src={`${PUBLIC_URL}/fbe.svg`}
alt="Factorio blueprint editor"
css={{ display: "inline-block", height: "24px", marginLeft: "10px" }}
style={{ display: "inline-block", height: "24px", marginLeft: "10px" }}
/>
)}
<Box css={{ display: "inline-block", flexGrow: 1, textAlign: "right" }}>

View File

@ -25,7 +25,7 @@ export const useFetch = <T>(
const response = await fetch(url);
const data = await response.json();
dataState.nested(url).set(data);
} catch (error) {
} catch (error: any) {
setError(error);
}
setLoading(false);

View File

@ -1,4 +1,3 @@
import React from "react";
import { NextPage } from "next";
import { SimpleGrid, Box, Text, Image, Link } from "@chakra-ui/react";
import { Panel } from "../components/Panel";
@ -25,10 +24,10 @@ export const Index: NextPage = () => {
})}
>
<Box>
<h2 css={{ fontSize: "30px" }}>Factorio Blueprints by Barry</h2>
<h2 style={{ fontSize: "30px" }}>Factorio Blueprints by Barry</h2>
</Box>
<Box css={{ marginRight: "1rem" }}>
<h3 css={{ color: "orange" }}>
<h3 style={{ color: "orange" }}>
Work in progress! <IoIosConstruct css={{ display: "inline-block" }} />
</h3>
</Box>
@ -67,7 +66,7 @@ export const Index: NextPage = () => {
Factorio Blueprints uses the work of Teoxoy with the
<a
href="https://github.com/Teoxoy/factorio-blueprint-editor"
css={{
style={{
textDecoration: "underline",
display: "inline-flex",
alignItems: "center",
@ -78,7 +77,7 @@ export const Index: NextPage = () => {
<img
src={`${PUBLIC_URL}/fbe.svg`}
alt="Factorio blueprint editor"
css={{ display: "inline-block", height: "18px" }}
style={{ display: "inline-block", height: "18px" }}
/>
Factorio Blueprints Editor
</a>
@ -88,7 +87,7 @@ export const Index: NextPage = () => {
And the{" "}
<a
href="https://github.com/demodude4u/Factorio-FBSR"
css={{ textDecoration: "underline" }}
style={{ textDecoration: "underline" }}
>
FBSR
</a>{" "}

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { NextPage } from "next";
import { useRouter } from "next/router";
import {
@ -97,9 +97,8 @@ export const Index: NextPage<IndexProps> = ({
const [blueprints, setBlueprints] = useState<BlueprintPageWithUserFavorite[]>([]);
const routerQueryToHref = useRouterQueryToHref();
const data = useFbeData();
const searchOptions = useFetch<{ entities: string[]; items: string[]; recipes: string[] }>(
"/api/searchoptions"
);
const searchOptions =
useFetch<{ entities: string[]; items: string[]; recipes: string[] }>("/api/searchoptions");
useEffect(() => {
setSearchQuery((router.query.q as string) || "");
@ -250,7 +249,7 @@ export const Index: NextPage<IndexProps> = ({
/>
))
) : (
<p css={{ marginTop: "10px" }}>No results found</p>
<Text css={{ marginTop: "10px" }}>No results found</Text>
)}
</Box>
<Box css={{ marginTop: "15px" }}>

View File

@ -1,4 +1,3 @@
import React from "react";
import { NextPage } from "next";
import Link from "next/link";
import {

View File

@ -1,4 +1,3 @@
import React from "react";
import { NextPage } from "next";
import {
FormControl,

View File

@ -47,7 +47,7 @@ export const fetchSteamProfile = async (steam_id: string, api_key: string) => {
} else {
throw Error("No players found for the given SteamID.");
}
} catch (error) {
} catch (error: any) {
throw Error("Steam server error: " + error.message);
}
};

View File

@ -14,7 +14,9 @@
"resolveJsonModule": true,
"isolatedModules": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true
"experimentalDecorators": true,
"jsxImportSource": "@emotion/react",
"incremental": true
},
"include": [
"**/*.ts",
@ -26,4 +28,4 @@
"exclude": [
"node_modules"
]
}
}

View File

@ -8,9 +8,13 @@
},
"include": [
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"**/*.d.ts"
]
}

View File

@ -11,4 +11,5 @@ module.exports = {
},
moduleFileExtensions: ["ts", "js", "html"],
coverageDirectory: "../../coverage/apps/factorioprints-scraper",
testEnvironment: "node",
};

View File

@ -4,6 +4,6 @@
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"exclude": ["**/*.spec.ts"],
"exclude": ["**/*.spec.ts", "**/*.test.ts"],
"include": ["**/*.ts"]
}

View File

@ -5,5 +5,5 @@
"module": "commonjs",
"types": ["jest", "node"]
},
"include": ["**/*.spec.ts", "**/*.d.ts"]
"include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts"]
}

View File

@ -13,7 +13,6 @@ RUN yarn
COPY . .
RUN yarn run db-gen
RUN yarn nx build blueprints --prod
FROM node:14-slim

15
jest.config.js vendored
View File

@ -1,12 +1,3 @@
module.exports = {
projects: [
"<rootDir>/apps/blueprints",
"<rootDir>/apps/blueprint-image-function",
"<rootDir>/apps/factorioprints-scraper",
"<rootDir>/libs/database",
"<rootDir>/libs/utils",
"<rootDir>/libs/common-utils",
"<rootDir>/libs/web-utils",
"<rootDir>/libs/types",
],
};
const { getJestProjects } = require("@nrwl/jest");
module.exports = { projects: [...getJestProjects(), "<rootDir>/libs/utils"] };

View File

@ -6,6 +6,6 @@
"declaration": true,
"types": ["node"]
},
"exclude": ["**/*.spec.ts"],
"exclude": ["**/*.spec.ts", "**/*.test.ts"],
"include": ["**/*.ts"]
}

View File

@ -5,5 +5,15 @@
"module": "commonjs",
"types": ["jest", "node"]
},
"include": ["**/*.spec.ts", "**/*.spec.tsx", "**/*.spec.js", "**/*.spec.jsx", "**/*.d.ts"]
"include": [
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"**/*.d.ts"
]
}

View File

@ -18,7 +18,7 @@ const mapBlueprintInstanceToEntry = (entity: BlueprintModel): Blueprint => ({
created_at: entity.created_at && entity.created_at.getTime() / 1000,
updated_at: entity.updated_at && entity.updated_at.getTime() / 1000,
game_version: entity.game_version || null,
data: (entity.data as unknown) as DbBlueprintData,
data: entity.data as unknown as DbBlueprintData,
});
export async function getBlueprintById(id: string): Promise<Blueprint | null> {
@ -31,6 +31,17 @@ export async function getBlueprintByHash(hash: string): Promise<Blueprint | null
return result ? mapBlueprintInstanceToEntry(result) : null;
}
export async function getPaginatedBlueprints({
skip = 0,
take = 30,
}: {
skip: number;
take: number;
}): Promise<Blueprint[]> {
const results = await prisma.blueprint.findMany({ skip, take });
return results.map(mapBlueprintInstanceToEntry);
}
export async function createBlueprint(
blueprint: BlueprintData,
extraInfo: {

View File

@ -92,6 +92,7 @@ export const createSession = async (user: user, useragent: string, ip: string) =
};
export const getSessionByToken = async (token: string) => {
if (token.length !== 36 && token.length !== 32) return null;
return await prisma.session.findUnique({
where: { session_token: token },
include: {

View File

@ -6,6 +6,6 @@
"declaration": true,
"types": ["node"]
},
"exclude": ["**/*.spec.ts"],
"exclude": ["**/*.spec.ts", "**/*.test.ts"],
"include": ["**/*.ts"]
}

View File

@ -5,5 +5,15 @@
"module": "commonjs",
"types": ["jest", "node"]
},
"include": ["**/*.spec.ts", "**/*.spec.tsx", "**/*.spec.js", "**/*.spec.jsx", "**/*.d.ts"]
"include": [
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"**/*.d.ts"
]
}

View File

@ -6,6 +6,6 @@
"declaration": true,
"types": ["node"]
},
"exclude": ["**/*.spec.ts"],
"exclude": ["**/*.spec.ts", "**/*.test.ts"],
"include": ["**/*.ts"]
}

View File

@ -5,5 +5,15 @@
"module": "commonjs",
"types": ["jest", "node"]
},
"include": ["**/*.spec.ts", "**/*.spec.tsx", "**/*.spec.js", "**/*.spec.jsx", "**/*.d.ts"]
"include": [
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"**/*.d.ts"
]
}

View File

@ -1,5 +1,5 @@
import { comment, blueprint, user } from "@prisma/client";
import { Icon, Signal } from "./blueprint-string";
import { Signal } from "./blueprint-string";
export interface ChildTreeBlueprint {
type: "blueprint";

View File

@ -6,6 +6,6 @@
"declaration": true,
"types": ["node"]
},
"exclude": ["**/*.spec.ts"],
"exclude": ["**/*.spec.ts", "**/*.test.ts"],
"include": ["**/*.ts"]
}

View File

@ -5,5 +5,15 @@
"module": "commonjs",
"types": ["jest", "node"]
},
"include": ["**/*.spec.ts", "**/*.spec.tsx", "**/*.spec.js", "**/*.spec.jsx", "**/*.d.ts"]
"include": [
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"**/*.d.ts"
]
}

View File

@ -4,6 +4,6 @@
"outDir": "../../dist/out-tsc",
"types": []
},
"exclude": ["**/*.spec.ts"],
"exclude": ["**/*.spec.ts", "**/*.test.ts"],
"include": ["**/*.ts"]
}

View File

@ -5,5 +5,15 @@
"module": "commonjs",
"types": ["jest", "node"]
},
"include": ["**/*.spec.ts", "**/*.spec.tsx", "**/*.spec.js", "**/*.spec.jsx", "**/*.d.ts"]
"include": [
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"**/*.d.ts"
]
}

View File

@ -1,28 +1,188 @@
{
"migrations": [
{
"version": "12.5.0-beta.1",
"description": "Rename the workspace-schematic script into workspace-generator script",
"factory": "./src/migrations/update-12-5-0/add-target-dependencies",
"cli": "nx",
"version": "12.0.0-beta.0",
"description": "Migrate tsconfig.json to allow new jsx transform to be used. Removes the need for `import React from 'react'`",
"factory": "./src/migrations/update-12-0-0/use-react-jsx-in-tsconfig",
"package": "@nrwl/react",
"name": "use-react-jsx-in-tsconfig-12.0.0"
"package": "@nrwl/workspace",
"name": "add-target-dependencies"
},
{
"version": "13.0.0-beta.1",
"description": "Add default base to nx.json if its not currently set",
"factory": "./src/migrations/update-13-0-0/set-default-base-if-not-set",
"cli": "nx",
"package": "@nrwl/workspace",
"name": "set-default-base-if-not-set"
},
{
"version": "13.0.0-beta.4",
"description": "Move global settings into nx.json, and project specific settings into workspace.json",
"cli": "nx",
"implementation": "./src/migrations/update-13-0-0/config-locations/config-locations",
"package": "@nrwl/workspace",
"name": "13-0-0-config-locations"
},
{
"version": "13.2.0",
"description": "Set --parallel=1 for existing repos to preserve the existing behavior",
"cli": "nx",
"implementation": "./src/migrations/update-13-2-0/set-parallel-default",
"package": "@nrwl/workspace",
"name": "set-parallel-default"
},
{
"cli": "nx",
"version": "12.0.0-beta.0",
"description": "Update workspace to use `@emotion/babel-plugin` instead of `@emotion/babel-preset-css-prop` to support new jsx transform",
"factory": "./src/migrations/update-12-0-0/update-emotion-setup",
"package": "@nrwl/react",
"name": "update-emotion-setup-12.0.0"
"version": "12.8.0-beta.0",
"description": "Remove Typescript Preprocessor Plugin",
"factory": "./src/migrations/update-12-8-0/remove-typescript-plugin",
"package": "@nrwl/cypress",
"name": "remove-typescript-plugin"
},
{
"version": "12.1.0-beta.1",
"cli": "nx",
"description": "Update jest-preset-angular to version 8.4.0",
"factory": "./src/migrations/update-12-1-2/update-jest-preset-angular",
"package": "@nrwl/jest",
"name": "update-jest-preset-angular-8-4-0"
},
{
"version": "12.1.2-beta.1",
"cli": "nx",
"description": "Replace tsConfig with tsconfig for ts-jest in jest.config.js",
"factory": "./src/migrations/update-12-1-2/update-ts-jest",
"package": "@nrwl/jest",
"name": "update-ts-jest-6-5-5"
},
{
"version": "12.4.0-beta.1",
"cli": "nx",
"description": "Add testEnvironment: 'jsdom' in web apps + libraries",
"factory": "./src/migrations/update-12-4-0/add-test-environment-for-node",
"package": "@nrwl/jest",
"name": "support-jest-27"
},
{
"version": "12.4.0-beta.1",
"cli": "nx",
"description": "Support for Jest 27 via updating ts-jest + jest-preset-angular",
"factory": "./src/migrations/update-12-4-0/update-jest-preset-angular",
"package": "@nrwl/jest",
"name": "update-ts-jest-and-jest-preset-angular"
},
{
"version": "12.6.0-beta.0",
"cli": "nx",
"description": "Uses `getJestProjects()` to populate projects array in root level `jest.config.js` file.",
"factory": "./src/migrations/update-12-6-0/update-base-jest-config",
"package": "@nrwl/jest",
"name": "update-jest-config-to-use-util"
},
{
"version": "13.1.2-beta.0",
"cli": "nx",
"description": "Support .test. file names in tsconfigs",
"factory": "./src/migrations/update-13-1-2/update-tsconfigs-for-tests",
"package": "@nrwl/jest",
"name": "update-ts-config-for-test-filenames"
},
{
"cli": "nx",
"version": "12.0.0-beta.0",
"description": "Remove @types/react-redux from package.json since react-redux installs the package automatically now",
"factory": "./src/migrations/update-12-0-0/remove-react-redux-types-package",
"version": "12.6.0-beta.0",
"description": "Add 'next' eslint config",
"factory": "./src/migrations/update-12-6-0/add-next-eslint",
"package": "@nrwl/next",
"name": "add-next-eslint-12.6.0"
},
{
"cli": "nx",
"version": "12.8.0-beta.11",
"description": "Adjust the Next.js lib babel configuration for styled-jsx",
"factory": "./src/migrations/update-12-8-0/remove-styled-jsx-babel-plugin",
"package": "@nrwl/next",
"name": "fix-nextjs-lib-babel-config-12.8.0"
},
{
"cli": "nx",
"version": "12.10.0-beta.1",
"description": "Updates .eslintrc.json file to use the correct pages directory for '@next/next/no-html-link-for-pages'.",
"factory": "./src/migrations/update-12-10-0/fix-page-dir-for-eslint",
"package": "@nrwl/next",
"name": "fix-page-dir-for-eslint"
},
{
"cli": "nx",
"version": "13.0.0-beta.0",
"description": "Update tsconfig.json to use `jsxImportSource` to support css prop",
"factory": "./src/migrations/update-13-0-0/update-emotion-setup",
"package": "@nrwl/next",
"name": "update-emotion-setup-13.0.0"
},
{
"cli": "nx",
"version": "13.0.0-beta.1",
"description": "Set `webpack5: true` for all next.js projects.",
"factory": "./src/migrations/update-13-0-0/update-to-webpack-5",
"package": "@nrwl/next",
"name": "update-to-webpack-5"
},
{
"cli": "nx",
"version": "13.0.3-beta.1",
"description": "Fix setup for less stylesheets",
"factory": "./src/migrations/update-13-0-3/fix-less",
"package": "@nrwl/next",
"name": "fix-less"
},
{
"cli": "nx",
"version": "13.1.1-beta.1",
"description": "Enables SWC for Next.js apps.",
"factory": "./src/migrations/update-13-1-1/enable-swc",
"package": "@nrwl/next",
"name": "enable-swc"
},
{
"cli": "nx",
"version": "13.0.0-beta.1",
"description": "Remove packages installed by Nx 12's `@nrwl/node:webpack5` generator.",
"factory": "./src/migrations/update-13-0-0/remove-webpack-5-packages-13-0-0",
"package": "@nrwl/node",
"name": "remove-webpack-5-packages"
},
{
"cli": "nx",
"version": "13.0.0-beta.0",
"description": "Update tsconfig.json to use `jsxImportSource` to support css prop",
"factory": "./src/migrations/update-13-0-0/update-emotion-setup",
"package": "@nrwl/react",
"name": "remove-react-redux-types-package-12.0.0"
"name": "update-emotion-setup-13.0.0"
},
{
"cli": "nx",
"version": "13.0.0-beta.0",
"description": "Migrate Storybook to use webpack 5",
"factory": "./src/migrations/update-13-0-0/migrate-storybook-to-webpack-5",
"package": "@nrwl/react",
"name": "migrate-storybook-to-webpack-5-13.0.0"
},
{
"cli": "nx",
"version": "13.0.0-beta.1",
"description": "Removes deprecated node-sass package (sass is already a dependency of @nrwl/web).",
"factory": "./src/migrations/update-13-0-0/remove-node-sass-13-0-0",
"package": "@nrwl/web",
"name": "remove-node-sass-13-0-0"
},
{
"cli": "nx",
"version": "13.0.0-beta.1",
"description": "Remove packages installed by Nx 12's `@nrwl/web:webpack5` generator.",
"factory": "./src/migrations/update-13-0-0/remove-webpack-5-packages-13-0-0",
"package": "@nrwl/web",
"name": "remove-webpack-5-packages"
}
]
}
}

48
nx.json
View File

@ -1,9 +1,14 @@
{
"npmScope": "factorio-sites",
"affected": { "defaultBase": "master" },
"affected": {
"defaultBase": "master"
},
"implicitDependencies": {
"workspace.json": "*",
"package.json": { "dependencies": "*", "devDependencies": "*" },
"package.json": {
"dependencies": "*",
"devDependencies": "*"
},
"tsconfig.base.json": "*",
"tslint.json": "*",
".eslintrc.json": "*",
@ -12,18 +17,35 @@
"tasksRunnerOptions": {
"default": {
"runner": "@nrwl/workspace/tasks-runners/default",
"options": { "cacheableOperations": ["build", "lint", "test", "e2e"] }
"options": {
"cacheableOperations": ["build", "lint", "test", "e2e"],
"parallel": 1
}
}
},
"projects": {
"blueprints": { "tags": [] },
"blueprints-e2e": { "tags": [], "implicitDependencies": ["blueprints"] },
"blueprint-image-function": { "tags": [] },
"factorioprints-scraper": { "tags": [] },
"database": { "tags": [] },
"node-utils": { "tags": [] },
"common-utils": { "tags": [] },
"web-utils": { "tags": [] },
"types": { "tags": [] }
"targetDependencies": {
"build": [
{
"target": "build",
"projects": "dependencies"
}
]
},
"cli": {
"defaultCollection": "@nrwl/next"
},
"defaultProject": "blueprints",
"generators": {
"@nrwl/react": {
"application": {
"babel": true
}
},
"@nrwl/next": {
"application": {
"style": "@emotion/styled",
"linter": "eslint"
}
}
}
}

View File

@ -25,99 +25,102 @@
"workspace-schematic": "nx workspace-schematic",
"dep-graph": "nx dep-graph",
"help": "nx help",
"db-gen": "npx prisma generate --schema=./apps/blueprints/prisma/schema.prisma"
"postinstall": "npx prisma generate --schema=./apps/blueprints/prisma/schema.prisma"
},
"dependencies": {
"@chakra-ui/react": "1.5.2",
"@emotion/react": "11.1.5",
"@emotion/server": "11.0.0",
"@emotion/styled": "11.3.0",
"@chakra-ui/react": "1.7.2",
"@emotion/react": "11.7.0",
"@emotion/server": "11.4.0",
"@emotion/styled": "11.6.0",
"@fbe/editor": "file:.yalc/@fbe/editor",
"@google-cloud/datastore": "6.3.1",
"@google-cloud/pubsub": "2.11.0",
"@google-cloud/secret-manager": "3.6.0",
"@google-cloud/storage": "5.8.3",
"@hookstate/core": "3.0.6",
"@prisma/client": "2.21.2",
"@google-cloud/datastore": "6.6.2",
"@google-cloud/pubsub": "2.18.3",
"@google-cloud/secret-manager": "3.10.1",
"@google-cloud/storage": "5.16.1",
"@hookstate/core": "3.0.13",
"@prisma/client": "3.6.0",
"bcrypt": "5.0.1",
"clsx": "1.1.1",
"cookie": "0.4.1",
"date-fns": "2.21.1",
"date-fns": "2.27.0",
"document-register-element": "1.14.10",
"easymde": "2.15.0",
"factorio-wasm": "file:.yalc/factorio-wasm",
"formik": "2.2.6",
"framer-motion": "4.1.8",
"next": "10.1.3",
"formik": "2.2.9",
"framer-motion": "5.3.3",
"next": "12.0.4",
"nprogress": "0.2.0",
"openid": "2.0.8",
"openid": "2.0.10",
"pako": "1.0.11",
"pg": "8.6.0",
"phin": "3.5.1",
"puppeteer": "8.0.0",
"pg": "8.7.1",
"phin": "3.6.1",
"puppeteer": "12.0.1",
"react": "17.0.2",
"react-cookie": "4.0.3",
"react-cookie": "4.1.1",
"react-dom": "17.0.2",
"react-icons": "4.2.0",
"react-icons": "4.3.1",
"react-map-interaction": "file:.yalc/react-map-interaction",
"react-markdown": "6.0.0",
"react-multi-select-component": "4.0.1",
"react-paginate": "7.1.2",
"react-simplemde-editor": "4.1.3",
"rehype-raw": "5.1.0",
"rehype-sanitize": "4.0.0",
"sharp": "0.28.1",
"ws": "7.4.4"
"react-markdown": "7.1.1",
"react-multi-select-component": "4.1.14",
"react-paginate": "8.0.2",
"react-simplemde-editor": "5.0.2",
"rehype-raw": "6.1.0",
"rehype-sanitize": "5.0.0",
"sharp": "0.29.3",
"ws": "8.3.0"
},
"devDependencies": {
"@babel/core": "7.13.15",
"@babel/preset-env": "7.13.15",
"@babel/preset-react": "7.13.13",
"@babel/preset-typescript": "7.13.0",
"@emotion/jest": "11.3.0",
"@emotion/jest": "11.6.0",
"@next/eslint-plugin-next": "10.1.3",
"@nrwl/cli": "12.0.6",
"@nrwl/cypress": "12.0.6",
"@nrwl/eslint-plugin-nx": "12.0.6",
"@nrwl/jest": "12.0.6",
"@nrwl/next": "12.0.6",
"@nrwl/node": "12.0.6",
"@nrwl/react": "12.0.6",
"@nrwl/web": "12.0.6",
"@nrwl/workspace": "12.0.6",
"@testing-library/react": "11.2.6",
"@types/bcrypt": "3.0.1",
"@types/cookie": "0.4.0",
"@types/jest": "26.0.22",
"@types/node": "14.14.41",
"@nrwl/cli": "13.2.1",
"@nrwl/cypress": "13.2.1",
"@nrwl/eslint-plugin-nx": "13.2.1",
"@nrwl/jest": "13.2.1",
"@nrwl/next": "13.2.1",
"@nrwl/node": "13.2.1",
"@nrwl/react": "13.2.1",
"@nrwl/web": "13.2.1",
"@nrwl/workspace": "13.2.1",
"@testing-library/react": "12.1.2",
"@types/bcrypt": "5.0.0",
"@types/cookie": "0.4.1",
"@types/jest": "27.0.3",
"@types/node": "16.11.11",
"@types/nprogress": "0.2.0",
"@types/openid": "2.0.1",
"@types/pako": "1.0.1",
"@types/puppeteer": "5.4.3",
"@types/react": "17.0.3",
"@types/react-dom": "17.0.3",
"@types/react-paginate": "6.2.1",
"@types/sharp": "0.28.0",
"@types/ws": "7.4.1",
"@typescript-eslint/eslint-plugin": "4.22.0",
"@typescript-eslint/parser": "4.22.0",
"babel-jest": "26.6.3",
"@types/openid": "2.0.2",
"@types/pako": "1.0.2",
"@types/puppeteer": "5.4.4",
"@types/react": "17.0.37",
"@types/react-dom": "17.0.11",
"@types/react-paginate": "7.1.1",
"@types/sharp": "0.29.4",
"@types/ws": "8.2.1",
"@typescript-eslint/eslint-plugin": "5.5.0",
"@typescript-eslint/parser": "5.5.0",
"babel-jest": "27.4.2",
"cypress": "7.1.0",
"dotenv": "8.2.0",
"dotenv": "10.0.0",
"eslint": "7.24.0",
"eslint-config-prettier": "8.2.0",
"eslint-plugin-cypress": "2.11.2",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-jsx-a11y": "6.4.1",
"eslint-plugin-react": "7.23.2",
"eslint-plugin-react-hooks": "4.2.0",
"fork-ts-checker-webpack-plugin": "6.2.1",
"jest": "26.6.3",
"prettier": "2.2.1",
"prisma": "2.21.2",
"ts-jest": "26.5.5",
"ts-node": "9.1.1",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-cypress": "2.12.1",
"eslint-plugin-import": "2.25.3",
"eslint-plugin-jsx-a11y": "6.5.1",
"eslint-plugin-react": "7.27.1",
"eslint-plugin-react-hooks": "4.3.0",
"fork-ts-checker-webpack-plugin": "6.5.0",
"jest": "27.4.3",
"prettier": "2.5.1",
"prisma": "3.6.0",
"ts-jest": "27.0.7",
"ts-node": "10.4.0",
"tslint": "6.1.3",
"typescript": "4.2.4",
"wasm-loader": "1.3.0"
"typescript": "4.5.2",
"wasm-loader": "1.3.0",
"@pulumi/pulumi": "3.0.0",
"@pulumi/gcp": "5.0.0"
}
}

2
pulumi/Pulumi.dev.yaml Normal file
View File

@ -0,0 +1,2 @@
config:
gcp:project: factorio-sites-dev

View File

@ -0,0 +1,2 @@
config:
gcp:project: factorio-sites

3
pulumi/Pulumi.yaml Normal file
View File

@ -0,0 +1,3 @@
name: factorio-sites
runtime: nodejs
description: Factorio blueprints mono repo

66
pulumi/dev.index.ts Normal file
View File

@ -0,0 +1,66 @@
import * as path from "path";
import * as dotenv from "dotenv";
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
dotenv.config({ path: path.join(__dirname, ".env") });
const account = new gcp.serviceaccount.Account("factorio-blueprints", {
accountId: "factorio-blueprints",
displayName: "Factorio blueprints service account",
});
// Can't get service account roles sorted, manual steps
// 1. Add a member in IAM with the same email as the service account
// 2. Assign roles to that member in IAM
// Roles used: Pub/Sub Editor, Secret Manager Secret Accessor, Storage Object Admin
const postgresPasswordSecret = new gcp.secretmanager.Secret("prd-postgres-password", {
replication: { automatic: true },
secretId: "prd-postgres-password",
});
const postgresPasswordSecretVersion = new gcp.secretmanager.SecretVersion(
"prd-postgres-password-version",
{
secret: postgresPasswordSecret.id,
secretData: process.env.DATABASE_PASSWORD,
}
);
const functionsBucket = new gcp.storage.Bucket("blueprint-thumbnail-render-code", {
storageClass: "STANDARD",
location: "EUROPE-WEST1",
});
const blueprintImagesBucket = new gcp.storage.Bucket("blueprint-thumbnail-images", {
storageClass: "STANDARD",
location: "EUROPE-WEST1",
});
const archive = new gcp.storage.BucketObject("archive", {
bucket: functionsBucket.name,
source: new pulumi.asset.FileArchive("../dist/apps/blueprint-image-function"),
});
const renderTopic = new gcp.pubsub.Topic("blueprint-thumbnail-render");
new gcp.cloudfunctions.Function("blueprint-image-render", {
eventTrigger: {
eventType: "google.pubsub.topic.publish",
resource: renderTopic.name,
},
sourceArchiveBucket: functionsBucket.name,
sourceArchiveObject: archive.name,
entryPoint: "renderImagePubSub",
runtime: "nodejs14",
region: "europe-west1",
timeout: 60,
environmentVariables: {
POSTGRES_DB: "factorio-blueprints",
POSTGRES_USER: "factorio-blueprints",
POSTGRES_HOST: process.env.POSTGRES_HOST,
POSTGRES_PASSWORD: postgresPasswordSecretVersion.id,
GCP_BLUEPRINT_IMAGES_BUCKET: blueprintImagesBucket.id,
},
serviceAccountEmail: account.email,
});

66
pulumi/index.ts Normal file
View File

@ -0,0 +1,66 @@
import * as path from "path";
import * as dotenv from "dotenv";
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
dotenv.config({ path: path.join(__dirname, ".env") });
// const account = new gcp.serviceaccount.Account("factorio-blueprints", {
// accountId: "factorio-blueprints",
// displayName: "Factorio blueprints service account",
// });
// Can't get service account roles sorted, manual steps
// 1. Add a member in IAM with the same email as the service account
// 2. Assign roles to that member in IAM
// Roles used: Pub/Sub Editor, Secret Manager Secret Accessor, Storage Object Admin
// const postgresPasswordSecret = new gcp.secretmanager.Secret("prd-postgres-password", {
// replication: { automatic: true },
// secretId: "prd-postgres-password",
// });
// const postgresPasswordSecretVersion = new gcp.secretmanager.SecretVersion(
// "prd-postgres-password-version",
// {
// secret: postgresPasswordSecret.id,
// secretData: process.env.DATABASE_PASSWORD,
// }
// );
const functionsBucket = new gcp.storage.Bucket("blueprint-thumbnail-render-code", {
storageClass: "STANDARD",
location: "EUROPE-WEST1",
});
// const blueprintImagesBucket = new gcp.storage.Bucket("blueprint-thumbnail-images", {
// storageClass: "STANDARD",
// location: "EUROPE-WEST1",
// });
const archive = new gcp.storage.BucketObject("archive", {
bucket: functionsBucket.name,
source: new pulumi.asset.FileArchive("../dist/apps/blueprint-image-function"),
});
// const renderTopic = new gcp.pubsub.Topic("blueprint-thumbnail-render");
new gcp.cloudfunctions.Function("blueprint-image-render", {
eventTrigger: {
eventType: "google.pubsub.topic.publish",
resource: process.env.IMAGE_RENDER_TOPIC,
},
sourceArchiveBucket: functionsBucket.name,
sourceArchiveObject: archive.name,
entryPoint: "renderImagePubSub",
runtime: "nodejs14",
region: "europe-west1",
timeout: 60,
environmentVariables: {
POSTGRES_DB: "factorio-blueprints",
POSTGRES_USER: "factorio-blueprints",
POSTGRES_HOST: process.env.POSTGRES_HOST,
POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD,
GCP_BLUEPRINT_IMAGES_BUCKET: "blueprint-images",
},
serviceAccountEmail: process.env.RENDER_FUNCTION_SERVICE_ACCOUNT,
});

13
pulumi/package.json Normal file
View File

@ -0,0 +1,13 @@
{
"name": "factorio-sites",
"version": "0.0.0",
"license": "MIT",
"private": true,
"scripts": {
"pulumi:up": "pulumi up"
},
"devDependencies": {
"@pulumi/pulumi": "^3.0.0",
"@pulumi/gcp": "^5.0.0"
}
}

View File

@ -17,11 +17,11 @@
"strict": true,
"baseUrl": ".",
"paths": {
"@factorio-sites/common-utils": ["libs/common-utils/src/index.ts"],
"@factorio-sites/database": ["libs/database/src/index.ts"],
"@factorio-sites/node-utils": ["libs/node-utils/src/index.ts"],
"@factorio-sites/common-utils": ["libs/common-utils/src/index.ts"],
"@factorio-sites/web-utils": ["libs/web-utils/src/index.ts"],
"@factorio-sites/types": ["libs/types/src/index.ts"]
"@factorio-sites/types": ["libs/types/src/index.ts"],
"@factorio-sites/web-utils": ["libs/web-utils/src/index.ts"]
}
},
"exclude": ["node_modules", "tmp"]

View File

@ -1,6 +1,64 @@
{
"version": 1,
"projects": {
"blueprint-image-function": {
"root": "apps/blueprint-image-function",
"sourceRoot": "apps/blueprint-image-function/src",
"projectType": "application",
"prefix": "blueprint-image-function",
"schematics": {},
"architect": {
"build": {
"builder": "@nrwl/node:build",
"options": {
"outputPath": "dist/apps/blueprint-image-function",
"main": "apps/blueprint-image-function/src/main.ts",
"tsConfig": "apps/blueprint-image-function/tsconfig.app.json",
"assets": [
{
"input": "apps/blueprints/prisma",
"glob": "schema.prisma",
"output": "./prisma"
}
],
"generatePackageJson": true
},
"configurations": {
"production": {
"optimization": true,
"extractLicenses": true,
"inspect": false,
"fileReplacements": [
{
"replace": "apps/blueprint-image-function/src/environments/environment.ts",
"with": "apps/blueprint-image-function/src/environments/environment.prod.ts"
}
]
}
}
},
"serve": {
"builder": "@nrwl/node:execute",
"options": {
"buildTarget": "blueprint-image-function:build"
}
},
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["apps/blueprint-image-function/**/*.ts"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "apps/blueprint-image-function/jest.config.js",
"passWithNoTests": true
}
}
},
"tags": []
},
"blueprints": {
"root": "apps/blueprints",
"sourceRoot": "apps/blueprints",
@ -39,9 +97,7 @@
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"apps/blueprints/**/*.{ts,tsx}"
]
"lintFilePatterns": ["apps/blueprints/**/*.{ts,tsx}"]
}
},
"test": {
@ -51,7 +107,8 @@
"passWithNoTests": true
}
}
}
},
"tags": []
},
"blueprints-e2e": {
"root": "apps/blueprints-e2e",
@ -74,64 +131,56 @@
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"apps/blueprints-e2e/**/*.{js,ts}"
]
"lintFilePatterns": ["apps/blueprints-e2e/**/*.{js,ts}"]
}
}
}
},
"tags": [],
"implicitDependencies": ["blueprints"]
},
"blueprint-image-function": {
"root": "apps/blueprint-image-function",
"sourceRoot": "apps/blueprint-image-function/src",
"projectType": "application",
"prefix": "blueprint-image-function",
"common-utils": {
"root": "libs/common-utils",
"sourceRoot": "libs/common-utils/src",
"projectType": "library",
"schematics": {},
"architect": {
"build": {
"builder": "@nrwl/node:build",
"options": {
"outputPath": "dist/apps/blueprint-image-function",
"main": "apps/blueprint-image-function/src/main.ts",
"tsConfig": "apps/blueprint-image-function/tsconfig.app.json",
"assets": []
},
"configurations": {
"production": {
"optimization": true,
"extractLicenses": true,
"inspect": false,
"fileReplacements": [
{
"replace": "apps/blueprint-image-function/src/environments/environment.ts",
"with": "apps/blueprint-image-function/src/environments/environment.prod.ts"
}
]
}
}
},
"serve": {
"builder": "@nrwl/node:execute",
"options": {
"buildTarget": "blueprint-image-function:build"
}
},
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"apps/blueprint-image-function/**/*.ts"
]
"lintFilePatterns": ["libs/common-utils/**/*.ts"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "apps/blueprint-image-function/jest.config.js",
"jestConfig": "libs/common-utils/jest.config.js",
"passWithNoTests": true
}
}
}
},
"tags": []
},
"database": {
"root": "libs/database",
"sourceRoot": "libs/database/src",
"projectType": "library",
"schematics": {},
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["libs/database/**/*.ts"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "libs/database/jest.config.js",
"passWithNoTests": true
}
}
},
"tags": []
},
"factorioprints-scraper": {
"root": "apps/factorioprints-scraper",
@ -146,9 +195,7 @@
"outputPath": "dist/apps/factorioprints-scraper",
"main": "apps/factorioprints-scraper/src/main.ts",
"tsConfig": "apps/factorioprints-scraper/tsconfig.app.json",
"assets": [
"apps/factorioprints-scraper/src/assets"
]
"assets": ["apps/factorioprints-scraper/src/assets"]
},
"configurations": {
"production": {
@ -173,9 +220,7 @@
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"apps/factorioprints-scraper/**/*.ts"
]
"lintFilePatterns": ["apps/factorioprints-scraper/**/*.ts"]
}
},
"test": {
@ -185,30 +230,8 @@
"passWithNoTests": true
}
}
}
},
"database": {
"root": "libs/database",
"sourceRoot": "libs/database/src",
"projectType": "library",
"schematics": {},
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"libs/database/**/*.ts"
]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "libs/database/jest.config.js",
"passWithNoTests": true
}
}
}
},
"tags": []
},
"node-utils": {
"root": "libs/node-utils",
@ -219,9 +242,7 @@
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"libs/node-utils/**/*.ts"
]
"lintFilePatterns": ["libs/node-utils/**/*.ts"]
}
},
"test": {
@ -231,30 +252,30 @@
"passWithNoTests": true
}
}
}
},
"tags": []
},
"common-utils": {
"root": "libs/common-utils",
"sourceRoot": "libs/common-utils/src",
"types": {
"root": "libs/types",
"sourceRoot": "libs/types/src",
"projectType": "library",
"schematics": {},
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"libs/common-utils/**/*.ts"
]
"lintFilePatterns": ["libs/types/**/*.ts"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"outputs": ["coverage/libs/types"],
"options": {
"jestConfig": "libs/common-utils/jest.config.js",
"jestConfig": "libs/types/jest.config.js",
"passWithNoTests": true
}
}
}
},
"tags": []
},
"web-utils": {
"root": "libs/web-utils",
@ -265,9 +286,7 @@
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"libs/web-utils/**/*.ts"
]
"lintFilePatterns": ["libs/web-utils/**/*.ts"]
}
},
"test": {
@ -277,49 +296,8 @@
"passWithNoTests": true
}
}
}
},
"types": {
"root": "libs/types",
"sourceRoot": "libs/types/src",
"projectType": "library",
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"libs/types/**/*.ts"
]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"outputs": [
"coverage/libs/types"
],
"options": {
"jestConfig": "libs/types/jest.config.js",
"passWithNoTests": true
}
}
}
},
"tags": []
}
},
"cli": {
"defaultCollection": "@nrwl/next"
},
"schematics": {
"@nrwl/react": {
"application": {
"babel": true
}
},
"@nrwl/next": {
"application": {
"style": "@emotion/styled",
"linter": "eslint"
}
}
},
"defaultProject": "blueprints"
}
}
}

13031
yarn.lock

File diff suppressed because it is too large Load Diff