Skip to content

Commit

Permalink
Merge pull request #26 from GravityTwoG/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
GravityTwoG authored Jun 21, 2024
2 parents 22411d2 + 3a8bb70 commit fee9e59
Show file tree
Hide file tree
Showing 28 changed files with 339 additions and 317 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ Backend application with REST API is implemented by my friend https://github.com
- ⬇︎ download your/shared/public saves
- 🗃️ extract game state information from files (player level, play time etc)

https://github.com/GravityTwoG/cloud-saves/assets/55045953/03c87ddd-96d8-45b7-8f34-fe2894ddb9cf


## Development

Install Git.
Install Node.js from official website or using volta.sh, nvm, fnm.
Install Node.js from official website or use volta.sh, nvm, fnm.

Install dependencies using command:

Expand Down
82 changes: 24 additions & 58 deletions src/backend/StatesManager.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
import fs from "fs/promises";
import path from "path";
import os from "os";
import { session } from "electron";

import { Game, GameState } from "@/types";
import { GameAPI, GameFromServer } from "@/client/api/GameAPI";
import { Fetcher } from "@/client/api/Fetcher";

import { ValueExtractor } from "./game-state-parameters/ValueExtractor";

import { moveFolder } from "./fs/moveFolder";
import { downloadToFolder } from "./fs/downloadToFolder";
import { extractZIP } from "./fs/zip/extractZIP";
import { zipFolderOrFile } from "./fs/zip/zipFolderOrFile";
import { getTempFolderPath } from "./fs/getTempFolderPath";

export class StatesManager {
private readonly valueExtractor: ValueExtractor;
private readonly fetcher: Fetcher;
private readonly tempFolderPath: string;

constructor(valueExtractor: ValueExtractor) {
constructor(valueExtractor: ValueExtractor, fetcher: Fetcher) {
this.valueExtractor = valueExtractor;
this.fetcher = fetcher;

this.tempFolderPath = getTempFolderPath();
}

async uploadState(folder: {
Expand All @@ -35,17 +41,12 @@ export class StatesManager {
gameStateData,
);

const response = await fetch(
`${import.meta.env.VITE_API_BASE_URL}/game-saves`,
{
method: "POST",
headers: {
Cookie: await this.buildCookieHeader(),
},
body: formData,
await this.fetcher.post(`/game-saves`, {
headers: {
Cookie: await this.buildCookieHeader(),
},
);
await this.handleError(response);
body: formData,
});

return gameStateData;
}
Expand All @@ -54,24 +55,18 @@ export class StatesManager {
const gameStateData = await this.getState(gameState);
const formData = this.mapToGameStateData(gameState, gameStateData);

const response = await fetch(
`${import.meta.env.VITE_API_BASE_URL}/game-saves/${gameState.id}`,
{
method: "PATCH",
headers: {
Cookie: await this.buildCookieHeader(),
},
body: formData,
await this.fetcher.patch(`/game-saves/${gameState.id}`, {
headers: {
Cookie: await this.buildCookieHeader(),
},
);

await this.handleError(response);
body: formData,
});

return gameStateData;
}

async downloadState(gameState: GameState) {
const archivePath = this.getTempFolderPath();
const archivePath = this.tempFolderPath;
const filename = `${gameState.name}-archive.zip`;
const filePath = path.join(archivePath, filename);

Expand All @@ -88,11 +83,6 @@ export class StatesManager {
await fs.rm(filePath, { recursive: true, force: true });
}

private getTempFolderPath() {
const tempPath = os.tmpdir();
return path.join(tempPath, "cloud-saves");
}

private mapToGameStateData = (
gameState: {
gameId: string;
Expand Down Expand Up @@ -130,10 +120,9 @@ export class StatesManager {
localPath: string;
name: string;
}) {
const tempFolderPath = this.getTempFolderPath();
// copy all files to temp folder
const tempFolderBeforeUpload = path.join(
tempFolderPath,
this.tempFolderPath,
`/before-upload/${folder.name}-${Math.random()}`,
);
await fs.cp(folder.localPath, tempFolderBeforeUpload, { recursive: true });
Expand All @@ -155,18 +144,11 @@ export class StatesManager {
}

private async getGame(gameId: string): Promise<Game> {
const response = await fetch(
`${import.meta.env.VITE_API_BASE_URL}/games/${gameId}`,
{
headers: {
Cookie: await this.buildCookieHeader(),
},
const game = await this.fetcher.get<GameFromServer>(`/games/${gameId}`, {
headers: {
Cookie: await this.buildCookieHeader(),
},
);

await this.handleError(response);

const game = (await response.json()) as GameFromServer;
});

return GameAPI.mapGameFromServer(game);
}
Expand All @@ -175,20 +157,4 @@ export class StatesManager {
const cookies = await session.defaultSession.cookies.get({});
return cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join(";");
}

private async handleError(response: Response) {
if (!response.ok) {
const error = new Error(`${response.status}:${response.statusText}`);

try {
const json = await response.json();
error.message = json.message;
error.cause = json;
} catch (error) {
console.log("response.json() error", error);
}

throw error;
}
}
}
7 changes: 7 additions & 0 deletions src/backend/fs/getTempFolderPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import os from "os";
import path from "path";

export function getTempFolderPath() {
const tempPath = os.tmpdir();
return path.join(tempPath, "cloud-saves");
}
7 changes: 6 additions & 1 deletion src/backend/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Fetcher } from "@/client/api/Fetcher";

import { StatesManager } from "./StatesManager";
import { SyncManager } from "./SyncManager";

Expand All @@ -13,5 +15,8 @@ const converters = {
};

const valueExtractor = new ValueExtractor(converters);
export const statesManager = new StatesManager(valueExtractor);

const fetcher = new Fetcher({ baseURL: import.meta.env.VITE_API_BASE_URL });

export const statesManager = new StatesManager(valueExtractor, fetcher);
export const syncManager = new SyncManager(statesManager);
17 changes: 7 additions & 10 deletions src/client/api/mocks/AuthAPIMock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "../interfaces/IAuthAPI";
import { ApiError } from "../ApiError";
import { LocalStorage } from "../LocalStorage";
import { sleep } from "./utils";

const ls = new LocalStorage("users_mock");

Expand Down Expand Up @@ -46,14 +47,10 @@ export class AuthAPIMock implements IAuthAPI {
}
}

private sleep = (ms: number) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};

onUnauthorized = () => {};

register = async (credentials: RegisterDTO): Promise<User> => {
await this.sleep(500);
await sleep(500);
const user = {
...credentials,
role: UserRole.USER,
Expand Down Expand Up @@ -89,7 +86,7 @@ export class AuthAPIMock implements IAuthAPI {
};

login = async (credentials: LoginDTO): Promise<User> => {
await this.sleep(500);
await sleep(500);
try {
const users = ls.getItem<(User & { password: string })[]>("users");
const user = users.find(
Expand All @@ -116,7 +113,7 @@ export class AuthAPIMock implements IAuthAPI {
};

getCurrentUser = async (): Promise<User> => {
await this.sleep(500);
await sleep(500);
const isAuthenticated = ls.getItem<string>("isAuthenticated");

if (!isAuthenticated || isAuthenticated !== "true") {
Expand All @@ -128,20 +125,20 @@ export class AuthAPIMock implements IAuthAPI {
};

changePassword = async (credentials: ChangePasswordDTO): Promise<void> => {
await this.sleep(500);
await sleep(500);
console.log("changePassword", credentials);
throw new ApiError("Not implemented");
};

requestPasswordReset = async (email: string): Promise<void> => {
await this.sleep(500);
await sleep(500);
console.log("requestPasswordReset", email);

throw new ApiError("Not implemented");
};

resetPassword = async (credentials: ResetPasswordDTO): Promise<void> => {
await this.sleep(500);
await sleep(500);
console.log("resetPassword", credentials);

throw new ApiError("Not implemented");
Expand Down
2 changes: 2 additions & 0 deletions src/client/api/mocks/GameStateAPIMock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ApiError } from "../ApiError";
import { IGameAPI } from "../interfaces/IGameAPI";
import { ResourceRequest, ResourceResponse } from "../interfaces/common";
import { LocalStorage } from "../LocalStorage";
import { sleep } from "./utils";

const ls = new LocalStorage("game_states_");

Expand Down Expand Up @@ -94,6 +95,7 @@ export class GameStateAPIMock implements IGameStateAPI {
): Promise<ResourceResponse<GameState>> => {
console.log("getUserStates", query);
try {
await sleep(1000);
const states = ls.getItem<Record<string, GameState>>("states");
const statesArray: GameState[] = [];

Expand Down
3 changes: 3 additions & 0 deletions src/client/api/mocks/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const sleep = (ms: number) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
24 changes: 1 addition & 23 deletions src/client/app/components/MainLayout/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import { clsx } from "clsx";
import classes from "./main-layout.module.scss";

import { navLinks } from "@/client/config/navLinks";
import { usePersistedState } from "@/client/shared/hooks/usePersistedState";

import { Container } from "@/client/ui/atoms/Container";
import { GoBack } from "@/client/ui/atoms/GoBack";
import { Sidebar } from "../Sidebar";
import { Footer } from "../Footer";

Expand All @@ -15,31 +12,12 @@ export type MainLayoutProps = {
};

export const MainLayout = ({ children }: MainLayoutProps) => {
const [isSidebarExpanded, setIsSidebarExpanded] = usePersistedState<boolean>(
"isSidebarExpanded",
false,
);

return (
<div className={classes.MainLayout}>
<div className={classes.DragBar} />
<Sidebar
links={navLinks}
isExpanded={isSidebarExpanded}
setIsExpanded={setIsSidebarExpanded}
/>
<Sidebar links={navLinks} />

<main className={clsx("custom-scrollbar", classes.Main)}>
<div
className={clsx(classes.Header, {
[classes.Expanded]: isSidebarExpanded,
})}
>
<Container>
<GoBack className={classes.GoBack} />
</Container>
</div>

<div className={classes.Content}>{children}</div>
<Footer />
</main>
Expand Down
27 changes: 0 additions & 27 deletions src/client/app/components/MainLayout/main-layout.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,3 @@
user-select: none;
-webkit-app-region: drag;
}

.Header {
position: fixed;
top: 2rem;
left: 0;
z-index: 2;
display: flex;
justify-content: center;
width: calc(100% - 12px);
padding-left: 5rem;
transition: padding-left 0.3s ease;
pointer-events: none;

& > div {
flex: 1;
}

& .GoBack {
position: relative;
z-index: 999;
pointer-events: all;
}
}

.Expanded {
padding-left: 14rem;
}
Loading

0 comments on commit fee9e59

Please sign in to comment.