1
0
mirror of https://github.com/barthuijgen/factorio-sites.git synced 2025-03-19 21:37:58 +02:00

Merge branch 'master' into dependabot/npm_and_yarn/react-multi-select-component-3.1.5

This commit is contained in:
Bart 2021-03-11 10:47:51 +01:00 committed by GitHub
commit 7882c1dd69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 105 additions and 58 deletions

24
.github/workflows/validate.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Nx validate CI
on:
push:
branches:
- master
- dev
pull_request:
branches:
- "*"
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: "14"
- run: yarn
- run: yarn db-gen
- run: yarn nx lint
- run: yarn nx test
- run: yarn nx build blueprints

View File

@ -1,10 +1,15 @@
import React from "react"; import React from "react";
import { render } from "@testing-library/react"; import { render } from "@testing-library/react";
import Index from "../src/pages/index"; import Index from "../src/pages/index";
import * as nextRouter from "next/router";
const useRouter = jest.spyOn(nextRouter, "useRouter");
describe("Index", () => { describe("Index", () => {
it("should render successfully", () => { it("should render successfully", () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
useRouter.mockImplementationOnce(() => ({ query: {} } as any));
const { baseElement } = render( const { baseElement } = render(
<Index totalItems={0} currentPage={0} totalPages={0} blueprints={[]} /> <Index totalItems={0} currentPage={0} totalPages={0} blueprints={[]} />
); );

View File

@ -4,7 +4,7 @@ import { FactorioIcon } from "./FactorioIcon";
class ImgTag extends Tag { class ImgTag extends Tag {
toReact() { toReact() {
const content = this.getContent(true); const content = this.getContent(true);
const img = (this.params as any).img; const img = (this.params as Record<string, string>).img;
const [type, item] = img.split("/"); const [type, item] = img.split("/");
if (type === "item") { if (type === "item") {
return ( return (
@ -21,7 +21,7 @@ class ImgTag extends Tag {
class ItemTag extends Tag { class ItemTag extends Tag {
toReact() { toReact() {
const content = this.getContent(true); const content = this.getContent(true);
const item = (this.params as any).item; const item = (this.params as Record<string, string>).item;
if (item) { if (item) {
return ( return (
<> <>
@ -36,7 +36,7 @@ class ItemTag extends Tag {
class VirtualSignalTag extends Tag { class VirtualSignalTag extends Tag {
toReact() { toReact() {
const signal = (this.params as any)["virtual-signal"]; const signal = (this.params as Record<string, string>)["virtual-signal"];
const content = this.getContent(true); const content = this.getContent(true);
if (signal) { if (signal) {
return ( return (
@ -50,9 +50,9 @@ class VirtualSignalTag extends Tag {
} }
} }
parser.registerTag("img", ImgTag as any); parser.registerTag("img", ImgTag as typeof Tag);
parser.registerTag("item", ItemTag as any); parser.registerTag("item", ItemTag as typeof Tag);
parser.registerTag("virtual-signal", VirtualSignalTag as any); parser.registerTag("virtual-signal", VirtualSignalTag as typeof Tag);
export const BBCode: React.FC<{ code: string }> = ({ code }) => { export const BBCode: React.FC<{ code: string }> = ({ code }) => {
return <>{parser.toReact(code)}</>; return <>{parser.toReact(code)}</>;

View File

@ -34,11 +34,13 @@ export const FullscreenImage: React.FC<FullscreenImageProps> = ({ alt, src, clos
<div <div
css={elementStyle} css={elementStyle}
onClick={(e) => { onClick={(e) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((e as any).target.nodeName.toUpperCase() !== "IMG") { if ((e as any).target.nodeName.toUpperCase() !== "IMG") {
close(); close();
} }
}} }}
onTouchEnd={(e) => { onTouchEnd={(e) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((e as any).target.nodeName.toUpperCase() !== "IMG") { if ((e as any).target.nodeName.toUpperCase() !== "IMG") {
close(); close();
} }

View File

@ -1,6 +1,7 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { createState, useState as useGlobalState } from "@hookstate/core"; import { createState, useState as useGlobalState } from "@hookstate/core";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const recordMapState = createState<Record<string, any>>({}); const recordMapState = createState<Record<string, any>>({});
export const useFetch = <T>( export const useFetch = <T>(
@ -31,6 +32,7 @@ export const useFetch = <T>(
}; };
fetchData(); fetchData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [url, skip]); }, [url, skip]);
return { loading, data: data ?? null, error }; return { loading, data: data ?? null, error };

View File

@ -220,7 +220,7 @@ export const Index: NextPage<IndexProps> = ({
{selected.data.blueprint_hash && typeof window !== "undefined" && ( {selected.data.blueprint_hash && typeof window !== "undefined" && (
<CopyButton <CopyButton
label="copy url" label="copy url"
content={`${window.location.origin}/api/string${selected.data.blueprint_hash}`} content={`${window.location.origin}/api/string/${selected.data.blueprint_hash}`}
marginBottom="0.5rem" marginBottom="0.5rem"
/> />
)} )}

View File

@ -1,6 +1,5 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { NextPage, NextPageContext } from "next"; import { NextPage, NextPageContext } from "next";
import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { searchBlueprintPages, init } from "@factorio-sites/database"; import { searchBlueprintPages, init } from "@factorio-sites/database";
import { BlueprintPage } from "@factorio-sites/types"; import { BlueprintPage } from "@factorio-sites/types";
@ -13,8 +12,6 @@ import { queryValueAsArray } from "../utils/query.utils";
import { useFbeData } from "../hooks/fbe.hook"; import { useFbeData } from "../hooks/fbe.hook";
import { import {
Box, Box,
Heading,
Flex,
Text, Text,
Input, Input,
InputGroup, InputGroup,

View File

@ -11,7 +11,7 @@ import {
Box, Box,
Text, Text,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { Formik, Field } from "formik"; import { Formik, Field, FieldProps } from "formik";
import { Panel } from "../components/Panel"; import { Panel } from "../components/Panel";
import { css } from "@emotion/react"; import { css } from "@emotion/react";
import { validateLoginForm } from "../utils/validate"; import { validateLoginForm } from "../utils/validate";
@ -56,11 +56,11 @@ export const Login: NextPage = () => {
{({ isSubmitting, handleSubmit, status }) => ( {({ isSubmitting, handleSubmit, status }) => (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<Field name="email"> <Field name="email">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl <FormControl
id="email" id="email"
isRequired isRequired
isInvalid={meta.touched && meta.error} isInvalid={meta.touched && !!meta.error}
css={FieldStyle} css={FieldStyle}
> >
<FormLabel>Email address</FormLabel> <FormLabel>Email address</FormLabel>
@ -71,11 +71,11 @@ export const Login: NextPage = () => {
</Field> </Field>
<Field name="password"> <Field name="password">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl <FormControl
id="password" id="password"
isRequired isRequired
isInvalid={meta.touched && meta.error} isInvalid={meta.touched && !!meta.error}
css={FieldStyle} css={FieldStyle}
> >
<FormLabel>Password</FormLabel> <FormLabel>Password</FormLabel>

View File

@ -11,7 +11,7 @@ import {
Text, Text,
Box, Box,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { Formik, Field } from "formik"; import { Formik, Field, FieldProps } from "formik";
import { Panel } from "../components/Panel"; import { Panel } from "../components/Panel";
import { css } from "@emotion/react"; import { css } from "@emotion/react";
import { validateRegisterForm } from "../utils/validate"; import { validateRegisterForm } from "../utils/validate";
@ -52,11 +52,11 @@ export const Register: NextPage = () => {
{({ isSubmitting, handleSubmit, errors }) => ( {({ isSubmitting, handleSubmit, errors }) => (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<Field name="email"> <Field name="email">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl <FormControl
id="email" id="email"
isRequired isRequired
isInvalid={meta.touched && meta.error} isInvalid={meta.touched && !!meta.error}
css={FieldStyle} css={FieldStyle}
> >
<FormLabel>Email address</FormLabel> <FormLabel>Email address</FormLabel>
@ -68,11 +68,11 @@ export const Register: NextPage = () => {
</Field> </Field>
<Field name="username"> <Field name="username">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl <FormControl
id="username" id="username"
isRequired isRequired
isInvalid={meta.touched && meta.error} isInvalid={meta.touched && !!meta.error}
css={FieldStyle} css={FieldStyle}
> >
<FormLabel>Username</FormLabel> <FormLabel>Username</FormLabel>
@ -83,11 +83,11 @@ export const Register: NextPage = () => {
</Field> </Field>
<Field name="password"> <Field name="password">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl <FormControl
id="password" id="password"
isRequired isRequired
isInvalid={meta.touched && meta.error} isInvalid={meta.touched && !!meta.error}
css={FieldStyle} css={FieldStyle}
> >
<FormLabel>Password</FormLabel> <FormLabel>Password</FormLabel>
@ -98,11 +98,11 @@ export const Register: NextPage = () => {
</Field> </Field>
<Field name="password_confirm"> <Field name="password_confirm">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl <FormControl
id="password_confirm" id="password_confirm"
isRequired isRequired
isInvalid={meta.touched && meta.error} isInvalid={meta.touched && !!meta.error}
css={FieldStyle} css={FieldStyle}
> >
<FormLabel>Repeat Password</FormLabel> <FormLabel>Repeat Password</FormLabel>

View File

@ -12,7 +12,7 @@ import {
Text, Text,
Textarea, Textarea,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { Formik, Field } from "formik"; import { Formik, Field, FieldProps } from "formik";
import { css } from "@emotion/react"; import { css } from "@emotion/react";
import { chakraResponsive } from "@factorio-sites/web-utils"; import { chakraResponsive } from "@factorio-sites/web-utils";
import { Panel } from "../../components/Panel"; import { Panel } from "../../components/Panel";
@ -62,11 +62,11 @@ export const UserBlueprintCreate: NextPage = () => {
<Panel title="Create new blueprint"> <Panel title="Create new blueprint">
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<Field name="title"> <Field name="title">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl <FormControl
id="title" id="title"
isRequired isRequired
isInvalid={meta.touched && meta.error} isInvalid={meta.touched && !!meta.error}
css={FieldStyle} css={FieldStyle}
> >
<FormLabel>Title</FormLabel> <FormLabel>Title</FormLabel>
@ -77,11 +77,11 @@ export const UserBlueprintCreate: NextPage = () => {
</Field> </Field>
<Field name="description"> <Field name="description">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl <FormControl
id="description" id="description"
isRequired isRequired
isInvalid={meta.touched && meta.error} isInvalid={meta.touched && !!meta.error}
css={FieldStyle} css={FieldStyle}
> >
<FormLabel>Description</FormLabel> <FormLabel>Description</FormLabel>
@ -92,8 +92,12 @@ export const UserBlueprintCreate: NextPage = () => {
</Field> </Field>
<Field name="tags"> <Field name="tags">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl id="tags" isInvalid={meta.touched && meta.error} css={FieldStyle}> <FormControl
id="tags"
isInvalid={meta.touched && !!meta.error}
css={FieldStyle}
>
<FormLabel>Tags (WIP)</FormLabel> <FormLabel>Tags (WIP)</FormLabel>
<Select <Select
options={[]} options={[]}
@ -106,11 +110,11 @@ export const UserBlueprintCreate: NextPage = () => {
</Field> </Field>
<Field name="string"> <Field name="string">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl <FormControl
id="string" id="string"
isRequired isRequired
isInvalid={meta.touched && meta.error} isInvalid={meta.touched && !!meta.error}
css={FieldStyle} css={FieldStyle}
> >
<FormLabel>Blueprint string</FormLabel> <FormLabel>Blueprint string</FormLabel>

View File

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { NextPage } from "next"; import { NextPage } from "next";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { Formik, Field } from "formik"; import { Formik, Field, FieldProps } from "formik";
import { css } from "@emotion/react"; import { css } from "@emotion/react";
import { import {
FormControl, FormControl,
@ -87,11 +87,11 @@ export const UserBlueprint: NextPage<UserBlueprintProps> = ({ blueprintPage, sel
<Panel title="Create new blueprint"> <Panel title="Create new blueprint">
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<Field name="title"> <Field name="title">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl <FormControl
id="title" id="title"
isRequired isRequired
isInvalid={meta.touched && meta.error} isInvalid={meta.touched && !!meta.error}
css={FieldStyle} css={FieldStyle}
> >
<FormLabel>Title</FormLabel> <FormLabel>Title</FormLabel>
@ -102,11 +102,11 @@ export const UserBlueprint: NextPage<UserBlueprintProps> = ({ blueprintPage, sel
</Field> </Field>
<Field name="description"> <Field name="description">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl <FormControl
id="description" id="description"
isRequired isRequired
isInvalid={meta.touched && meta.error} isInvalid={meta.touched && !!meta.error}
css={FieldStyle} css={FieldStyle}
> >
<FormLabel>Description</FormLabel> <FormLabel>Description</FormLabel>
@ -117,8 +117,12 @@ export const UserBlueprint: NextPage<UserBlueprintProps> = ({ blueprintPage, sel
</Field> </Field>
<Field name="tags"> <Field name="tags">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl id="tags" isInvalid={meta.touched && meta.error} css={FieldStyle}> <FormControl
id="tags"
isInvalid={meta.touched && !!meta.error}
css={FieldStyle}
>
<FormLabel>Tags (WIP)</FormLabel> <FormLabel>Tags (WIP)</FormLabel>
<Select <Select
options={[]} options={[]}
@ -131,11 +135,11 @@ export const UserBlueprint: NextPage<UserBlueprintProps> = ({ blueprintPage, sel
</Field> </Field>
<Field name="string"> <Field name="string">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl <FormControl
id="string" id="string"
isRequired isRequired
isInvalid={meta.touched && meta.error} isInvalid={meta.touched && !!meta.error}
css={FieldStyle} css={FieldStyle}
> >
<FormLabel>Blueprint string</FormLabel> <FormLabel>Blueprint string</FormLabel>

View File

@ -9,7 +9,7 @@ import {
SimpleGrid, SimpleGrid,
Button, Button,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { Formik, Field } from "formik"; import { Formik, Field, FieldProps } from "formik";
import { css } from "@emotion/react"; import { css } from "@emotion/react";
import { Panel } from "../../components/Panel"; import { Panel } from "../../components/Panel";
import { validateUserForm } from "../../utils/validate"; import { validateUserForm } from "../../utils/validate";
@ -50,11 +50,11 @@ export const UserEdit: NextPage = () => {
{({ isSubmitting, handleSubmit }) => ( {({ isSubmitting, handleSubmit }) => (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<Field name="email"> <Field name="email">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl <FormControl
id="email" id="email"
isRequired={!auth?.steam_id} isRequired={!auth?.steam_id}
isInvalid={meta.touched && meta.error} isInvalid={meta.touched && !!meta.error}
css={FieldStyle} css={FieldStyle}
> >
<FormLabel>Email address</FormLabel> <FormLabel>Email address</FormLabel>
@ -65,11 +65,11 @@ export const UserEdit: NextPage = () => {
</Field> </Field>
<Field name="username"> <Field name="username">
{({ field, meta }: any) => ( {({ field, meta }: FieldProps) => (
<FormControl <FormControl
id="username" id="username"
isRequired isRequired
isInvalid={meta.touched && meta.error} isInvalid={meta.touched && !!meta.error}
css={FieldStyle} css={FieldStyle}
> >
<FormLabel>Username</FormLabel> <FormLabel>Username</FormLabel>

View File

@ -27,10 +27,11 @@ export class ApiError extends Error {
} }
export const apiHandler = ( export const apiHandler = (
fn: (req: NextApiRequest, res: NextApiResponse, ctx: CustomContext) => Promise<any> fn: (req: NextApiRequest, res: NextApiResponse, ctx: CustomContext) => Promise<void>
) => async (req: NextApiRequest, res: NextApiResponse) => { ) => async (req: NextApiRequest, res: NextApiResponse) => {
await init(); await init();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const ip_header = (req.headers["x-forwarded-for"] || (req as any).ip) as string; const ip_header = (req.headers["x-forwarded-for"] || (req as any).ip) as string;
const ip = ip_header ? ip_header.split(",")[0] : ""; const ip = ip_header ? ip_header.split(",")[0] : "";
const useragent = req.headers["user-agent"] as string; const useragent = req.headers["user-agent"] as string;

View File

@ -1,3 +1,4 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const parseDatabaseError = (reason: any): Record<string, string> | null => { export const parseDatabaseError = (reason: any): Record<string, string> | null => {
const errors: Record<string, string> = {}; const errors: Record<string, string> = {};
console.log(reason); console.log(reason);

View File

@ -20,7 +20,7 @@ export const getSteamRedirectUrl = async (realm_url: string): Promise<string> =>
export const fetchSteamProfile = async (steam_id: string, api_key: string) => { export const fetchSteamProfile = async (steam_id: string, api_key: string) => {
try { try {
const response = await phin<{ response: { players: any } }>({ const response = await phin<{ response: { players?: Record<string, string>[] } }>({
url: `https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=${api_key}&steamids=${steam_id}`, url: `https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=${api_key}&steamids=${steam_id}`,
parse: "json", parse: "json",
}); });

View File

@ -3,6 +3,7 @@ import { getSessionByToken, init } from "@factorio-sites/database";
import { getSessionToken } from "@factorio-sites/node-utils"; import { getSessionToken } from "@factorio-sites/node-utils";
interface GetServerSidePropsReturn { interface GetServerSidePropsReturn {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
props: Record<string, any>; props: Record<string, any>;
} }

View File

@ -1,22 +1,24 @@
import { Storage } from "@google-cloud/storage"; import { Storage } from "@google-cloud/storage";
import { getEnvOrThrow } from "./env.util"; import { getEnv } from "./env.util";
const storage = new Storage(); const storage = new Storage();
const BLUEPRINT_BUCKET = storage.bucket(getEnvOrThrow("GCP_BLUEPRINT_STRINGS_BUCKET")); const BLUEPRINT_BUCKET = getEnv("GCP_BLUEPRINT_STRINGS_BUCKET");
const IMAGE_BUCKET = storage.bucket(getEnvOrThrow("GCP_BLUEPRINT_IMAGES_BUCKET")); const IMAGE_BUCKET = getEnv("GCP_BLUEPRINT_IMAGES_BUCKET");
/* /*
* BlueprintString * BlueprintString
*/ */
export async function getBlueprintStringByHash(hash: string): Promise<string | null> { export async function getBlueprintStringByHash(hash: string): Promise<string | null> {
const [buffer] = await BLUEPRINT_BUCKET.file(hash).download(); if (!BLUEPRINT_BUCKET) throw Error("Missing GCP_BLUEPRINT_STRINGS_BUCKET env variable");
const [buffer] = await storage.bucket(BLUEPRINT_BUCKET).file(hash).download();
return buffer ? buffer.toString() : null; return buffer ? buffer.toString() : null;
} }
export async function saveBlueprintString(hash: string, content: string) { export async function saveBlueprintString(hash: string, content: string) {
await BLUEPRINT_BUCKET.file(hash).save(content); if (!BLUEPRINT_BUCKET) throw Error("Missing GCP_BLUEPRINT_STRINGS_BUCKET env variable");
await storage.bucket(BLUEPRINT_BUCKET).file(hash).save(content);
} }
/* /*
@ -30,7 +32,8 @@ export async function saveBlueprintImage(
image: Buffer, image: Buffer,
type: sizeType = "original" type: sizeType = "original"
): Promise<void> { ): Promise<void> {
return IMAGE_BUCKET.file(`${type}/${hash}.webp`).save(image, { if (!IMAGE_BUCKET) throw Error("Missing GCP_BLUEPRINT_IMAGES_BUCKET env variable");
return storage.bucket(IMAGE_BUCKET).file(`${type}/${hash}.webp`).save(image, {
contentType: "image/webp", contentType: "image/webp",
}); });
} }
@ -39,7 +42,8 @@ export async function hasBlueprintImage(
hash: string, hash: string,
type: sizeType = "original" type: sizeType = "original"
): Promise<boolean> { ): Promise<boolean> {
const [result] = await IMAGE_BUCKET.file(`${type}/${hash}.webp`).exists(); if (!IMAGE_BUCKET) throw Error("Missing GCP_BLUEPRINT_IMAGES_BUCKET env variable");
const [result] = await storage.bucket(IMAGE_BUCKET).file(`${type}/${hash}.webp`).exists();
return result; return result;
} }
@ -47,6 +51,7 @@ export async function getBlueprintByImageHash(
hash: string, hash: string,
type: sizeType = "original" type: sizeType = "original"
): Promise<Buffer> { ): Promise<Buffer> {
const [result] = await IMAGE_BUCKET.file(`${type}/${hash}.webp`).download(); if (!IMAGE_BUCKET) throw Error("Missing GCP_BLUEPRINT_IMAGES_BUCKET env variable");
const [result] = await storage.bucket(IMAGE_BUCKET).file(`${type}/${hash}.webp`).download();
return result; return result;
} }

View File

@ -39,6 +39,7 @@ const promise = _init()
return result; return result;
}) })
.catch((reason) => { .catch((reason) => {
if (process.env.JEST_WORKER_ID) return;
console.log("Database failed to init!", reason); console.log("Database failed to init!", reason);
}); });