I rightly do not know what this all is
This commit is contained in:
parent
af6b2b752e
commit
287c1f67c5
78 changed files with 3484 additions and 3365 deletions
254
.gitignore
vendored
254
.gitignore
vendored
|
@ -1,127 +1,127 @@
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
# .gitignore
|
# .gitignore
|
||||||
# Bare Minimum Git
|
# Bare Minimum Git
|
||||||
# https://salferrarello.com/starter-gitignore-file/
|
# https://salferrarello.com/starter-gitignore-file/
|
||||||
# ver 20221125
|
# ver 20221125
|
||||||
#
|
#
|
||||||
# From the root of your project run
|
# From the root of your project run
|
||||||
# curl -O https://gist.githubusercontent.com/salcode/10017553/raw/.gitignore
|
# curl -O https://gist.githubusercontent.com/salcode/10017553/raw/.gitignore
|
||||||
# to download this file
|
# to download this file
|
||||||
#
|
#
|
||||||
# This file is tailored for a general web project, it
|
# This file is tailored for a general web project, it
|
||||||
# is NOT optimized for a WordPress project. See
|
# is NOT optimized for a WordPress project. See
|
||||||
# https://gist.github.com/salcode/b515f520d3f8207ecd04
|
# https://gist.github.com/salcode/b515f520d3f8207ecd04
|
||||||
# for a WordPress specific .gitignore
|
# for a WordPress specific .gitignore
|
||||||
#
|
#
|
||||||
# This file specifies intentionally untracked files to ignore
|
# This file specifies intentionally untracked files to ignore
|
||||||
# http://git-scm.com/docs/gitignore
|
# http://git-scm.com/docs/gitignore
|
||||||
#
|
#
|
||||||
# NOTES:
|
# NOTES:
|
||||||
# The purpose of gitignore files is to ensure that certain files not
|
# The purpose of gitignore files is to ensure that certain files not
|
||||||
# tracked by Git remain untracked.
|
# tracked by Git remain untracked.
|
||||||
#
|
#
|
||||||
# To ignore uncommitted changes in a file that is already tracked,
|
# To ignore uncommitted changes in a file that is already tracked,
|
||||||
# use `git update-index --assume-unchanged`.
|
# use `git update-index --assume-unchanged`.
|
||||||
#
|
#
|
||||||
# To stop tracking a file that is currently tracked,
|
# To stop tracking a file that is currently tracked,
|
||||||
# use `git rm --cached`
|
# use `git rm --cached`
|
||||||
#
|
#
|
||||||
# Change Log:
|
# Change Log:
|
||||||
# 20221125 ignore /dist directory
|
# 20221125 ignore /dist directory
|
||||||
# unignore /.git-blame-ignore-revs
|
# unignore /.git-blame-ignore-revs
|
||||||
# 20220720 ignore /build directory
|
# 20220720 ignore /build directory
|
||||||
# 20220128 unignore .nvmrc
|
# 20220128 unignore .nvmrc
|
||||||
# 20210211 unignore .env.example
|
# 20210211 unignore .env.example
|
||||||
# 20190705 ignore private/secret files
|
# 20190705 ignore private/secret files
|
||||||
# 20181206 remove trailing whitespaces
|
# 20181206 remove trailing whitespaces
|
||||||
# 20180714 unignore .phpcs.xml.dist
|
# 20180714 unignore .phpcs.xml.dist
|
||||||
# 20170502 unignore composer.lock
|
# 20170502 unignore composer.lock
|
||||||
# 20170502 ignore components loaded via Bower
|
# 20170502 ignore components loaded via Bower
|
||||||
# 20150326 ignore jekyll build directory `/_site`
|
# 20150326 ignore jekyll build directory `/_site`
|
||||||
# 20150324 Reorganized file to list ignores first and whitelisted last,
|
# 20150324 Reorganized file to list ignores first and whitelisted last,
|
||||||
# change WordPress .gitignore link to preferred gist,
|
# change WordPress .gitignore link to preferred gist,
|
||||||
# add curl line for quick installation
|
# add curl line for quick installation
|
||||||
# ignore composer files (vendor directory and lock file)
|
# ignore composer files (vendor directory and lock file)
|
||||||
# 20140606 Add .editorconfig as a tracked file
|
# 20140606 Add .editorconfig as a tracked file
|
||||||
# 20140418 remove explicit inclusion
|
# 20140418 remove explicit inclusion
|
||||||
# of readme.md (this is not an ignored file by default)
|
# of readme.md (this is not an ignored file by default)
|
||||||
# 20140407 Initially Published
|
# 20140407 Initially Published
|
||||||
#
|
#
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
|
|
||||||
# ignore all files starting with . or ~
|
# ignore all files starting with . or ~
|
||||||
.*
|
.*
|
||||||
~*
|
~*
|
||||||
|
|
||||||
# ignore node/grunt dependency directories
|
# ignore node/grunt dependency directories
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
# Ignore build directories.
|
# Ignore build directories.
|
||||||
/build
|
/build
|
||||||
/dist
|
/dist
|
||||||
|
|
||||||
# ignore composer vendor directory
|
# ignore composer vendor directory
|
||||||
/vendor
|
/vendor
|
||||||
|
|
||||||
# ignore components loaded via Bower
|
# ignore components loaded via Bower
|
||||||
/bower_components
|
/bower_components
|
||||||
|
|
||||||
# ignore jekyll build directory
|
# ignore jekyll build directory
|
||||||
/_site
|
/_site
|
||||||
|
|
||||||
# ignore OS generated files
|
# ignore OS generated files
|
||||||
ehthumbs.db
|
ehthumbs.db
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
# ignore Editor files
|
# ignore Editor files
|
||||||
*.sublime-project
|
*.sublime-project
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
*.komodoproject
|
*.komodoproject
|
||||||
|
|
||||||
# ignore log files and databases
|
# ignore log files and databases
|
||||||
*.log
|
*.log
|
||||||
*.sql
|
*.sql
|
||||||
*.sqlite
|
*.sqlite
|
||||||
|
|
||||||
# ignore compiled files
|
# ignore compiled files
|
||||||
*.com
|
*.com
|
||||||
*.class
|
*.class
|
||||||
*.dll
|
*.dll
|
||||||
*.exe
|
*.exe
|
||||||
*.o
|
*.o
|
||||||
*.so
|
*.so
|
||||||
|
|
||||||
# ignore packaged files
|
# ignore packaged files
|
||||||
*.7z
|
*.7z
|
||||||
*.dmg
|
*.dmg
|
||||||
*.gz
|
*.gz
|
||||||
*.iso
|
*.iso
|
||||||
*.jar
|
*.jar
|
||||||
*.rar
|
*.rar
|
||||||
*.tar
|
*.tar
|
||||||
*.zip
|
*.zip
|
||||||
|
|
||||||
# ignore private/secret files
|
# ignore private/secret files
|
||||||
*.der
|
*.der
|
||||||
*.key
|
*.key
|
||||||
*.pem
|
*.pem
|
||||||
|
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
# BEGIN Explictly Allowed Files (i.e. do NOT ignore these)
|
# BEGIN Explictly Allowed Files (i.e. do NOT ignore these)
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
|
|
||||||
# track these files, if they exist
|
# track these files, if they exist
|
||||||
!.editorconfig
|
!.editorconfig
|
||||||
!.env.example
|
!.env.example
|
||||||
!.git-blame-ignore-revs
|
!.git-blame-ignore-revs
|
||||||
!.gitignore
|
!.gitignore
|
||||||
!.nvmrc
|
!.nvmrc
|
||||||
!.phpcs.xml.dist
|
!.phpcs.xml.dist
|
||||||
|
|
||||||
# ------------
|
# ------------
|
||||||
# USER CHANGES
|
# USER CHANGES
|
||||||
# ------------
|
# ------------
|
||||||
|
|
||||||
# ignore public directory (we build it on server)
|
# ignore public directory (we build it on server)
|
||||||
/public
|
/public
|
||||||
/data/
|
/data/
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
"symfony/uid": "^7.1",
|
"symfony/uid": "^7.1",
|
||||||
"php-curl-class/php-curl-class": "^9.19",
|
"php-curl-class/php-curl-class": "^9.19",
|
||||||
"symfony/cache": "^7.1",
|
"symfony/cache": "^7.1",
|
||||||
"phpoffice/phpspreadsheet": "^2.1"
|
"phpoffice/phpspreadsheet": "^2.1",
|
||||||
|
"symfony/polyfill-iconv": "^1.31"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
452
composer.lock
generated
452
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,3 @@
|
||||||
@import "@fortawesome/fontawesome-free/scss/fontawesome";
|
@import "@fortawesome/fontawesome-free/scss/fontawesome";
|
||||||
@import "@fortawesome/fontawesome-free/scss/solid";
|
@import "@fortawesome/fontawesome-free/scss/solid";
|
||||||
@import "@fortawesome/fontawesome-free/scss/brands";
|
@import "@fortawesome/fontawesome-free/scss/brands";
|
|
@ -1,44 +1,44 @@
|
||||||
@import "bootstrap";
|
@import "bootstrap";
|
||||||
@import "fonts";
|
@import "fonts";
|
||||||
|
|
||||||
$main-container-max-width: 992px;
|
$main-container-max-width: 992px;
|
||||||
|
|
||||||
.cursor-pointer {
|
.cursor-pointer {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation-container,
|
.navigation-container,
|
||||||
main {
|
main {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: $main-container-max-width;
|
max-width: $main-container-max-width;
|
||||||
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ratio-1 {
|
.ratio-1 {
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
height: 3rem;
|
height: 3rem;
|
||||||
|
|
||||||
.avatar-login-method-icon {
|
.avatar-login-method-icon {
|
||||||
scale: 1.5;
|
scale: 1.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include media-breakpoint-down(lg) {
|
@include media-breakpoint-down(lg) {
|
||||||
.mode-switch {
|
.mode-switch {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding-top: 0.5rem;
|
padding-top: 0.5rem;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation-container {
|
.navigation-container {
|
||||||
.navbar-brand {
|
.navbar-brand {
|
||||||
margin: 0 auto !important;
|
margin: 0 auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
import "./theme";
|
import "./theme";
|
||||||
|
|
||||||
import "bootstrap/js/src/collapse";
|
import "bootstrap/js/src/collapse";
|
|
@ -1,39 +1,39 @@
|
||||||
(() => {
|
(() => {
|
||||||
function setTheme (mode = 'auto') {
|
function setTheme (mode = 'auto') {
|
||||||
const userMode = localStorage.getItem('bs-theme');
|
const userMode = localStorage.getItem('bs-theme');
|
||||||
const sysMode = window.matchMedia('(prefers-color-scheme: light)').matches;
|
const sysMode = window.matchMedia('(prefers-color-scheme: light)').matches;
|
||||||
const useSystem = mode === 'system' || (!userMode && mode === 'auto');
|
const useSystem = mode === 'system' || (!userMode && mode === 'auto');
|
||||||
|
|
||||||
const modeChosen = selectMode(mode);
|
const modeChosen = selectMode(mode);
|
||||||
|
|
||||||
if (useSystem) {
|
if (useSystem) {
|
||||||
localStorage.removeItem('bs-theme');
|
localStorage.removeItem('bs-theme');
|
||||||
} else {
|
} else {
|
||||||
localStorage.setItem('bs-theme', modeChosen);
|
localStorage.setItem('bs-theme', modeChosen);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.documentElement.setAttribute('data-bs-theme', useSystem ? (sysMode ? 'light' : 'dark') : modeChosen);
|
document.documentElement.setAttribute('data-bs-theme', useSystem ? (sysMode ? 'light' : 'dark') : modeChosen);
|
||||||
document.querySelectorAll('.mode-switch .btn').forEach(e => e.classList.remove('text-body'));
|
document.querySelectorAll('.mode-switch .btn').forEach(e => e.classList.remove('text-body'));
|
||||||
document.getElementById(modeChosen)?.classList.add('text-body');
|
document.getElementById(modeChosen)?.classList.add('text-body');
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectMode(mode: string): string {
|
function selectMode(mode: string): string {
|
||||||
const userMode = <string>localStorage.getItem('bs-theme');
|
const userMode = <string>localStorage.getItem('bs-theme');
|
||||||
const useSystem = mode === 'system' || (!userMode && mode === 'auto');
|
const useSystem = mode === 'system' || (!userMode && mode === 'auto');
|
||||||
|
|
||||||
if (useSystem) {
|
if (useSystem) {
|
||||||
return 'system';
|
return 'system';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode === 'dark' || mode === 'light') {
|
if (mode === 'dark' || mode === 'light') {
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
return userMode;
|
return userMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
setTheme();
|
setTheme();
|
||||||
document.querySelectorAll('.mode-switch .btn').forEach(e => e.addEventListener('click', () => setTheme(e.id)));
|
document.querySelectorAll('.mode-switch .btn').forEach(e => e.addEventListener('click', () => setTheme(e.id)));
|
||||||
window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', () => setTheme());
|
window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', () => setTheme());
|
||||||
});
|
});
|
||||||
})()
|
})()
|
|
@ -1,102 +1,102 @@
|
||||||
import '../../../css/common/index.scss';
|
import '../../../css/common/index.scss';
|
||||||
import 'datatables.net-bs5/css/dataTables.bootstrap5.css';
|
import 'datatables.net-bs5/css/dataTables.bootstrap5.css';
|
||||||
import '../../common/index';
|
import '../../common/index';
|
||||||
|
|
||||||
import DataTable from 'datatables.net-bs5';
|
import DataTable from 'datatables.net-bs5';
|
||||||
import {Modal} from "bootstrap";
|
import {Modal} from "bootstrap";
|
||||||
|
|
||||||
const DT_SERVERSIDE_PROCESSING_URL = '/api/dt/accounts';
|
const DT_SERVERSIDE_PROCESSING_URL = '/api/dt/accounts';
|
||||||
const TABLE = document.querySelector('#user-table');
|
const TABLE = document.querySelector('#user-table');
|
||||||
|
|
||||||
function displayEdit(data: any) {
|
function displayEdit(data: any) {
|
||||||
|
|
||||||
const modalElem = document.querySelector('#edit-modal');
|
const modalElem = document.querySelector('#edit-modal');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
modalElem.querySelector('.name-input').textContent = data.name;
|
modalElem.querySelector('.name-input').textContent = data.name;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
modalElem.querySelector('.login-method').textContent = data.loginMethod;
|
modalElem.querySelector('.login-method').textContent = data.loginMethod;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
modalElem.querySelector('.permission-editor').value = data.permissionIndex;
|
modalElem.querySelector('.permission-editor').value = data.permissionIndex;
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
modalElem?.querySelector('.js--save').dataset.userid = data.userid;
|
modalElem?.querySelector('.js--save').dataset.userid = data.userid;
|
||||||
|
|
||||||
Modal.getOrCreateInstance('#edit-modal').show();
|
Modal.getOrCreateInstance('#edit-modal').show();
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const dt = new DataTable('#user-table', {
|
const dt = new DataTable('#user-table', {
|
||||||
ajax: {
|
ajax: {
|
||||||
url: DT_SERVERSIDE_PROCESSING_URL,
|
url: DT_SERVERSIDE_PROCESSING_URL,
|
||||||
},
|
},
|
||||||
serverSide: true,
|
serverSide: true,
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
data: 'profilePictureUrl',
|
data: 'profilePictureUrl',
|
||||||
render(data, type) {
|
render(data, type) {
|
||||||
if (type !== 'display') {
|
if (type !== 'display') {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="${ data }" alt="Profile Picture" class="ratio-1 rounded-circle w-100" />`
|
return `<img src="${ data }" alt="Profile Picture" class="ratio-1 rounded-circle w-100" />`
|
||||||
},
|
},
|
||||||
orderable: false,
|
orderable: false,
|
||||||
searchable: false
|
searchable: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
data: 'name'
|
data: 'name'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Permission',
|
name: 'Permission',
|
||||||
data: 'permission'
|
data: 'permission'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Login-Method',
|
name: 'Login-Method',
|
||||||
data: 'loginMethod'
|
data: 'loginMethod'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
order: [ [1, 'asc'] ],
|
order: [ [1, 'asc'] ],
|
||||||
drawCallback: function (settings) {
|
drawCallback: function (settings) {
|
||||||
const api = new DataTable.Api(settings);
|
const api = new DataTable.Api(settings);
|
||||||
api.rows().every(function (row) {
|
api.rows().every(function (row) {
|
||||||
const node = this.node();
|
const node = this.node();
|
||||||
const data = this.data();
|
const data = this.data();
|
||||||
|
|
||||||
node.addEventListener('click', (e) => {
|
node.addEventListener('click', (e) => {
|
||||||
displayEdit(data);
|
displayEdit(data);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const button = <HTMLButtonElement>document.querySelector('#edit-modal .js--save');
|
const button = <HTMLButtonElement>document.querySelector('#edit-modal .js--save');
|
||||||
if (!button) {
|
if (!button) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.addEventListener('click', async (e) => {
|
button.addEventListener('click', async (e) => {
|
||||||
const permissionEditor = <HTMLSelectElement>document.querySelector('#edit-modal .permission-editor');
|
const permissionEditor = <HTMLSelectElement>document.querySelector('#edit-modal .permission-editor');
|
||||||
if (!permissionEditor) {
|
if (!permissionEditor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.set('permission', permissionEditor.value);
|
formData.set('permission', permissionEditor.value);
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`/api/web/users/${button.dataset.userid}`,
|
`/api/web/users/${button.dataset.userid}`,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData
|
body: formData
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(response.statusText);
|
throw new Error(response.statusText);
|
||||||
}
|
}
|
||||||
|
|
||||||
Modal.getOrCreateInstance('#edit-modal').hide();
|
Modal.getOrCreateInstance('#edit-modal').hide();
|
||||||
dt.draw();
|
dt.draw();
|
||||||
})
|
})
|
||||||
})
|
})
|
|
@ -1,2 +1,2 @@
|
||||||
import '../../css/common/index.scss';
|
import '../../css/common/index.scss';
|
||||||
import '../common/index';
|
import '../common/index';
|
|
@ -1,148 +1,148 @@
|
||||||
import {getCurrentlySelectedList} from "./userlists";
|
import {getCurrentlySelectedList} from "./userlists";
|
||||||
import {getTableAPI} from "./table";
|
import {getTableAPI} from "./table";
|
||||||
|
|
||||||
async function checkImportFile() {
|
async function checkImportFile() {
|
||||||
const fileInput = document.querySelector<HTMLInputElement>('#import');
|
const fileInput = document.querySelector<HTMLInputElement>('#import');
|
||||||
|
|
||||||
if (!fileInput) {
|
if (!fileInput) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fileInput.files || !fileInput.files[0]) {
|
if (!fileInput.files || !fileInput.files[0]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.set('file', fileInput.files[0]);
|
formData.set('file', fileInput.files[0]);
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`/api/web/keys/import/prepare`,
|
`/api/web/keys/import/prepare`,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData
|
body: formData
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(response.statusText);
|
throw new Error(response.statusText);
|
||||||
}
|
}
|
||||||
|
|
||||||
const jsonData = await response.json();
|
const jsonData = await response.json();
|
||||||
|
|
||||||
const container = document.getElementById('import-info-container');
|
const container = document.getElementById('import-info-container');
|
||||||
if (!container) {
|
if (!container) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
container.classList.remove('d-none');
|
container.classList.remove('d-none');
|
||||||
createTableContents(jsonData);
|
createTableContents(jsonData);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTableContents(data: {index: string, displayName: string, guessedAttribute: string}[]) {
|
function createTableContents(data: {index: string, displayName: string, guessedAttribute: string}[]) {
|
||||||
const table = document.querySelector<HTMLTableElement>('#import-attribute-table');
|
const table = document.querySelector<HTMLTableElement>('#import-attribute-table');
|
||||||
if (!table) {
|
if (!table) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tbody = table.querySelector<HTMLTableSectionElement>('tbody');
|
const tbody = table.querySelector<HTMLTableSectionElement>('tbody');
|
||||||
if (!tbody) {
|
if (!tbody) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let child of tbody.children) {
|
for (let child of tbody.children) {
|
||||||
child.remove();
|
child.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyAttributeMeta = document.querySelector<HTMLMetaElement>('meta[name="key-attributes"]')?.content;
|
const keyAttributeMeta = document.querySelector<HTMLMetaElement>('meta[name="key-attributes"]')?.content;
|
||||||
const attributes = JSON.parse(keyAttributeMeta ?? '');
|
const attributes = JSON.parse(keyAttributeMeta ?? '');
|
||||||
|
|
||||||
const select = document.createElement('select');
|
const select = document.createElement('select');
|
||||||
select.classList.add('form-select', 'w-100');
|
select.classList.add('form-select', 'w-100');
|
||||||
for (let attributeName in attributes) {
|
for (let attributeName in attributes) {
|
||||||
const attributeValue = attributes[attributeName];
|
const attributeValue = attributes[attributeName];
|
||||||
const option = document.createElement('option');
|
const option = document.createElement('option');
|
||||||
option.value = attributeValue;
|
option.value = attributeValue;
|
||||||
option.textContent = attributeName;
|
option.textContent = attributeName;
|
||||||
|
|
||||||
select.add(option);
|
select.add(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
data.forEach((datum) => {
|
data.forEach((datum) => {
|
||||||
const row = tbody.insertRow();
|
const row = tbody.insertRow();
|
||||||
const indexCell = row.insertCell();
|
const indexCell = row.insertCell();
|
||||||
indexCell.classList.add('index-cell');
|
indexCell.classList.add('index-cell');
|
||||||
indexCell.innerText = datum.index;
|
indexCell.innerText = datum.index;
|
||||||
|
|
||||||
const displayNameCEll = row.insertCell();
|
const displayNameCEll = row.insertCell();
|
||||||
displayNameCEll.classList.add('name-cell');
|
displayNameCEll.classList.add('name-cell');
|
||||||
displayNameCEll.innerText = datum.displayName;
|
displayNameCEll.innerText = datum.displayName;
|
||||||
|
|
||||||
const attributeCell = row.insertCell();
|
const attributeCell = row.insertCell();
|
||||||
attributeCell.classList.add('attribute-cell');
|
attributeCell.classList.add('attribute-cell');
|
||||||
const rowSelect = <HTMLSelectElement>select.cloneNode(true);
|
const rowSelect = <HTMLSelectElement>select.cloneNode(true);
|
||||||
rowSelect.value = datum.guessedAttribute;
|
rowSelect.value = datum.guessedAttribute;
|
||||||
attributeCell.appendChild(rowSelect);
|
attributeCell.appendChild(rowSelect);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doImport() {
|
async function doImport() {
|
||||||
const fileInput = document.querySelector<HTMLInputElement>('#import');
|
const fileInput = document.querySelector<HTMLInputElement>('#import');
|
||||||
|
|
||||||
if (!fileInput) {
|
if (!fileInput) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fileInput.files || !fileInput.files[0]) {
|
if (!fileInput.files || !fileInput.files[0]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.set('file', fileInput.files[0]);
|
formData.set('file', fileInput.files[0]);
|
||||||
|
|
||||||
const tbody = document.querySelector<HTMLTableSectionElement>('#import-attribute-table tbody');
|
const tbody = document.querySelector<HTMLTableSectionElement>('#import-attribute-table tbody');
|
||||||
if (!tbody) {
|
if (!tbody) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let row of tbody.querySelectorAll('tr')) {
|
for (let row of tbody.querySelectorAll('tr')) {
|
||||||
const columnIndex = row.querySelector('.index-cell')?.textContent;
|
const columnIndex = row.querySelector('.index-cell')?.textContent;
|
||||||
const attribute = row.querySelector<HTMLSelectElement>('.attribute-cell select')?.value ?? 'none';
|
const attribute = row.querySelector<HTMLSelectElement>('.attribute-cell select')?.value ?? 'none';
|
||||||
|
|
||||||
formData.set(`columns[${columnIndex}]`, attribute);
|
formData.set(`columns[${columnIndex}]`, attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
const listId = getCurrentlySelectedList() ?? 0;
|
const listId = getCurrentlySelectedList() ?? 0;
|
||||||
formData.set('listid', listId.toString());
|
formData.set('listid', listId.toString());
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`/api/web/keys/import/perform`,
|
`/api/web/keys/import/perform`,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData
|
body: formData
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(response.statusText);
|
throw new Error(response.statusText);
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = document.getElementById('import-info-container');
|
const container = document.getElementById('import-info-container');
|
||||||
if (!container) {
|
if (!container) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
container.classList.add('d-none');
|
container.classList.add('d-none');
|
||||||
fileInput.value = '';
|
fileInput.value = '';
|
||||||
|
|
||||||
getTableAPI().ajax.reload();
|
getTableAPI().ajax.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function init() {
|
export function init() {
|
||||||
|
|
||||||
|
|
||||||
const importButton = document.querySelector('.js--send-import');
|
const importButton = document.querySelector('.js--send-import');
|
||||||
importButton?.addEventListener('click', checkImportFile);
|
importButton?.addEventListener('click', checkImportFile);
|
||||||
|
|
||||||
const doImportButton = document.querySelector('.js--do-import');
|
const doImportButton = document.querySelector('.js--do-import');
|
||||||
doImportButton?.addEventListener('click', doImport);
|
doImportButton?.addEventListener('click', doImport);
|
||||||
}
|
}
|
|
@ -1,24 +1,26 @@
|
||||||
import '../../../css/common/index.scss';
|
import '../../../css/common/index.scss';
|
||||||
import '../../common/index';
|
import '../../common/index';
|
||||||
|
|
||||||
import { Tab } from 'bootstrap';
|
import { Tab } from 'bootstrap';
|
||||||
|
|
||||||
import {init as initImport} from "./import";
|
import {init as initImport} from "./import";
|
||||||
import {init as initTable} from "./table";
|
import {init as initTable} from "./table";
|
||||||
import {init as initUserLists} from "./userlists";
|
import {init as initUserLists} from "./userlists";
|
||||||
|
import {initShare} from "./share";
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
const triggerTabList = document.querySelectorAll('#key-tab button')
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
triggerTabList.forEach(triggerEl => {
|
const triggerTabList = document.querySelectorAll('#key-tab button')
|
||||||
const tabTrigger = new Tab(triggerEl)
|
triggerTabList.forEach(triggerEl => {
|
||||||
|
const tabTrigger = new Tab(triggerEl)
|
||||||
triggerEl.addEventListener('click', event => {
|
|
||||||
event.preventDefault()
|
triggerEl.addEventListener('click', event => {
|
||||||
tabTrigger.show()
|
event.preventDefault()
|
||||||
})
|
tabTrigger.show()
|
||||||
})
|
})
|
||||||
|
})
|
||||||
initImport();
|
|
||||||
initTable();
|
initImport();
|
||||||
initUserLists();
|
initTable();
|
||||||
|
initUserLists();
|
||||||
|
initShare();
|
||||||
})
|
})
|
24
src/js/pages/keys/share.ts
Normal file
24
src/js/pages/keys/share.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import DataTable from "datatables.net-bs5";
|
||||||
|
|
||||||
|
export function initShare() {
|
||||||
|
const table = document.querySelector<HTMLTableElement>('#shared-users-table');
|
||||||
|
if (!table) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new DataTable(table, {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: '',
|
||||||
|
data: 'icon',
|
||||||
|
searchable: false,
|
||||||
|
orderable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Name',
|
||||||
|
data: 'name',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
order: [ [1, "desc"] ]
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,108 +1,108 @@
|
||||||
import DataTable, {Api} from "datatables.net-bs5";
|
import DataTable, {Api} from "datatables.net-bs5";
|
||||||
import 'datatables.net-bs5/css/dataTables.bootstrap5.css';
|
import 'datatables.net-bs5/css/dataTables.bootstrap5.css';
|
||||||
import {Key} from "../types/entities";
|
import {Key} from "../types/entities";
|
||||||
import {getIconForKeyState, getKeyStateExplanation} from "../types/keyState";
|
import {getIconForKeyState, getKeyStateExplanation} from "../types/keyState";
|
||||||
|
|
||||||
import {Dropdown, Tooltip} from "bootstrap";
|
import {Dropdown, Tooltip} from "bootstrap";
|
||||||
import {getCurrentlySelectedList} from "./userlists";
|
import {getCurrentlySelectedList} from "./userlists";
|
||||||
|
|
||||||
const TABLE_AJAX_URL = '/api/dt/keys/provider';
|
const TABLE_AJAX_URL = '/api/dt/keys/provider';
|
||||||
|
|
||||||
let tableAPI: Api;
|
let tableAPI: Api;
|
||||||
|
|
||||||
export function getTableAPI(): Api {
|
export function getTableAPI(): Api {
|
||||||
return tableAPI;
|
return tableAPI;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getKeyDisplay(parent: HTMLElement, keys: Key[]) {
|
function getKeyDisplay(parent: HTMLElement, keys: Key[]) {
|
||||||
const header = document.createElement("h1");
|
const header = document.createElement("h1");
|
||||||
header.innerText = 'Keys';
|
header.innerText = 'Keys';
|
||||||
header.classList.add('h6');
|
header.classList.add('h6');
|
||||||
|
|
||||||
const table = document.createElement("table");
|
const table = document.createElement("table");
|
||||||
table.classList.add('table', 'table-striped', 'w-100');
|
table.classList.add('table', 'table-striped', 'w-100');
|
||||||
const body = table.createTBody();
|
const body = table.createTBody();
|
||||||
|
|
||||||
for (const {fromWhere, state, key, store, store_link} of keys) {
|
for (const {fromWhere, state, key, store, store_link} of keys) {
|
||||||
const row = body.insertRow();
|
const row = body.insertRow();
|
||||||
row.classList.add('cursor-pointer');
|
row.classList.add('cursor-pointer');
|
||||||
|
|
||||||
const stateCell = row.insertCell();
|
const stateCell = row.insertCell();
|
||||||
stateCell.classList.add('w-auto', 'align-content-center');
|
stateCell.classList.add('w-auto', 'align-content-center');
|
||||||
stateCell.innerHTML = `<a data-bs-toggle="tooltip" data-bs-title="${getKeyStateExplanation(state)}"><i class="fa-solid ${ getIconForKeyState(state) }"></i></a>`
|
stateCell.innerHTML = `<a data-bs-toggle="tooltip" data-bs-title="${getKeyStateExplanation(state)}"><i class="fa-solid ${ getIconForKeyState(state) }"></i></a>`
|
||||||
|
|
||||||
const anchor = stateCell.querySelector('a');
|
const anchor = stateCell.querySelector('a');
|
||||||
if (!anchor) {
|
if (!anchor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
new Tooltip(anchor);
|
new Tooltip(anchor);
|
||||||
|
|
||||||
row.insertCell().textContent = key;
|
row.insertCell().textContent = key;
|
||||||
row.insertCell().textContent = store === 'external' ? store_link : store;
|
row.insertCell().textContent = store === 'external' ? store_link : store;
|
||||||
row.insertCell().textContent = fromWhere;
|
row.insertCell().textContent = fromWhere;
|
||||||
|
|
||||||
row.addEventListener('click', () => {
|
row.addEventListener('click', () => {
|
||||||
console.log('Key options');
|
console.log('Key options');
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.appendChild(header);
|
parent.appendChild(header);
|
||||||
parent.appendChild(table);
|
parent.appendChild(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function init() {
|
export function init() {
|
||||||
const keyTable = document.querySelector<HTMLTableElement>('.key-table');
|
const keyTable = document.querySelector<HTMLTableElement>('.key-table');
|
||||||
if (!keyTable) {
|
if (!keyTable) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const table = tableAPI = new DataTable(keyTable, {
|
const table = tableAPI = new DataTable(keyTable, {
|
||||||
ajax: {
|
ajax: {
|
||||||
url: TABLE_AJAX_URL,
|
url: TABLE_AJAX_URL,
|
||||||
data: function (d) {
|
data: function (d) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
d.listid = getCurrentlySelectedList();
|
d.listid = getCurrentlySelectedList();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
processing: true,
|
processing: true,
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
data: 'gamePicture',
|
data: 'gamePicture',
|
||||||
searchable: false
|
searchable: false,
|
||||||
},
|
orderable: false
|
||||||
{
|
},
|
||||||
data: 'name',
|
{
|
||||||
},
|
data: 'name',
|
||||||
{
|
},
|
||||||
data: 'keysAmount',
|
{
|
||||||
searchable: false,
|
data: 'keysAmount',
|
||||||
},
|
searchable: false,
|
||||||
{
|
},
|
||||||
data: 'igdbState',
|
{
|
||||||
searchable: false,
|
data: 'igdbState',
|
||||||
}
|
searchable: false,
|
||||||
],
|
}
|
||||||
ordering: false,
|
],
|
||||||
order: [ [1, 'asc'] ],
|
order: [ [1, 'asc'] ],
|
||||||
createdRow(row: Node, data: any) {
|
createdRow(row: Node, data: any) {
|
||||||
const tableRow = <HTMLTableRowElement>row;
|
const tableRow = <HTMLTableRowElement>row;
|
||||||
tableRow.classList.add('cursor-pointer');
|
tableRow.classList.add('cursor-pointer');
|
||||||
|
|
||||||
tableRow.addEventListener('click', () => {
|
tableRow.addEventListener('click', () => {
|
||||||
const rowAPI = table.row(row);
|
const rowAPI = table.row(row);
|
||||||
if (rowAPI.child.isShown()) {
|
if (rowAPI.child.isShown()) {
|
||||||
rowAPI.child.hide();
|
rowAPI.child.hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const childRow = document.createElement('tr');
|
const childRow = document.createElement('tr');
|
||||||
const cell = childRow.insertCell();
|
const cell = childRow.insertCell();
|
||||||
cell.colSpan = row.childNodes.length;
|
cell.colSpan = row.childNodes.length;
|
||||||
|
|
||||||
getKeyDisplay(cell, data.keys);
|
getKeyDisplay(cell, data.keys);
|
||||||
|
|
||||||
rowAPI.child(childRow).show();
|
rowAPI.child(childRow).show();
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -1,71 +1,71 @@
|
||||||
import {Modal} from "bootstrap";
|
import {Modal} from "bootstrap";
|
||||||
import {getTableAPI} from "./table";
|
import {getTableAPI} from "./table";
|
||||||
|
|
||||||
export function getCurrentlySelectedList(): number|null {
|
export function getCurrentlySelectedList(): number|null {
|
||||||
const listSelect = document.querySelector<HTMLSelectElement>('#list-select');
|
const listSelect = document.querySelector<HTMLSelectElement>('#list-select');
|
||||||
if (!listSelect) {
|
if (!listSelect) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseInt(listSelect.value);
|
return parseInt(listSelect.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function init() {
|
export function init() {
|
||||||
const modal = document.querySelector('#create-list-modal');
|
const modal = document.querySelector('#create-list-modal');
|
||||||
if (!modal) {
|
if (!modal) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const modalObj = new Modal(modal);
|
const modalObj = new Modal(modal);
|
||||||
|
|
||||||
modal.addEventListener('show.bs.modal', (e) => {
|
modal.addEventListener('show.bs.modal', (e) => {
|
||||||
const input = modal.querySelector<HTMLInputElement>('#createListName');
|
const input = modal.querySelector<HTMLInputElement>('#createListName');
|
||||||
if (!input) {
|
if (!input) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.value = '';
|
input.value = '';
|
||||||
})
|
})
|
||||||
modal.querySelector('.js--create-list')?.addEventListener('click', async (e) => {
|
modal.querySelector('.js--create-list')?.addEventListener('click', async (e) => {
|
||||||
const input = modal.querySelector<HTMLInputElement>('#createListName');
|
const input = modal.querySelector<HTMLInputElement>('#createListName');
|
||||||
if (!input) {
|
if (!input) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newName = input.value;
|
const newName = input.value;
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('name', newName);
|
formData.append('name', newName);
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`/api/web/keys/list/create`,
|
`/api/web/keys/list/create`,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData
|
body: formData
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(response.statusText);
|
throw new Error(response.statusText);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
const listSelect = document.querySelector<HTMLSelectElement>('#list-select');
|
const listSelect = document.querySelector<HTMLSelectElement>('#list-select');
|
||||||
if (listSelect) {
|
if (listSelect) {
|
||||||
listSelect.addEventListener('change', (e) => {
|
listSelect.addEventListener('change', (e) => {
|
||||||
if (listSelect.value === '_create') {
|
if (listSelect.value === '_create') {
|
||||||
modalObj.show()
|
modalObj.show()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTableAPI().ajax.reload();
|
getTableAPI().ajax.reload();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const newButton = document.querySelector('.js--create-list-button');
|
const newButton = document.querySelector('.js--create-list-button');
|
||||||
if (newButton) {
|
if (newButton) {
|
||||||
newButton.addEventListener('click', () => modalObj.show())
|
newButton.addEventListener('click', () => modalObj.show())
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,25 +1,25 @@
|
||||||
import {KeyState} from "./keyState";
|
import {KeyState} from "./keyState";
|
||||||
|
|
||||||
enum Store {
|
enum Store {
|
||||||
STEAM = 'steam',
|
STEAM = 'steam',
|
||||||
GOG = 'gog',
|
GOG = 'gog',
|
||||||
EPICGAMES = 'epicgames',
|
EPICGAMES = 'epicgames',
|
||||||
ORIGIN = 'origin',
|
ORIGIN = 'origin',
|
||||||
UPLAY = 'uplay',
|
UPLAY = 'uplay',
|
||||||
BATTLENET = 'battlenet',
|
BATTLENET = 'battlenet',
|
||||||
EXTERNAL = 'external'
|
EXTERNAL = 'external'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Game = {
|
export type Game = {
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Key = {
|
export type Key = {
|
||||||
game?: Game,
|
game?: Game,
|
||||||
key: string,
|
key: string,
|
||||||
store: Store,
|
store: Store,
|
||||||
state: KeyState,
|
state: KeyState,
|
||||||
store_link: string|null,
|
store_link: string|null,
|
||||||
fromWhere: string|null,
|
fromWhere: string|null,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,35 +1,35 @@
|
||||||
export enum KeyState {
|
export enum KeyState {
|
||||||
AVAILABLE = 1,
|
AVAILABLE = 1,
|
||||||
UNKNOWN = 0,
|
UNKNOWN = 0,
|
||||||
RESERVED_FOR_GIFT = -1,
|
RESERVED_FOR_GIFT = -1,
|
||||||
CLAIMED = -10
|
CLAIMED = -10
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getIconForKeyState(keyState: KeyState): string {
|
export function getIconForKeyState(keyState: KeyState): string {
|
||||||
switch (keyState) {
|
switch (keyState) {
|
||||||
case KeyState.AVAILABLE:
|
case KeyState.AVAILABLE:
|
||||||
return "fa-check text-success";
|
return "fa-check text-success";
|
||||||
default:
|
default:
|
||||||
case KeyState.UNKNOWN:
|
case KeyState.UNKNOWN:
|
||||||
return 'fa-question text-info';
|
return 'fa-question text-info';
|
||||||
case KeyState.RESERVED_FOR_GIFT:
|
case KeyState.RESERVED_FOR_GIFT:
|
||||||
return 'fa-gift text-warning';
|
return 'fa-gift text-warning';
|
||||||
case KeyState.CLAIMED:
|
case KeyState.CLAIMED:
|
||||||
return 'fa-x text-danger';
|
return 'fa-x text-danger';
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getKeyStateExplanation(keyState: KeyState): string {
|
export function getKeyStateExplanation(keyState: KeyState): string {
|
||||||
switch (keyState) {
|
switch (keyState) {
|
||||||
case KeyState.AVAILABLE:
|
case KeyState.AVAILABLE:
|
||||||
return "This key is available";
|
return "This key is available";
|
||||||
default:
|
default:
|
||||||
case KeyState.UNKNOWN:
|
case KeyState.UNKNOWN:
|
||||||
return 'The state of this key is unknown';
|
return 'The state of this key is unknown';
|
||||||
case KeyState.RESERVED_FOR_GIFT:
|
case KeyState.RESERVED_FOR_GIFT:
|
||||||
return 'This key is reserved for a gift';
|
return 'This key is reserved for a gift';
|
||||||
case KeyState.CLAIMED:
|
case KeyState.CLAIMED:
|
||||||
return 'This key was claimed';
|
return 'This key was claimed';
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,79 +1,79 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Api;
|
namespace GamesShop\Api;
|
||||||
|
|
||||||
use Curl\Curl;
|
use Curl\Curl;
|
||||||
use Exception;
|
use Exception;
|
||||||
use GamesShop\Environment\EnvironmentHandler;
|
use GamesShop\Environment\EnvironmentHandler;
|
||||||
use GamesShop\Errors\ExtendedException;
|
use GamesShop\Errors\ExtendedException;
|
||||||
|
|
||||||
final class DiscordAPI
|
final class DiscordAPI
|
||||||
{
|
{
|
||||||
const string OAUTH_TOKEN_URL = "https://discord.com/api/oauth2/token";
|
const string OAUTH_TOKEN_URL = "https://discord.com/api/oauth2/token";
|
||||||
const string USER_ME_URL = 'https://discord.com/api/users/@me';
|
const string USER_ME_URL = 'https://discord.com/api/users/@me';
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly EnvironmentHandler $env
|
private readonly EnvironmentHandler $env
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array{id: string, global_name: string, avatar: string, discriminator: int}
|
* @return array{id: string, global_name: string, avatar: string, discriminator: int}
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function getUserFromCode(string $code, string $redirectUri): array {
|
public function getUserFromCode(string $code, string $redirectUri): array {
|
||||||
$discordEnv = $this->env->getDiscordEnvironment();
|
$discordEnv = $this->env->getDiscordEnvironment();
|
||||||
|
|
||||||
$curl = new Curl();
|
$curl = new Curl();
|
||||||
$curl->setHeader('Content-Type', 'application/x-www-form-urlencoded');
|
$curl->setHeader('Content-Type', 'application/x-www-form-urlencoded');
|
||||||
$curl->setBasicAuthentication($discordEnv->clientId, $discordEnv->clientSecret);
|
$curl->setBasicAuthentication($discordEnv->clientId, $discordEnv->clientSecret);
|
||||||
|
|
||||||
$data =[
|
$data =[
|
||||||
'grant_type' => 'authorization_code',
|
'grant_type' => 'authorization_code',
|
||||||
'code' => $code,
|
'code' => $code,
|
||||||
'redirect_uri' => $redirectUri
|
'redirect_uri' => $redirectUri
|
||||||
];
|
];
|
||||||
$curl->post(self::OAUTH_TOKEN_URL, $data);
|
$curl->post(self::OAUTH_TOKEN_URL, $data);
|
||||||
|
|
||||||
if ($curl->error) {
|
if ($curl->error) {
|
||||||
$curl->diagnose();
|
$curl->diagnose();
|
||||||
|
|
||||||
throw new ExtendedException($curl->errorMessage, [ 'response' => $curl->response, 'data' => $data ]);
|
throw new ExtendedException($curl->errorMessage, [ 'response' => $curl->response, 'data' => $data ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$accessToken = $curl->response->access_token;
|
$accessToken = $curl->response->access_token;
|
||||||
$tokenType = $curl->response->token_type;
|
$tokenType = $curl->response->token_type;
|
||||||
|
|
||||||
$curl = new Curl();
|
$curl = new Curl();
|
||||||
$curl->setHeader("authorization", "$tokenType $accessToken");
|
$curl->setHeader("authorization", "$tokenType $accessToken");
|
||||||
$curl->get(self::USER_ME_URL);
|
$curl->get(self::USER_ME_URL);
|
||||||
|
|
||||||
if ($curl->error) {
|
if ($curl->error) {
|
||||||
$curl->diagnose();
|
$curl->diagnose();
|
||||||
|
|
||||||
throw new ExtendedException($curl->errorMessage, [ 'response' => $curl->response, ]);
|
throw new ExtendedException($curl->errorMessage, [ 'response' => $curl->response, ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'id' => $curl->response->id,
|
'id' => $curl->response->id,
|
||||||
'global_name' => $curl->response->global_name,
|
'global_name' => $curl->response->global_name,
|
||||||
'avatar' => $curl->response->avatar,
|
'avatar' => $curl->response->avatar,
|
||||||
'discriminator' => (int) $curl->response->discriminator
|
'discriminator' => (int) $curl->response->discriminator
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAvatarURL(string $userId, string|int $avatarHash) {
|
public function getAvatarURL(string $userId, string|int $avatarHash) {
|
||||||
if (is_int($avatarHash)) {
|
if (is_int($avatarHash)) {
|
||||||
return "https://cdn.discordapp.com/embed/avatars/{$avatarHash}.png";
|
return "https://cdn.discordapp.com/embed/avatars/{$avatarHash}.png";
|
||||||
}
|
}
|
||||||
|
|
||||||
$extension = 'png';
|
$extension = 'png';
|
||||||
if (str_starts_with($avatarHash, 'a_')) {
|
if (str_starts_with($avatarHash, 'a_')) {
|
||||||
$extension = 'gif';
|
$extension = 'gif';
|
||||||
}
|
}
|
||||||
|
|
||||||
return "https://cdn.discordapp.com/avatars/{$userId}/{$avatarHash}.{$extension}";
|
return "https://cdn.discordapp.com/avatars/{$userId}/{$avatarHash}.{$extension}";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,39 +1,39 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop;
|
namespace GamesShop;
|
||||||
|
|
||||||
use League\Container\Container;
|
use League\Container\Container;
|
||||||
use League\Container\ReflectionContainer;
|
use League\Container\ReflectionContainer;
|
||||||
|
|
||||||
final class ContainerHandler
|
final class ContainerHandler
|
||||||
{
|
{
|
||||||
private static Container|null $instance = null;
|
private static Container|null $instance = null;
|
||||||
|
|
||||||
public static function getInstance(): Container
|
public static function getInstance(): Container
|
||||||
{
|
{
|
||||||
if (self::$instance === null) {
|
if (self::$instance === null) {
|
||||||
self::createInstance();
|
self::createInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$instance;
|
return self::$instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template RequestedType
|
* @template RequestedType
|
||||||
*
|
*
|
||||||
* @param class-string<RequestedType>|string $id
|
* @param class-string<RequestedType>|string $id
|
||||||
*
|
*
|
||||||
* @return RequestedType|mixed
|
* @return RequestedType|mixed
|
||||||
*/
|
*/
|
||||||
public static function get(string $id) {
|
public static function get(string $id) {
|
||||||
return self::getInstance()->get($id);
|
return self::getInstance()->get($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function createInstance()
|
private static function createInstance()
|
||||||
{
|
{
|
||||||
self::$instance = new Container();
|
self::$instance = new Container();
|
||||||
$reflectionContainer = new ReflectionContainer(true);
|
$reflectionContainer = new ReflectionContainer(true);
|
||||||
self::$instance->delegate($reflectionContainer);
|
self::$instance->delegate($reflectionContainer);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,35 +1,35 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop;
|
namespace GamesShop;
|
||||||
|
|
||||||
use Doctrine\DBAL\Connection;
|
use Doctrine\DBAL\Connection;
|
||||||
use Doctrine\DBAL\DriverManager;
|
use Doctrine\DBAL\DriverManager;
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\ORMSetup;
|
use Doctrine\ORM\ORMSetup;
|
||||||
use GamesShop\Environment\EnvironmentHandler;
|
use GamesShop\Environment\EnvironmentHandler;
|
||||||
|
|
||||||
final class DoctrineManager
|
final class DoctrineManager
|
||||||
{
|
{
|
||||||
public function setup() {
|
public function setup() {
|
||||||
$container = ContainerHandler::getInstance();
|
$container = ContainerHandler::getInstance();
|
||||||
|
|
||||||
$environmentHandler = $container->get(EnvironmentHandler::class);
|
$environmentHandler = $container->get(EnvironmentHandler::class);
|
||||||
|
|
||||||
$config = ORMSetup::createAttributeMetadataConfiguration(
|
$config = ORMSetup::createAttributeMetadataConfiguration(
|
||||||
paths: [ Paths::PHP_SOURCE_PATH . '/Entities' ],
|
paths: [ Paths::PHP_SOURCE_PATH . '/Entities' ],
|
||||||
isDevMode: !$environmentHandler->isProduction()
|
isDevMode: !$environmentHandler->isProduction()
|
||||||
);
|
);
|
||||||
|
|
||||||
$dbEnvironment = $environmentHandler->getDatabaseEnvironment();
|
$dbEnvironment = $environmentHandler->getDatabaseEnvironment();
|
||||||
$connection = DriverManager::getConnection($dbEnvironment->getDoctrineConfig());
|
$connection = DriverManager::getConnection($dbEnvironment->getDoctrineConfig());
|
||||||
|
|
||||||
$entityManager = new EntityManager($connection, $config);
|
$entityManager = new EntityManager($connection, $config);
|
||||||
|
|
||||||
$container->addShared(EntityManager::class, $entityManager);
|
$container->addShared(EntityManager::class, $entityManager);
|
||||||
$container->addShared(EntityManagerInterface::class, $entityManager);
|
$container->addShared(EntityManagerInterface::class, $entityManager);
|
||||||
$container->addShared(Connection::class, $connection);
|
$container->addShared(Connection::class, $connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,85 +1,85 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Entities\Account;
|
namespace GamesShop\Entities\Account;
|
||||||
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use GamesShop\Login\LoginMethod;
|
use GamesShop\Login\LoginMethod;
|
||||||
use GamesShop\Login\UserPermission;
|
use GamesShop\Login\UserPermission;
|
||||||
use Symfony\Component\Uid\Uuid;
|
use Symfony\Component\Uid\Uuid;
|
||||||
use Symfony\Component\Uid\UuidV4;
|
use Symfony\Component\Uid\UuidV4;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ORM\Table(name: "users")]
|
#[ORM\Table(name: "users")]
|
||||||
final class User
|
final class User
|
||||||
{
|
{
|
||||||
|
|
||||||
#[ORM\Id()]
|
#[ORM\Id()]
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
#[ORM\GeneratedValue]
|
#[ORM\GeneratedValue]
|
||||||
private int|null $id = null;
|
private int|null $id = null;
|
||||||
#[ORM\Column(type: 'integer', enumType: LoginMethod::class)]
|
#[ORM\Column(type: 'integer', enumType: LoginMethod::class)]
|
||||||
private LoginMethod $loginMethod;
|
private LoginMethod $loginMethod;
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
private string|null $foreignLoginId = null;
|
private string|null $foreignLoginId = null;
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
private string $name;
|
private string $name;
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
private string $profilePictureUrl;
|
private string $profilePictureUrl;
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
private UserPermission $permission;
|
private UserPermission $permission;
|
||||||
|
|
||||||
public function __construct(LoginMethod $loginMethod, ?string $foreignLoginId, string $name, string $profilePictureUrl, UserPermission $permission)
|
public function __construct(LoginMethod $loginMethod, ?string $foreignLoginId, string $name, string $profilePictureUrl, UserPermission $permission)
|
||||||
{
|
{
|
||||||
$this->loginMethod = $loginMethod;
|
$this->loginMethod = $loginMethod;
|
||||||
$this->foreignLoginId = $foreignLoginId;
|
$this->foreignLoginId = $foreignLoginId;
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
$this->profilePictureUrl = $profilePictureUrl;
|
$this->profilePictureUrl = $profilePictureUrl;
|
||||||
$this->permission = $permission;
|
$this->permission = $permission;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLoginMethod(): LoginMethod
|
public function getLoginMethod(): LoginMethod
|
||||||
{
|
{
|
||||||
return $this->loginMethod;
|
return $this->loginMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getForeignLoginId(): ?string
|
public function getForeignLoginId(): ?string
|
||||||
{
|
{
|
||||||
return $this->foreignLoginId;
|
return $this->foreignLoginId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getName(): string
|
public function getName(): string
|
||||||
{
|
{
|
||||||
return $this->name;
|
return $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getProfilePictureUrl(): string
|
public function getProfilePictureUrl(): string
|
||||||
{
|
{
|
||||||
return $this->profilePictureUrl;
|
return $this->profilePictureUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPermission(): UserPermission
|
public function getPermission(): UserPermission
|
||||||
{
|
{
|
||||||
return $this->permission;
|
return $this->permission;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setName(string $name): void
|
public function setName(string $name): void
|
||||||
{
|
{
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setProfilePictureUrl(string $profilePictureUrl): void
|
public function setProfilePictureUrl(string $profilePictureUrl): void
|
||||||
{
|
{
|
||||||
$this->profilePictureUrl = $profilePictureUrl;
|
$this->profilePictureUrl = $profilePictureUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPermission(UserPermission $permission): void
|
public function setPermission(UserPermission $permission): void
|
||||||
{
|
{
|
||||||
$this->permission = $permission;
|
$this->permission = $permission;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,36 +1,36 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Entities\Games;
|
namespace GamesShop\Entities\Games;
|
||||||
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ORM\Table(name: 'games')]
|
#[ORM\Table(name: 'games')]
|
||||||
class Game
|
class Game
|
||||||
{
|
{
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
|
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
|
||||||
#[ORM\GeneratedValue]
|
#[ORM\GeneratedValue]
|
||||||
private int|null $id;
|
private int|null $id;
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
private string $name;
|
private string $name;
|
||||||
|
|
||||||
public function getName(): string
|
public function getName(): string
|
||||||
{
|
{
|
||||||
return $this->name;
|
return $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $name
|
* @param string $name
|
||||||
*/
|
*/
|
||||||
public function __construct(string $name)
|
public function __construct(string $name)
|
||||||
{
|
{
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,96 +1,96 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Entities\Games;
|
namespace GamesShop\Entities\Games;
|
||||||
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use GamesShop\Entities\Account\User;
|
use GamesShop\Entities\Account\User;
|
||||||
use GamesShop\Entities\GamesList;
|
use GamesShop\Entities\GamesList;
|
||||||
use JsonSerializable;
|
use JsonSerializable;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ORM\Table(name: 'keys')]
|
#[ORM\Table(name: 'keys')]
|
||||||
final class Key implements JsonSerializable
|
final class Key implements JsonSerializable
|
||||||
{
|
{
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
|
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
|
||||||
#[ORM\GeneratedValue]
|
#[ORM\GeneratedValue]
|
||||||
private int|null $id;
|
private int|null $id;
|
||||||
#[ORM\ManyToOne]
|
#[ORM\ManyToOne]
|
||||||
private Game $game;
|
private Game $game;
|
||||||
#[ORM\ManyToOne]
|
#[ORM\ManyToOne]
|
||||||
private GamesList $list;
|
private GamesList $list;
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
private string $key;
|
private string $key;
|
||||||
#[ORM\Column(type: 'string', enumType: Store::class)]
|
#[ORM\Column(type: 'string', enumType: Store::class)]
|
||||||
private Store $store;
|
private Store $store;
|
||||||
#[ORM\Column(nullable: true)]
|
#[ORM\Column(nullable: true)]
|
||||||
private string|null $storeLink;
|
private string|null $storeLink;
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
private string|null $fromWhere;
|
private string|null $fromWhere;
|
||||||
#[ORM\Column(type: 'integer', enumType: KeyState::class)]
|
#[ORM\Column(type: 'integer', enumType: KeyState::class)]
|
||||||
private KeyState $state;
|
private KeyState $state;
|
||||||
|
|
||||||
public function __construct(Game $game, GamesList $list, string $key, Store $store, ?string $storeLink, ?string $fromWhere)
|
public function __construct(Game $game, GamesList $list, string $key, Store $store, ?string $storeLink, ?string $fromWhere)
|
||||||
{
|
{
|
||||||
$this->game = $game;
|
$this->game = $game;
|
||||||
$this->list = $list;
|
$this->list = $list;
|
||||||
$this->key = $key;
|
$this->key = $key;
|
||||||
$this->store = $store;
|
$this->store = $store;
|
||||||
$this->storeLink = $storeLink;
|
$this->storeLink = $storeLink;
|
||||||
$this->fromWhere = $fromWhere;
|
$this->fromWhere = $fromWhere;
|
||||||
$this->state = KeyState::AVAILABLE;
|
$this->state = KeyState::AVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getGame(): Game
|
public function getGame(): Game
|
||||||
{
|
{
|
||||||
return $this->game;
|
return $this->game;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getContributedUser(): User
|
public function getContributedUser(): User
|
||||||
{
|
{
|
||||||
return $this->contributedUser;
|
return $this->contributedUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getKey(): string
|
public function getKey(): string
|
||||||
{
|
{
|
||||||
return $this->key;
|
return $this->key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStore(): Store
|
public function getStore(): Store
|
||||||
{
|
{
|
||||||
return $this->store;
|
return $this->store;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStoreLink(): ?string
|
public function getStoreLink(): ?string
|
||||||
{
|
{
|
||||||
return $this->storeLink;
|
return $this->storeLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFromWhere(): ?string
|
public function getFromWhere(): ?string
|
||||||
{
|
{
|
||||||
return $this->fromWhere;
|
return $this->fromWhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getState(): KeyState
|
public function getState(): KeyState
|
||||||
{
|
{
|
||||||
return $this->state;
|
return $this->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): mixed
|
public function jsonSerialize(): mixed
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'id' => $this->id,
|
'id' => $this->id,
|
||||||
'key' => $this->key,
|
'key' => $this->key,
|
||||||
'store' => $this->store->value,
|
'store' => $this->store->value,
|
||||||
'store_link' => $this->storeLink,
|
'store_link' => $this->storeLink,
|
||||||
'from_where' => $this->fromWhere,
|
'from_where' => $this->fromWhere,
|
||||||
'state' => $this->state->value,
|
'state' => $this->state->value,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,22 +1,22 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Entities\Games;
|
namespace GamesShop\Entities\Games;
|
||||||
|
|
||||||
enum KeyAttribute: string
|
enum KeyAttribute: string
|
||||||
{
|
{
|
||||||
case NONE = 'none';
|
case NONE = 'none';
|
||||||
case GAME_NAME = "game_name";
|
case GAME_NAME = "game_name";
|
||||||
case KEY = "key";
|
case KEY = "key";
|
||||||
case STORE = 'store';
|
case STORE = 'store';
|
||||||
case FROM = 'from';
|
case FROM = 'from';
|
||||||
|
|
||||||
public static function casesAsAssociative(): array {
|
public static function casesAsAssociative(): array {
|
||||||
$result = [];
|
$result = [];
|
||||||
foreach (self::cases() as $case) {
|
foreach (self::cases() as $case) {
|
||||||
$result[$case->name] = $case->value;
|
$result[$case->name] = $case->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Entities\Games;
|
namespace GamesShop\Entities\Games;
|
||||||
|
|
||||||
enum KeyState: int
|
enum KeyState: int
|
||||||
{
|
{
|
||||||
case AVAILABLE = 1;
|
case AVAILABLE = 1;
|
||||||
case UNKNOWN = 0;
|
case UNKNOWN = 0;
|
||||||
case RESERVED_FOR_GIFT = -1;
|
case RESERVED_FOR_GIFT = -1;
|
||||||
case CLAIMED = -10;
|
case CLAIMED = -10;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Entities\Games;
|
namespace GamesShop\Entities\Games;
|
||||||
|
|
||||||
enum Store: string
|
enum Store: string
|
||||||
{
|
{
|
||||||
case STEAM = 'steam';
|
case STEAM = 'steam';
|
||||||
case GOG = 'gog';
|
case GOG = 'gog';
|
||||||
case EPICGAMES = 'epicgames';
|
case EPICGAMES = 'epicgames';
|
||||||
case ORIGIN = 'origin';
|
case ORIGIN = 'origin';
|
||||||
case UPLAY = 'uplay';
|
case UPLAY = 'uplay';
|
||||||
case BATTLENET = 'battlenet';
|
case BATTLENET = 'battlenet';
|
||||||
case EXTERNAL = 'external';
|
case EXTERNAL = 'external';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,62 +1,62 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Entities;
|
namespace GamesShop\Entities;
|
||||||
|
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use GamesShop\Entities\Account\User;
|
use GamesShop\Entities\Account\User;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ORM\Table(name: 'games_lists')]
|
#[ORM\Table(name: 'games_lists')]
|
||||||
final class GamesList
|
final class GamesList
|
||||||
{
|
{
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
|
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
|
||||||
#[ORM\GeneratedValue]
|
#[ORM\GeneratedValue]
|
||||||
private int|null $id;
|
private int|null $id;
|
||||||
|
|
||||||
#[ORM\ManyToOne]
|
#[ORM\ManyToOne]
|
||||||
private User $owner;
|
private User $owner;
|
||||||
|
|
||||||
#[ORM\Column(nullable: true)]
|
#[ORM\Column(nullable: true)]
|
||||||
private string|null $name;
|
private string|null $name;
|
||||||
|
|
||||||
#[ORM\JoinTable(name: 'games_list_claimer')]
|
#[ORM\JoinTable(name: 'games_list_claimer')]
|
||||||
#[ORM\JoinColumn(name: 'id', referencedColumnName: 'id')]
|
#[ORM\JoinColumn(name: 'id', referencedColumnName: 'id')]
|
||||||
#[ORM\ManyToMany(targetEntity: User::class)]
|
#[ORM\ManyToMany(targetEntity: User::class)]
|
||||||
private Collection $claimer;
|
private Collection $claimer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param User $owner
|
* @param User $owner
|
||||||
* @param string|null $name
|
* @param string|null $name
|
||||||
*/
|
*/
|
||||||
public function __construct(User $owner, ?string $name)
|
public function __construct(User $owner, ?string $name)
|
||||||
{
|
{
|
||||||
$this->owner = $owner;
|
$this->owner = $owner;
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getOwner(): User
|
public function getOwner(): User
|
||||||
{
|
{
|
||||||
return $this->owner;
|
return $this->owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getName(): ?string
|
public function getName(): ?string
|
||||||
{
|
{
|
||||||
return $this->name;
|
return $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getClaimer(): array
|
public function getClaimer(): array
|
||||||
{
|
{
|
||||||
return $this->claimer;
|
return $this->claimer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,34 +1,34 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Entities;
|
namespace GamesShop\Entities;
|
||||||
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ORM\Table(name: 'attributes')]
|
#[ORM\Table(name: 'attributes')]
|
||||||
final class SystemAttribute
|
final class SystemAttribute
|
||||||
{
|
{
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
private string $name;
|
private string $name;
|
||||||
|
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
private string $value;
|
private string $value;
|
||||||
|
|
||||||
public function __construct(string $name, string $value)
|
public function __construct(string $name, string $value)
|
||||||
{
|
{
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
$this->value = $value;
|
$this->value = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getName(): string
|
public function getName(): string
|
||||||
{
|
{
|
||||||
return $this->name;
|
return $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getValue(): string
|
public function getValue(): string
|
||||||
{
|
{
|
||||||
return $this->value;
|
return $this->value;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,21 +1,21 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Environment;
|
namespace GamesShop\Environment;
|
||||||
|
|
||||||
final readonly class DatabaseEnvironment
|
final readonly class DatabaseEnvironment
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $driver,
|
public string $driver,
|
||||||
public string $path
|
public string $path
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDoctrineConfig() {
|
public function getDoctrineConfig() {
|
||||||
return [
|
return [
|
||||||
'driver' => $this->driver,
|
'driver' => $this->driver,
|
||||||
'path' => $this->path
|
'path' => $this->path
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Environment;
|
namespace GamesShop\Environment;
|
||||||
|
|
||||||
final readonly class DiscordEnvironment
|
final readonly class DiscordEnvironment
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $clientId,
|
public string $clientId,
|
||||||
public string $clientSecret,
|
public string $clientSecret,
|
||||||
public string $loginUrl,
|
public string $loginUrl,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -1,40 +1,40 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Environment;
|
namespace GamesShop\Environment;
|
||||||
|
|
||||||
use DotenvVault\DotenvVault;
|
use DotenvVault\DotenvVault;
|
||||||
use GamesShop\Paths;
|
use GamesShop\Paths;
|
||||||
|
|
||||||
final class EnvironmentHandler
|
final class EnvironmentHandler
|
||||||
{
|
{
|
||||||
private const string ENVIRONMENT_PATH = Paths::ROOT_PATH . '/config';
|
private const string ENVIRONMENT_PATH = Paths::ROOT_PATH . '/config';
|
||||||
|
|
||||||
public function load() {
|
public function load() {
|
||||||
$dotEnv = DotenvVault::createImmutable(
|
$dotEnv = DotenvVault::createImmutable(
|
||||||
self::ENVIRONMENT_PATH
|
self::ENVIRONMENT_PATH
|
||||||
);
|
);
|
||||||
|
|
||||||
$dotEnv->safeLoad();
|
$dotEnv->safeLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDiscordEnvironment(): DiscordEnvironment {
|
public function getDiscordEnvironment(): DiscordEnvironment {
|
||||||
return new DiscordEnvironment(
|
return new DiscordEnvironment(
|
||||||
$_SERVER['DISCORD_CLIENT_ID'],
|
$_SERVER['DISCORD_CLIENT_ID'],
|
||||||
$_SERVER['DISCORD_CLIENT_SECRET'],
|
$_SERVER['DISCORD_CLIENT_SECRET'],
|
||||||
$_SERVER['DISCORD_CLIENT_LOGIN_URI'],
|
$_SERVER['DISCORD_CLIENT_LOGIN_URI'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDatabaseEnvironment(): DatabaseEnvironment
|
public function getDatabaseEnvironment(): DatabaseEnvironment
|
||||||
{
|
{
|
||||||
return new DatabaseEnvironment(
|
return new DatabaseEnvironment(
|
||||||
$_SERVER['DB_DRIVER'],
|
$_SERVER['DB_DRIVER'],
|
||||||
$_SERVER['DB_PATH']
|
$_SERVER['DB_PATH']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isProduction(): bool {
|
public function isProduction(): bool {
|
||||||
return $_SERVER['PRODUCTION'] === 'true';
|
return $_SERVER['PRODUCTION'] === 'true';
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,26 +1,26 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
namespace GamesShop\Errors;
|
namespace GamesShop\Errors;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use GamesShop\ContainerHandler;
|
use GamesShop\ContainerHandler;
|
||||||
use Whoops\Handler\HandlerInterface;
|
use Whoops\Handler\HandlerInterface;
|
||||||
use Whoops\Handler\PrettyPageHandler;
|
use Whoops\Handler\PrettyPageHandler;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
final class ExtendedException extends Exception
|
final class ExtendedException extends Exception
|
||||||
{
|
{
|
||||||
public function __construct(string $message = "", array $additionals = [], int $code = 0, ?Throwable $previous = null)
|
public function __construct(string $message = "", array $additionals = [], int $code = 0, ?Throwable $previous = null)
|
||||||
{
|
{
|
||||||
parent::__construct($message, $code, $previous);
|
parent::__construct($message, $code, $previous);
|
||||||
|
|
||||||
$handler = ContainerHandler::get(HandlerInterface::class);
|
$handler = ContainerHandler::get(HandlerInterface::class);
|
||||||
|
|
||||||
if (!($handler instanceof PrettyPageHandler)) {
|
if (!($handler instanceof PrettyPageHandler)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$handler->addDataTable("Additional Info", $additionals);
|
$handler->addDataTable("Additional Info", $additionals);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Whoops\Run;
|
use Whoops\Run;
|
||||||
|
|
||||||
final class WhoopsHandler
|
final class WhoopsHandler
|
||||||
{
|
{
|
||||||
}
|
}
|
|
@ -1,166 +1,166 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Importer;
|
namespace GamesShop\Importer;
|
||||||
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Exception;
|
use Exception;
|
||||||
use GamesShop\Entities\Account\User;
|
use GamesShop\Entities\Account\User;
|
||||||
use GamesShop\Entities\Games\Game;
|
use GamesShop\Entities\Games\Game;
|
||||||
use GamesShop\Entities\Games\Key;
|
use GamesShop\Entities\Games\Key;
|
||||||
use GamesShop\Entities\Games\KeyAttribute;
|
use GamesShop\Entities\Games\KeyAttribute;
|
||||||
use GamesShop\Entities\Games\Store;
|
use GamesShop\Entities\Games\Store;
|
||||||
use GamesShop\Entities\GamesList;
|
use GamesShop\Entities\GamesList;
|
||||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||||
|
|
||||||
final class GameImporter
|
final class GameImporter
|
||||||
{
|
{
|
||||||
private const HEADER_ROW_INDEX = 1;
|
private const HEADER_ROW_INDEX = 1;
|
||||||
|
|
||||||
private const STORE_ADDITIONAL_CASES = [
|
private const STORE_ADDITIONAL_CASES = [
|
||||||
'epic' => Store::EPICGAMES,
|
'epic' => Store::EPICGAMES,
|
||||||
'ea' => Store::ORIGIN,
|
'ea' => Store::ORIGIN,
|
||||||
'eaplay' => Store::ORIGIN,
|
'eaplay' => Store::ORIGIN,
|
||||||
'ubisoft' => Store::UPLAY,
|
'ubisoft' => Store::UPLAY,
|
||||||
'activision' => Store::BATTLENET
|
'activision' => Store::BATTLENET
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly EntityManagerInterface $entityManager,
|
private readonly EntityManagerInterface $entityManager,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $path
|
* @param string $path
|
||||||
* @return ImportColumnInterpretation[]
|
* @return ImportColumnInterpretation[]
|
||||||
*/
|
*/
|
||||||
public function interpret(string $path): array {
|
public function interpret(string $path): array {
|
||||||
$spreadsheet = IOFactory::load($path);
|
$spreadsheet = IOFactory::load($path);
|
||||||
|
|
||||||
$worksheet = $spreadsheet->getSheet(0);
|
$worksheet = $spreadsheet->getSheet(0);
|
||||||
$result = [];
|
$result = [];
|
||||||
foreach ($worksheet->getColumnIterator() as $column) {
|
foreach ($worksheet->getColumnIterator() as $column) {
|
||||||
$columnIndex = $column->getColumnIndex();
|
$columnIndex = $column->getColumnIndex();
|
||||||
|
|
||||||
$value = $worksheet->getCell(sprintf('%s%d', $columnIndex, self::HEADER_ROW_INDEX))->getValueString();
|
$value = $worksheet->getCell(sprintf('%s%d', $columnIndex, self::HEADER_ROW_INDEX))->getValueString();
|
||||||
|
|
||||||
if (empty(trim($value))) {
|
if (empty(trim($value))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$guessedAttribute = $this->guessAttribute($value);
|
$guessedAttribute = $this->guessAttribute($value);
|
||||||
$result[] = new ImportColumnInterpretation(
|
$result[] = new ImportColumnInterpretation(
|
||||||
$columnIndex,
|
$columnIndex,
|
||||||
$value,
|
$value,
|
||||||
$guessedAttribute
|
$guessedAttribute
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function guessAttribute(string $value): KeyAttribute|null {
|
private function guessAttribute(string $value): KeyAttribute|null {
|
||||||
$value = trim($value);
|
$value = trim($value);
|
||||||
$value = strtolower($value);
|
$value = strtolower($value);
|
||||||
$value = str_replace(' ', '_', $value);
|
$value = str_replace(' ', '_', $value);
|
||||||
|
|
||||||
$attribute = match($value) {
|
$attribute = match($value) {
|
||||||
'key' => KeyAttribute::KEY,
|
'key' => KeyAttribute::KEY,
|
||||||
'name', 'game_name', 'game' => KeyAttribute::GAME_NAME,
|
'name', 'game_name', 'game' => KeyAttribute::GAME_NAME,
|
||||||
'from' => KeyAttribute::FROM,
|
'from' => KeyAttribute::FROM,
|
||||||
'store', 'for' => KeyAttribute::STORE,
|
'store', 'for' => KeyAttribute::STORE,
|
||||||
default => null
|
default => null
|
||||||
};
|
};
|
||||||
|
|
||||||
return $attribute;
|
return $attribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string[] $columnDefinitions
|
* @param string[] $columnDefinitions
|
||||||
*/
|
*/
|
||||||
public function import(string $path, array $columnDefinitions, GamesList $list): array {
|
public function import(string $path, array $columnDefinitions, GamesList $list): array {
|
||||||
$spreadsheet = IOFactory::load($path);
|
$spreadsheet = IOFactory::load($path);
|
||||||
|
|
||||||
$worksheet = $spreadsheet->getSheet(0);
|
$worksheet = $spreadsheet->getSheet(0);
|
||||||
|
|
||||||
$totalRows = 0;
|
$totalRows = 0;
|
||||||
$addedAmount = 0;
|
$addedAmount = 0;
|
||||||
|
|
||||||
foreach ($worksheet->getRowIterator(self::HEADER_ROW_INDEX + 1) as $row) {
|
foreach ($worksheet->getRowIterator(self::HEADER_ROW_INDEX + 1) as $row) {
|
||||||
$totalRows++;
|
$totalRows++;
|
||||||
$values = [
|
$values = [
|
||||||
'name' => null,
|
'name' => null,
|
||||||
'key' => null,
|
'key' => null,
|
||||||
'from' => null,
|
'from' => null,
|
||||||
'store' => null,
|
'store' => null,
|
||||||
'store_link' => null
|
'store_link' => null
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($columnDefinitions as $columnIndex => $attribute) {
|
foreach ($columnDefinitions as $columnIndex => $attribute) {
|
||||||
$value = $worksheet->getCell(sprintf('%s%d', $columnIndex, $row->getRowIndex()))->getValueString();
|
$value = $worksheet->getCell(sprintf('%s%d', $columnIndex, $row->getRowIndex()))->getValueString();
|
||||||
|
|
||||||
switch(KeyAttribute::from($attribute)) {
|
switch(KeyAttribute::from($attribute)) {
|
||||||
case KeyAttribute::NONE:
|
case KeyAttribute::NONE:
|
||||||
break;
|
break;
|
||||||
case KeyAttribute::GAME_NAME:
|
case KeyAttribute::GAME_NAME:
|
||||||
$values['name'] = $value;
|
$values['name'] = $value;
|
||||||
break;
|
break;
|
||||||
case KeyAttribute::KEY:
|
case KeyAttribute::KEY:
|
||||||
$values['key'] = $value;
|
$values['key'] = $value;
|
||||||
break;
|
break;
|
||||||
case KeyAttribute::FROM:
|
case KeyAttribute::FROM:
|
||||||
$values['from'] = $value;
|
$values['from'] = $value;
|
||||||
break;
|
break;
|
||||||
case KeyAttribute::STORE:
|
case KeyAttribute::STORE:
|
||||||
$store = $this->interpretStore($value);
|
$store = $this->interpretStore($value);
|
||||||
$values['store'] = $store;
|
$values['store'] = $store;
|
||||||
if ($store === Store::EXTERNAL) {
|
if ($store === Store::EXTERNAL) {
|
||||||
$values['store_link'] = $value;
|
$values['store_link'] = $value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($values['key'] === null || $values['name'] === null || $values['store'] === null) {
|
if ($values['key'] === null || $values['name'] === null || $values['store'] === null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$game = $this->entityManager->getRepository(Game::class)->findOneBy([ 'name' => $values['name'] ]);
|
$game = $this->entityManager->getRepository(Game::class)->findOneBy([ 'name' => $values['name'] ]);
|
||||||
if ($game === null) {
|
if ($game === null) {
|
||||||
$game = new Game($values['name']);
|
$game = new Game($values['name']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$key = new Key(
|
$key = new Key(
|
||||||
$game,
|
$game,
|
||||||
$list,
|
$list,
|
||||||
$values['key'],
|
$values['key'],
|
||||||
$values['store'],
|
$values['store'],
|
||||||
$values['store_link'],
|
$values['store_link'],
|
||||||
$values['from'],
|
$values['from'],
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->entityManager->persist($game);
|
$this->entityManager->persist($game);
|
||||||
$this->entityManager->persist($key);
|
$this->entityManager->persist($key);
|
||||||
$addedAmount++;
|
$addedAmount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
return [$totalRows, $addedAmount];
|
return [$totalRows, $addedAmount];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function interpretStore(string $storeString): Store {
|
private function interpretStore(string $storeString): Store {
|
||||||
$storeString = trim($storeString);
|
$storeString = trim($storeString);
|
||||||
$storeString = strtolower($storeString);
|
$storeString = strtolower($storeString);
|
||||||
$storeString = str_replace(' ', '', $storeString);
|
$storeString = str_replace(' ', '', $storeString);
|
||||||
|
|
||||||
$triedConversion = Store::tryFrom($storeString);
|
$triedConversion = Store::tryFrom($storeString);
|
||||||
if ($triedConversion !== null) {
|
if ($triedConversion !== null) {
|
||||||
return $triedConversion;
|
return $triedConversion;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists($storeString, self::STORE_ADDITIONAL_CASES)) {
|
if (array_key_exists($storeString, self::STORE_ADDITIONAL_CASES)) {
|
||||||
return self::STORE_ADDITIONAL_CASES[$storeString];
|
return self::STORE_ADDITIONAL_CASES[$storeString];
|
||||||
}
|
}
|
||||||
|
|
||||||
return Store::EXTERNAL;
|
return Store::EXTERNAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,26 +1,26 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Importer;
|
namespace GamesShop\Importer;
|
||||||
|
|
||||||
use GamesShop\Entities\Games\KeyAttribute;
|
use GamesShop\Entities\Games\KeyAttribute;
|
||||||
use JsonSerializable;
|
use JsonSerializable;
|
||||||
|
|
||||||
final readonly class ImportColumnInterpretation implements JsonSerializable
|
final readonly class ImportColumnInterpretation implements JsonSerializable
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $index,
|
public string $index,
|
||||||
public string $displayName,
|
public string $displayName,
|
||||||
public KeyAttribute|null $guessedAttribute
|
public KeyAttribute|null $guessedAttribute
|
||||||
)
|
)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public function jsonSerialize(): array
|
public function jsonSerialize(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'index' => $this->index,
|
'index' => $this->index,
|
||||||
'displayName' => $this->displayName,
|
'displayName' => $this->displayName,
|
||||||
'guessedAttribute' => $this->guessedAttribute ?? null
|
'guessedAttribute' => $this->guessedAttribute ?? null
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,47 +1,47 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Login;
|
namespace GamesShop\Login;
|
||||||
|
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use GamesShop\Api\DiscordAPI;
|
use GamesShop\Api\DiscordAPI;
|
||||||
use GamesShop\ContainerHandler;
|
use GamesShop\ContainerHandler;
|
||||||
use GamesShop\Entities\Account\User;
|
use GamesShop\Entities\Account\User;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
final class DiscordLoginProvider implements LoginProvider
|
final class DiscordLoginProvider implements LoginProvider
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly EntityManager $entityManager
|
private readonly EntityManager $entityManager
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUser(ServerRequestInterface $request): User
|
public function getUser(ServerRequestInterface $request): User
|
||||||
{
|
{
|
||||||
$discordApiHandler = ContainerHandler::get(DiscordAPI::class);
|
$discordApiHandler = ContainerHandler::get(DiscordAPI::class);
|
||||||
$result = $discordApiHandler->getUserFromCode($request->getQueryParams()['code'], (string)$request->getUri()->withQuery(''));
|
$result = $discordApiHandler->getUserFromCode($request->getQueryParams()['code'], (string)$request->getUri()->withQuery(''));
|
||||||
|
|
||||||
$repo = $this->entityManager->getRepository(User::class);
|
$repo = $this->entityManager->getRepository(User::class);
|
||||||
$users = $repo->findBy(['loginMethod' => LoginMethod::DISCORD, 'foreignLoginId' => $result['id']]);
|
$users = $repo->findBy(['loginMethod' => LoginMethod::DISCORD, 'foreignLoginId' => $result['id']]);
|
||||||
|
|
||||||
$profilePictureUrl = $discordApiHandler->getAvatarURL($result['id'], $result['avatar'] ?? $result['discriminator'] % 5);
|
$profilePictureUrl = $discordApiHandler->getAvatarURL($result['id'], $result['avatar'] ?? $result['discriminator'] % 5);
|
||||||
if (!empty($users)) {
|
if (!empty($users)) {
|
||||||
$user = $users[0];
|
$user = $users[0];
|
||||||
$user->setName($result['global_name']);
|
$user->setName($result['global_name']);
|
||||||
$user->setProfilePictureUrl($profilePictureUrl);
|
$user->setProfilePictureUrl($profilePictureUrl);
|
||||||
|
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
$newUser = new User(
|
$newUser = new User(
|
||||||
LoginMethod::DISCORD,
|
LoginMethod::DISCORD,
|
||||||
$result['id'],
|
$result['id'],
|
||||||
$result['global_name'],
|
$result['global_name'],
|
||||||
$profilePictureUrl,
|
$profilePictureUrl,
|
||||||
UserPermission::VIEWER
|
UserPermission::VIEWER
|
||||||
);
|
);
|
||||||
|
|
||||||
return $newUser;
|
return $newUser;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,76 +1,76 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Login;
|
namespace GamesShop\Login;
|
||||||
|
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use Exception;
|
use Exception;
|
||||||
use GamesShop\ContainerHandler;
|
use GamesShop\ContainerHandler;
|
||||||
use GamesShop\Entities\Account\User;
|
use GamesShop\Entities\Account\User;
|
||||||
|
|
||||||
final class LoginHandler
|
final class LoginHandler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @return class-string[]
|
* @return class-string[]
|
||||||
*/
|
*/
|
||||||
private static array $providers;
|
private static array $providers;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly EntityManager $entityManager
|
private readonly EntityManager $entityManager
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isLoggedIn(): bool {
|
public function isLoggedIn(): bool {
|
||||||
$this->ensureSession();
|
$this->ensureSession();
|
||||||
|
|
||||||
return isset($_SESSION['accountid']);
|
return isset($_SESSION['accountid']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function getLoginProvider(string $method): LoginProvider {
|
public function getLoginProvider(string $method): LoginProvider {
|
||||||
$providers = self::getProviders();
|
$providers = self::getProviders();
|
||||||
if (!array_key_exists($method, $providers)) {
|
if (!array_key_exists($method, $providers)) {
|
||||||
throw new Exception("Couldn't find method for login '{$method}'");
|
throw new Exception("Couldn't find method for login '{$method}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ContainerHandler::get($providers[$method]);
|
return ContainerHandler::get($providers[$method]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setCurrentUser(User $user) {
|
public function setCurrentUser(User $user) {
|
||||||
$this->ensureSession();
|
$this->ensureSession();
|
||||||
|
|
||||||
$_SESSION['accountid'] = $user->getId();
|
$_SESSION['accountid'] = $user->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCurrentUser(): User {
|
public function getCurrentUser(): User {
|
||||||
$this->ensureSession();
|
$this->ensureSession();
|
||||||
|
|
||||||
$userid = $_SESSION['accountid'];
|
$userid = $_SESSION['accountid'];
|
||||||
return $this->entityManager->getRepository(User::class)->find($userid);
|
return $this->entityManager->getRepository(User::class)->find($userid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleteSession() {
|
public function deleteSession() {
|
||||||
$this->ensureSession();
|
$this->ensureSession();
|
||||||
session_destroy();
|
session_destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function ensureSession()
|
private function ensureSession()
|
||||||
{
|
{
|
||||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||||
session_start();
|
session_start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return class-string[]
|
* @return class-string[]
|
||||||
*/
|
*/
|
||||||
private static function getProviders(): array
|
private static function getProviders(): array
|
||||||
{
|
{
|
||||||
return self::$providers ??= [
|
return self::$providers ??= [
|
||||||
'discord' => DiscordLoginProvider::class
|
'discord' => DiscordLoginProvider::class
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,23 +1,23 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Login;
|
namespace GamesShop\Login;
|
||||||
|
|
||||||
enum LoginMethod: int
|
enum LoginMethod: int
|
||||||
{
|
{
|
||||||
case DISCORD = 1;
|
case DISCORD = 1;
|
||||||
|
|
||||||
public function getIconClass(): string
|
public function getIconClass(): string
|
||||||
{
|
{
|
||||||
return match ($this) {
|
return match ($this) {
|
||||||
self::DISCORD => 'fa-discord',
|
self::DISCORD => 'fa-discord',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHumanReadableName(): string
|
public function getHumanReadableName(): string
|
||||||
{
|
{
|
||||||
return match ($this) {
|
return match ($this) {
|
||||||
self::DISCORD => 'Discord',
|
self::DISCORD => 'Discord',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Login;
|
namespace GamesShop\Login;
|
||||||
|
|
||||||
use GamesShop\Entities\Account\User;
|
use GamesShop\Entities\Account\User;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
interface LoginProvider
|
interface LoginProvider
|
||||||
{
|
{
|
||||||
public function getUser(ServerRequestInterface $request): User;
|
public function getUser(ServerRequestInterface $request): User;
|
||||||
}
|
}
|
|
@ -1,25 +1,25 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Login;
|
namespace GamesShop\Login;
|
||||||
|
|
||||||
enum UserPermission : int
|
enum UserPermission : int
|
||||||
{
|
{
|
||||||
case NONE = 0;
|
case NONE = 0;
|
||||||
case VIEWER = 1;
|
case VIEWER = 1;
|
||||||
case PROVIDER = 10;
|
case PROVIDER = 10;
|
||||||
case ADMIN = 100;
|
case ADMIN = 100;
|
||||||
|
|
||||||
public function hasLevel(UserPermission $userPermission): bool {
|
public function hasLevel(UserPermission $userPermission): bool {
|
||||||
return $this->value >= $userPermission->value;
|
return $this->value >= $userPermission->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHumanReadableName() {
|
public function getHumanReadableName() {
|
||||||
return match ($this) {
|
return match ($this) {
|
||||||
self::VIEWER => "Claimer",
|
self::VIEWER => "Claimer",
|
||||||
self::PROVIDER => "Provider",
|
self::PROVIDER => "Provider",
|
||||||
self::ADMIN => "Admin",
|
self::ADMIN => "Admin",
|
||||||
default => "None",
|
default => "None",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop;
|
namespace GamesShop;
|
||||||
|
|
||||||
final class Paths
|
final class Paths
|
||||||
{
|
{
|
||||||
public const string ROOT_PATH = __DIR__ . '/../..';
|
public const string ROOT_PATH = __DIR__ . '/../..';
|
||||||
public const string PUBLIC_PATH = self::ROOT_PATH . '/public';
|
public const string PUBLIC_PATH = self::ROOT_PATH . '/public';
|
||||||
public const string SOURCE_PATH = self::ROOT_PATH . '/src';
|
public const string SOURCE_PATH = self::ROOT_PATH . '/src';
|
||||||
|
|
||||||
public const string PHP_SOURCE_PATH = self::SOURCE_PATH . '/php';
|
public const string PHP_SOURCE_PATH = self::SOURCE_PATH . '/php';
|
||||||
}
|
}
|
|
@ -1,49 +1,49 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing;
|
namespace GamesShop\Routing;
|
||||||
|
|
||||||
use GamesShop\ContainerHandler;
|
use GamesShop\ContainerHandler;
|
||||||
use GamesShop\Login\LoginHandler;
|
use GamesShop\Login\LoginHandler;
|
||||||
use GamesShop\Login\UserPermission;
|
use GamesShop\Login\UserPermission;
|
||||||
use GamesShop\Routing\Responses\TemplateResponse;
|
use GamesShop\Routing\Responses\TemplateResponse;
|
||||||
use Laminas\Diactoros\Response;
|
use Laminas\Diactoros\Response;
|
||||||
use Laminas\Diactoros\Response\EmptyResponse;
|
use Laminas\Diactoros\Response\EmptyResponse;
|
||||||
use Laminas\Diactoros\Response\RedirectResponse;
|
use Laminas\Diactoros\Response\RedirectResponse;
|
||||||
use League\Route\Http\Exception\ForbiddenException;
|
use League\Route\Http\Exception\ForbiddenException;
|
||||||
use League\Route\Http\Exception\UnauthorizedException;
|
use League\Route\Http\Exception\UnauthorizedException;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
final class AdminAccountConfigRoute
|
final class AdminAccountConfigRoute
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly LoginHandler $loginHandler
|
private readonly LoginHandler $loginHandler
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws ForbiddenException
|
* @throws ForbiddenException
|
||||||
* @throws UnauthorizedException
|
* @throws UnauthorizedException
|
||||||
*/
|
*/
|
||||||
public function __invoke(ServerRequestInterface $request): ResponseInterface
|
public function __invoke(ServerRequestInterface $request): ResponseInterface
|
||||||
{
|
{
|
||||||
if (!$this->loginHandler->isLoggedIn()) {
|
if (!$this->loginHandler->isLoggedIn()) {
|
||||||
throw new UnauthorizedException();
|
throw new UnauthorizedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->loginHandler->getCurrentUser();
|
$user = $this->loginHandler->getCurrentUser();
|
||||||
if ($user->getPermission()->value < UserPermission::ADMIN->value) {
|
if ($user->getPermission()->value < UserPermission::ADMIN->value) {
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return new TemplateResponse('admin/accounts');
|
return new TemplateResponse('admin/accounts');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function applyRoutes(\League\Route\Router $router) {
|
public static function applyRoutes(\League\Route\Router $router) {
|
||||||
$router->get('/accounts', self::class);
|
$router->get('/accounts', self::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,16 +1,16 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing\Api;
|
namespace GamesShop\Routing\Api;
|
||||||
|
|
||||||
use GamesShop\Routing\Api\DataTables\DataTablesAPIRoutes;
|
use GamesShop\Routing\Api\DataTables\DataTablesAPIRoutes;
|
||||||
use GamesShop\Routing\Api\Web\WebAPIRoutes;
|
use GamesShop\Routing\Api\Web\WebAPIRoutes;
|
||||||
use League\Route\Router;
|
use League\Route\Router;
|
||||||
|
|
||||||
final class APIRoutes
|
final class APIRoutes
|
||||||
{
|
{
|
||||||
public static function applyRoutes(Router $router) {
|
public static function applyRoutes(Router $router) {
|
||||||
$router->group('/api/dt', DataTablesAPIRoutes::setupRoutes(...));
|
$router->group('/api/dt', DataTablesAPIRoutes::setupRoutes(...));
|
||||||
$router->group('/api/web', WebAPIRoutes::applyRoutes(...));
|
$router->group('/api/web', WebAPIRoutes::applyRoutes(...));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,80 +1,80 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing\Api\DataTables;
|
namespace GamesShop\Routing\Api\DataTables;
|
||||||
|
|
||||||
use Doctrine\Common\Collections\Criteria;
|
use Doctrine\Common\Collections\Criteria;
|
||||||
use Doctrine\Common\Collections\Expr\Comparison;
|
use Doctrine\Common\Collections\Expr\Comparison;
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use GamesShop\Entities\Account\User;
|
use GamesShop\Entities\Account\User;
|
||||||
use GamesShop\Login\LoginHandler;
|
use GamesShop\Login\LoginHandler;
|
||||||
use GamesShop\Login\UserPermission;
|
use GamesShop\Login\UserPermission;
|
||||||
use Laminas\Diactoros\Response\JsonResponse;
|
use Laminas\Diactoros\Response\JsonResponse;
|
||||||
use League\Route\Http\Exception\ForbiddenException;
|
use League\Route\Http\Exception\ForbiddenException;
|
||||||
use League\Route\Http\Exception\UnauthorizedException;
|
use League\Route\Http\Exception\UnauthorizedException;
|
||||||
use League\Route\RouteGroup;
|
use League\Route\RouteGroup;
|
||||||
use League\Route\Router;
|
use League\Route\Router;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
final class AccountsEndpoint
|
final class AccountsEndpoint
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly LoginHandler $loginHandler,
|
private readonly LoginHandler $loginHandler,
|
||||||
private readonly EntityManager $entityManager,
|
private readonly EntityManager $entityManager,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(ServerRequestInterface $request): ResponseInterface
|
public function __invoke(ServerRequestInterface $request): ResponseInterface
|
||||||
{
|
{
|
||||||
if (!$this->loginHandler->isLoggedIn()) {
|
if (!$this->loginHandler->isLoggedIn()) {
|
||||||
throw new UnauthorizedException();
|
throw new UnauthorizedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->loginHandler->getCurrentUser();
|
$user = $this->loginHandler->getCurrentUser();
|
||||||
if (!$user->getPermission()->hasLevel(UserPermission::ADMIN)) {
|
if (!$user->getPermission()->hasLevel(UserPermission::ADMIN)) {
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$params = $request->getQueryParams();
|
$params = $request->getQueryParams();
|
||||||
$draw = $params['draw'];
|
$draw = $params['draw'];
|
||||||
$start = $params['start'];
|
$start = $params['start'];
|
||||||
$length = $params['length'];
|
$length = $params['length'];
|
||||||
|
|
||||||
$searchValue = $params['search']['value'];
|
$searchValue = $params['search']['value'];
|
||||||
|
|
||||||
$repo = $this->entityManager->getRepository(User::class);
|
$repo = $this->entityManager->getRepository(User::class);
|
||||||
$total = $repo->count();
|
$total = $repo->count();
|
||||||
|
|
||||||
$criteria = Criteria::create();
|
$criteria = Criteria::create();
|
||||||
$criteria->where(Criteria::expr()->contains('name', $searchValue));
|
$criteria->where(Criteria::expr()->contains('name', $searchValue));
|
||||||
$criteria->setFirstResult((int)$start);
|
$criteria->setFirstResult((int)$start);
|
||||||
$criteria->setMaxResults((int)$length);
|
$criteria->setMaxResults((int)$length);
|
||||||
|
|
||||||
$values = $repo->matching($criteria);
|
$values = $repo->matching($criteria);
|
||||||
$filteredCount = $values->count();
|
$filteredCount = $values->count();
|
||||||
|
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
'draw' => $draw,
|
'draw' => $draw,
|
||||||
'recordsTotal' => $total,
|
'recordsTotal' => $total,
|
||||||
'recordsFiltered' => $filteredCount,
|
'recordsFiltered' => $filteredCount,
|
||||||
'data' =>
|
'data' =>
|
||||||
$values->map(function (User $user) {
|
$values->map(function (User $user) {
|
||||||
return [
|
return [
|
||||||
'userid' => $user->getId(),
|
'userid' => $user->getId(),
|
||||||
'name' => $user->getName(),
|
'name' => $user->getName(),
|
||||||
'profilePictureUrl' => $user->getProfilePictureUrl(),
|
'profilePictureUrl' => $user->getProfilePictureUrl(),
|
||||||
'permission' => $user->getPermission()->getHumanReadableName(),
|
'permission' => $user->getPermission()->getHumanReadableName(),
|
||||||
'permissionIndex' => $user->getPermission()->value,
|
'permissionIndex' => $user->getPermission()->value,
|
||||||
'loginMethod' => $user->getLoginMethod()->getHumanReadableName(),
|
'loginMethod' => $user->getLoginMethod()->getHumanReadableName(),
|
||||||
];
|
];
|
||||||
})->toArray()
|
})->toArray()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function applyRoutes(RouteGroup $router) {
|
public static function applyRoutes(RouteGroup $router) {
|
||||||
$router->get('/accounts', AccountsEndpoint::class);
|
$router->get('/accounts', AccountsEndpoint::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,16 +1,16 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing\Api\DataTables;
|
namespace GamesShop\Routing\Api\DataTables;
|
||||||
|
|
||||||
use League\Route\RouteGroup;
|
use League\Route\RouteGroup;
|
||||||
use League\Route\Router;
|
use League\Route\Router;
|
||||||
|
|
||||||
final class DataTablesAPIRoutes
|
final class DataTablesAPIRoutes
|
||||||
{
|
{
|
||||||
public static function setupRoutes(RouteGroup $group): void {
|
public static function setupRoutes(RouteGroup $group): void {
|
||||||
AccountsEndpoint::applyRoutes($group);
|
AccountsEndpoint::applyRoutes($group);
|
||||||
|
|
||||||
$group->get('/keys/provider', ProviderKeysEndpoint::class);
|
$group->get('/keys/provider', ProviderKeysEndpoint::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,78 +1,78 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing\Api\DataTables;
|
namespace GamesShop\Routing\Api\DataTables;
|
||||||
|
|
||||||
use Doctrine\Common\Collections\Criteria;
|
use Doctrine\Common\Collections\Criteria;
|
||||||
use Doctrine\Common\Collections\Order;
|
use Doctrine\Common\Collections\Order;
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use GamesShop\Entities\Account\User;
|
use GamesShop\Entities\Account\User;
|
||||||
use GamesShop\Entities\Games\Game;
|
use GamesShop\Entities\Games\Game;
|
||||||
use GamesShop\Entities\Games\Key;
|
use GamesShop\Entities\Games\Key;
|
||||||
use GamesShop\Entities\GamesList;
|
use GamesShop\Entities\GamesList;
|
||||||
use GamesShop\Login\LoginHandler;
|
use GamesShop\Login\LoginHandler;
|
||||||
use GamesShop\Login\UserPermission;
|
use GamesShop\Login\UserPermission;
|
||||||
use Laminas\Diactoros\Response\JsonResponse;
|
use Laminas\Diactoros\Response\JsonResponse;
|
||||||
use League\Route\Http\Exception\BadRequestException;
|
use League\Route\Http\Exception\BadRequestException;
|
||||||
use League\Route\Http\Exception\ForbiddenException;
|
use League\Route\Http\Exception\ForbiddenException;
|
||||||
use League\Route\Http\Exception\UnauthorizedException;
|
use League\Route\Http\Exception\UnauthorizedException;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
final class ProviderKeysEndpoint
|
final class ProviderKeysEndpoint
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly LoginHandler $loginHandler,
|
private readonly LoginHandler $loginHandler,
|
||||||
private readonly EntityManager $entityManager,
|
private readonly EntityManager $entityManager,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(ServerRequestInterface $request): ResponseInterface
|
public function __invoke(ServerRequestInterface $request): ResponseInterface
|
||||||
{
|
{
|
||||||
if (!$this->loginHandler->isLoggedIn()) {
|
if (!$this->loginHandler->isLoggedIn()) {
|
||||||
throw new UnauthorizedException();
|
throw new UnauthorizedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->loginHandler->getCurrentUser();
|
$user = $this->loginHandler->getCurrentUser();
|
||||||
if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) {
|
if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) {
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$body = $request->getQueryParams();
|
$body = $request->getQueryParams();
|
||||||
if (!array_key_exists('listid', $body)) {
|
if (!array_key_exists('listid', $body)) {
|
||||||
throw new BadRequestException();
|
throw new BadRequestException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$list = $this->entityManager->getRepository(GamesList::class)->findOneBy([ 'owner' => $user, 'id' => $body['listid'] ]);
|
$list = $this->entityManager->getRepository(GamesList::class)->findOneBy([ 'owner' => $user, 'id' => $body['listid'] ]);
|
||||||
if (!$list instanceof GamesList) {
|
if (!$list instanceof GamesList) {
|
||||||
throw new BadRequestException();
|
throw new BadRequestException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$keys = $this->entityManager->getRepository(Key::class)->findBy(['list' => $list]);
|
$keys = $this->entityManager->getRepository(Key::class)->findBy(['list' => $list]);
|
||||||
$gameToKeyArray = [];
|
$gameToKeyArray = [];
|
||||||
foreach ($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
$game = $key->getGame();
|
$game = $key->getGame();
|
||||||
$id = $game->getId();
|
$id = $game->getId();
|
||||||
|
|
||||||
if (!array_key_exists($id, $gameToKeyArray)) {
|
if (!array_key_exists($id, $gameToKeyArray)) {
|
||||||
$gameToKeyArray[$id] = [ $game, [] ];
|
$gameToKeyArray[$id] = [ $game, [] ];
|
||||||
}
|
}
|
||||||
|
|
||||||
$gameToKeyArray[$id][1][] = $key;
|
$gameToKeyArray[$id][1][] = $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = [];
|
$result = [];
|
||||||
foreach ($gameToKeyArray as [$game, $keys]) {
|
foreach ($gameToKeyArray as [$game, $keys]) {
|
||||||
$result[] = [
|
$result[] = [
|
||||||
'gamePicture' => '',
|
'gamePicture' => '',
|
||||||
'name' => $game->getName(),
|
'name' => $game->getName(),
|
||||||
'keysAmount' => count($keys),
|
'keysAmount' => count($keys),
|
||||||
'igdbState' => 'not implermented',
|
'igdbState' => 'not implermented',
|
||||||
'keys' => $keys,
|
'keys' => $keys,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return new JsonResponse([ 'data' => $result ]);
|
return new JsonResponse([ 'data' => $result ]);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,49 +1,49 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing\Api\Web;
|
namespace GamesShop\Routing\Api\Web;
|
||||||
|
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use GamesShop\Entities\GamesList;
|
use GamesShop\Entities\GamesList;
|
||||||
use GamesShop\Login\LoginHandler;
|
use GamesShop\Login\LoginHandler;
|
||||||
use GamesShop\Login\UserPermission;
|
use GamesShop\Login\UserPermission;
|
||||||
use Laminas\Diactoros\Response;
|
use Laminas\Diactoros\Response;
|
||||||
use League\Route\Http\Exception\BadRequestException;
|
use League\Route\Http\Exception\BadRequestException;
|
||||||
use League\Route\Http\Exception\ForbiddenException;
|
use League\Route\Http\Exception\ForbiddenException;
|
||||||
use League\Route\Http\Exception\UnauthorizedException;
|
use League\Route\Http\Exception\UnauthorizedException;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
final class CreateKeyListRoute
|
final class CreateKeyListRoute
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly LoginHandler $loginHandler,
|
private readonly LoginHandler $loginHandler,
|
||||||
private readonly EntityManager $entityManager
|
private readonly EntityManager $entityManager
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(ServerRequestInterface $request): ResponseInterface
|
public function __invoke(ServerRequestInterface $request): ResponseInterface
|
||||||
{
|
{
|
||||||
if (!$this->loginHandler->isLoggedIn()) {
|
if (!$this->loginHandler->isLoggedIn()) {
|
||||||
throw new UnauthorizedException();
|
throw new UnauthorizedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->loginHandler->getCurrentUser();
|
$user = $this->loginHandler->getCurrentUser();
|
||||||
if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) {
|
if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) {
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$body = $request->getParsedBody();
|
$body = $request->getParsedBody();
|
||||||
if (!array_key_exists('name', $body)) {
|
if (!array_key_exists('name', $body)) {
|
||||||
throw new BadRequestException();
|
throw new BadRequestException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$name = $body['name'];
|
$name = $body['name'];
|
||||||
$list = new GamesList($user, $name);
|
$list = new GamesList($user, $name);
|
||||||
$this->entityManager->persist($list);
|
$this->entityManager->persist($list);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
return new Response();
|
return new Response();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,50 +1,50 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing\Api\Web;
|
namespace GamesShop\Routing\Api\Web;
|
||||||
|
|
||||||
use GamesShop\Importer\GameImporter;
|
use GamesShop\Importer\GameImporter;
|
||||||
use GamesShop\Login\LoginHandler;
|
use GamesShop\Login\LoginHandler;
|
||||||
use GamesShop\Login\UserPermission;
|
use GamesShop\Login\UserPermission;
|
||||||
use Laminas\Diactoros\Response\JsonResponse;
|
use Laminas\Diactoros\Response\JsonResponse;
|
||||||
use Laminas\Diactoros\UploadedFile;
|
use Laminas\Diactoros\UploadedFile;
|
||||||
use League\Route\Http\Exception\BadRequestException;
|
use League\Route\Http\Exception\BadRequestException;
|
||||||
use League\Route\Http\Exception\ForbiddenException;
|
use League\Route\Http\Exception\ForbiddenException;
|
||||||
use League\Route\Http\Exception\UnauthorizedException;
|
use League\Route\Http\Exception\UnauthorizedException;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
final class ImportKeysPrepareRoute
|
final class ImportKeysPrepareRoute
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly LoginHandler $loginHandler,
|
private readonly LoginHandler $loginHandler,
|
||||||
private readonly GameImporter $importer,
|
private readonly GameImporter $importer,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public function __invoke(ServerRequestInterface $request): ResponseInterface
|
public function __invoke(ServerRequestInterface $request): ResponseInterface
|
||||||
{
|
{
|
||||||
if (!$this->loginHandler->isLoggedIn()) {
|
if (!$this->loginHandler->isLoggedIn()) {
|
||||||
throw new UnauthorizedException();
|
throw new UnauthorizedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->loginHandler->getCurrentUser();
|
$user = $this->loginHandler->getCurrentUser();
|
||||||
if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) {
|
if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) {
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var UploadedFile $file
|
* @var UploadedFile $file
|
||||||
*/
|
*/
|
||||||
$file = $request->getUploadedFiles()['file'] ?? null;
|
$file = $request->getUploadedFiles()['file'] ?? null;
|
||||||
if (!$file === null) {
|
if (!$file === null) {
|
||||||
throw new BadRequestException();
|
throw new BadRequestException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$fileName = tempnam(sys_get_temp_dir(), 'ImportKeys');
|
$fileName = tempnam(sys_get_temp_dir(), 'ImportKeys');
|
||||||
$file->moveTo($fileName);
|
$file->moveTo($fileName);
|
||||||
$results = $this->importer->interpret($fileName);
|
$results = $this->importer->interpret($fileName);
|
||||||
unlink($fileName);
|
unlink($fileName);
|
||||||
|
|
||||||
return new JsonResponse($results);
|
return new JsonResponse($results);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,66 +1,66 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing\Api\Web;
|
namespace GamesShop\Routing\Api\Web;
|
||||||
|
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use GamesShop\Entities\GamesList;
|
use GamesShop\Entities\GamesList;
|
||||||
use GamesShop\Importer\GameImporter;
|
use GamesShop\Importer\GameImporter;
|
||||||
use GamesShop\Login\LoginHandler;
|
use GamesShop\Login\LoginHandler;
|
||||||
use GamesShop\Login\UserPermission;
|
use GamesShop\Login\UserPermission;
|
||||||
use Laminas\Diactoros\Response\JsonResponse;
|
use Laminas\Diactoros\Response\JsonResponse;
|
||||||
use Laminas\Diactoros\UploadedFile;
|
use Laminas\Diactoros\UploadedFile;
|
||||||
use League\Route\Http\Exception\BadRequestException;
|
use League\Route\Http\Exception\BadRequestException;
|
||||||
use League\Route\Http\Exception\ForbiddenException;
|
use League\Route\Http\Exception\ForbiddenException;
|
||||||
use League\Route\Http\Exception\UnauthorizedException;
|
use League\Route\Http\Exception\UnauthorizedException;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
final class ImportKeysRoute
|
final class ImportKeysRoute
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly LoginHandler $loginHandler,
|
private readonly LoginHandler $loginHandler,
|
||||||
private readonly GameImporter $importer,
|
private readonly GameImporter $importer,
|
||||||
private readonly EntityManager $entityManager,
|
private readonly EntityManager $entityManager,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public function __invoke(ServerRequestInterface $request): ResponseInterface
|
public function __invoke(ServerRequestInterface $request): ResponseInterface
|
||||||
{
|
{
|
||||||
if (!$this->loginHandler->isLoggedIn()) {
|
if (!$this->loginHandler->isLoggedIn()) {
|
||||||
throw new UnauthorizedException();
|
throw new UnauthorizedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->loginHandler->getCurrentUser();
|
$user = $this->loginHandler->getCurrentUser();
|
||||||
if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) {
|
if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) {
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$body = $request->getParsedBody();
|
$body = $request->getParsedBody();
|
||||||
if (!array_key_exists('listid', $body)) {
|
if (!array_key_exists('listid', $body)) {
|
||||||
throw new BadRequestException();
|
throw new BadRequestException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$list = $this->entityManager->getRepository(GamesList::class)->findOneBy([ 'owner' => $user, 'id' => $body['listid'] ]);
|
$list = $this->entityManager->getRepository(GamesList::class)->findOneBy([ 'owner' => $user, 'id' => $body['listid'] ]);
|
||||||
if (!$list instanceof GamesList) {
|
if (!$list instanceof GamesList) {
|
||||||
throw new BadRequestException();
|
throw new BadRequestException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var UploadedFile $file
|
* @var UploadedFile $file
|
||||||
*/
|
*/
|
||||||
$file = $request->getUploadedFiles()['file'] ?? null;
|
$file = $request->getUploadedFiles()['file'] ?? null;
|
||||||
if (!$file === null) {
|
if (!$file === null) {
|
||||||
throw new BadRequestException();
|
throw new BadRequestException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$fileName = tempnam(sys_get_temp_dir(), 'ImportKeys');
|
$fileName = tempnam(sys_get_temp_dir(), 'ImportKeys');
|
||||||
$file->moveTo($fileName);
|
$file->moveTo($fileName);
|
||||||
|
|
||||||
$columnDefs = $request->getParsedBody()['columns'];
|
$columnDefs = $request->getParsedBody()['columns'];
|
||||||
|
|
||||||
[$total, $imported] = $this->importer->import($fileName, $columnDefs, $list);
|
[$total, $imported] = $this->importer->import($fileName, $columnDefs, $list);
|
||||||
unlink($fileName);
|
unlink($fileName);
|
||||||
|
|
||||||
return new JsonResponse([ 'success' => true, 'total' => $total, 'imported' => $imported ]);
|
return new JsonResponse([ 'success' => true, 'total' => $total, 'imported' => $imported ]);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,46 +1,46 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing\Api\Web;
|
namespace GamesShop\Routing\Api\Web;
|
||||||
|
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use GamesShop\Entities\Account\User;
|
use GamesShop\Entities\Account\User;
|
||||||
use GamesShop\Login\LoginHandler;
|
use GamesShop\Login\LoginHandler;
|
||||||
use GamesShop\Login\UserPermission;
|
use GamesShop\Login\UserPermission;
|
||||||
use Laminas\Diactoros\Response\EmptyResponse;
|
use Laminas\Diactoros\Response\EmptyResponse;
|
||||||
use League\Route\Http\Exception\ForbiddenException;
|
use League\Route\Http\Exception\ForbiddenException;
|
||||||
use League\Route\Http\Exception\UnauthorizedException;
|
use League\Route\Http\Exception\UnauthorizedException;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
final readonly class UserModifyRoute
|
final readonly class UserModifyRoute
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private LoginHandler $loginHandler,
|
private LoginHandler $loginHandler,
|
||||||
private EntityManager $entityManager,
|
private EntityManager $entityManager,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(ServerRequestInterface $request, array $args): ResponseInterface
|
public function __invoke(ServerRequestInterface $request, array $args): ResponseInterface
|
||||||
{
|
{
|
||||||
if (!$this->loginHandler->isLoggedIn()) {
|
if (!$this->loginHandler->isLoggedIn()) {
|
||||||
throw new UnauthorizedException();
|
throw new UnauthorizedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->loginHandler->getCurrentUser();
|
$user = $this->loginHandler->getCurrentUser();
|
||||||
if (!$user->getPermission()->hasLevel(UserPermission::ADMIN)) {
|
if (!$user->getPermission()->hasLevel(UserPermission::ADMIN)) {
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$permissions = $request->getParsedBody()['permission'];
|
$permissions = $request->getParsedBody()['permission'];
|
||||||
|
|
||||||
$toChangeUser = $this->entityManager->getRepository(User::class)->find((int)$args['id']);
|
$toChangeUser = $this->entityManager->getRepository(User::class)->find((int)$args['id']);
|
||||||
$toChangeUser->setPermission(UserPermission::from((int)$permissions));
|
$toChangeUser->setPermission(UserPermission::from((int)$permissions));
|
||||||
|
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
return new EmptyResponse(200);
|
return new EmptyResponse(200);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,18 +1,18 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing\Api\Web;
|
namespace GamesShop\Routing\Api\Web;
|
||||||
|
|
||||||
use League\Route\RouteGroup;
|
use League\Route\RouteGroup;
|
||||||
|
|
||||||
final class WebAPIRoutes
|
final class WebAPIRoutes
|
||||||
{
|
{
|
||||||
public static function applyRoutes(RouteGroup $group): void {
|
public static function applyRoutes(RouteGroup $group): void {
|
||||||
$group->post('/users/{id:number}', UserModifyRoute::class);
|
$group->post('/users/{id:number}', UserModifyRoute::class);
|
||||||
|
|
||||||
$group->post('/keys/import/prepare', ImportKeysPrepareRoute::class);
|
$group->post('/keys/import/prepare', ImportKeysPrepareRoute::class);
|
||||||
$group->post('/keys/import/perform', ImportKeysRoute::class);
|
$group->post('/keys/import/perform', ImportKeysRoute::class);
|
||||||
|
|
||||||
$group->post('/keys/list/create', CreateKeyListRoute::class);
|
$group->post('/keys/list/create', CreateKeyListRoute::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,20 +1,20 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing;
|
namespace GamesShop\Routing;
|
||||||
|
|
||||||
use GamesShop\ContainerHandler;
|
use GamesShop\ContainerHandler;
|
||||||
use GamesShop\Templates\TemplateEngine;
|
use GamesShop\Templates\TemplateEngine;
|
||||||
use Laminas\Diactoros\Response;
|
use Laminas\Diactoros\Response;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
final class ErrorRoute
|
final class ErrorRoute
|
||||||
{
|
{
|
||||||
public function renderErrorPage(int $errorCode): ResponseInterface {
|
public function renderErrorPage(int $errorCode): ResponseInterface {
|
||||||
$pageContent = ContainerHandler::get(TemplateEngine::class)->renderErrorPage($errorCode);
|
$pageContent = ContainerHandler::get(TemplateEngine::class)->renderErrorPage($errorCode);
|
||||||
|
|
||||||
$response = new Response(status: $errorCode);
|
$response = new Response(status: $errorCode);
|
||||||
$response->getBody()->write($pageContent);
|
$response->getBody()->write($pageContent);
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,33 +1,33 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing;
|
namespace GamesShop\Routing;
|
||||||
|
|
||||||
use GamesShop\ContainerHandler;
|
use GamesShop\ContainerHandler;
|
||||||
use GamesShop\Login\LoginHandler;
|
use GamesShop\Login\LoginHandler;
|
||||||
use GamesShop\Templates\TemplateEngine;
|
use GamesShop\Templates\TemplateEngine;
|
||||||
use Laminas\Diactoros\Response;
|
use Laminas\Diactoros\Response;
|
||||||
use Laminas\Diactoros\Response\RedirectResponse;
|
use Laminas\Diactoros\Response\RedirectResponse;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
final class IndexRoute
|
final class IndexRoute
|
||||||
{
|
{
|
||||||
public function __invoke(ServerRequestInterface $request): ResponseInterface {
|
public function __invoke(ServerRequestInterface $request): ResponseInterface {
|
||||||
$loginHandler = ContainerHandler::get(LoginHandler::class);
|
$loginHandler = ContainerHandler::get(LoginHandler::class);
|
||||||
|
|
||||||
if (!$loginHandler->isLoggedIn()) {
|
if (!$loginHandler->isLoggedIn()) {
|
||||||
return new RedirectResponse('/login');
|
return new RedirectResponse('/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
$pageContent = ContainerHandler::get(TemplateEngine::class)->renderPage('index');
|
$pageContent = ContainerHandler::get(TemplateEngine::class)->renderPage('index');
|
||||||
|
|
||||||
$response = new Response;
|
$response = new Response;
|
||||||
$response->getBody()->write($pageContent);
|
$response->getBody()->write($pageContent);
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function applyRoutes(\League\Route\Router $router): void {
|
public static function applyRoutes(\League\Route\Router $router): void {
|
||||||
$router->get('/', self::class);
|
$router->get('/', self::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,45 +1,45 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing;
|
namespace GamesShop\Routing;
|
||||||
|
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use GamesShop\Entities\GamesList;
|
use GamesShop\Entities\GamesList;
|
||||||
use GamesShop\Login\LoginHandler;
|
use GamesShop\Login\LoginHandler;
|
||||||
use GamesShop\Login\UserPermission;
|
use GamesShop\Login\UserPermission;
|
||||||
use GamesShop\Routing\Responses\TemplateResponse;
|
use GamesShop\Routing\Responses\TemplateResponse;
|
||||||
use League\Route\Http\Exception\ForbiddenException;
|
use League\Route\Http\Exception\ForbiddenException;
|
||||||
use League\Route\Http\Exception\UnauthorizedException;
|
use League\Route\Http\Exception\UnauthorizedException;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
final class KeysRoute
|
final class KeysRoute
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly LoginHandler $loginHandler,
|
private readonly LoginHandler $loginHandler,
|
||||||
private readonly EntityManager $entityManager
|
private readonly EntityManager $entityManager
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(ServerRequestInterface $request): ResponseInterface
|
public function __invoke(ServerRequestInterface $request): ResponseInterface
|
||||||
{
|
{
|
||||||
if (!$this->loginHandler->isLoggedIn()) {
|
if (!$this->loginHandler->isLoggedIn()) {
|
||||||
throw new UnauthorizedException();
|
throw new UnauthorizedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->loginHandler->getCurrentUser();
|
$user = $this->loginHandler->getCurrentUser();
|
||||||
if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) {
|
if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) {
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$entityManager = $this->entityManager->getRepository(GamesList::class);
|
$entityManager = $this->entityManager->getRepository(GamesList::class);
|
||||||
$lists = $entityManager->findBy([ 'owner' => $user ]);
|
$lists = $entityManager->findBy([ 'owner' => $user ]);
|
||||||
return new TemplateResponse('key-manager', [ 'usersLists' => $lists ]);
|
return new TemplateResponse('key-manager', [ 'usersLists' => $lists ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function applyRoutes(\League\Route\Router $router): void
|
public static function applyRoutes(\League\Route\Router $router): void
|
||||||
{
|
{
|
||||||
$router->get('/keys', KeysRoute::class);
|
$router->get('/keys', KeysRoute::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,72 +1,72 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing;
|
namespace GamesShop\Routing;
|
||||||
|
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use Doctrine\ORM\Exception\ORMException;
|
use Doctrine\ORM\Exception\ORMException;
|
||||||
use Doctrine\ORM\OptimisticLockException;
|
use Doctrine\ORM\OptimisticLockException;
|
||||||
use GamesShop\ContainerHandler;
|
use GamesShop\ContainerHandler;
|
||||||
use GamesShop\Environment\EnvironmentHandler;
|
use GamesShop\Environment\EnvironmentHandler;
|
||||||
use GamesShop\Login\LoginHandler;
|
use GamesShop\Login\LoginHandler;
|
||||||
use GamesShop\Routing\Responses\TemplateResponse;
|
use GamesShop\Routing\Responses\TemplateResponse;
|
||||||
use GamesShop\Templates\TemplateEngine;
|
use GamesShop\Templates\TemplateEngine;
|
||||||
use Laminas\Diactoros\Response;
|
use Laminas\Diactoros\Response;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
final class LoginRoutes
|
final class LoginRoutes
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly EntityManager $entityManager
|
private readonly EntityManager $entityManager
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function login(ServerRequestInterface $request) {
|
public function login(ServerRequestInterface $request) {
|
||||||
$discordEnv = ContainerHandler::get(EnvironmentHandler::class)->getDiscordEnvironment();
|
$discordEnv = ContainerHandler::get(EnvironmentHandler::class)->getDiscordEnvironment();
|
||||||
|
|
||||||
return new TemplateResponse('login', [
|
return new TemplateResponse('login', [
|
||||||
'discordUrl' => $discordEnv->loginUrl
|
'discordUrl' => $discordEnv->loginUrl
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws OptimisticLockException
|
* @throws OptimisticLockException
|
||||||
* @throws ORMException
|
* @throws ORMException
|
||||||
*/
|
*/
|
||||||
public function loginCallback(ServerRequestInterface $request, array $args): ResponseInterface {
|
public function loginCallback(ServerRequestInterface $request, array $args): ResponseInterface {
|
||||||
if (array_key_exists('error', $request->getQueryParams())) {
|
if (array_key_exists('error', $request->getQueryParams())) {
|
||||||
return new Response\RedirectResponse('/login');
|
return new Response\RedirectResponse('/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
$method = $args['method'];
|
$method = $args['method'];
|
||||||
$loginHandler = ContainerHandler::get(LoginHandler::class);
|
$loginHandler = ContainerHandler::get(LoginHandler::class);
|
||||||
$loginProvider = $loginHandler->getLoginProvider($method);
|
$loginProvider = $loginHandler->getLoginProvider($method);
|
||||||
|
|
||||||
$user = $loginProvider->getUser($request);
|
$user = $loginProvider->getUser($request);
|
||||||
if ($user->getId() === null) {
|
if ($user->getId() === null) {
|
||||||
$this->entityManager->persist($user);
|
$this->entityManager->persist($user);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
$loginHandler->setCurrentUser($user);
|
$loginHandler->setCurrentUser($user);
|
||||||
return new Response\RedirectResponse('/');
|
return new Response\RedirectResponse('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function logout(ServerRequestInterface $request): ResponseInterface
|
public function logout(ServerRequestInterface $request): ResponseInterface
|
||||||
{
|
{
|
||||||
$loginHandler = ContainerHandler::get(LoginHandler::class);
|
$loginHandler = ContainerHandler::get(LoginHandler::class);
|
||||||
$loginHandler->deleteSession();
|
$loginHandler->deleteSession();
|
||||||
|
|
||||||
return new Response\RedirectResponse('/login');
|
return new Response\RedirectResponse('/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function addRoutes(\League\Route\Router $router): void {
|
public static function addRoutes(\League\Route\Router $router): void {
|
||||||
$routes = ContainerHandler::get(LoginRoutes::class);
|
$routes = ContainerHandler::get(LoginRoutes::class);
|
||||||
|
|
||||||
$router->get('/login', $routes->login(...));
|
$router->get('/login', $routes->login(...));
|
||||||
$router->get('/login-callback/{method:word}', $routes->loginCallback(...));
|
$router->get('/login-callback/{method:word}', $routes->loginCallback(...));
|
||||||
$router->get('/logout', $routes->logout(...));
|
$router->get('/logout', $routes->logout(...));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,55 +1,55 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing;
|
namespace GamesShop\Routing;
|
||||||
|
|
||||||
use GamesShop\ContainerHandler;
|
use GamesShop\ContainerHandler;
|
||||||
use GamesShop\Paths;
|
use GamesShop\Paths;
|
||||||
use Laminas\Diactoros\Response;
|
use Laminas\Diactoros\Response;
|
||||||
use Laminas\Diactoros\Uri;
|
use Laminas\Diactoros\Uri;
|
||||||
use Mimey\MimeTypes;
|
use Mimey\MimeTypes;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
final class ResourceRoute
|
final class ResourceRoute
|
||||||
{
|
{
|
||||||
private const array RESOURCE_EXTENSIONS = [
|
private const array RESOURCE_EXTENSIONS = [
|
||||||
'js',
|
'js',
|
||||||
'css',
|
'css',
|
||||||
'ttf', 'woff', 'woff2',
|
'ttf', 'woff', 'woff2',
|
||||||
'gif', 'svg', 'png', 'jpg'
|
'gif', 'svg', 'png', 'jpg'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function isValid(Uri $uri): bool {
|
public function isValid(Uri $uri): bool {
|
||||||
$path = $uri->getPath();
|
$path = $uri->getPath();
|
||||||
foreach (self::RESOURCE_EXTENSIONS as $extension) {
|
foreach (self::RESOURCE_EXTENSIONS as $extension) {
|
||||||
if (!str_ends_with($path, $extension)) {
|
if (!str_ends_with($path, $extension)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getResponse(Uri $uri): ResponseInterface {
|
public function getResponse(Uri $uri): ResponseInterface {
|
||||||
$filePath = Paths::PUBLIC_PATH . $uri->getPath();
|
$filePath = Paths::PUBLIC_PATH . $uri->getPath();
|
||||||
|
|
||||||
if (!file_exists($filePath)) {
|
if (!file_exists($filePath)) {
|
||||||
$response = new Response(status: 404);
|
$response = new Response(status: 404);
|
||||||
$response->getBody()->write('File not found');
|
$response->getBody()->write('File not found');
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
$mimey = ContainerHandler::get(MimeTypes::class);
|
$mimey = ContainerHandler::get(MimeTypes::class);
|
||||||
$response = new Response(
|
$response = new Response(
|
||||||
headers: [
|
headers: [
|
||||||
'Content-Type' => $mimey->getMimeType(pathinfo($filePath, PATHINFO_EXTENSION)),
|
'Content-Type' => $mimey->getMimeType(pathinfo($filePath, PATHINFO_EXTENSION)),
|
||||||
'Cache-Control' => 'public, max-age=3600, must-revalidate',
|
'Cache-Control' => 'public, max-age=3600, must-revalidate',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$response->getBody()->write(file_get_contents($filePath));
|
$response->getBody()->write(file_get_contents($filePath));
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,20 +1,20 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing\Responses;
|
namespace GamesShop\Routing\Responses;
|
||||||
|
|
||||||
use GamesShop\ContainerHandler;
|
use GamesShop\ContainerHandler;
|
||||||
use GamesShop\Templates\TemplateEngine;
|
use GamesShop\Templates\TemplateEngine;
|
||||||
use Laminas\Diactoros\Response;
|
use Laminas\Diactoros\Response;
|
||||||
|
|
||||||
final class TemplateResponse extends Response
|
final class TemplateResponse extends Response
|
||||||
{
|
{
|
||||||
public function __construct(string $templateName, array $data = [], array $headers = [])
|
public function __construct(string $templateName, array $data = [], array $headers = [])
|
||||||
{
|
{
|
||||||
parent::__construct('php://memory', 200, $headers);
|
parent::__construct('php://memory', 200, $headers);
|
||||||
|
|
||||||
$templateEngine = ContainerHandler::get(TemplateEngine::class);
|
$templateEngine = ContainerHandler::get(TemplateEngine::class);
|
||||||
$body = $templateEngine->renderPage($templateName, $data);
|
$body = $templateEngine->renderPage($templateName, $data);
|
||||||
$this->getBody()->write($body);
|
$this->getBody()->write($body);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,61 +1,61 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing;
|
namespace GamesShop\Routing;
|
||||||
|
|
||||||
use GamesShop\ContainerHandler;
|
use GamesShop\ContainerHandler;
|
||||||
use GamesShop\Login\LoginHandler;
|
use GamesShop\Login\LoginHandler;
|
||||||
use GamesShop\Routing\Api\APIRoutes;
|
use GamesShop\Routing\Api\APIRoutes;
|
||||||
use GamesShop\Templates\TemplateEngine;
|
use GamesShop\Templates\TemplateEngine;
|
||||||
use Laminas\Diactoros\Response;
|
use Laminas\Diactoros\Response;
|
||||||
use Laminas\Diactoros\ServerRequestFactory;
|
use Laminas\Diactoros\ServerRequestFactory;
|
||||||
use League\Container\Container;
|
use League\Container\Container;
|
||||||
use League\Route\Http\Exception\BadRequestException;
|
use League\Route\Http\Exception\BadRequestException;
|
||||||
use League\Route\Http\Exception\ForbiddenException;
|
use League\Route\Http\Exception\ForbiddenException;
|
||||||
use League\Route\Http\Exception\NotFoundException;
|
use League\Route\Http\Exception\NotFoundException;
|
||||||
use League\Route\Http\Exception\UnauthorizedException;
|
use League\Route\Http\Exception\UnauthorizedException;
|
||||||
use League\Route\Strategy\ApplicationStrategy;
|
use League\Route\Strategy\ApplicationStrategy;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
final class Router
|
final class Router
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private ResourceRoute $resourceRoute
|
private ResourceRoute $resourceRoute
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function route(): ResponseInterface
|
public function route(): ResponseInterface
|
||||||
{
|
{
|
||||||
$request = ServerRequestFactory::fromGlobals(
|
$request = ServerRequestFactory::fromGlobals(
|
||||||
$_SERVER, $_GET, $_POST, $_COOKIE, $_FILES
|
$_SERVER, $_GET, $_POST, $_COOKIE, $_FILES
|
||||||
);
|
);
|
||||||
if ($this->resourceRoute->isValid($request->getUri())) {
|
if ($this->resourceRoute->isValid($request->getUri())) {
|
||||||
return $this->resourceRoute->getResponse($request->getUri());
|
return $this->resourceRoute->getResponse($request->getUri());
|
||||||
}
|
}
|
||||||
|
|
||||||
$router = new \League\Route\Router;
|
$router = new \League\Route\Router;
|
||||||
$strategy = (new ApplicationStrategy)->setContainer(ContainerHandler::getInstance());
|
$strategy = (new ApplicationStrategy)->setContainer(ContainerHandler::getInstance());
|
||||||
$router->setStrategy($strategy);
|
$router->setStrategy($strategy);
|
||||||
|
|
||||||
IndexRoute::applyRoutes($router);
|
IndexRoute::applyRoutes($router);
|
||||||
LoginRoutes::addRoutes($router);
|
LoginRoutes::addRoutes($router);
|
||||||
SetupRoute::applyRoutes($router);
|
SetupRoute::applyRoutes($router);
|
||||||
|
|
||||||
KeysRoute::applyRoutes($router);
|
KeysRoute::applyRoutes($router);
|
||||||
AdminAccountConfigRoute::applyRoutes($router);
|
AdminAccountConfigRoute::applyRoutes($router);
|
||||||
|
|
||||||
APIRoutes::applyRoutes($router);
|
APIRoutes::applyRoutes($router);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return $router->dispatch($request);
|
return $router->dispatch($request);
|
||||||
} catch (NotFoundException $e) {
|
} catch (NotFoundException $e) {
|
||||||
return (new ErrorRoute())->renderErrorPage(404);
|
return (new ErrorRoute())->renderErrorPage(404);
|
||||||
} catch (UnauthorizedException) {
|
} catch (UnauthorizedException) {
|
||||||
return (new ErrorRoute())->renderErrorPage(401);
|
return (new ErrorRoute())->renderErrorPage(401);
|
||||||
} catch (ForbiddenException) {
|
} catch (ForbiddenException) {
|
||||||
return (new ErrorRoute())->renderErrorPage(403);
|
return (new ErrorRoute())->renderErrorPage(403);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,52 +1,52 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Routing;
|
namespace GamesShop\Routing;
|
||||||
|
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use GamesShop\Entities\SystemAttribute;
|
use GamesShop\Entities\SystemAttribute;
|
||||||
use GamesShop\Login\LoginHandler;
|
use GamesShop\Login\LoginHandler;
|
||||||
use GamesShop\Login\UserPermission;
|
use GamesShop\Login\UserPermission;
|
||||||
use Laminas\Diactoros\Response\RedirectResponse;
|
use Laminas\Diactoros\Response\RedirectResponse;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
final class SetupRoute
|
final class SetupRoute
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly EntityManager $entityManager,
|
private readonly EntityManager $entityManager,
|
||||||
private readonly LoginHandler $loginHandler
|
private readonly LoginHandler $loginHandler
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(ServerRequestInterface $request): ResponseInterface {
|
public function __invoke(ServerRequestInterface $request): ResponseInterface {
|
||||||
if (!$this->loginHandler->isLoggedIn()) {
|
if (!$this->loginHandler->isLoggedIn()) {
|
||||||
return new RedirectResponse('/login');
|
return new RedirectResponse('/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
$repo = $this->entityManager->getRepository(SystemAttribute::class);
|
$repo = $this->entityManager->getRepository(SystemAttribute::class);
|
||||||
|
|
||||||
$attribute = $repo->find('ADMIN_SETUP_COMPLETED');
|
$attribute = $repo->find('ADMIN_SETUP_COMPLETED');
|
||||||
if ($attribute) {
|
if ($attribute) {
|
||||||
return new RedirectResponse('/');
|
return new RedirectResponse('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->loginHandler->getCurrentUser();
|
$user = $this->loginHandler->getCurrentUser();
|
||||||
$user->setPermission(UserPermission::ADMIN);
|
$user->setPermission(UserPermission::ADMIN);
|
||||||
|
|
||||||
$attribute = new SystemAttribute(
|
$attribute = new SystemAttribute(
|
||||||
'ADMIN_SETUP_COMPLETED',
|
'ADMIN_SETUP_COMPLETED',
|
||||||
'true'
|
'true'
|
||||||
);
|
);
|
||||||
$this->entityManager->persist($attribute);
|
$this->entityManager->persist($attribute);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
return new RedirectResponse('/');
|
return new RedirectResponse('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function applyRoutes(\League\Route\Router $router) {
|
public static function applyRoutes(\League\Route\Router $router) {
|
||||||
$router->get('/setup-admin', self::class);
|
$router->get('/setup-admin', self::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop;
|
namespace GamesShop;
|
||||||
|
|
||||||
final class SetupHandler
|
final class SetupHandler
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,17 +1,17 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Templates;
|
namespace GamesShop\Templates;
|
||||||
|
|
||||||
use GamesShop\Login\UserPermission;
|
use GamesShop\Login\UserPermission;
|
||||||
|
|
||||||
final class NavigationHeader
|
final class NavigationHeader
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public readonly string $title,
|
public readonly string $title,
|
||||||
public readonly string $link,
|
public readonly string $link,
|
||||||
public readonly UserPermission $minimumPermission,
|
public readonly UserPermission $minimumPermission,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,16 +1,16 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Templates;
|
namespace GamesShop\Templates;
|
||||||
|
|
||||||
final class ResourceEntry
|
final class ResourceEntry
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param string[] $js
|
* @param string[] $js
|
||||||
* @param string[] $css
|
* @param string[] $css
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public readonly array $js,
|
public readonly array $js,
|
||||||
public readonly array $css,
|
public readonly array $css,
|
||||||
) { }
|
) { }
|
||||||
}
|
}
|
|
@ -1,51 +1,51 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Templates;
|
namespace GamesShop\Templates;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use GamesShop\Paths;
|
use GamesShop\Paths;
|
||||||
|
|
||||||
final class ResourceIndex
|
final class ResourceIndex
|
||||||
{
|
{
|
||||||
private const string PATH = Paths::SOURCE_PATH . '/file-index.json';
|
private const string PATH = Paths::SOURCE_PATH . '/file-index.json';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ResourceEntry[]
|
* @var ResourceEntry[]
|
||||||
*/
|
*/
|
||||||
private array $resources = [];
|
private array $resources = [];
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$fileContents = file_get_contents(self::PATH);
|
$fileContents = file_get_contents(self::PATH);
|
||||||
$index = json_decode($fileContents, true);
|
$index = json_decode($fileContents, true);
|
||||||
|
|
||||||
foreach ($index as $entryKey => $resource) {
|
foreach ($index as $entryKey => $resource) {
|
||||||
$js = $resource['js'];
|
$js = $resource['js'];
|
||||||
if (is_string($js)) {
|
if (is_string($js)) {
|
||||||
$js = [$js];
|
$js = [$js];
|
||||||
}
|
}
|
||||||
|
|
||||||
$css = $resource['css'];
|
$css = $resource['css'];
|
||||||
if (is_string($css)) {
|
if (is_string($css)) {
|
||||||
$css = [$css];
|
$css = [$css];
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->resources[$entryKey] = new ResourceEntry(
|
$this->resources[$entryKey] = new ResourceEntry(
|
||||||
$js, $css
|
$js, $css
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function getResource(string $entry): ResourceEntry
|
public function getResource(string $entry): ResourceEntry
|
||||||
{
|
{
|
||||||
if (!array_key_exists($entry, $this->resources)) {
|
if (!array_key_exists($entry, $this->resources)) {
|
||||||
throw new Exception("Entry '$entry' not found");
|
throw new Exception("Entry '$entry' not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->resources[$entry];
|
return $this->resources[$entry];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,34 +1,34 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GamesShop\Templates;
|
namespace GamesShop\Templates;
|
||||||
|
|
||||||
use GamesShop\Login\LoginHandler;
|
use GamesShop\Login\LoginHandler;
|
||||||
use GamesShop\Paths;
|
use GamesShop\Paths;
|
||||||
use League\Plates\Engine;
|
use League\Plates\Engine;
|
||||||
|
|
||||||
final class TemplateEngine extends Engine
|
final class TemplateEngine extends Engine
|
||||||
{
|
{
|
||||||
private const string TEMPLATES_PATH = Paths::SOURCE_PATH . '/templates';
|
private const string TEMPLATES_PATH = Paths::SOURCE_PATH . '/templates';
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private ResourceIndex $resourceIndex,
|
private ResourceIndex $resourceIndex,
|
||||||
LoginHandler $loginHandler,
|
LoginHandler $loginHandler,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
parent::__construct(self::TEMPLATES_PATH, 'php');
|
parent::__construct(self::TEMPLATES_PATH, 'php');
|
||||||
$this->addData([
|
$this->addData([
|
||||||
'resources' => $this->resourceIndex,
|
'resources' => $this->resourceIndex,
|
||||||
'activeUser' => $loginHandler->isLoggedIn() ? $loginHandler->getCurrentUser() : null,
|
'activeUser' => $loginHandler->isLoggedIn() ? $loginHandler->getCurrentUser() : null,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderPage(string $page, array $data = array())
|
public function renderPage(string $page, array $data = array())
|
||||||
{
|
{
|
||||||
return parent::render("pages/$page", $data);
|
return parent::render("pages/$page", $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderErrorPage(int $error) {
|
public function renderErrorPage(int $error) {
|
||||||
return self::renderPage('error', [ 'errorCode' => $error ]);
|
return self::renderPage('error', [ 'errorCode' => $error ]);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,15 +1,15 @@
|
||||||
#!/usr/bin/env php
|
#!/usr/bin/env php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use Doctrine\ORM\Tools\Console\ConsoleRunner;
|
use Doctrine\ORM\Tools\Console\ConsoleRunner;
|
||||||
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
|
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
|
||||||
use GamesShop\ContainerHandler;
|
use GamesShop\ContainerHandler;
|
||||||
|
|
||||||
require_once __DIR__ . '/../bootstrap.php';
|
require_once __DIR__ . '/../bootstrap.php';
|
||||||
|
|
||||||
$entityManager = ContainerHandler::get(EntityManager::class);
|
$entityManager = ContainerHandler::get(EntityManager::class);
|
||||||
ConsoleRunner::run(
|
ConsoleRunner::run(
|
||||||
new SingleManagerProvider($entityManager)
|
new SingleManagerProvider($entityManager)
|
||||||
);
|
);
|
|
@ -1,11 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use GamesShop\ContainerHandler;
|
use GamesShop\ContainerHandler;
|
||||||
use GamesShop\DoctrineManager;
|
use GamesShop\DoctrineManager;
|
||||||
use GamesShop\Environment\EnvironmentHandler;
|
use GamesShop\Environment\EnvironmentHandler;
|
||||||
|
|
||||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||||
|
|
||||||
ContainerHandler::get(EnvironmentHandler::class)->load();
|
ContainerHandler::get(EnvironmentHandler::class)->load();
|
||||||
ContainerHandler::get(DoctrineManager::class)->setup();
|
ContainerHandler::get(DoctrineManager::class)->setup();
|
|
@ -1,25 +1,25 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use GamesShop\ContainerHandler;
|
use GamesShop\ContainerHandler;
|
||||||
use GamesShop\DoctrineManager;
|
use GamesShop\DoctrineManager;
|
||||||
use GamesShop\Environment\EnvironmentHandler;
|
use GamesShop\Environment\EnvironmentHandler;
|
||||||
use GamesShop\Routing\Router;
|
use GamesShop\Routing\Router;
|
||||||
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
|
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
|
||||||
use Whoops\Handler\HandlerInterface;
|
use Whoops\Handler\HandlerInterface;
|
||||||
use Whoops\Handler\PrettyPageHandler;
|
use Whoops\Handler\PrettyPageHandler;
|
||||||
use Whoops\Run;
|
use Whoops\Run;
|
||||||
|
|
||||||
require_once __DIR__ . '/../src/php/bootstrap.php';
|
require_once __DIR__ . '/../src/php/bootstrap.php';
|
||||||
|
|
||||||
$whoops = new Run();
|
$whoops = new Run();
|
||||||
$prettyPageHandler = new PrettyPageHandler();
|
$prettyPageHandler = new PrettyPageHandler();
|
||||||
$whoops->pushHandler($prettyPageHandler);
|
$whoops->pushHandler($prettyPageHandler);
|
||||||
$whoops->register();
|
$whoops->register();
|
||||||
|
|
||||||
ContainerHandler::getInstance()->addShared(HandlerInterface::class, $prettyPageHandler);
|
ContainerHandler::getInstance()->addShared(HandlerInterface::class, $prettyPageHandler);
|
||||||
|
|
||||||
$router = ContainerHandler::getInstance()->get(Router::class);
|
$router = ContainerHandler::getInstance()->get(Router::class);
|
||||||
$result = $router->route();
|
$result = $router->route();
|
||||||
|
|
||||||
(new SapiEmitter)->emit($result);
|
(new SapiEmitter)->emit($result);
|
|
@ -1,2 +1,2 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
|
@ -1,35 +1,35 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use GamesShop\Entities\Account\User;
|
use GamesShop\Entities\Account\User;
|
||||||
|
|
||||||
/** @var User|null $activeUser */
|
/** @var User|null $activeUser */
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<?php if ($activeUser !== null): ?>
|
<?php if ($activeUser !== null): ?>
|
||||||
<div class="d-flex avatar justify-content-center">
|
<div class="d-flex avatar justify-content-center">
|
||||||
|
|
||||||
<div class="avatar-icon h-100 position-relative me-2 ratio-1">
|
<div class="avatar-icon h-100 position-relative me-2 ratio-1">
|
||||||
<img src="<?= $activeUser->getProfilePictureUrl(); ?>" class="rounded-circle h-100" alt="User Profile Picture" />
|
<img src="<?= $activeUser->getProfilePictureUrl(); ?>" class="rounded-circle h-100" alt="User Profile Picture" />
|
||||||
<div class="position-absolute bottom-0 end-0 ratio-1 d-flex align-items-center z-1 avatar-login-method">
|
<div class="position-absolute bottom-0 end-0 ratio-1 d-flex align-items-center z-1 avatar-login-method">
|
||||||
<i class="fa-brands <?= $activeUser->getLoginMethod()->getIconClass() ?>"></i>
|
<i class="fa-brands <?= $activeUser->getLoginMethod()->getIconClass() ?>"></i>
|
||||||
<span class="position-absolute w-100 h-100 bottom-0 end-0 ratio-1 bg-body rounded-circle z-n1 avatar-login-method-icon"></span>
|
<span class="position-absolute w-100 h-100 bottom-0 end-0 ratio-1 bg-body rounded-circle z-n1 avatar-login-method-icon"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex flex-column">
|
<div class="d-flex flex-column">
|
||||||
<span class="me-2 h-3">
|
<span class="me-2 h-3">
|
||||||
<?= $activeUser->getName() ?>
|
<?= $activeUser->getName() ?>
|
||||||
</span>
|
</span>
|
||||||
<small class="text-muted">
|
<small class="text-muted">
|
||||||
<?= $activeUser->getPermission()->getHumanReadableName() ?>
|
<?= $activeUser->getPermission()->getHumanReadableName() ?>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="h-100 d-flex align-items-center ms-2">
|
<div class="h-100 d-flex align-items-center ms-2">
|
||||||
<a href="/logout">
|
<a href="/logout">
|
||||||
<i class="fa-solid fa-arrow-right-to-bracket fa-xl text-danger"></i>
|
<i class="fa-solid fa-arrow-right-to-bracket fa-xl text-danger"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,49 +1,49 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use GamesShop\Templates\ResourceIndex;
|
use GamesShop\Templates\ResourceIndex;
|
||||||
use League\Plates\Template\Template;
|
use League\Plates\Template\Template;
|
||||||
|
|
||||||
assert($this instanceof Template);
|
assert($this instanceof Template);
|
||||||
/** @var ResourceIndex $resources */
|
/** @var ResourceIndex $resources */
|
||||||
assert($resources instanceof ResourceIndex);
|
assert($resources instanceof ResourceIndex);
|
||||||
|
|
||||||
if (!isset($resourceEntry)) {
|
if (!isset($resourceEntry)) {
|
||||||
throw new Exception("Resource entry not set");
|
throw new Exception("Resource entry not set");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string $resourceEntry
|
* @var string $resourceEntry
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$resource = $resources->getResource($resourceEntry);
|
$resource = $resources->getResource($resourceEntry);
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Game Shop</title>
|
<title>Game Shop</title>
|
||||||
|
|
||||||
<?php foreach ($resource->js as $js): ?>
|
<?php foreach ($resource->js as $js): ?>
|
||||||
<script src="/<?= $js ?>"></script>
|
<script src="/<?= $js ?>"></script>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php foreach ($resource->css as $css): ?>
|
<?php foreach ($resource->css as $css): ?>
|
||||||
<link rel="stylesheet" href="/<?= $css ?>">
|
<link rel="stylesheet" href="/<?= $css ?>">
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</head>
|
</head>
|
||||||
<body class="vh-100 d-flex flex-column">
|
<body class="vh-100 d-flex flex-column">
|
||||||
<?= $this->insert('layout/navbar') ?>
|
<?= $this->insert('layout/navbar') ?>
|
||||||
|
|
||||||
<main class="mt-2 position-relative flex-grow-1">
|
<main class="mt-2 position-relative flex-grow-1">
|
||||||
<?= $this->section('content'); ?>
|
<?= $this->section('content'); ?>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<?= $this->section('modal') ?>
|
<?= $this->section('modal') ?>
|
||||||
|
|
||||||
<div class="position-absolute bottom-0 start-50 opacity-25 text-center translate-middle-x">
|
<div class="position-absolute bottom-0 start-50 opacity-25 text-center translate-middle-x">
|
||||||
<span class="h1">PROTOTYPE / POC</span>
|
<span class="h1">PROTOTYPE / POC</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,53 +1,53 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use GamesShop\Entities\Account\User;
|
use GamesShop\Entities\Account\User;
|
||||||
use GamesShop\Login\UserPermission;
|
use GamesShop\Login\UserPermission;
|
||||||
use GamesShop\Templates\NavigationHeader;
|
use GamesShop\Templates\NavigationHeader;
|
||||||
|
|
||||||
$headers = [
|
$headers = [
|
||||||
new NavigationHeader('My Keys', '/keys', UserPermission::PROVIDER),
|
new NavigationHeader('My Keys', '/keys', UserPermission::PROVIDER),
|
||||||
new NavigationHeader('Accounts', '/accounts', UserPermission::ADMIN)
|
new NavigationHeader('Accounts', '/accounts', UserPermission::ADMIN)
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @var User|null $activeUser */
|
/** @var User|null $activeUser */
|
||||||
$currentPermission = $activeUser === null ? UserPermission::NONE : $activeUser->getPermission();
|
$currentPermission = $activeUser === null ? UserPermission::NONE : $activeUser->getPermission();
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-lg bg-body-tertiary main-navigation">
|
<nav class="navbar navbar-expand-lg bg-body-tertiary main-navigation">
|
||||||
<div class="container-fluid navigation-container">
|
<div class="container-fluid navigation-container">
|
||||||
<a class="navbar-brand" href="/">Game Shop</a>
|
<a class="navbar-brand" href="/">Game Shop</a>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar-content" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar-content" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navbar-content">
|
<div class="collapse navbar-collapse" id="navbar-content">
|
||||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
<?php foreach ($headers as $header):
|
<?php foreach ($headers as $header):
|
||||||
if (!$currentPermission->hasLevel($header->minimumPermission)) {
|
if (!$currentPermission->hasLevel($header->minimumPermission)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
|
||||||
<li class="nav-link">
|
<li class="nav-link">
|
||||||
<a href="<?= $header->link ?>" class="nav-link"><?= $header->title ?></a>
|
<a href="<?= $header->link ?>" class="nav-link"><?= $header->title ?></a>
|
||||||
</li>
|
</li>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<?= $this->insert('layout/accountDisplay'); ?>
|
<?= $this->insert('layout/accountDisplay'); ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex mode-switch me-3">
|
<div class="d-flex mode-switch me-3">
|
||||||
<button title="Use dark mode" id="dark" class="btn btn-sm btn-default text-secondary">
|
<button title="Use dark mode" id="dark" class="btn btn-sm btn-default text-secondary">
|
||||||
<i class="fa-regular fa-moon"></i>
|
<i class="fa-regular fa-moon"></i>
|
||||||
</button>
|
</button>
|
||||||
<button title="Use light mode" id="light" class="btn btn-sm btn-default text-secondary">
|
<button title="Use light mode" id="light" class="btn btn-sm btn-default text-secondary">
|
||||||
<i class="fa-regular fa-sun"></i>
|
<i class="fa-regular fa-sun"></i>
|
||||||
</button>
|
</button>
|
||||||
<button title="Use system preferred mode" id="system" class="btn btn-sm btn-default text-secondary">
|
<button title="Use system preferred mode" id="system" class="btn btn-sm btn-default text-secondary">
|
||||||
<i class="fa-solid fa-display"></i>
|
<i class="fa-solid fa-display"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
|
@ -1,57 +1,57 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use GamesShop\Login\UserPermission;
|
use GamesShop\Login\UserPermission;
|
||||||
|
|
||||||
$this->layout('layout/main', [ 'resourceEntry' => 'admin/accounts' ]);
|
$this->layout('layout/main', [ 'resourceEntry' => 'admin/accounts' ]);
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<h1>Users</h1>
|
<h1>Users</h1>
|
||||||
|
|
||||||
<table id="user-table" class="table table-striped w-100">
|
<table id="user-table" class="table table-striped w-100">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="2.4rem"></th>
|
<th width="2.4rem"></th>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Permission</th>
|
<th>Permission</th>
|
||||||
<th>Login-Method</th>
|
<th>Login-Method</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody></tbody>
|
<tbody></tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<?php $this->start('modal') ?>
|
<?php $this->start('modal') ?>
|
||||||
<div class="modal" id="edit-modal">
|
<div class="modal" id="edit-modal">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h1 class="modal-title h3">
|
<h1 class="modal-title h3">
|
||||||
Edit User
|
Edit User
|
||||||
</h1>
|
</h1>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
Name: <span class="name-input"></span>
|
Name: <span class="name-input"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
Login Method: <span class="login-method"></span>
|
Login Method: <span class="login-method"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="permissions">Permissions:</label>
|
<label for="permissions">Permissions:</label>
|
||||||
<select name="" id="permissions" class="form-select permission-editor">
|
<select name="" id="permissions" class="form-select permission-editor">
|
||||||
<?php foreach (UserPermission::cases() as $userPermission):?>
|
<?php foreach (UserPermission::cases() as $userPermission):?>
|
||||||
<option value="<?= $userPermission->value ?>"><?= $userPermission->getHumanReadableName() ?></option>
|
<option value="<?= $userPermission->value ?>"><?= $userPermission->getHumanReadableName() ?></option>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
<button type="button" class="btn btn-primary js--save">Save changes</button>
|
<button type="button" class="btn btn-primary js--save">Save changes</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php $this->end() ?>
|
<?php $this->end() ?>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
$this->layout('layout/main', [ 'resourceEntry' => 'index' ]);
|
$this->layout('layout/main', [ 'resourceEntry' => 'index' ]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int $errorCode
|
* @var int $errorCode
|
||||||
*/
|
*/
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<img src="https://http.dog/<?= $errorCode ?>.jpg" alt="Error <?= $errorCode ?>" class="position-absolute top-50 start-50 translate-middle w-100" style="max-width: 800px" />
|
<img src="https://http.dog/<?= $errorCode ?>.jpg" alt="Error <?= $errorCode ?>" class="position-absolute top-50 start-50 translate-middle w-100" style="max-width: 800px" />
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
$this->layout('layout/main', [ 'resourceEntry' => 'index' ]);
|
$this->layout('layout/main', [ 'resourceEntry' => 'index' ]);
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<h1>
|
<h1>
|
||||||
Hello
|
Hello
|
||||||
</h1>
|
</h1>
|
|
@ -1,143 +1,153 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use GamesShop\Entities\Games\KeyAttribute;
|
use GamesShop\Entities\Games\KeyAttribute;
|
||||||
use GamesShop\Entities\GamesList;
|
use GamesShop\Entities\GamesList;
|
||||||
use League\Plates\Template\Template;
|
use League\Plates\Template\Template;
|
||||||
|
|
||||||
assert($this instanceof Template);
|
assert($this instanceof Template);
|
||||||
|
|
||||||
/** @var GamesList[] $usersLists */
|
/** @var GamesList[] $usersLists */
|
||||||
|
|
||||||
$this->layout('layout/main', [ 'resourceEntry' => 'keys' ]);
|
$this->layout('layout/main', [ 'resourceEntry' => 'keys' ]);
|
||||||
?>
|
?>
|
||||||
<meta name="key-attributes" content="<?= htmlspecialchars(json_encode(KeyAttribute::casesAsAssociative())) ?>" />
|
<meta name="key-attributes" content="<?= htmlspecialchars(json_encode(KeyAttribute::casesAsAssociative())) ?>" />
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<h1>My Keys</h1>
|
<h1>My Keys</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6 align-self-center">
|
<div class="col-sm-6 align-self-center">
|
||||||
<?php if (!empty($usersLists)): ?>
|
<?php if (!empty($usersLists)): ?>
|
||||||
<select name="lists" id="list-select" class="form-select w-100">
|
<select name="lists" id="list-select" class="form-select w-100">
|
||||||
<?php foreach ($usersLists as $list): ?>
|
<?php foreach ($usersLists as $list): ?>
|
||||||
<option value="<?= $list->getId() ?>"><?= $list->getName() ?></option>
|
<option value="<?= $list->getId() ?>"><?= $list->getName() ?></option>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<option value="_create">+ Create New</option>
|
<option value="_create">+ Create New</option>
|
||||||
</select>
|
</select>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php if (empty($usersLists)): ?>
|
<?php if (empty($usersLists)): ?>
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<p class="fs-4 mb-4">You don't have a key list. Create one here.</p>
|
<p class="fs-4 mb-4">You don't have a key list. Create one here.</p>
|
||||||
|
|
||||||
<button class="btn btn-primary btn-lg js--create-list-button">
|
<button class="btn btn-primary btn-lg js--create-list-button">
|
||||||
Create
|
Create
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
|
|
||||||
<ul id="key-tab" class="nav nav-tabs">
|
<ul id="key-tab" class="nav nav-tabs">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#keys-tab-pane" role="tab">
|
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#keys-tab-pane" role="tab">
|
||||||
Keys
|
Keys
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#import-tab-pane" role="tab">
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#import-tab-pane" role="tab">
|
||||||
Import
|
Import
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#share-tab-pane" role="tab">
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#share-tab-pane" role="tab">
|
||||||
Share
|
Share
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane fade show active" id="keys-tab-pane" role="tabpanel">
|
<div class="tab-pane fade show active" id="keys-tab-pane" role="tabpanel">
|
||||||
<table class="table table-striped key-table">
|
<table class="table table-striped key-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<th></th>
|
||||||
<td>Game Name</td>
|
<th>Game Name</th>
|
||||||
<td>Amount Keys</td>
|
<th>Amount Keys</th>
|
||||||
<td>IGDB State</td>
|
<th>IGDB State</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody></tbody>
|
<tbody></tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="import-tab-pane" role="tabpanel">
|
<div class="tab-pane fade" id="import-tab-pane" role="tabpanel">
|
||||||
<h2>Importer</h2>
|
<h2>Importer</h2>
|
||||||
<label for="formFile" class="form-label mt-3">Insert import file:</label>
|
<label for="formFile" class="form-label mt-3">Insert import file:</label>
|
||||||
|
|
||||||
<div class="mb-3 input-group">
|
<div class="mb-3 input-group">
|
||||||
<input class="form-control" type="file" id="import" accept="text/csv,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">
|
<input class="form-control" type="file" id="import" accept="text/csv,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">
|
||||||
<button class="btn btn-primary js--send-import">Send</button>
|
<button class="btn btn-primary js--send-import">Send</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-none" id="import-info-container">
|
<div class="d-none" id="import-info-container">
|
||||||
<h3>Import Details:</h3>
|
<h3>Import Details:</h3>
|
||||||
|
|
||||||
<table class="table table-striped w-100" id="import-attribute-table">
|
<table class="table table-striped w-100" id="import-attribute-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Column</td>
|
<th>Column</th>
|
||||||
<td>Header</td>
|
<th>Header</th>
|
||||||
<td>Attribute</td>
|
<th>Attribute</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody></tbody>
|
<tbody></tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<button class="btn btn-primary js--do-import">
|
<button class="btn btn-primary js--do-import">
|
||||||
Import
|
Import
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="share-tab-pane" role="tabpanel">
|
<div class="tab-pane fade" id="share-tab-pane" role="tabpanel">
|
||||||
<h2>
|
<h2>
|
||||||
Share your list
|
Share your list
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<label for="share-user-search">Search for a user...</label>
|
<label for="share-user-search">Search for a user...</label>
|
||||||
<div class="input-group">
|
<div class="input-group mb-3">
|
||||||
<input type="text" class="form-control" id="share-user-search" placeholder="">
|
<input type="text" class="form-control" id="share-user-search" placeholder="">
|
||||||
<button class="btn btn-primary js--search-shared-user">Search</button>
|
<button class="btn btn-primary js--search-shared-user">Search</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h3>Users shared to</h3>
|
||||||
</div>
|
<table id="shared-users-table" class="table table-striped w-100">
|
||||||
</div>
|
<thead>
|
||||||
|
<tr>
|
||||||
<?php endif; ?>
|
<th width="2.4rem"></th>
|
||||||
|
<th>Name</th>
|
||||||
<?php $this->start('modal') ?>
|
</tr>
|
||||||
|
</thead>
|
||||||
<div class="modal" id="create-list-modal">
|
<tbody></tbody>
|
||||||
<div class="modal-dialog">
|
</table>
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
</div>
|
||||||
<h1 class="modal-title h3">
|
</div>
|
||||||
Create list
|
|
||||||
</h1>
|
<?php endif; ?>
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
<?php $this->start('modal') ?>
|
||||||
<div class="form-floating">
|
|
||||||
<input class="form-control" type="text" id="createListName" placeholder="">
|
<div class="modal" id="create-list-modal">
|
||||||
<label for="createListName">Name</label>
|
<div class="modal-dialog">
|
||||||
</div>
|
<div class="modal-content">
|
||||||
</div>
|
<div class="modal-header">
|
||||||
<div class="modal-footer">
|
<h1 class="modal-title h3">
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
Create list
|
||||||
<button type="button" class="btn btn-primary js--create-list">Create</button>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="modal-body">
|
||||||
</div>
|
<div class="form-floating">
|
||||||
</div>
|
<input class="form-control" type="text" id="createListName" placeholder="">
|
||||||
|
<label for="createListName">Name</label>
|
||||||
<?php $this->end() ?>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary js--create-list">Create</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php $this->end() ?>
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
$this->layout('layout/main', [ 'resourceEntry' => 'index' ]);
|
$this->layout('layout/main', [ 'resourceEntry' => 'index' ]);
|
||||||
|
|
||||||
/** @var string $discordUrl */
|
/** @var string $discordUrl */
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<a href="<?= $discordUrl ?>">
|
<a href="<?= $discordUrl ?>">
|
||||||
<button class="btn btn-primary position-absolute top-50 start-50 translate-middle">
|
<button class="btn btn-primary position-absolute top-50 start-50 translate-middle">
|
||||||
<i class="fa-brands fa-discord"></i>
|
<i class="fa-brands fa-discord"></i>
|
||||||
Login with Discord
|
Login with Discord
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</a>
|
</a>
|
|
@ -1,10 +1,10 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2016",
|
"target": "es2016",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"skipLibCheck": true
|
"skipLibCheck": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,102 +1,102 @@
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin'),
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin'),
|
||||||
Path = require('path'),
|
Path = require('path'),
|
||||||
AssetsPlugin = require('assets-webpack-plugin'),
|
AssetsPlugin = require('assets-webpack-plugin'),
|
||||||
CopyPlugin = require('copy-webpack-plugin');
|
CopyPlugin = require('copy-webpack-plugin');
|
||||||
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
|
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
|
||||||
|
|
||||||
const PUBLIC_FOLDER = Path.resolve(__dirname, 'public'),
|
const PUBLIC_FOLDER = Path.resolve(__dirname, 'public'),
|
||||||
SOURCE_FOLDER = Path.resolve(__dirname, 'src'),
|
SOURCE_FOLDER = Path.resolve(__dirname, 'src'),
|
||||||
JS_FOLDER = Path.resolve(SOURCE_FOLDER, 'js'),
|
JS_FOLDER = Path.resolve(SOURCE_FOLDER, 'js'),
|
||||||
CSS_FOLDER = Path.resolve(SOURCE_FOLDER, 'css'),
|
CSS_FOLDER = Path.resolve(SOURCE_FOLDER, 'css'),
|
||||||
PHP_FOLDER = Path.resolve(SOURCE_FOLDER, 'php');
|
PHP_FOLDER = Path.resolve(SOURCE_FOLDER, 'php');
|
||||||
|
|
||||||
const PROD = false;
|
const PROD = false;
|
||||||
|
|
||||||
const INDEX_PATH = Path.resolve(PHP_FOLDER, PROD ? 'index.prod.php' : 'index.dev.php');
|
const INDEX_PATH = Path.resolve(PHP_FOLDER, PROD ? 'index.prod.php' : 'index.dev.php');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
new AssetsPlugin({
|
new AssetsPlugin({
|
||||||
filename: 'file-index.json',
|
filename: 'file-index.json',
|
||||||
path: SOURCE_FOLDER,
|
path: SOURCE_FOLDER,
|
||||||
includeAllFileTypes: false,
|
includeAllFileTypes: false,
|
||||||
entrypoints: true,
|
entrypoints: true,
|
||||||
removeFullPathAutoPrefix: true
|
removeFullPathAutoPrefix: true
|
||||||
}),
|
}),
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
filename: 'css/[name].css'
|
filename: 'css/[name].css'
|
||||||
}),
|
}),
|
||||||
new CopyPlugin({
|
new CopyPlugin({
|
||||||
patterns: [
|
patterns: [
|
||||||
{
|
{
|
||||||
from: INDEX_PATH,
|
from: INDEX_PATH,
|
||||||
to: PUBLIC_FOLDER + '/index.php'
|
to: PUBLIC_FOLDER + '/index.php'
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
mode: PROD ? 'production' : 'development',
|
mode: PROD ? 'production' : 'development',
|
||||||
devtool: 'source-map',
|
devtool: 'source-map',
|
||||||
optimization: {
|
optimization: {
|
||||||
runtimeChunk: "single",
|
runtimeChunk: "single",
|
||||||
minimize: true,
|
minimize: true,
|
||||||
minimizer: [
|
minimizer: [
|
||||||
new CssMinimizerPlugin()
|
new CssMinimizerPlugin()
|
||||||
],
|
],
|
||||||
splitChunks: {
|
splitChunks: {
|
||||||
cacheGroups: {
|
cacheGroups: {
|
||||||
vendor: {
|
vendor: {
|
||||||
test: /[\\/]node_modules[\\/]/,
|
test: /[\\/]node_modules[\\/]/,
|
||||||
name: 'vendors',
|
name: 'vendors',
|
||||||
chunks: 'all',
|
chunks: 'all',
|
||||||
},
|
},
|
||||||
common: {
|
common: {
|
||||||
test: /[\\/]common[\\/]/,
|
test: /[\\/]common[\\/]/,
|
||||||
name: 'common',
|
name: 'common',
|
||||||
chunks: 'all'
|
chunks: 'all'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.css$/i,
|
test: /\.css$/i,
|
||||||
use: [
|
use: [
|
||||||
MiniCssExtractPlugin.loader,
|
MiniCssExtractPlugin.loader,
|
||||||
'css-loader'
|
'css-loader'
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.scss$/i,
|
test: /\.scss$/i,
|
||||||
use: [
|
use: [
|
||||||
MiniCssExtractPlugin.loader,
|
MiniCssExtractPlugin.loader,
|
||||||
'css-loader',
|
'css-loader',
|
||||||
'resolve-url-loader',
|
'resolve-url-loader',
|
||||||
"sass-loader"
|
"sass-loader"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.ts$/i,
|
test: /\.ts$/i,
|
||||||
use: 'ts-loader',
|
use: 'ts-loader',
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(woff|woff2|eot|ttf|otf|gif|svg)$/i,
|
test: /\.(woff|woff2|eot|ttf|otf|gif|svg)$/i,
|
||||||
type: 'asset/resource',
|
type: 'asset/resource',
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.js', '.ts'],
|
extensions: ['.js', '.ts'],
|
||||||
},
|
},
|
||||||
entry: {
|
entry: {
|
||||||
index: JS_FOLDER + "/pages/index",
|
index: JS_FOLDER + "/pages/index",
|
||||||
'admin/accounts': JS_FOLDER + "/pages/admin/accounts",
|
'admin/accounts': JS_FOLDER + "/pages/admin/accounts",
|
||||||
keys: JS_FOLDER + "/pages/keys/index",
|
keys: JS_FOLDER + "/pages/keys/index",
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: PUBLIC_FOLDER,
|
path: PUBLIC_FOLDER,
|
||||||
filename: 'js/[name].js'
|
filename: 'js/[name].js'
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue