diff --git a/README.md b/README.md new file mode 100644 index 0000000..a20dd8f --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +# Games Shop +This is the repository for the free games shop hosted on [shop.iedsoftworks.com](https://shop.iedsoftworks.com). + +## Developers and local testers +To start a local variant of the site, you **need** Docker (with docker compose). + +### 1 Cloning the repo +Start with cloning the repository + +`git clone https://git.php.fail/neintonine/gamesshop.git` + +and entering the folder for it + +`cd gamesshop` + +### 2 Installing dependencies +Then you need to install/build the NPM and composer portion. +There are two ways of doing it. + +#### 2.1 Local NPM & Composer +If you have NodeJS and PHP8.3 with composer installed, you can simply run: + +`npm install && npm run build && composer install` + +#### 2.2 Docker +If you don't have those installed, and are unwilling to install them, you can use docker to install and build the required packages. + +JS: `docker run --rm --volume "$PWD":/app -w /app node npm install && npm run build` + +PHP: `docker run --rm --volume "$PWD":/app -w /app composer composer install --ignore-platform-req=ext-gd` + +### 3 Creating the environment file +To run the site, you need a valid environment file. +#### 3.1 Copy the example +Under the folder 'config', you can find a `.env.example`-file, which you can use to create your enviroment file. +Start of by coping the file, and rename it to `.env` + +`cp config/.env.example config/.env` + +The example already includes settings for the database and logs. + +#### 3.2 Adding your Discord-App +One thing, that the example doesn't contain is the discord app. Thats something you need to add. +Simply create an app in the [Discord Developer Portal](https://discord.com/developers/applications) and add the required values in the environment file + +### 4 Creating the Database +Now after ensuring you have the dependencies and a valid environment file, we can create the database. + +Again, if you have PHP installed you can just do: `php ./src/php/bin/doctrine.php orm:schema-tool:create` + +If you don't want to do this, just let docker to do it: + +```bash +docker run -v .:/app --rm -it php:8.3-cli php /app/src/php/bin/doctrine.php orm:schema-tool:create +``` + +### 5 Running the containers +Now finally, after creating the database, just go into the deploy folder (`cd deploy`) and start the compose file. + +You may see, that there are two file (`docker-compose.yaml` and `docker-compose-dev.yml`). As you may expect the second is for local development so probably the one you should use. +The first does effectively the same thing, but for the production environment. + +```bash +docker compose -f ./docker-compose-dev.yml up +``` + +Congratulations! The website should now boot up and after docker is done downloading, you should be able to use http://localhost:8080 to open the page. \ No newline at end of file diff --git a/src/js/pages/keys/index.ts b/src/js/pages/keys/index.ts index b22fecc..f1b3d0d 100644 --- a/src/js/pages/keys/index.ts +++ b/src/js/pages/keys/index.ts @@ -7,6 +7,8 @@ import {init as initImport} from "./import"; import {init as initTable} from "./table"; import {init as initUserLists} from "./userlists"; import {initShare} from "./share"; +import {init as initEditModal} from "./modals/edit"; +import {init as initDeleteModal} from "./modals/delete"; document.addEventListener('DOMContentLoaded', () => { const triggerTabList = document.querySelectorAll('#key-tab button') @@ -23,4 +25,7 @@ document.addEventListener('DOMContentLoaded', () => { initTable(); initUserLists(); initShare(); + + initEditModal(); + initDeleteModal(); }) \ No newline at end of file diff --git a/src/js/pages/keys/modals/delete.ts b/src/js/pages/keys/modals/delete.ts new file mode 100644 index 0000000..24286c8 --- /dev/null +++ b/src/js/pages/keys/modals/delete.ts @@ -0,0 +1,42 @@ +import {getCurrentlySelectedList} from "../userlists"; + +export function init() { + const modalElem = document.querySelector('#delete-list-modal'); + + if (!modalElem) { + return; + } + + modalElem.addEventListener('show.bs.modal', () => { + const listSelect = document.querySelector('#list-select'); + const text = listSelect?.selectedOptions[0].text ?? ''; + + const listnameElem = modalElem.querySelector('.list-name'); + if (!listnameElem) { + return; + } + listnameElem.textContent = text; + }) + + modalElem.querySelector('.js--yes') + ?.addEventListener('click', async () => { + const id = getCurrentlySelectedList(); + + const formData = new FormData(); + formData.append('id', id?.toString() ?? '-1'); + + const response = await fetch( + `/api/web/keys/list/delete`, + { + method: 'POST', + body: formData + } + ); + + if (!response.ok) { + throw new Error(response.statusText); + } + + window.location.reload(); + }) +} diff --git a/src/js/pages/keys/modals/edit.ts b/src/js/pages/keys/modals/edit.ts new file mode 100644 index 0000000..6c79cee --- /dev/null +++ b/src/js/pages/keys/modals/edit.ts @@ -0,0 +1,18 @@ +export function init() { + const modalElem = document.querySelector('#edit-list-modal'); + + if (!modalElem) { + return; + } + + modalElem.addEventListener('show.bs.modal', () => { + const listSelect = document.querySelector('#list-select'); + const text = listSelect?.selectedOptions[0].text ?? ''; + + const listnameElem = modalElem.querySelector('.list-name'); + if (!listnameElem) { + return; + } + listnameElem.textContent = text; + }) +} \ No newline at end of file diff --git a/src/php/Routing/Api/Web/DeleteKeyList.php b/src/php/Routing/Api/Web/DeleteKeyList.php new file mode 100644 index 0000000..db550cb --- /dev/null +++ b/src/php/Routing/Api/Web/DeleteKeyList.php @@ -0,0 +1,70 @@ +loginHandler->isLoggedIn()) { + throw new UnauthorizedException(); + } + + $user = $this->loginHandler->getCurrentUser(); + if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) { + throw new ForbiddenException(); + } + + $body = $request->getParsedBody(); + if (!array_key_exists('id', $body)) { + throw new BadRequestException(); + } + + $id = $body['id']; + $list = $this->entityManager->getRepository(GamesList::class)->findOneBy( + [ + 'id' => $id, + 'owner' => $user, + ] + ); + + if ($list === null) { + throw new BadRequestException(); + } + + $this->entityManager->remove($list); + + $keys = $this->entityManager->getRepository(Key::class)->findBy( + [ + 'list' => $list + ] + ); + foreach ($keys as $key) { + $this->entityManager->remove($key); + } + + $this->entityManager->flush(); + + return new Response(); + } +} \ No newline at end of file diff --git a/src/php/Routing/Api/Web/WebAPIRoutes.php b/src/php/Routing/Api/Web/WebAPIRoutes.php index 02f2033..66347f4 100644 --- a/src/php/Routing/Api/Web/WebAPIRoutes.php +++ b/src/php/Routing/Api/Web/WebAPIRoutes.php @@ -14,6 +14,7 @@ final class WebAPIRoutes $group->post('/keys/import/perform', ImportKeysRoute::class); $group->post('/keys/list/create', CreateKeyListRoute::class); + $group->post('/keys/list/delete',DeleteKeyList::class); $group->get('/share/search', SearchForUsers::class); $group->post('/share/add', AddUserToList::class); diff --git a/src/templates/pages/key-manager.php b/src/templates/pages/key-manager.php index 4182414..0248605 100644 --- a/src/templates/pages/key-manager.php +++ b/src/templates/pages/key-manager.php @@ -19,12 +19,17 @@ $this->layout('layout/main', [ 'resourceEntry' => 'keys' ]);
- +
+ + +
@@ -151,4 +156,56 @@ $this->layout('layout/main', [ 'resourceEntry' => 'keys' ]); + + + + + end() ?>