├── .prettierrc
├── packages
├── client
│ ├── src
│ │ ├── hooks
│ │ │ ├── index.ts
│ │ │ └── useApp.ts
│ │ ├── client.d.ts
│ │ ├── components
│ │ │ ├── molecules
│ │ │ │ ├── index.ts
│ │ │ │ └── Field
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── Field.tsx
│ │ │ ├── organisms
│ │ │ │ ├── index.ts
│ │ │ │ └── LoginForm
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── LoginForm.tsx
│ │ │ ├── atoms
│ │ │ │ ├── Button
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── Button.tsx
│ │ │ │ ├── Input
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── Input.tsx
│ │ │ │ ├── ErrorLine
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── ErrorLine.tsx
│ │ │ │ └── index.ts
│ │ │ ├── pages
│ │ │ │ ├── LoginPage
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── LoginPage.tsx
│ │ │ │ ├── Game.tsx
│ │ │ │ ├── Error.tsx
│ │ │ │ ├── Forum.tsx
│ │ │ │ ├── Main.tsx
│ │ │ │ ├── Profile.tsx
│ │ │ │ ├── SignIn.tsx
│ │ │ │ ├── SignUp.tsx
│ │ │ │ ├── ForumTopic.tsx
│ │ │ │ ├── LeaderBoard.tsx
│ │ │ │ └── index.ts
│ │ │ ├── index.ts
│ │ │ ├── App.tsx
│ │ │ └── App.test.tsx
│ │ ├── vite-env.d.ts
│ │ ├── index.css
│ │ ├── services
│ │ │ ├── api
│ │ │ │ └── index.ts
│ │ │ └── helpers
│ │ │ │ └── ErrorBoundary.tsx
│ │ ├── main.tsx
│ │ ├── store
│ │ │ └── configure.ts
│ │ └── router
│ │ │ └── index.tsx
│ ├── postcss.config.js
│ ├── tailwind.config.js
│ ├── tsconfig.node.json
│ ├── jest.config.js
│ ├── nginx.conf
│ ├── .gitignore
│ ├── index.html
│ ├── vite.config.ts
│ ├── tsconfig.json
│ ├── package.json
│ └── public
│ │ └── vite.svg
└── server
│ ├── jest.config.js
│ ├── tsconfig.prod.json
│ ├── __tests__
│ └── example.test.ts
│ ├── index.ts
│ ├── db.ts
│ ├── tsconfig.json
│ └── package.json
├── .gitignore
├── docs
├── image.png
├── scenario
│ ├── matrix.png
│ └── subsequence.png
├── README.md
├── workFlow.md
└── scenario.md
├── .editorconfig
├── .github
├── pull_request_template.md
└── workflows
│ └── checks.yml
├── init.js
├── .env.sample
├── lerna.json
├── lefthook.yml
├── .eslintrc.js
├── Dockerfile.client
├── Dockerfile.server
├── package.json
├── docker-compose.yml
└── README.md
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "width": 50
3 | }
4 |
--------------------------------------------------------------------------------
/packages/client/src/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./useApp";
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .env
3 | .idea
4 | .vscode
5 | dist
6 | tmp
--------------------------------------------------------------------------------
/packages/client/src/client.d.ts:
--------------------------------------------------------------------------------
1 | declare const __SERVER_PORT__: number;
2 |
--------------------------------------------------------------------------------
/packages/client/src/components/molecules/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./Field";
2 |
--------------------------------------------------------------------------------
/packages/client/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/docs/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzenxxx/cybreach/HEAD/docs/image.png
--------------------------------------------------------------------------------
/packages/client/src/components/organisms/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./LoginForm";
2 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*.{js,ts}]
4 | indent_style = space
5 | indent_size = 2
--------------------------------------------------------------------------------
/docs/scenario/matrix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzenxxx/cybreach/HEAD/docs/scenario/matrix.png
--------------------------------------------------------------------------------
/packages/client/src/components/atoms/Button/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Button } from "./Button";
2 |
--------------------------------------------------------------------------------
/packages/client/src/components/atoms/Input/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Input } from "./Input";
2 |
--------------------------------------------------------------------------------
/packages/client/src/components/molecules/Field/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Field } from "./Field";
2 |
--------------------------------------------------------------------------------
/packages/client/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/docs/scenario/subsequence.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrzenxxx/cybreach/HEAD/docs/scenario/subsequence.png
--------------------------------------------------------------------------------
/packages/client/src/components/atoms/ErrorLine/index.ts:
--------------------------------------------------------------------------------
1 | export { default as ErrorLine } from "./ErrorLine";
2 |
--------------------------------------------------------------------------------
/packages/client/src/components/pages/LoginPage/index.ts:
--------------------------------------------------------------------------------
1 | export { default as LoginPage } from "./LoginPage";
2 |
--------------------------------------------------------------------------------
/packages/client/src/components/organisms/LoginForm/index.ts:
--------------------------------------------------------------------------------
1 | export { default as LoginForm } from "./LoginForm";
2 |
--------------------------------------------------------------------------------
/packages/client/src/services/api/index.ts:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/blob/redux/src/services/api/index.js
2 |
--------------------------------------------------------------------------------
/packages/server/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: "ts-jest",
3 | testEnvironment: "node",
4 | };
5 |
--------------------------------------------------------------------------------
/packages/client/src/components/pages/Game.tsx:
--------------------------------------------------------------------------------
1 | export function Game(): JSX.Element {
2 | return
Game
;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/client/src/components/pages/Error.tsx:
--------------------------------------------------------------------------------
1 | export function Error(): JSX.Element {
2 | return 404
;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/client/src/components/pages/Forum.tsx:
--------------------------------------------------------------------------------
1 | export function Forum(): JSX.Element {
2 | return Forum
;
3 | }
4 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ### Какую задачу решаем
2 |
3 |
4 | ### Скриншоты/видяшка (если есть)
5 |
6 | ### TBD (если есть)
--------------------------------------------------------------------------------
/packages/client/src/components/pages/Main.tsx:
--------------------------------------------------------------------------------
1 | export default function Main(): JSX.Element {
2 | return Main
;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/client/src/components/pages/Profile.tsx:
--------------------------------------------------------------------------------
1 | export function Profile(): JSX.Element {
2 | return Profile
;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/client/src/components/pages/SignIn.tsx:
--------------------------------------------------------------------------------
1 | export function SignIn(): JSX.Element {
2 | return SignIn
;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/client/src/components/pages/SignUp.tsx:
--------------------------------------------------------------------------------
1 | export function SignUp(): JSX.Element {
2 | return SignUp
;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/client/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/packages/client/src/components/atoms/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./Button";
2 | export * from "./ErrorLine";
3 | export * from "./Input";
4 |
--------------------------------------------------------------------------------
/init.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 |
3 | fs.copyFileSync('.env.sample', '.env')
4 |
5 | fs.mkdirSync('tmp/pgdata', { recursive: true })
6 |
--------------------------------------------------------------------------------
/packages/client/src/components/pages/ForumTopic.tsx:
--------------------------------------------------------------------------------
1 | export function ForumTopic(): JSX.Element {
2 | return ForumTopic
;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/server/tsconfig.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig",
3 | "exclude": ["**/*.test.ts", "**/*.mock.ts", "**/__test__"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/client/src/components/pages/LeaderBoard.tsx:
--------------------------------------------------------------------------------
1 | export function LeaderBoard(): JSX.Element {
2 | return LeaderBoard
;
3 | }
4 |
--------------------------------------------------------------------------------
/.env.sample:
--------------------------------------------------------------------------------
1 | CLIENT_PORT=3000
2 | SERVER_PORT=3001
3 | POSTGRES_USER=postgres
4 | POSTGRES_PASSWORD=postgres
5 | POSTGRES_DB=postgres
6 | POSTGRES_PORT=5432
7 |
--------------------------------------------------------------------------------
/packages/client/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./atoms";
2 | export * from "./molecules";
3 | export * from "./organisms";
4 | export * from "./pages";
5 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Документация
2 |
3 | - [Сценарий игры](scenario.md) - Разработка сценария игры
4 | - [Flow работы с проектом](workFlow.md) - Описания порядка работы с проектом
--------------------------------------------------------------------------------
/packages/client/tailwind.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
3 | theme: {
4 | extend: {},
5 | },
6 | plugins: [],
7 | };
8 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json",
3 | "useNx": true,
4 | "npmClient": "yarn",
5 | "useWorkspaces": true,
6 | "version": "0.0.0"
7 | }
8 |
--------------------------------------------------------------------------------
/packages/client/src/components/App.tsx:
--------------------------------------------------------------------------------
1 | import { RouterProvider } from "react-router-dom";
2 | import { router } from "../router";
3 |
4 | function App() {
5 | return ;
6 | }
7 |
8 | export default App;
9 |
--------------------------------------------------------------------------------
/packages/client/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/lefthook.yml:
--------------------------------------------------------------------------------
1 | pre-commit:
2 | parallel: true
3 | commands:
4 | lint:
5 | glob: '*.{ts,tsx}'
6 | run: yarn eslint {staged_files}
7 | prettier:
8 | glob: '*.{ts,tsx,css}'
9 | run: yarn prettier -w {staged_files}
10 | stage_fixed: true
11 |
--------------------------------------------------------------------------------
/packages/client/jest.config.js:
--------------------------------------------------------------------------------
1 | import dotenv from "dotenv";
2 | dotenv.config();
3 |
4 | export default {
5 | preset: "ts-jest",
6 | testEnvironment: "jsdom",
7 | testMatch: ["/src/**/*.test.{ts,tsx}"],
8 | globals: {
9 | __SERVER_PORT__: process.env.SERVER_PORT,
10 | },
11 | };
12 |
--------------------------------------------------------------------------------
/packages/client/src/components/atoms/ErrorLine/ErrorLine.tsx:
--------------------------------------------------------------------------------
1 | interface ErrorLineProps {
2 | error: string | null;
3 | }
4 |
5 | export default function ErrorLine(props: ErrorLineProps) {
6 | const { error } = props;
7 |
8 | return {error};
9 | }
10 |
--------------------------------------------------------------------------------
/packages/client/src/components/pages/LoginPage/LoginPage.tsx:
--------------------------------------------------------------------------------
1 | import { LoginForm } from "@/components";
2 |
3 | export default function LoginPage() {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/packages/server/__tests__/example.test.ts:
--------------------------------------------------------------------------------
1 | const magic = "🪄";
2 |
3 | const cast = (spell: string, item: any) => {
4 | if (spell.startsWith(magic)) {
5 | return "🐷";
6 | }
7 |
8 | return item;
9 | };
10 |
11 | test("spell casting", () => {
12 | const result = cast(magic, "🐸");
13 | expect(result).toBe("🐷");
14 | });
15 |
--------------------------------------------------------------------------------
/packages/client/nginx.conf:
--------------------------------------------------------------------------------
1 | events {
2 | }
3 |
4 | http {
5 | include mime.types;
6 | server {
7 | listen 80;
8 | listen [::]:80;
9 |
10 | location / {
11 | root /app;
12 | try_files $uri /index.html;
13 | add_header Access-Control-Allow-Origin *;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/packages/client/src/components/App.test.tsx:
--------------------------------------------------------------------------------
1 | import App from "./App";
2 | import { render, screen } from "@testing-library/react";
3 |
4 | // @ts-ignore
5 | global.fetch = jest.fn(() =>
6 | Promise.resolve({ json: () => Promise.resolve("hey") })
7 | );
8 |
9 | test("Example test", async () => {
10 | render();
11 | expect(screen).toBeDefined();
12 | });
13 |
--------------------------------------------------------------------------------
/packages/client/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/packages/client/src/components/pages/index.ts:
--------------------------------------------------------------------------------
1 | export { LeaderBoard } from "./LeaderBoard";
2 | export { Forum } from "./Forum";
3 | export { ForumTopic } from "./ForumTopic";
4 | export { SignIn } from "./SignIn";
5 | export { SignUp } from "./SignUp";
6 | export { Profile } from "./Profile";
7 | export { Game } from "./Game";
8 | export { Error } from "./Error";
9 |
10 | export * from "./LoginPage";
11 |
--------------------------------------------------------------------------------
/packages/client/src/components/atoms/Button/Button.tsx:
--------------------------------------------------------------------------------
1 | import { ButtonHTMLAttributes } from "react";
2 |
3 | interface ButtonProps extends ButtonHTMLAttributes {
4 | label?: string;
5 | }
6 |
7 | export default function Button({ label, ...props }: ButtonProps) {
8 | return (
9 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/packages/client/src/hooks/useApp.ts:
--------------------------------------------------------------------------------
1 | import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
2 | import type { RootState, AppDispatch } from "@/store/configure";
3 |
4 | // Use throughout your app instead of plain `useDispatch` and `useSelector`
5 | const useAppDispatch: () => AppDispatch = useDispatch;
6 | const useAppSelector: TypedUseSelectorHook = useSelector;
7 |
8 | export { useAppDispatch, useAppSelector };
9 |
--------------------------------------------------------------------------------
/packages/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {TODO: Set title :) }
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es2020: true,
5 | node: true,
6 | },
7 | extends: [
8 | 'eslint:recommended',
9 | 'plugin:@typescript-eslint/recommended',
10 | 'prettier',
11 | ],
12 | parser: '@typescript-eslint/parser',
13 | parserOptions: {
14 | ecmaVersion: 11,
15 | },
16 | plugins: ['@typescript-eslint'],
17 | rules: {
18 | '@typescript-eslint/ban-ts-comment': 1,
19 | },
20 | }
21 |
--------------------------------------------------------------------------------
/packages/client/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "@/components/App";
4 | import "./index.css";
5 | import { Provider } from "react-redux";
6 | import store from "./store/configure";
7 |
8 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
9 |
10 |
11 |
12 |
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/packages/server/index.ts:
--------------------------------------------------------------------------------
1 | import dotenv from "dotenv";
2 | import cors from "cors";
3 | dotenv.config();
4 |
5 | import express from "express";
6 | import { createClientAndConnect } from "./db";
7 |
8 | const app = express();
9 | app.use(cors());
10 | const port = Number(process.env.SERVER_PORT) || 3001;
11 |
12 | createClientAndConnect();
13 |
14 | app.get("/", (_, res) => {
15 | res.json("👋 Howdy from the server :)");
16 | });
17 |
18 | app.listen(port, () => {
19 | console.log(` ➜ 🎸 Server is listening on port: ${port}`);
20 | });
21 |
--------------------------------------------------------------------------------
/packages/client/src/components/atoms/Input/Input.tsx:
--------------------------------------------------------------------------------
1 | import { InputHTMLAttributes, FormEventHandler } from "react";
2 |
3 | interface InputProps extends InputHTMLAttributes {
4 | handleInput?: FormEventHandler;
5 | }
6 |
7 | export default function Input(props: InputProps) {
8 | const { handleInput } = props;
9 |
10 | return (
11 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/packages/client/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import { resolve } from "path";
3 | import react from "@vitejs/plugin-react";
4 | import dotenv from "dotenv";
5 | dotenv.config();
6 |
7 | // https://vitejs.dev/config/
8 | export default defineConfig({
9 | server: {
10 | port: Number(process.env.CLIENT_PORT) || 3000,
11 | },
12 | define: {
13 | __SERVER_PORT__: process.env.SERVER_PORT,
14 | },
15 | plugins: [react()],
16 | resolve: {
17 | alias: {
18 | "@": resolve("./src"),
19 | },
20 | },
21 | });
22 |
--------------------------------------------------------------------------------
/packages/client/src/store/configure.ts:
--------------------------------------------------------------------------------
1 | // https://github.com/diegohaz/arc/wiki/Redux-modules
2 | import { configureStore } from "@reduxjs/toolkit";
3 |
4 | const store = configureStore({
5 | reducer: {},
6 | });
7 |
8 | // Infer the `RootState` and `AppDispatch` types from the store itself
9 | type RootState = ReturnType;
10 | // Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
11 | type AppDispatch = typeof store.dispatch;
12 |
13 | export default store;
14 |
15 | export type { RootState, AppDispatch };
16 |
--------------------------------------------------------------------------------
/.github/workflows/checks.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - main
8 | - dev
9 |
10 | jobs:
11 | eslint:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v3
15 | - uses: actions/setup-node@v3
16 | with:
17 | node-version: 16
18 | - name: Install main deps
19 | run: yarn
20 | - name: Initialize
21 | run: yarn lerna bootstrap
22 | - name: Lint
23 | run: yarn lint
24 | - name: Test
25 | run: yarn test
26 |
--------------------------------------------------------------------------------
/Dockerfile.client:
--------------------------------------------------------------------------------
1 | ARG NODE_VERSION=16
2 | ARG CLIENT_PORT=3001
3 |
4 | FROM node:$NODE_VERSION-buster as base
5 |
6 | WORKDIR /app
7 |
8 | FROM base as builder
9 |
10 | COPY package.json yarn.lock ./
11 | RUN yarn install --frozen-lockfile
12 |
13 | COPY . .
14 |
15 | RUN yarn lerna bootstrap
16 | RUN rm -rf /app/packages/client/dist/ && yarn build --scope=client
17 |
18 |
19 | FROM nginx:latest as production
20 | WORKDIR /app
21 |
22 | COPY --from=builder /app/packages/client/dist/ /app/
23 | COPY --from=builder /app/packages/client/nginx.conf /etc/nginx/nginx.conf
24 |
25 | EXPOSE $CLIENT_PORT
26 | CMD [ "nginx", "-g", "daemon off;" ]
27 |
--------------------------------------------------------------------------------
/packages/client/src/components/molecules/Field/Field.tsx:
--------------------------------------------------------------------------------
1 | import { ErrorLine } from "@/components";
2 | import { Input } from "@/components";
3 |
4 | interface FieldProps {
5 | label: string;
6 | type: string;
7 | name: string;
8 | error: string | null;
9 | }
10 |
11 | export default function Field(props: FieldProps) {
12 | const { label, type, name, error } = props;
13 |
14 | return (
15 |
16 |
19 |
20 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/Dockerfile.server:
--------------------------------------------------------------------------------
1 | ARG NODE_VERSION=16
2 | ARG SERVER_PORT=3001
3 |
4 | FROM node:$NODE_VERSION-buster as base
5 |
6 | WORKDIR /app
7 |
8 | FROM base as builder
9 |
10 | COPY package.json yarn.lock ./
11 | RUN yarn install --frozen-lockfile
12 |
13 | COPY . .
14 |
15 | RUN yarn lerna bootstrap
16 | RUN rm -rf /app/packages/server/dist/ && yarn build --scope=server
17 |
18 |
19 | FROM node:$NODE_VERSION-buster-slim as production
20 | WORKDIR /app
21 |
22 | COPY --from=builder /app/packages/server/dist/ /app/
23 | COPY --from=builder /app/packages/server/package.json /app/package.json
24 | RUN yarn install --production=true
25 |
26 | EXPOSE $SERVER_PORT
27 | CMD [ "node", "/app/index.js" ]
--------------------------------------------------------------------------------
/packages/client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx",
18 | "paths": {
19 | "@/*": ["./src/*"]
20 | }
21 | },
22 | "include": ["src"],
23 | "references": [{ "path": "./tsconfig.node.json" }]
24 | }
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client-server-template-with-vite",
3 | "private": true,
4 | "scripts": {
5 | "bootstrap": "yarn && node init.js && lerna clean && yarn && lerna bootstrap",
6 | "build": "lerna run build",
7 | "dev:client": "lerna run dev --scope=client",
8 | "dev:server": "lerna run dev --scope=server",
9 | "dev": "lerna run dev",
10 | "test": "lerna run test",
11 | "lint": "lerna run lint",
12 | "format": "lerna run format",
13 | "preview": "lerna run preview"
14 | },
15 | "license": "MIT",
16 | "workspaces": [
17 | "packages/*"
18 | ],
19 | "engines": {
20 | "node": ">=15"
21 | },
22 | "devDependencies": {
23 | "@evilmartians/lefthook": "^1.3.9",
24 | "lerna": "^5.4.3"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/server/db.ts:
--------------------------------------------------------------------------------
1 | import { Client } from "pg";
2 |
3 | const { POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, POSTGRES_PORT } =
4 | process.env;
5 |
6 | export const createClientAndConnect = async (): Promise => {
7 | try {
8 | const client = new Client({
9 | user: POSTGRES_USER,
10 | host: "localhost",
11 | database: POSTGRES_DB,
12 | password: String(POSTGRES_PASSWORD),
13 | port: Number(POSTGRES_PORT),
14 | });
15 |
16 | await client.connect();
17 |
18 | const res = await client.query("SELECT NOW()");
19 | console.log(" ➜ 🎸 Connected to the database at:", res?.rows?.[0].now);
20 | client.end();
21 |
22 | return client;
23 | } catch (e) {
24 | console.error(e);
25 | }
26 |
27 | return null;
28 | };
29 |
--------------------------------------------------------------------------------
/packages/server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "target": "es2019",
5 | "module": "commonjs",
6 | "moduleResolution": "node",
7 | "esModuleInterop": true,
8 | "sourceMap": true,
9 | "declaration": true,
10 | "declarationMap": true,
11 | "removeComments": true,
12 | "strict": true,
13 | "noImplicitAny": true,
14 | "noImplicitReturns": true,
15 | "noImplicitOverride": true,
16 | "noFallthroughCasesInSwitch": true,
17 | "noUnusedParameters": true,
18 | "noUnusedLocals": true,
19 | "forceConsistentCasingInFileNames": true,
20 | "importsNotUsedAsValues": "error",
21 | "lib": ["es2019", "esnext.asynciterable"],
22 | "types": ["node", "jest"],
23 | "baseUrl": ".",
24 | "paths": {
25 | "*": ["types/*"]
26 | },
27 | "outDir": "dist"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/client/src/services/helpers/ErrorBoundary.tsx:
--------------------------------------------------------------------------------
1 | import { Component, ErrorInfo, ReactNode } from "react";
2 |
3 | interface Props {
4 | fallback?: ReactNode;
5 | children?: ReactNode;
6 | }
7 |
8 | interface State {
9 | hasError: boolean;
10 | }
11 |
12 | export class ErrorBoundary extends Component {
13 | public state: State = {
14 | hasError: false,
15 | };
16 |
17 | static getDerivedStateFromError() {
18 | return { hasError: true };
19 | }
20 |
21 | public componentDidCatch(error: Error, info: ErrorInfo) {
22 | console.error(error, info.componentStack);
23 | }
24 |
25 | public render(): ReactNode {
26 | if (this.state.hasError) {
27 | return (
28 | this.props.fallback || (
29 | Что-то пошло не так
30 | )
31 | );
32 | }
33 |
34 | return this.props.children;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/packages/client/src/router/index.tsx:
--------------------------------------------------------------------------------
1 | import { createBrowserRouter } from "react-router-dom";
2 | import {
3 | Forum,
4 | ForumTopic,
5 | Game,
6 | LeaderBoard,
7 | Profile,
8 | LoginPage,
9 | SignUp,
10 | Error,
11 | } from "../components";
12 | import Main from "../components/pages/Main";
13 |
14 | export const router = createBrowserRouter([
15 | {
16 | path: "/",
17 | element: ,
18 | errorElement: ,
19 | },
20 | {
21 | path: "signin",
22 | element: ,
23 | },
24 | {
25 | path: "signup",
26 | element: ,
27 | },
28 | {
29 | path: "profile",
30 | element: ,
31 | },
32 | {
33 | path: "game",
34 | element: ,
35 | },
36 | {
37 | path: "leaderboard",
38 | element: ,
39 | },
40 | {
41 | path: "forum",
42 | element: ,
43 | },
44 | {
45 | path: "forum/:id",
46 | element: ,
47 | },
48 | ]);
49 |
--------------------------------------------------------------------------------
/docs/workFlow.md:
--------------------------------------------------------------------------------
1 | # Порядок работы с проектом
2 |
3 | - [1. GitHub](#1-github)
4 | - [2. Документация](#2-документация)
5 |
6 | ## 1. GitHub
7 |
8 | - Создаем новую ветку от dev ветки, делаем туда коммиты
9 | - Когда все готово создаем PR в основной репо
10 | - Проходим ревью
11 | - Вливаем в ветку dev (через squash & merge)
12 | - Оповещаем всех остальных (чтобы они подтянули изменения из dev и сразу порешали merge conflict)
13 | - В main вливаем только стабильную версию для деплоя
14 |
15 | Шаблон названия веток: CYB-{номер задачи в linear}
16 | Например:
17 | ```markdown
18 | CYB-23
19 | ```
20 |
21 | Коммиты именуем с кратким описанием действия коммита вначале
22 | Например:
23 | ```markdown
24 | add: logics game, fix: logout error, refactor: update score, remove: unused variable
25 | ```
26 |
27 | ## 2. Документация
28 |
29 | 1. Создаем файл для описания чего-либо в папке `docs`
30 | 2. Добавляем ссылку на созданный файл в `docs/README.md`
31 |
32 | ```markdown
33 | - [Название раздела](ИмяФайла.md) - Краткое описание
34 | ```
35 |
36 | 3. Пишем документацию. (Если необходимо добавить изображения создаем папку одноименную файлу)
--------------------------------------------------------------------------------
/packages/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "description": "",
5 | "scripts": {
6 | "build": "tsc --p ./tsconfig.prod.json",
7 | "preview": "node ./dist/index.js",
8 | "dev": "cross-env NODE_ENV=development nodemon index.ts",
9 | "lint": "eslint .",
10 | "format": "prettier --write .",
11 | "test": "jest ."
12 | },
13 | "dependencies": {
14 | "cors": "^2.8.5",
15 | "cross-env": "^7.0.3",
16 | "dotenv": "^16.0.2",
17 | "eslint-config-prettier": "^8.5.0",
18 | "express": "^4.18.1",
19 | "pg": "^8.8.0",
20 | "prettier": "^2.7.1"
21 | },
22 | "devDependencies": {
23 | "@types/cors": "^2.8.12",
24 | "@types/express": "^4.17.13",
25 | "@types/jest": "^28.1.8",
26 | "@types/pg": "^8.6.5",
27 | "@typescript-eslint/eslint-plugin": "^5.35.1",
28 | "@typescript-eslint/parser": "^5.35.1",
29 | "babel-jest": "^29.0.1",
30 | "eslint": "^8.23.0",
31 | "jest": "^28",
32 | "nodemon": "^2.0.19",
33 | "prettier": "^2.7.1",
34 | "ts-jest": "^28.0.8",
35 | "ts-node": "^10.9.1",
36 | "typescript": "^4.8.2"
37 | },
38 | "license": "MIT"
39 | }
40 |
--------------------------------------------------------------------------------
/packages/client/src/components/organisms/LoginForm/LoginForm.tsx:
--------------------------------------------------------------------------------
1 | import { Field } from "@/components";
2 | import { Button } from "@/components";
3 |
4 | interface FormProps {
5 | title?: string;
6 | isPending?: boolean;
7 | handleSubmit?: () => void;
8 | }
9 |
10 | export default function LoginForm(props: FormProps) {
11 | // mock
12 | const errors = {
13 | email: "wrong signature",
14 | password: "wrong signature",
15 | };
16 |
17 | return (
18 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.9"
2 |
3 | services:
4 | client:
5 | container_name: prakticum-client
6 | image: prakticum-client
7 | build:
8 | context: .
9 | dockerfile: Dockerfile.client
10 | args:
11 | CLIENT_PORT: ${CLIENT_PORT}
12 | restart: always
13 | ports:
14 | - "${CLIENT_PORT}:80"
15 | environment:
16 | - CLIENT_PORT=${CLIENT_PORT}
17 | - SERVER_PORT=${SERVER_PORT}
18 | server:
19 | container_name: prakticum-server
20 | image: prackicum-server
21 | build:
22 | context: .
23 | dockerfile: Dockerfile.server
24 | args:
25 | SERVER_PORT: ${SERVER_PORT}
26 | restart: always
27 | ports:
28 | - "${SERVER_PORT}:${SERVER_PORT}"
29 | environment:
30 | SERVER_PORT: ${SERVER_PORT}
31 |
32 | postgres:
33 | image: postgres:14
34 | ports:
35 | - "${POSTGRES_PORT}:${POSTGRES_PORT}"
36 | environment:
37 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
38 | POSTGRES_USER: ${POSTGRES_USER}
39 | POSTGRES_DB: ${POSTGRES_DB}
40 | volumes:
41 | - ./tmp/pgdata:/var/lib/postgresql/data
42 |
43 |
--------------------------------------------------------------------------------
/packages/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "tsc && vite build",
8 | "preview": "vite preview",
9 | "lint": "eslint .",
10 | "format": "prettier --write ."
11 | },
12 | "dependencies": {
13 | "@reduxjs/toolkit": "^1.9.7",
14 | "dotenv": "^16.0.2",
15 | "eslint-config-prettier": "^8.5.0",
16 | "prettier": "^2.7.1",
17 | "react": "^18.2.0",
18 | "react-dom": "^18.2.0",
19 | "react-redux": "^8.1.3",
20 | "react-router-dom": "^6.18.0"
21 | },
22 | "devDependencies": {
23 | "@testing-library/react": "^13.3.0",
24 | "@types/jest": "^28.1.8",
25 | "@types/react": "^18.0.17",
26 | "@types/react-dom": "^18.0.6",
27 | "@typescript-eslint/eslint-plugin": "^5.35.1",
28 | "@typescript-eslint/parser": "^5.35.1",
29 | "@vitejs/plugin-react": "^2.0.1",
30 | "autoprefixer": "^10.4.16",
31 | "eslint": "^8.23.0",
32 | "jest": "^28",
33 | "jest-environment-jsdom": "^29.0.1",
34 | "lefthook": "^1.3.9",
35 | "postcss": "^8.4.31",
36 | "prettier": "^2.7.1",
37 | "tailwindcss": "^3.3.5",
38 | "ts-jest": "^28.0.8",
39 | "typescript": "^4.8.2",
40 | "vite": "^3.0.7"
41 | },
42 | "license": "MIT"
43 | }
44 |
--------------------------------------------------------------------------------
/docs/scenario.md:
--------------------------------------------------------------------------------
1 | # Сценарий игры
2 |
3 | Игрок должен за отведенное время и количество шагов выбрать нужные комбинации цифр и символов в матрице 5х5 в порядке, указанном в последовательности. Выбор начинается с верхней строчки, второй кусок кода выбирается из столбца, в котором находился первый кусок выбранного кода, третий – из строчки, в которой находился второй выбранного кода и так далее. Выбранные куски кода исчезают из матрицы. Обратный отсчет начнется с момента запуска раунда. Количество последовательностей, необходимых для победы, время и ограничение ходов меняется со сложностью игры.
4 |
5 | Легая сложность:
6 | - 1 последователность из 4 кусков кода;
7 | - 4 шага;
8 | - 30 секунд раунда.
9 |
10 | Средняя сложность:
11 | - 2 последователности из 3х кусков кода;
12 | - 7 шагов;
13 | - 40 секунд раунда.
14 |
15 | Тяжелая сложность:
16 | - 3 последователности из 2х/3х/3х кусков кода;
17 | - 9 шагов;
18 | - 1 минута раунда.
19 |
20 | Пример матрицы
21 |
22 | 
23 |
24 | Пример последовательности
25 |
26 | 
27 |
28 | _TODO:_
29 |
30 | - [ ] Реализовать систему поощрений, в виде ачивок.
31 |
32 | _Хотелки:_
33 |
34 | - [ ] Реализовать анимации для переходов по матрице, удаления кусков кода, успешного или нет завершения последовательности.
35 | - [ ] Добавить возможность изменения размера матрицы для получения большего количества очков.
36 |
--------------------------------------------------------------------------------
/packages/client/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### Как запускать?
2 |
3 | 1. Убедитесь что у вас установлен `node` и `docker`
4 | 2. Выполните команду `yarn bootstrap` - это обязательный шаг, без него ничего работать не будет :)
5 | 3. Выполните команду `yarn dev`
6 | 3. Выполните команду `yarn dev --scope=client` чтобы запустить только клиент
7 | 4. Выполните команду `yarn dev --scope=server` чтобы запустить только server
8 |
9 |
10 | ### Как добавить зависимости?
11 | В этом проекте используется `monorepo` на основе [`lerna`](https://github.com/lerna/lerna)
12 |
13 | Чтобы добавить зависимость для клиента
14 | ```yarn lerna add {your_dep} --scope client```
15 |
16 | Для сервера
17 | ```yarn lerna add {your_dep} --scope server```
18 |
19 | И для клиента и для сервера
20 | ```yarn lerna add {your_dep}```
21 |
22 |
23 | Если вы хотите добавить dev зависимость, проделайте то же самое, но с флагом `dev`
24 | ```yarn lerna add {your_dep} --dev --scope server```
25 |
26 |
27 | ### Тесты
28 |
29 | Для клиента используется [`react-testing-library`](https://testing-library.com/docs/react-testing-library/intro/)
30 |
31 | ```yarn test```
32 |
33 | ### Линтинг
34 |
35 | ```yarn lint```
36 |
37 | ### Форматирование prettier
38 |
39 | ```yarn format```
40 |
41 | ### Production build
42 |
43 | ```yarn build```
44 |
45 | И чтобы посмотреть что получилось
46 |
47 |
48 | `yarn preview --scope client`
49 | `yarn preview --scope server`
50 |
51 | ## Хуки
52 | В проекте используется [lefthook](https://github.com/evilmartians/lefthook)
53 | Если очень-очень нужно пропустить проверки, используйте `--no-verify` (но не злоупотребляйте :)
54 |
55 | ## Ой, ничего не работает :(
56 |
57 | Откройте issue, я приду :)
58 |
59 | ## Автодеплой статики на vercel
60 | Зарегистрируйте аккаунт на [vercel](https://vercel.com/)
61 | Следуйте [инструкции](https://vitejs.dev/guide/static-deploy.html#vercel-for-git)
62 | В качестве `root directory` укажите `packages/client`
63 |
64 | Все ваши PR будут автоматически деплоиться на vercel. URL вам предоставит деплоящий бот
65 |
66 | ## Production окружение в докере
67 | Перед первым запуском выполните `node init.js`
68 |
69 |
70 | `docker compose up` - запустит три сервиса
71 | 1. nginx, раздающий клиентскую статику (client)
72 | 2. node, ваш сервер (server)
73 | 3. postgres, вашу базу данных (postgres)
74 |
75 | Если вам понадобится только один сервис, просто уточните какой в команде
76 | `docker compose up {sevice_name}`, например `docker compose up server`
77 |
78 | [Документация](docs/README.md)
--------------------------------------------------------------------------------