feat: Update drone pipeline and remove items from the repo #39
@ -1,49 +0,0 @@
|
|||||||
---
|
|
||||||
- name: install and configure PHP8 and composer
|
|
||||||
hosts: sptarkov
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
- name: Delete spt-items-api before adding everything again
|
|
||||||
file:
|
|
||||||
state: absent
|
|
||||||
path: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
|
||||||
- name: Copy the project
|
|
||||||
copy:
|
|
||||||
src: ../items/api/
|
|
||||||
dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
|
||||||
- name: Copy PHP .env file
|
|
||||||
template:
|
|
||||||
src: ./templates/.php-env.j2
|
|
||||||
dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}/.env"
|
|
||||||
- name: Get JS chunks name
|
|
||||||
shell:
|
|
||||||
cmd: find "{{ lookup('env', 'SPT_ITEMS_PATH') }}" -type f -name "*chunk.js" -printf "%f\n"
|
|
||||||
register: find_output
|
|
||||||
- name: Get file names from find output
|
|
||||||
set_fact:
|
|
||||||
chunk_list: "{{ find_output['stdout'].split('\n') }}"
|
|
||||||
- name: Copy app.blade.php file
|
|
||||||
template:
|
|
||||||
src: ./templates/app.blade.php.j2
|
|
||||||
dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}/resources/views/app.blade.php"
|
|
||||||
|
|
||||||
- name: Download and installs all composer libs and dependencies
|
|
||||||
community.general.composer:
|
|
||||||
command: install
|
|
||||||
working_dir: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
|
||||||
|
|
||||||
# Ensure the permissions
|
|
||||||
- name: Reset files permissions
|
|
||||||
file:
|
|
||||||
path: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
|
||||||
owner: "{{ lookup('env', 'DEPLOY_USER') }}"
|
|
||||||
group: "{{ lookup('env', 'DEPLOY_USER_GROUP') }}"
|
|
||||||
mode: 0775
|
|
||||||
recurse: yes
|
|
||||||
|
|
||||||
- name: Initialize database
|
|
||||||
uri:
|
|
||||||
url: "https://{{ lookup('env', 'SPT_ITEMS_HOSTNAME') }}/api/refresh"
|
|
||||||
method: GET
|
|
||||||
status_code: [200, 204]
|
|
||||||
timeout: 60
|
|
@ -1,2 +0,0 @@
|
|||||||
collections:
|
|
||||||
- name: community.general # For composer
|
|
@ -1,19 +0,0 @@
|
|||||||
APP_NAME="Item Finder"
|
|
||||||
APP_ENV=production
|
|
||||||
APP_KEY=
|
|
||||||
APP_DEBUG=false
|
|
||||||
APP_URL="https://{{ lookup('env', 'SPT_ITEMS_HOSTNAME') }}"
|
|
||||||
APP_TIMEZONE=UTC
|
|
||||||
|
|
||||||
LOG_CHANNEL=stack
|
|
||||||
LOG_SLACK_WEBHOOK_URL=
|
|
||||||
|
|
||||||
DB_CONNECTION=mysql
|
|
||||||
DB_HOST=127.0.0.1
|
|
||||||
DB_PORT=3306
|
|
||||||
DB_DATABASE=homestead
|
|
||||||
DB_USERNAME=homestead
|
|
||||||
DB_PASSWORD=secret
|
|
||||||
|
|
||||||
CACHE_DRIVER=file
|
|
||||||
QUEUE_CONNECTION=sync
|
|
@ -1,23 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="icon" href="/favicon.svg" />
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
||||||
<meta name="theme-color" content="#000000" />
|
|
||||||
<meta name="description" content="items finder" />
|
|
||||||
<link rel="apple-touch-icon" href="/favicon.ico" />
|
|
||||||
<link rel="manifest" href="/manifest.json" />
|
|
||||||
<title>Item Finder</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body><noscript>You need to enable JavaScript to run this app.</noscript>
|
|
||||||
<div id="root"></div>
|
|
||||||
<script>!function (e) { function t(t) { for (var n, u, a = t[0], c = t[1], f = t[2], s = 0, p = []; s < a.length; s++)u = a[s], Object.prototype.hasOwnProperty.call(o, u) && o[u] && p.push(o[u][0]), o[u] = 0; for (n in c) Object.prototype.hasOwnProperty.call(c, n) && (e[n] = c[n]); for (l && l(t); p.length;)p.shift()(); return i.push.apply(i, f || []), r() } function r() { for (var e, t = 0; t < i.length; t++) { for (var r = i[t], n = !0, a = 1; a < r.length; a++) { var c = r[a]; 0 !== o[c] && (n = !1) } n && (i.splice(t--, 1), e = u(u.s = r[0])) } return e } var n = {}, o = { 1: 0 }, i = []; function u(t) { if (n[t]) return n[t].exports; var r = n[t] = { i: t, l: !1, exports: {} }; return e[t].call(r.exports, r, r.exports, u), r.l = !0, r.exports } u.e = function (e) { var t = [], r = o[e]; if (0 !== r) if (r) t.push(r[2]); else { var n = new Promise((function (t, n) { r = o[e] = [t, n] })); t.push(r[2] = n); var i, a = document.createElement("script"); a.charset = "utf-8", a.timeout = 120, u.nc && a.setAttribute("nonce", u.nc), a.src = function (e) { return u.p + "static/js/" + ({}[e] || e) + "." + { 3: "ff1076df" }[e] + ".chunk.js" }(e); var c = new Error; i = function (t) { a.onerror = a.onload = null, clearTimeout(f); var r = o[e]; if (0 !== r) { if (r) { var n = t && ("load" === t.type ? "missing" : t.type), i = t && t.target && t.target.src; c.message = "Loading chunk " + e + " failed.\n(" + n + ": " + i + ")", c.name = "ChunkLoadError", c.type = n, c.request = i, r[1](c) } o[e] = void 0 } }; var f = setTimeout((function () { i({ type: "timeout", target: a }) }), 12e4); a.onerror = a.onload = i, document.head.appendChild(a) } return Promise.all(t) }, u.m = e, u.c = n, u.d = function (e, t, r) { u.o(e, t) || Object.defineProperty(e, t, { enumerable: !0, get: r }) }, u.r = function (e) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e, "__esModule", { value: !0 }) }, u.t = function (e, t) { if (1 & t && (e = u(e)), 8 & t) return e; if (4 & t && "object" == typeof e && e && e.__esModule) return e; var r = Object.create(null); if (u.r(r), Object.defineProperty(r, "default", { enumerable: !0, value: e }), 2 & t && "string" != typeof e) for (var n in e) u.d(r, n, function (t) { return e[t] }.bind(null, n)); return r }, u.n = function (e) { var t = e && e.__esModule ? function () { return e.default } : function () { return e }; return u.d(t, "a", t), t }, u.o = function (e, t) { return Object.prototype.hasOwnProperty.call(e, t) }, u.p = "/", u.oe = function (e) { throw console.error(e), e }; var a = this["webpackJsonpnew-item-finder-website"] = this["webpackJsonpnew-item-finder-website"] || [], c = a.push.bind(a); a.push = t, a = a.slice(); for (var f = 0; f < a.length; f++)t(a[f]); var l = c; r() }([])</script>
|
|
||||||
{% for chunk in chunk_list %}
|
|
||||||
<script src="/static/js/{{ chunk }}"></script>
|
|
||||||
{% endfor %}
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -1,11 +0,0 @@
|
|||||||
all:
|
|
||||||
children:
|
|
||||||
sptarkov:
|
|
||||||
hosts:
|
|
||||||
sp-tarkov:
|
|
||||||
ansible_host: {{ DEPLOY_HOSTNAME }}
|
|
||||||
ansible_user: {{ DEPLOY_USER }}
|
|
||||||
ansible_connection: paramiko_ssh
|
|
||||||
ansible_ssh_pass: {{ DEPLOY_SSH_KEY_PASSPHRASE }}
|
|
||||||
ansible_ssh_user: {{ DEPLOY_USER }}
|
|
||||||
ansible_ssh_private_key_file: /drone/src/private.key # Hardcoded because created by the Drone plugin
|
|
@ -1,122 +1,11 @@
|
|||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
name: db deployment
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: replace hosts and user variables
|
|
||||||
image: ubuntu:impish
|
|
||||||
environment:
|
|
||||||
SPT_ITEMS_HOSTNAME:
|
|
||||||
from_secret: spt_items_hostname
|
|
||||||
DEPLOY_HOSTNAME:
|
|
||||||
from_secret: deploy_hostname
|
|
||||||
DEPLOY_USER:
|
|
||||||
from_secret: deploy_username
|
|
||||||
DEPLOY_PRIVATE_KEY:
|
|
||||||
from_secret: deploy_ssh_key
|
|
||||||
DEPLOY_SSH_KEY_PASSPHRASE:
|
|
||||||
from_secret: deploy_ssh_key_passphrase
|
|
||||||
commands:
|
|
||||||
- sed -i 's/{{ SPT_ITEMS_HOSTNAME }}/'"$SPT_ITEMS_HOSTNAME"'/g' ./items/frontend/.env.example
|
|
||||||
- mv ./items/frontend/.env.example ./items/frontend/.env
|
|
||||||
- echo "$DEPLOY_PRIVATE_KEY" > private.key && chmod 600 private.key
|
|
||||||
- sed -i 's/{{ DEPLOY_HOSTNAME }}/'"$DEPLOY_HOSTNAME"'/g' ./.ansible-items/inventory
|
|
||||||
- sed -i 's/{{ DEPLOY_SSH_KEY_PASSPHRASE }}/'"$DEPLOY_SSH_KEY_PASSPHRASE"'/g' ./.ansible-items/inventory
|
|
||||||
- sed -i 's/{{ DEPLOY_USER }}/'"$DEPLOY_USER"'/g' ./.ansible-items/inventory
|
|
||||||
|
|
||||||
- name: install dependencies and build frontend
|
|
||||||
image: node:lts-alpine3.14
|
|
||||||
commands:
|
|
||||||
- node -v
|
|
||||||
- npm -v
|
|
||||||
- yarn --version
|
|
||||||
- yarn --cwd ./items/frontend install
|
|
||||||
- yarn --cwd ./items/frontend build --pure-lockfile
|
|
||||||
- rm -rf ./items/api/public/static/*
|
|
||||||
- cp -r ./items/frontend/build/* ./items/api/public
|
|
||||||
- rm ./items/api/public/index.html
|
|
||||||
depends_on:
|
|
||||||
- replace hosts and user variables
|
|
||||||
|
|
||||||
- name: frontend
|
|
||||||
image: nginx:1.21.4-alpine
|
|
||||||
commands:
|
|
||||||
- cp -r ./items/frontend/build/* /usr/share/nginx/html
|
|
||||||
- cp ./items/frontend/src/cypress/nginx_config/default.conf /etc/nginx/conf.d/default.conf
|
|
||||||
- nginx -g "daemon off;"
|
|
||||||
detach: true
|
|
||||||
depends_on:
|
|
||||||
- install dependencies and build frontend
|
|
||||||
|
|
||||||
- name: test frontend
|
|
||||||
image: cypress/browsers:node16.5.0-chrome94-ff93
|
|
||||||
commands:
|
|
||||||
- node -v
|
|
||||||
- npm -v
|
|
||||||
- yarn --version
|
|
||||||
- yarn --cwd ./items/frontend cy:ci
|
|
||||||
depends_on:
|
|
||||||
- install dependencies and build frontend
|
|
||||||
|
|
||||||
- name: check db ansible syntax
|
|
||||||
image: plugins/ansible:3
|
|
||||||
settings:
|
|
||||||
playbook: ./.ansible-items/playbook.yml
|
|
||||||
inventory: ./.ansible-items/inventory
|
|
||||||
galaxy: ./.ansible-items/requirements.yml
|
|
||||||
syntax_check: true
|
|
||||||
depends_on:
|
|
||||||
- clone
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- development
|
|
||||||
|
|
||||||
- name: apply ansible playbook for items database
|
|
||||||
image: plugins/ansible:3
|
|
||||||
settings:
|
|
||||||
playbook: ./.ansible-items/playbook.yml
|
|
||||||
inventory: ./.ansible-items/inventory
|
|
||||||
galaxy: ./.ansible-items/requirements.yml
|
|
||||||
timeout: 60
|
|
||||||
verbose: 2
|
|
||||||
environment:
|
|
||||||
SPT_ITEMS_HOSTNAME:
|
|
||||||
from_secret: spt_items_hostname
|
|
||||||
DEPLOY_HOSTNAME:
|
|
||||||
from_secret: deploy_hostname
|
|
||||||
DEPLOY_USER:
|
|
||||||
from_secret: deploy_username
|
|
||||||
DEPLOY_USER_GROUP:
|
|
||||||
from_secret: deploy_user_group
|
|
||||||
SPT_ITEMS_PATH:
|
|
||||||
from_secret: db_deploy_path
|
|
||||||
depends_on:
|
|
||||||
- test frontend
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
- main
|
|
||||||
event:
|
|
||||||
- promote
|
|
||||||
target:
|
|
||||||
- db
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: website deployment
|
name: website deployment
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
event:
|
event:
|
||||||
- promote
|
- push
|
||||||
branch:
|
- pull_request
|
||||||
- development
|
|
||||||
- main
|
|
||||||
- master
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: replace hosts and user variables
|
- name: replace hosts and user variables
|
||||||
@ -132,28 +21,42 @@ steps:
|
|||||||
from_secret: deploy_ssh_key_passphrase
|
from_secret: deploy_ssh_key_passphrase
|
||||||
commands:
|
commands:
|
||||||
- echo "$DEPLOY_PRIVATE_KEY" > private.key && chmod 600 private.key
|
- echo "$DEPLOY_PRIVATE_KEY" > private.key && chmod 600 private.key
|
||||||
- sed -i 's/{{ DEPLOY_HOSTNAME }}/'"$DEPLOY_HOSTNAME"'/g' ./.ansible-website/inventory
|
- sed -i 's/{{ DEPLOY_HOSTNAME }}/'"$DEPLOY_HOSTNAME"'/g' ./.ansible/inventory
|
||||||
- sed -i 's/{{ DEPLOY_SSH_KEY_PASSPHRASE }}/'"$DEPLOY_SSH_KEY_PASSPHRASE"'/g' ./.ansible-website/inventory
|
- sed -i 's/{{ DEPLOY_SSH_KEY_PASSPHRASE }}/'"$DEPLOY_SSH_KEY_PASSPHRASE"'/g' ./.ansible/inventory
|
||||||
- sed -i 's/{{ DEPLOY_USER }}/'"$DEPLOY_USER"'/g' ./.ansible-website/inventory
|
- sed -i 's/{{ DEPLOY_USER }}/'"$DEPLOY_USER"'/g' ./.ansible/inventory
|
||||||
|
when:
|
||||||
|
branch:
|
||||||
|
- master
|
||||||
|
- development
|
||||||
|
- main
|
||||||
|
- develop
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
|
||||||
- name: check website ansible syntax
|
- name: check website ansible syntax
|
||||||
image: plugins/ansible:3
|
image: plugins/ansible:3
|
||||||
settings:
|
settings:
|
||||||
playbook: ./.ansible-website/playbook.yml
|
playbook: ./.ansible/playbook.yml
|
||||||
inventory: ./.ansible-website/inventory
|
inventory: ./.ansible/inventory
|
||||||
syntax_check: true
|
syntax_check: true
|
||||||
|
depends_on:
|
||||||
|
- replace hosts and user variables
|
||||||
|
when:
|
||||||
branch:
|
branch:
|
||||||
|
- master
|
||||||
- development
|
- development
|
||||||
- main
|
- main
|
||||||
- master
|
- develop
|
||||||
target:
|
event:
|
||||||
- website
|
- push
|
||||||
|
- pull_request
|
||||||
|
|
||||||
- name: check ansible playbook website
|
- name: dry-run ansible playbook website
|
||||||
image: plugins/ansible:3
|
image: plugins/ansible:3
|
||||||
settings:
|
settings:
|
||||||
playbook: ./.ansible-website/playbook.yml
|
playbook: ./.ansible/playbook.yml
|
||||||
inventory: ./.ansible-website/inventory
|
inventory: ./.ansible/inventory
|
||||||
timeout: 60
|
timeout: 60
|
||||||
verbose: 2
|
verbose: 2
|
||||||
check: true
|
check: true
|
||||||
@ -166,16 +69,23 @@ steps:
|
|||||||
from_secret: deploy_user_group
|
from_secret: deploy_user_group
|
||||||
SPT_WEBSITE_PATH:
|
SPT_WEBSITE_PATH:
|
||||||
from_secret: website_deploy_path
|
from_secret: website_deploy_path
|
||||||
|
depends_on:
|
||||||
|
- check website ansible syntax
|
||||||
|
when:
|
||||||
branch:
|
branch:
|
||||||
|
- master
|
||||||
- development
|
- development
|
||||||
target:
|
- main
|
||||||
- website
|
- develop
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
|
||||||
- name: apply ansible playbook website
|
- name: apply ansible playbook website
|
||||||
image: plugins/ansible:3
|
image: plugins/ansible:3
|
||||||
settings:
|
settings:
|
||||||
playbook: ./.ansible-website/playbook.yml
|
playbook: ./.ansible/playbook.yml
|
||||||
inventory: ./.ansible-website/inventory
|
inventory: ./.ansible/inventory
|
||||||
timeout: 60
|
timeout: 60
|
||||||
verbose: 2
|
verbose: 2
|
||||||
environment:
|
environment:
|
||||||
@ -187,8 +97,11 @@ steps:
|
|||||||
from_secret: deploy_user_group
|
from_secret: deploy_user_group
|
||||||
SPT_WEBSITE_PATH:
|
SPT_WEBSITE_PATH:
|
||||||
from_secret: website_deploy_path
|
from_secret: website_deploy_path
|
||||||
|
depends_on:
|
||||||
|
- check website ansible syntax
|
||||||
|
when:
|
||||||
branch:
|
branch:
|
||||||
- main
|
|
||||||
- master
|
- master
|
||||||
target:
|
- main
|
||||||
- website
|
event:
|
||||||
|
- push
|
||||||
|
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "items"]
|
||||||
|
path = items
|
||||||
|
url = https://dev.sp-tarkov.com/WillDaPope/DB-Website
|
1
items
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit c806cc45eb701f4cc0a54dc885b8fba784f30c73
|
@ -1,47 +0,0 @@
|
|||||||
# SPT Items
|
|
||||||
|
|
||||||
* You can use either of the two drone pipeline types:
|
|
||||||
* [docker](../.drone-docker.yml)
|
|
||||||
* [kubernetes](../.drone-kubernetes.yml)
|
|
||||||
* Some enhancement ideas can be found [here](#some-enhancement-ideas)
|
|
||||||
|
|
||||||
## Required secrets
|
|
||||||
| secret name | description | example |
|
|
||||||
| :----------------- | :---------- | :------- |
|
|
||||||
| spt_items_hostname | The remote server where spt-items-finder will be reachable <br> used for the frontend resolution | spt-items.my.server.com |
|
|
||||||
| deploy_path | The path to deploy to in the remote machine | `/var/www/html/aki/Website/items` |
|
|
||||||
| deploy_hostname | The remote server where to deploy <br> used by Ansible SSH | my.server.com |
|
|
||||||
| deploy_username | The default username to use on the remote server <br> used by Ansible SSH | www-data |
|
|
||||||
| deploy_user_group | The default user group to use on the remote server <br> used to set permission on the website folder | www-data |
|
|
||||||
| deploy_ssh_key | The **content** of the ssh private key used to connect to the remote server <br> The key needs to be in RSA in "RSA PRIVATE KEY" format <br> The ssh publick key needs to already be in the user used in the remote server ~/.ssh/authorized_keys | -----BEGIN RSA PRIVATE KEY----- <br> The key <br> -----END RSA PRIVATE KEY----- |
|
|
||||||
| deploy_ssh_key_passphrase | The passphrase to decrypt the SSH private key | test |
|
|
||||||
|
|
||||||
## ⚠ Important notes for the deployment ⚠
|
|
||||||
* Add all required secrets in Drone
|
|
||||||
* Server permissions:
|
|
||||||
1. The server must be able to use `apt` package manager
|
|
||||||
1. The *deploy_usernam* must exists, be part of the group *deploy_user_group* and be able to SSH into the server
|
|
||||||
1. If the parent folder of *deploy_path* already exists, *deploy_username* must have read and write permissions on it
|
|
||||||
* PHP:
|
|
||||||
1. `php8.0-fpm` and all its dependencies must already be installed
|
|
||||||
1. `php8.0-fpm` must be configured to use *deploy_username* (to ensure the cache created by Laravel can be deleted before every new deployment)
|
|
||||||
* Nginx:
|
|
||||||
1. Nginx must be using uses the user group *deploy_user_group*
|
|
||||||
1. Nginx must be configured to use HTTPS
|
|
||||||
1. Nginx must be configured to listen to *spt_items_hostname* and to point to the *deploy_path*
|
|
||||||
|
|
||||||
## The pipeline summary
|
|
||||||
1. Each push will:
|
|
||||||
1. Test the frontend
|
|
||||||
2. IF on `development` or `master`/`main` branch
|
|
||||||
1. Builds the frontend
|
|
||||||
3. IF pushed from `master` or `main` main branch
|
|
||||||
1. Move the build frontend in the backend `public` folder
|
|
||||||
2. Deploys to the server
|
|
||||||
|
|
||||||
## The pipeline walkthrough
|
|
||||||
see [Walkthrough.md](./docs/Walkthrough.md)
|
|
||||||
|
|
||||||
## Some enhancement ideas
|
|
||||||
- Store the build so that it is not rebuilt on any `promote` event
|
|
||||||
- Use a volume or a cache for Yarn install
|
|
@ -1,15 +0,0 @@
|
|||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
end_of_line = lf
|
|
||||||
insert_final_newline = true
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
trim_trailing_whitespace = false
|
|
||||||
|
|
||||||
[*.{yml,yaml}]
|
|
||||||
indent_size = 2
|
|
@ -1,19 +0,0 @@
|
|||||||
APP_NAME=Lumen
|
|
||||||
APP_ENV=local
|
|
||||||
APP_KEY=
|
|
||||||
APP_DEBUG=true
|
|
||||||
APP_URL=http://localhost
|
|
||||||
APP_TIMEZONE=UTC
|
|
||||||
|
|
||||||
LOG_CHANNEL=stack
|
|
||||||
LOG_SLACK_WEBHOOK_URL=
|
|
||||||
|
|
||||||
DB_CONNECTION=mysql
|
|
||||||
DB_HOST=127.0.0.1
|
|
||||||
DB_PORT=3306
|
|
||||||
DB_DATABASE=homestead
|
|
||||||
DB_USERNAME=homestead
|
|
||||||
DB_PASSWORD=secret
|
|
||||||
|
|
||||||
CACHE_DRIVER=file
|
|
||||||
QUEUE_CONNECTION=sync
|
|
@ -1,6 +0,0 @@
|
|||||||
php:
|
|
||||||
preset: laravel
|
|
||||||
disabled:
|
|
||||||
- unused_use
|
|
||||||
js: true
|
|
||||||
css: true
|
|
@ -1,13 +0,0 @@
|
|||||||
# SP Tarkov items API
|
|
||||||
|
|
||||||
## How to install
|
|
||||||
|
|
||||||
* Clone the repo
|
|
||||||
* Make sure you have PHP and composer installed on your PC
|
|
||||||
* To verify that you have PHP and composer working, try these commands `php -v` and `composer -v`
|
|
||||||
* Go inside the cloned repo and install the dependencies with this command `composer install`
|
|
||||||
* If you don't have a local server like Apache or Nginx, you can use PHP to start a server with this command in the root folder of the repo `php -S localhost:8000 -t public`
|
|
||||||
|
|
||||||
## How to use
|
|
||||||
|
|
||||||
* Check the `routes/web.php` for the available routes and use whatever utility you are confortable with to call the API endpoints, ex: Postman
|
|
@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Config;
|
|
||||||
|
|
||||||
final class GiteaConfig {
|
|
||||||
public const RAW_ITEMS_URL = 'https://dev.sp-tarkov.com/SPT-AKI/Server/raw/branch/development/project/assets/database/templates/items.json';
|
|
||||||
public const RAW_CUSTOMIZATION_URL = 'https://dev.sp-tarkov.com/SPT-AKI/Server/raw/branch/development/project/assets/database/templates/customization.json';
|
|
||||||
public const LOCALES_GLOBAL_URL = 'https://dev.sp-tarkov.com/api/v1/repos/SPT-AKI/Server/contents/project%2Fassets%2Fdatabase%2Flocales%2Fglobal';
|
|
||||||
public const RAW_LOCALES_GLOBAL_BASE_URL = 'https://dev.sp-tarkov.com/SPT-AKI/Server/raw/branch/development/project/assets/database/locales/global/';
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Data\ItemsCollection;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class RefreshItemsCache extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The console command name.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = "items:refresh";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = "Refresh all items cache";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the console command.
|
|
||||||
*
|
|
||||||
* @param ItemsCollection $itemsCollection
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function handle(ItemsCollection $itemsCollection): void
|
|
||||||
{
|
|
||||||
$itemsCollection->refreshAllCache();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console;
|
|
||||||
|
|
||||||
use App\Console\Commands\RefreshItemsCache;
|
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
|
||||||
use Laravel\Lumen\Console\Kernel as ConsoleKernel;
|
|
||||||
|
|
||||||
class Kernel extends ConsoleKernel
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The Artisan commands provided by your application.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $commands = [
|
|
||||||
RefreshItemsCache::class,
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the application's command schedule.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function schedule(Schedule $schedule)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,217 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Data;
|
|
||||||
|
|
||||||
use App\Exceptions\ItemNotFoundException;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Facades\Cache;
|
|
||||||
use Illuminate\Support\Facades\Http;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use App\Config\GiteaConfig;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
class ItemsCollection
|
|
||||||
{
|
|
||||||
protected Collection $items;
|
|
||||||
protected Collection $locales;
|
|
||||||
|
|
||||||
private string $items_cache_key = 'items';
|
|
||||||
private string $locales_cache_key = 'locales';
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
if (!Cache::has($this->items_cache_key)) {
|
|
||||||
$this->refreshItemsCache();
|
|
||||||
} else {
|
|
||||||
$this->items = Cache::get($this->items_cache_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Cache::has($this->locales_cache_key)) {
|
|
||||||
$this->refreshLocalesCache();
|
|
||||||
} else {
|
|
||||||
$this->locales = Cache::get($this->locales_cache_key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function refreshLocalesCache(): void
|
|
||||||
{
|
|
||||||
$this->locales = collect();
|
|
||||||
$rawLocalesGlobalBaseUrl = GiteaConfig::RAW_LOCALES_GLOBAL_BASE_URL;
|
|
||||||
|
|
||||||
// Getting all locales in project/assets/database/locales/global from the Server development branch repository
|
|
||||||
|
|
||||||
$localesList = collect(Http::withOptions(['verify' => false])->get(GiteaConfig::LOCALES_GLOBAL_URL)->json());
|
|
||||||
foreach ($localesList as $item) {
|
|
||||||
// Extract the json name for the locale
|
|
||||||
preg_match('/([a-z]{2}(-[a-z]{2})?).json/', $item['name'], $currentLocaleName, PREG_OFFSET_CAPTURE);
|
|
||||||
|
|
||||||
// If the name is not supported for any reason, dont add it to the locales
|
|
||||||
if (empty($currentLocaleName) || !$currentLocaleName[1][0]) continue;
|
|
||||||
|
|
||||||
$trimmedCurrentLocaleName = trim($currentLocaleName[1][0]);
|
|
||||||
$currentLocaleJson = Http::withOptions(['verify' => false])
|
|
||||||
->get("${rawLocalesGlobalBaseUrl}/${trimmedCurrentLocaleName}.json")->json();
|
|
||||||
$templateLocale = collect($currentLocaleJson['templates']);
|
|
||||||
$customizationLocale = collect($currentLocaleJson['customization']);
|
|
||||||
$this->locales = $this->locales->merge([$trimmedCurrentLocaleName => $templateLocale->concat($customizationLocale)]);
|
|
||||||
}
|
|
||||||
Cache::put($this->locales_cache_key, $this->locales);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function refreshItemsCache(): void
|
|
||||||
{
|
|
||||||
$this->items = collect(Http::withOptions(['verify' => false])->get(GiteaConfig::RAW_ITEMS_URL)->json());
|
|
||||||
$this->items = $this->items->merge(collect(Http::withOptions(['verify' => false])
|
|
||||||
->get(GiteaConfig::RAW_CUSTOMIZATION_URL)->json()));
|
|
||||||
Cache::put($this->items_cache_key, $this->items);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Collection
|
|
||||||
*/
|
|
||||||
public function getLocales(): Collection
|
|
||||||
{
|
|
||||||
return $this->locales->keys();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function refreshAllCache(): void
|
|
||||||
{
|
|
||||||
$this->refreshItemsCache();
|
|
||||||
$this->refreshLocalesCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the query is in the key
|
|
||||||
* @param string $key
|
|
||||||
* @param string $query
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function contains(string $key, string $query): bool
|
|
||||||
{
|
|
||||||
$key = Str::lower(trim($key));
|
|
||||||
$query = Str::lower(trim($query));
|
|
||||||
|
|
||||||
return Str::contains($key, $query);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $query the content of the query eg. 'AK'
|
|
||||||
* @param string $locale the chosen local. Default to 'en'
|
|
||||||
* @return Collection
|
|
||||||
*/
|
|
||||||
public function findItem(string $query, string $locale): Collection
|
|
||||||
{
|
|
||||||
return $this->items->filter(function ($val, $key) use ($query, $locale) {
|
|
||||||
return $this->contains($val['_id'], $query)
|
|
||||||
|| $this->contains($val['_name'], $query)
|
|
||||||
|| $this->contains($val['_parent'], $query)
|
|
||||||
|| (($this->locales[$locale][$key] ?? false)
|
|
||||||
&& ($this->contains($this->locales[$locale][$key]['Name'], $query) || $this->contains($this->locales[$locale][$key]['ShortName'], $query))
|
|
||||||
);
|
|
||||||
})->map(function ($item) use ($locale) {
|
|
||||||
return [
|
|
||||||
'item' => [
|
|
||||||
'_id' => $item['_id'],
|
|
||||||
'_name' => $item['_name'],
|
|
||||||
],
|
|
||||||
'locale' => $this->locales[$locale][$item['_id']] ?? ''
|
|
||||||
];
|
|
||||||
})->values();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $id the item ID to look for
|
|
||||||
* @param string $locale the chosen local. Default to 'en'
|
|
||||||
* @return array
|
|
||||||
* @throws ItemNotFoundException
|
|
||||||
*/
|
|
||||||
public function getItemById(string $id, string $locale): array
|
|
||||||
{
|
|
||||||
$item = $this->items[$id] ?? throw new ItemNotFoundException('Item not found');
|
|
||||||
return [
|
|
||||||
'item' => $item,
|
|
||||||
'locale' => $this->locales[$locale][$id] ?? ''
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getHierarchy(string $id, string $locale = 'en'): Collection {
|
|
||||||
// Return 404 if the item does not exist
|
|
||||||
$itemData = $this->items[$id] ?? throw new ItemNotFoundException('Item not found');
|
|
||||||
|
|
||||||
// Initialize the hierarchy with the current item
|
|
||||||
$item = [
|
|
||||||
'item'=> [
|
|
||||||
'_id' => $itemData['_id'],
|
|
||||||
'_name' => $itemData['_name'],
|
|
||||||
'_parent' => $itemData['_parent']
|
|
||||||
],
|
|
||||||
'locale' => $this->locales[$locale][$id] ?? ''
|
|
||||||
];
|
|
||||||
$hierarchy = collect([$id => $item]);
|
|
||||||
|
|
||||||
// Check the whole hierarchy and merge into the return variable
|
|
||||||
while (!empty($item['item']['_parent'] ?? '')) {
|
|
||||||
$itemtId = $item['item']['_parent'];
|
|
||||||
$itemData = $this->items[$itemtId] ?? null;
|
|
||||||
$item = [
|
|
||||||
'item'=> [
|
|
||||||
'_id' => $itemData['_id'],
|
|
||||||
'_name' => $itemData['_name'],
|
|
||||||
'_parent' => $itemData['_parent']
|
|
||||||
],
|
|
||||||
'locale' => $this->locales[$locale][$itemtId] ?? ''
|
|
||||||
];
|
|
||||||
$hierarchy = $hierarchy->merge([$itemtId => $item]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $hierarchy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getItemLocale(string $locale, string $itemID): array {
|
|
||||||
return $this->locales[$locale][$itemID] ?? [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $locale the chosen local. Default to 'en'
|
|
||||||
* @return array
|
|
||||||
* @throws ItemNotFoundException
|
|
||||||
*/
|
|
||||||
public function getAllItemsName(string $locale): Collection
|
|
||||||
{
|
|
||||||
return $this->items->map(function ($item) use ($locale) {
|
|
||||||
return [
|
|
||||||
'item' => [
|
|
||||||
'_id' => $item['_id'],
|
|
||||||
],
|
|
||||||
'locale' => collect($this->getItemLocale($locale, $item['_id']))->only(['Name', 'ShortName'])
|
|
||||||
];
|
|
||||||
})->filter(function ($item) {
|
|
||||||
return json_encode($item['locale']) != '[]';
|
|
||||||
})->values();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $id the item ID to look for
|
|
||||||
* @param string $locale the chosen local. Default to 'en'
|
|
||||||
* @return array
|
|
||||||
* @throws ItemNotFoundException
|
|
||||||
*/
|
|
||||||
public function getItemNameById(string $id, string $locale): array
|
|
||||||
{
|
|
||||||
$itemLocale = $this->locales[$locale][$id] ?? '';
|
|
||||||
|
|
||||||
if ($itemLocale == '') {
|
|
||||||
return $itemLocale;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'locale' => collect($itemLocale)->only(['Name', 'ShortName'])
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Events;
|
|
||||||
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
abstract class Event
|
|
||||||
{
|
|
||||||
use SerializesModels;
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Events;
|
|
||||||
|
|
||||||
class ExampleEvent extends Event
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Create a new event instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Exceptions;
|
|
||||||
|
|
||||||
use Illuminate\Auth\Access\AuthorizationException;
|
|
||||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
||||||
use Illuminate\Validation\ValidationException;
|
|
||||||
use Laravel\Lumen\Exceptions\Handler as ExceptionHandler;
|
|
||||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
class Handler extends ExceptionHandler
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* A list of the exception types that should not be reported.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $dontReport = [
|
|
||||||
AuthorizationException::class,
|
|
||||||
HttpException::class,
|
|
||||||
ModelNotFoundException::class,
|
|
||||||
ValidationException::class,
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Report or log an exception.
|
|
||||||
*
|
|
||||||
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
|
|
||||||
*
|
|
||||||
* @param \Throwable $exception
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function report(Throwable $exception)
|
|
||||||
{
|
|
||||||
parent::report($exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render an exception into an HTTP response.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @param \Throwable $exception
|
|
||||||
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
|
|
||||||
*
|
|
||||||
* @throws \Throwable
|
|
||||||
*/
|
|
||||||
public function render($request, Throwable $exception)
|
|
||||||
{
|
|
||||||
if ($exception instanceof NotFoundHttpException) {
|
|
||||||
return redirect('/search');
|
|
||||||
}
|
|
||||||
return parent::render($request, $exception);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Exceptions;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
|
|
||||||
class ItemNotFoundException extends Exception
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use Laravel\Lumen\Routing\Controller as BaseController;
|
|
||||||
|
|
||||||
class Controller extends BaseController
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
@ -1,162 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use App\Data\ItemsCollection;
|
|
||||||
use Illuminate\Http\JsonResponse;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Http\Response;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
class ItemController extends Controller
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var ItemsCollection
|
|
||||||
*/
|
|
||||||
private ItemsCollection $itemsCollection;
|
|
||||||
|
|
||||||
public function __construct(ItemsCollection $itemsCollection)
|
|
||||||
{
|
|
||||||
$this->itemsCollection = $itemsCollection;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Request $request
|
|
||||||
* @return JsonResponse
|
|
||||||
*/
|
|
||||||
public function search(Request $request): JsonResponse
|
|
||||||
{
|
|
||||||
$status = Response::HTTP_NO_CONTENT;
|
|
||||||
$result = $this->itemsCollection->findItem($request->input('query'), $request->input('locale') ?? 'en');
|
|
||||||
$response = [];
|
|
||||||
if ($result->isNotEmpty()) {
|
|
||||||
$status = Response::HTTP_OK;
|
|
||||||
$response = [
|
|
||||||
'items' => $result->toArray(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return response()->json($response, $status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Request $request
|
|
||||||
* @return JsonResponse
|
|
||||||
*/
|
|
||||||
public function getItem(Request $request): JsonResponse
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return response()->json(
|
|
||||||
$this->itemsCollection->getItemById(
|
|
||||||
$request->query('id', ''), $request->query('locale', 'en')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} catch (Throwable $exception) {
|
|
||||||
Log::error($exception->getMessage());
|
|
||||||
Log::error($exception->getTraceAsString());
|
|
||||||
return response()->json([
|
|
||||||
'error' => 'Item not found.',
|
|
||||||
], Response::HTTP_NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// $router->get('item/list', 'ItemController@getAllItems');
|
|
||||||
// $router->get('item/nameByID', 'ItemController@getItemNameByID');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Request $request
|
|
||||||
* @return JsonResponse
|
|
||||||
*/
|
|
||||||
public function getAllItemsName(Request $request): JsonResponse
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return response()->json(
|
|
||||||
$this->itemsCollection->getAllItemsName($request->query('locale', 'en'))
|
|
||||||
);
|
|
||||||
} catch (Throwable $exception) {
|
|
||||||
Log::error($exception->getMessage());
|
|
||||||
Log::error($exception->getTraceAsString());
|
|
||||||
return response()->json([
|
|
||||||
'error' => 'Error during items collection.',
|
|
||||||
], Response::HTTP_INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Request $request
|
|
||||||
* @return JsonResponse
|
|
||||||
*/
|
|
||||||
public function getItemNameByID(Request $request): JsonResponse
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return response()->json(
|
|
||||||
$this->itemsCollection->getItemNameById(
|
|
||||||
$request->query('id', ''), $request->query('locale', 'en')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} catch (Throwable $exception) {
|
|
||||||
Log::error($exception->getMessage());
|
|
||||||
Log::error($exception->getTraceAsString());
|
|
||||||
return response()->json([
|
|
||||||
'error' => 'Item not found.',
|
|
||||||
], Response::HTTP_NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Request $request The request
|
|
||||||
* @return JsonResponse Either {"error":"item not found"} or an object containing
|
|
||||||
* for each id, an object looking like
|
|
||||||
* {"item":{"_id":"id","_name":"name","_parent":"parent"}, "locale": {"Description":"description", "Name":"name", "ShortName":"shortName"}}
|
|
||||||
*/
|
|
||||||
public function getHierarchy(Request $request): JsonResponse
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return response()->json(
|
|
||||||
$this->itemsCollection->getHierarchy(
|
|
||||||
$request->query('id'), $request->query('locale', 'en')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} catch (Throwable $exception) {
|
|
||||||
Log::error($exception->getMessage());
|
|
||||||
Log::error($exception->getTraceAsString());
|
|
||||||
return response()->json([
|
|
||||||
'error' => 'Item not found.',
|
|
||||||
], Response::HTTP_NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return JsonResponse
|
|
||||||
*/
|
|
||||||
public function getLocales(): JsonResponse
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return response()->json($this->itemsCollection->getLocales());
|
|
||||||
} catch (Throwable $exception) {
|
|
||||||
Log::error($exception->getMessage());
|
|
||||||
Log::error($exception->getTraceAsString());
|
|
||||||
return response()->json([
|
|
||||||
'error' => 'No locale found.',
|
|
||||||
], Response::HTTP_NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return JsonResponse
|
|
||||||
*/
|
|
||||||
public function refreshAllCache(): JsonResponse
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$this->itemsCollection->refreshAllCache();
|
|
||||||
return response()->json([], Response::HTTP_NO_CONTENT);
|
|
||||||
} catch (Throwable $exception) {
|
|
||||||
Log::error($exception->getMessage());
|
|
||||||
Log::error($exception->getTraceAsString());
|
|
||||||
return response()->json([
|
|
||||||
'error' => 'Internal error',
|
|
||||||
], Response::HTTP_INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Illuminate\Contracts\Auth\Factory as Auth;
|
|
||||||
|
|
||||||
class Authenticate
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The authentication guard factory instance.
|
|
||||||
*
|
|
||||||
* @var \Illuminate\Contracts\Auth\Factory
|
|
||||||
*/
|
|
||||||
protected $auth;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new middleware instance.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Contracts\Auth\Factory $auth
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(Auth $auth)
|
|
||||||
{
|
|
||||||
$this->auth = $auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle an incoming request.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @param \Closure $next
|
|
||||||
* @param string|null $guard
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function handle($request, Closure $next, $guard = null)
|
|
||||||
{
|
|
||||||
if ($this->auth->guard($guard)->guest()) {
|
|
||||||
return response('Unauthorized.', 401);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
|
|
||||||
class ExampleMiddleware
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Handle an incoming request.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @param \Closure $next
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function handle($request, Closure $next)
|
|
||||||
{
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Jobs;
|
|
||||||
|
|
||||||
class ExampleJob extends Job
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Create a new job instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the job.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Jobs;
|
|
||||||
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
abstract class Job implements ShouldQueue
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Queueable Jobs
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| This job base class provides a central location to place any logic that
|
|
||||||
| is shared across all of your jobs. The trait included with the class
|
|
||||||
| provides access to the "queueOn" and "delay" queue helper methods.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
use InteractsWithQueue, Queueable, SerializesModels;
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Listeners;
|
|
||||||
|
|
||||||
use App\Events\ExampleEvent;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
|
|
||||||
class ExampleListener
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Create the event listener.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the event.
|
|
||||||
*
|
|
||||||
* @param \App\Events\ExampleEvent $event
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function handle(ExampleEvent $event)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Models;
|
|
||||||
|
|
||||||
use Illuminate\Auth\Authenticatable;
|
|
||||||
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
|
|
||||||
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
use Laravel\Lumen\Auth\Authorizable;
|
|
||||||
|
|
||||||
class User extends Model implements AuthenticatableContract, AuthorizableContract
|
|
||||||
{
|
|
||||||
use Authenticatable, Authorizable, HasFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The attributes that are mass assignable.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $fillable = [
|
|
||||||
'name', 'email',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The attributes excluded from the model's JSON form.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $hidden = [
|
|
||||||
'password',
|
|
||||||
];
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Providers;
|
|
||||||
|
|
||||||
use Illuminate\Support\ServiceProvider;
|
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Register any application services.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function register()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bootstrap any application services.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function boot()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Providers;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Support\Facades\Gate;
|
|
||||||
use Illuminate\Support\ServiceProvider;
|
|
||||||
|
|
||||||
class AuthServiceProvider extends ServiceProvider
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Register any application services.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function register()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Boot the authentication services for the application.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function boot()
|
|
||||||
{
|
|
||||||
// Here you may define how you wish users to be authenticated for your Lumen
|
|
||||||
// application. The callback which receives the incoming request instance
|
|
||||||
// should return either a User instance or null. You're free to obtain
|
|
||||||
// the User instance via an API token or any other method necessary.
|
|
||||||
|
|
||||||
$this->app['auth']->viaRequest('api', function ($request) {
|
|
||||||
if ($request->input('api_token')) {
|
|
||||||
return User::where('api_token', $request->input('api_token'))->first();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Providers;
|
|
||||||
|
|
||||||
use Laravel\Lumen\Providers\EventServiceProvider as ServiceProvider;
|
|
||||||
|
|
||||||
class EventServiceProvider extends ServiceProvider
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The event listener mappings for the application.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $listen = [
|
|
||||||
\App\Events\ExampleEvent::class => [
|
|
||||||
\App\Listeners\ExampleListener::class,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
#!/usr/bin/env php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
use Symfony\Component\Console\Input\ArgvInput;
|
|
||||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Create The Application
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| First we need to get an application instance. This creates an instance
|
|
||||||
| of the application / container and bootstraps the application so it
|
|
||||||
| is ready to receive HTTP / Console requests from the environment.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app = require __DIR__.'/bootstrap/app.php';
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Run The Artisan Application
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| When we run the console application, the current CLI command will be
|
|
||||||
| executed in this console and the response sent back to a terminal
|
|
||||||
| or another output device for the developers. Here goes nothing!
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
$kernel = $app->make(
|
|
||||||
'Illuminate\Contracts\Console\Kernel'
|
|
||||||
);
|
|
||||||
|
|
||||||
exit($kernel->handle(new ArgvInput, new ConsoleOutput));
|
|
@ -1,117 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
require_once __DIR__.'/../vendor/autoload.php';
|
|
||||||
|
|
||||||
(new Laravel\Lumen\Bootstrap\LoadEnvironmentVariables(
|
|
||||||
dirname(__DIR__)
|
|
||||||
))->bootstrap();
|
|
||||||
|
|
||||||
date_default_timezone_set(env('APP_TIMEZONE', 'UTC'));
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Create The Application
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Here we will load the environment and create the application instance
|
|
||||||
| that serves as the central piece of this framework. We'll use this
|
|
||||||
| application as an "IoC" container and router for this framework.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app = new Laravel\Lumen\Application(
|
|
||||||
dirname(__DIR__)
|
|
||||||
);
|
|
||||||
|
|
||||||
$app->withFacades();
|
|
||||||
|
|
||||||
// $app->withEloquent();
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Register Container Bindings
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Now we will register a few bindings in the service container. We will
|
|
||||||
| register the exception handler and the console kernel. You may add
|
|
||||||
| your own bindings here if you like or you can make another file.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app->singleton(
|
|
||||||
Illuminate\Contracts\Debug\ExceptionHandler::class,
|
|
||||||
App\Exceptions\Handler::class
|
|
||||||
);
|
|
||||||
|
|
||||||
$app->singleton(
|
|
||||||
Illuminate\Contracts\Console\Kernel::class,
|
|
||||||
App\Console\Kernel::class
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Register Config Files
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Now we will register the "app" configuration file. If the file exists in
|
|
||||||
| your configuration directory it will be loaded; otherwise, we'll load
|
|
||||||
| the default version. You may register other files below as needed.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app->configure('app');
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Register Middleware
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Next, we will register the middleware with the application. These can
|
|
||||||
| be global middleware that run before and after each request into a
|
|
||||||
| route or middleware that'll be assigned to some specific routes.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app->middleware([
|
|
||||||
Fruitcake\Cors\HandleCors::class,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// $app->routeMiddleware([
|
|
||||||
// 'auth' => App\Http\Middleware\Authenticate::class,
|
|
||||||
// ]);
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Register Service Providers
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Here we will register all of the application's service providers which
|
|
||||||
| are used to bind services into the container. Service providers are
|
|
||||||
| totally optional, so you are not required to uncomment this line.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
// $app->register(App\Providers\AppServiceProvider::class);
|
|
||||||
// $app->register(App\Providers\AuthServiceProvider::class);
|
|
||||||
// $app->register(App\Providers\EventServiceProvider::class);
|
|
||||||
$app->register(Fruitcake\Cors\CorsServiceProvider::class);
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Load The Application Routes
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Next we will include the routes file so that they can all be added to
|
|
||||||
| the application. This will provide all of the URLs the application
|
|
||||||
| can respond to, as well as the controllers that may handle them.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
$app->configure('cors');
|
|
||||||
|
|
||||||
$app->router->group([
|
|
||||||
'namespace' => 'App\Http\Controllers',
|
|
||||||
], function ($router) {
|
|
||||||
require __DIR__.'/../routes/web.php';
|
|
||||||
});
|
|
||||||
|
|
||||||
return $app;
|
|
@ -1,42 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "laravel/lumen",
|
|
||||||
"description": "The Laravel Lumen Framework.",
|
|
||||||
"keywords": ["framework", "laravel", "lumen"],
|
|
||||||
"license": "MIT",
|
|
||||||
"type": "project",
|
|
||||||
"require": {
|
|
||||||
"fruitcake/laravel-cors": "^2.0",
|
|
||||||
"guzzlehttp/guzzle": "^7.3",
|
|
||||||
"laravel/lumen-framework": "^8.0"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"deployer/dist": "^6.8",
|
|
||||||
"fakerphp/faker": "^1.9.1",
|
|
||||||
"mockery/mockery": "^1.3.1",
|
|
||||||
"phpunit/phpunit": "^9.3"
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"App\\": "app/",
|
|
||||||
"Database\\Factories\\": "database/factories/",
|
|
||||||
"Database\\Seeders\\": "database/seeders/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload-dev": {
|
|
||||||
"classmap": [
|
|
||||||
"tests/"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"config": {
|
|
||||||
"preferred-install": "dist",
|
|
||||||
"sort-packages": true,
|
|
||||||
"optimize-autoloader": true
|
|
||||||
},
|
|
||||||
"minimum-stability": "dev",
|
|
||||||
"prefer-stable": true,
|
|
||||||
"scripts": {
|
|
||||||
"post-root-package-install": [
|
|
||||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
7778
items/api/composer.lock
generated
@ -1,60 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
return [
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Laravel CORS Options
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| The allowed_methods and allowed_headers options are case-insensitive.
|
|
||||||
|
|
|
||||||
| You don't need to provide both allowed_origins and allowed_origins_patterns.
|
|
||||||
| If one of the strings passed matches, it is considered a valid origin.
|
|
||||||
|
|
|
||||||
| If ['*'] is provided to allowed_methods, allowed_origins or allowed_headers
|
|
||||||
| all methods / origins / headers are allowed.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* You can enable CORS for 1 or multiple paths.
|
|
||||||
* Example: ['api/*']
|
|
||||||
*/
|
|
||||||
'paths' => ['api/*'],
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Matches the request method. `['*']` allows all methods.
|
|
||||||
*/
|
|
||||||
'allowed_methods' => ['*'],
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Matches the request origin. `['*']` allows all origins. Wildcards can be used, eg `*.mydomain.com`
|
|
||||||
*/
|
|
||||||
'allowed_origins' => ['*'],
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Patterns that can be used with `preg_match` to match the origin.
|
|
||||||
*/
|
|
||||||
'allowed_origins_patterns' => [],
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sets the Access-Control-Allow-Headers response header. `['*']` allows all headers.
|
|
||||||
*/
|
|
||||||
'allowed_headers' => ['*'],
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sets the Access-Control-Expose-Headers response header with these headers.
|
|
||||||
*/
|
|
||||||
'exposed_headers' => [],
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sets the Access-Control-Max-Age response header when > 0.
|
|
||||||
*/
|
|
||||||
'max_age' => 0,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sets the Access-Control-Allow-Credentials header.
|
|
||||||
*/
|
|
||||||
'supports_credentials' => false,
|
|
||||||
];
|
|
@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Database\Factories;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
|
||||||
|
|
||||||
class UserFactory extends Factory
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name of the factory's corresponding model.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $model = User::class;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the model's default state.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function definition()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => $this->faker->name,
|
|
||||||
'email' => $this->faker->unique()->safeEmail,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Database\Seeders;
|
|
||||||
|
|
||||||
use Illuminate\Database\Seeder;
|
|
||||||
|
|
||||||
class DatabaseSeeder extends Seeder
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the database seeds.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function run()
|
|
||||||
{
|
|
||||||
// $this->call('UsersTableSeeder');
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Deployer;
|
|
||||||
|
|
||||||
require 'recipe/laravel.php';
|
|
||||||
|
|
||||||
// Project name
|
|
||||||
set('application', 'SPT-Items-API');
|
|
||||||
|
|
||||||
// Project repository
|
|
||||||
set('repository', 'https://dev.sp-tarkov.com/Rev/spt-items-api.git');
|
|
||||||
|
|
||||||
set('shared_dirs', [
|
|
||||||
'storage/app',
|
|
||||||
'storage/framework/cache',
|
|
||||||
'storage/framework/sessions',
|
|
||||||
'storage/framework/views',
|
|
||||||
'storage/logs',
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Writable dirs by web server
|
|
||||||
add('writable_dirs', []);
|
|
||||||
set('writable_use_sudo', false);
|
|
||||||
|
|
||||||
// Hosts
|
|
||||||
host('spt-server')
|
|
||||||
->multiplexing(false)
|
|
||||||
->become('apache')
|
|
||||||
->set('deploy_path', '/var/www/html');
|
|
||||||
|
|
||||||
task('artisan:cache:clear', function () {
|
|
||||||
run('{{bin/php}} {{release_path}}/artisan cache:clear');
|
|
||||||
})->desc('Execute artisan cache:clear');
|
|
||||||
|
|
||||||
task('deploy:vendors', function () {
|
|
||||||
run('cd {{release_path}} && /usr/local/bin/composer install --verbose --prefer-dist --no-progress --no-interaction --optimize-autoloader --no-suggest');
|
|
||||||
})->desc('installing composer packages');
|
|
||||||
|
|
||||||
|
|
||||||
task('artisan:config:cache', function() {})->setPrivate();
|
|
||||||
task('artisan:down', function() {})->setPrivate();
|
|
||||||
task('artisan:event:cache', function() {})->setPrivate();
|
|
||||||
task('artisan:event:clear', function() {})->setPrivate();
|
|
||||||
task('artisan:horizon:terminate', function() {})->setPrivate();
|
|
||||||
task('artisan:optimize', function() {})->setPrivate();
|
|
||||||
task('artisan:optimize:clear', function() {})->setPrivate();
|
|
||||||
task('artisan:route:cache', function() {})->setPrivate();
|
|
||||||
task('artisan:storage:link', function() {})->setPrivate();
|
|
||||||
task('artisan:up', function() {})->setPrivate();
|
|
||||||
task('artisan:view:cache', function() {})->setPrivate();
|
|
||||||
task('artisan:view:clear', function() {})->setPrivate();
|
|
||||||
|
|
||||||
// custom task
|
|
||||||
task('items:refresh', function () {
|
|
||||||
run('cd {{release_path}} && php artisan items:refresh');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Tasks
|
|
||||||
task('deploy', [
|
|
||||||
'deploy:info',
|
|
||||||
'deploy:prepare',
|
|
||||||
'deploy:lock',
|
|
||||||
'deploy:release',
|
|
||||||
'deploy:update_code',
|
|
||||||
'deploy:shared',
|
|
||||||
'deploy:vendors',
|
|
||||||
'deploy:writable',
|
|
||||||
'artisan:cache:clear',
|
|
||||||
'items:refresh',
|
|
||||||
'deploy:symlink',
|
|
||||||
'deploy:unlock',
|
|
||||||
'cleanup',
|
|
||||||
]);
|
|
||||||
|
|
||||||
// [Optional] If deploy fails automatically unlock.
|
|
||||||
after('deploy:failed', 'deploy:unlock');
|
|
@ -1,17 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
|
|
||||||
bootstrap="vendor/autoload.php"
|
|
||||||
colors="true"
|
|
||||||
>
|
|
||||||
<testsuites>
|
|
||||||
<testsuite name="Application Test Suite">
|
|
||||||
<directory suffix="Test.php">./tests</directory>
|
|
||||||
</testsuite>
|
|
||||||
</testsuites>
|
|
||||||
<php>
|
|
||||||
<env name="APP_ENV" value="testing"/>
|
|
||||||
<env name="CACHE_DRIVER" value="array"/>
|
|
||||||
<env name="QUEUE_CONNECTION" value="sync"/>
|
|
||||||
</php>
|
|
||||||
</phpunit>
|
|
@ -1,21 +0,0 @@
|
|||||||
<IfModule mod_rewrite.c>
|
|
||||||
<IfModule mod_negotiation.c>
|
|
||||||
Options -MultiViews -Indexes
|
|
||||||
</IfModule>
|
|
||||||
|
|
||||||
RewriteEngine On
|
|
||||||
|
|
||||||
# Handle Authorization Header
|
|
||||||
RewriteCond %{HTTP:Authorization} .
|
|
||||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
|
||||||
|
|
||||||
# Redirect Trailing Slashes If Not A Folder...
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-d
|
|
||||||
RewriteCond %{REQUEST_URI} (.+)/$
|
|
||||||
RewriteRule ^ %1 [L,R=301]
|
|
||||||
|
|
||||||
# Handle Front Controller...
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-d
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-f
|
|
||||||
RewriteRule ^ index.php [L]
|
|
||||||
</IfModule>
|
|
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"files": {
|
|
||||||
"main.js": "/static/js/main.f3c30fdb.chunk.js",
|
|
||||||
"main.js.map": "/static/js/main.f3c30fdb.chunk.js.map",
|
|
||||||
"runtime-main.js": "/static/js/runtime-main.18486819.js",
|
|
||||||
"runtime-main.js.map": "/static/js/runtime-main.18486819.js.map",
|
|
||||||
"static/js/2.21456f47.chunk.js": "/static/js/2.21456f47.chunk.js",
|
|
||||||
"static/js/2.21456f47.chunk.js.map": "/static/js/2.21456f47.chunk.js.map",
|
|
||||||
"static/js/3.00f77fcf.chunk.js": "/static/js/3.00f77fcf.chunk.js",
|
|
||||||
"static/js/3.00f77fcf.chunk.js.map": "/static/js/3.00f77fcf.chunk.js.map",
|
|
||||||
"index.html": "/index.html",
|
|
||||||
"static/js/2.21456f47.chunk.js.LICENSE.txt": "/static/js/2.21456f47.chunk.js.LICENSE.txt"
|
|
||||||
},
|
|
||||||
"entrypoints": [
|
|
||||||
"static/js/runtime-main.18486819.js",
|
|
||||||
"static/js/2.21456f47.chunk.js",
|
|
||||||
"static/js/main.f3c30fdb.chunk.js"
|
|
||||||
]
|
|
||||||
}
|
|
Before Width: | Height: | Size: 2.2 KiB |
@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Create The Application
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| First we need to get an application instance. This creates an instance
|
|
||||||
| of the application / container and bootstraps the application so it
|
|
||||||
| is ready to receive HTTP / Console requests from the environment.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app = require __DIR__.'/../bootstrap/app.php';
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Run The Application
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Once we have the application, we can handle the incoming request
|
|
||||||
| through the kernel, and send the associated response back to
|
|
||||||
| the client's browser allowing them to enjoy the creative
|
|
||||||
| and wonderful application we have prepared for them.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app->run();
|
|
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 81 KiB |
@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
object-assign
|
|
||||||
(c) Sindre Sorhus
|
|
||||||
@license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A better abstraction over CSS.
|
|
||||||
*
|
|
||||||
* @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present
|
|
||||||
* @website https://github.com/cssinjs/jss
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* React Router v6.0.2
|
|
||||||
*
|
|
||||||
* Copyright (c) Remix Software Inc.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE.md file in the root directory of this source tree.
|
|
||||||
*
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @license MUI v5.0.1
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @license React v0.20.2
|
|
||||||
* scheduler.production.min.js
|
|
||||||
*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @license React v16.13.1
|
|
||||||
* react-is.production.min.js
|
|
||||||
*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @license React v17.0.2
|
|
||||||
* react-dom.production.min.js
|
|
||||||
*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @license React v17.0.2
|
|
||||||
* react-is.production.min.js
|
|
||||||
*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @license React v17.0.2
|
|
||||||
* react-jsx-runtime.production.min.js
|
|
||||||
*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @license React v17.0.2
|
|
||||||
* react.production.min.js
|
|
||||||
*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
@ -1,2 +0,0 @@
|
|||||||
(this["webpackJsonpnew-item-finder-website"]=this["webpackJsonpnew-item-finder-website"]||[]).push([[3],{210:function(e,t,n){"use strict";n.r(t),n.d(t,"getCLS",(function(){return p})),n.d(t,"getFCP",(function(){return S})),n.d(t,"getFID",(function(){return F})),n.d(t,"getLCP",(function(){return k})),n.d(t,"getTTFB",(function(){return C}));var i,a,r,o,u=function(e,t){return{name:e,value:void 0===t?-1:t,delta:0,entries:[],id:"v1-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)}},c=function(e,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){if("first-input"===e&&!("PerformanceEventTiming"in self))return;var n=new PerformanceObserver((function(e){return e.getEntries().map(t)}));return n.observe({type:e,buffered:!0}),n}}catch(e){}},f=function(e,t){var n=function n(i){"pagehide"!==i.type&&"hidden"!==document.visibilityState||(e(i),t&&(removeEventListener("visibilitychange",n,!0),removeEventListener("pagehide",n,!0)))};addEventListener("visibilitychange",n,!0),addEventListener("pagehide",n,!0)},s=function(e){addEventListener("pageshow",(function(t){t.persisted&&e(t)}),!0)},m="function"==typeof WeakSet?new WeakSet:new Set,d=function(e,t,n){var i;return function(){t.value>=0&&(n||m.has(t)||"hidden"===document.visibilityState)&&(t.delta=t.value-(i||0),(t.delta||void 0===i)&&(i=t.value,e(t)))}},p=function(e,t){var n,i=u("CLS",0),a=function(e){e.hadRecentInput||(i.value+=e.value,i.entries.push(e),n())},r=c("layout-shift",a);r&&(n=d(e,i,t),f((function(){r.takeRecords().map(a),n()})),s((function(){i=u("CLS",0),n=d(e,i,t)})))},v=-1,l=function(){return"hidden"===document.visibilityState?0:1/0},h=function(){f((function(e){var t=e.timeStamp;v=t}),!0)},g=function(){return v<0&&(v=l(),h(),s((function(){setTimeout((function(){v=l(),h()}),0)}))),{get timeStamp(){return v}}},S=function(e,t){var n,i=g(),a=u("FCP"),r=function(e){"first-contentful-paint"===e.name&&(f&&f.disconnect(),e.startTime<i.timeStamp&&(a.value=e.startTime,a.entries.push(e),m.add(a),n()))},o=performance.getEntriesByName("first-contentful-paint")[0],f=o?null:c("paint",r);(o||f)&&(n=d(e,a,t),o&&r(o),s((function(i){a=u("FCP"),n=d(e,a,t),requestAnimationFrame((function(){requestAnimationFrame((function(){a.value=performance.now()-i.timeStamp,m.add(a),n()}))}))})))},y={passive:!0,capture:!0},w=new Date,E=function(e,t){i||(i=t,a=e,r=new Date,b(removeEventListener),L())},L=function(){if(a>=0&&a<r-w){var e={entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+a};o.forEach((function(t){t(e)})),o=[]}},T=function(e){if(e.cancelable){var t=(e.timeStamp>1e12?new Date:performance.now())-e.timeStamp;"pointerdown"==e.type?function(e,t){var n=function(){E(e,t),a()},i=function(){a()},a=function(){removeEventListener("pointerup",n,y),removeEventListener("pointercancel",i,y)};addEventListener("pointerup",n,y),addEventListener("pointercancel",i,y)}(t,e):E(t,e)}},b=function(e){["mousedown","keydown","touchstart","pointerdown"].forEach((function(t){return e(t,T,y)}))},F=function(e,t){var n,r=g(),p=u("FID"),v=function(e){e.startTime<r.timeStamp&&(p.value=e.processingStart-e.startTime,p.entries.push(e),m.add(p),n())},l=c("first-input",v);n=d(e,p,t),l&&f((function(){l.takeRecords().map(v),l.disconnect()}),!0),l&&s((function(){var r;p=u("FID"),n=d(e,p,t),o=[],a=-1,i=null,b(addEventListener),r=v,o.push(r),L()}))},k=function(e,t){var n,i=g(),a=u("LCP"),r=function(e){var t=e.startTime;t<i.timeStamp&&(a.value=t,a.entries.push(e)),n()},o=c("largest-contentful-paint",r);if(o){n=d(e,a,t);var p=function(){m.has(a)||(o.takeRecords().map(r),o.disconnect(),m.add(a),n())};["keydown","click"].forEach((function(e){addEventListener(e,p,{once:!0,capture:!0})})),f(p,!0),s((function(i){a=u("LCP"),n=d(e,a,t),requestAnimationFrame((function(){requestAnimationFrame((function(){a.value=performance.now()-i.timeStamp,m.add(a),n()}))}))}))}},C=function(e){var t,n=u("TTFB");t=function(){try{var t=performance.getEntriesByType("navigation")[0]||function(){var e=performance.timing,t={entryType:"navigation",startTime:0};for(var n in e)"navigationStart"!==n&&"toJSON"!==n&&(t[n]=Math.max(e[n]-e.navigationStart,0));return t}();if(n.value=n.delta=t.responseStart,n.value<0)return;n.entries=[t],e(n)}catch(e){}},"complete"===document.readyState?setTimeout(t,0):addEventListener("pageshow",t)}}}]);
|
|
||||||
//# sourceMappingURL=3.00f77fcf.chunk.js.map
|
|
@ -1,2 +0,0 @@
|
|||||||
!function(e){function t(t){for(var n,u,a=t[0],c=t[1],f=t[2],s=0,p=[];s<a.length;s++)u=a[s],Object.prototype.hasOwnProperty.call(o,u)&&o[u]&&p.push(o[u][0]),o[u]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(l&&l(t);p.length;)p.shift()();return i.push.apply(i,f||[]),r()}function r(){for(var e,t=0;t<i.length;t++){for(var r=i[t],n=!0,a=1;a<r.length;a++){var c=r[a];0!==o[c]&&(n=!1)}n&&(i.splice(t--,1),e=u(u.s=r[0]))}return e}var n={},o={1:0},i=[];function u(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,u),r.l=!0,r.exports}u.e=function(e){var t=[],r=o[e];if(0!==r)if(r)t.push(r[2]);else{var n=new Promise((function(t,n){r=o[e]=[t,n]}));t.push(r[2]=n);var i,a=document.createElement("script");a.charset="utf-8",a.timeout=120,u.nc&&a.setAttribute("nonce",u.nc),a.src=function(e){return u.p+"static/js/"+({}[e]||e)+"."+{3:"00f77fcf"}[e]+".chunk.js"}(e);var c=new Error;i=function(t){a.onerror=a.onload=null,clearTimeout(f);var r=o[e];if(0!==r){if(r){var n=t&&("load"===t.type?"missing":t.type),i=t&&t.target&&t.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+i+")",c.name="ChunkLoadError",c.type=n,c.request=i,r[1](c)}o[e]=void 0}};var f=setTimeout((function(){i({type:"timeout",target:a})}),12e4);a.onerror=a.onload=i,document.head.appendChild(a)}return Promise.all(t)},u.m=e,u.c=n,u.d=function(e,t,r){u.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},u.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},u.t=function(e,t){if(1&t&&(e=u(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(u.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)u.d(r,n,function(t){return e[t]}.bind(null,n));return r},u.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return u.d(t,"a",t),t},u.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},u.p="/",u.oe=function(e){throw console.error(e),e};var a=this["webpackJsonpnew-item-finder-website"]=this["webpackJsonpnew-item-finder-website"]||[],c=a.push.bind(a);a.push=t,a=a.slice();for(var f=0;f<a.length;f++)t(a[f]);var l=c;r()}([]);
|
|
||||||
//# sourceMappingURL=runtime-main.18486819.js.map
|
|
@ -1,22 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="icon" href="favicon.ico" />
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
||||||
<meta name="theme-color" content="#000000" />
|
|
||||||
<meta name="description" content="item finder" />
|
|
||||||
<link rel="apple-touch-icon" href="/logo_christmas_192.png" />
|
|
||||||
<link rel="manifest" href="/manifest.json" />
|
|
||||||
<title>Item Finder</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body><noscript>You need to enable JavaScript to run this app.</noscript>
|
|
||||||
<div id="root"></div>
|
|
||||||
<script>!function (e) { function t(t) { for (var n, u, a = t[0], c = t[1], f = t[2], s = 0, p = []; s < a.length; s++)u = a[s], Object.prototype.hasOwnProperty.call(o, u) && o[u] && p.push(o[u][0]), o[u] = 0; for (n in c) Object.prototype.hasOwnProperty.call(c, n) && (e[n] = c[n]); for (l && l(t); p.length;)p.shift()(); return i.push.apply(i, f || []), r() } function r() { for (var e, t = 0; t < i.length; t++) { for (var r = i[t], n = !0, a = 1; a < r.length; a++) { var c = r[a]; 0 !== o[c] && (n = !1) } n && (i.splice(t--, 1), e = u(u.s = r[0])) } return e } var n = {}, o = { 1: 0 }, i = []; function u(t) { if (n[t]) return n[t].exports; var r = n[t] = { i: t, l: !1, exports: {} }; return e[t].call(r.exports, r, r.exports, u), r.l = !0, r.exports } u.e = function (e) { var t = [], r = o[e]; if (0 !== r) if (r) t.push(r[2]); else { var n = new Promise((function (t, n) { r = o[e] = [t, n] })); t.push(r[2] = n); var i, a = document.createElement("script"); a.charset = "utf-8", a.timeout = 120, u.nc && a.setAttribute("nonce", u.nc), a.src = function (e) { return u.p + "static/js/" + ({}[e] || e) + "." + { 3: "ff1076df" }[e] + ".chunk.js" }(e); var c = new Error; i = function (t) { a.onerror = a.onload = null, clearTimeout(f); var r = o[e]; if (0 !== r) { if (r) { var n = t && ("load" === t.type ? "missing" : t.type), i = t && t.target && t.target.src; c.message = "Loading chunk " + e + " failed.\n(" + n + ": " + i + ")", c.name = "ChunkLoadError", c.type = n, c.request = i, r[1](c) } o[e] = void 0 } }; var f = setTimeout((function () { i({ type: "timeout", target: a }) }), 12e4); a.onerror = a.onload = i, document.head.appendChild(a) } return Promise.all(t) }, u.m = e, u.c = n, u.d = function (e, t, r) { u.o(e, t) || Object.defineProperty(e, t, { enumerable: !0, get: r }) }, u.r = function (e) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e, "__esModule", { value: !0 }) }, u.t = function (e, t) { if (1 & t && (e = u(e)), 8 & t) return e; if (4 & t && "object" == typeof e && e && e.__esModule) return e; var r = Object.create(null); if (u.r(r), Object.defineProperty(r, "default", { enumerable: !0, value: e }), 2 & t && "string" != typeof e) for (var n in e) u.d(r, n, function (t) { return e[t] }.bind(null, n)); return r }, u.n = function (e) { var t = e && e.__esModule ? function () { return e.default } : function () { return e }; return u.d(t, "a", t), t }, u.o = function (e, t) { return Object.prototype.hasOwnProperty.call(e, t) }, u.p = "/", u.oe = function (e) { throw console.error(e), e }; var a = this["webpackJsonpnew-item-finder-website"] = this["webpackJsonpnew-item-finder-website"] || [], c = a.push.bind(a); a.push = t, a = a.slice(); for (var f = 0; f < a.length; f++)t(a[f]); var l = c; r() }([])</script>
|
|
||||||
<script src="/static/js/2.21456f47.chunk.js"></script>
|
|
||||||
<script src="/static/js/main.f3c30fdb.chunk.js"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/** @var \Laravel\Lumen\Routing\Router $router */
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Application Routes
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Here is where you can register all of the routes for an application.
|
|
||||||
| It is a breeze. Simply tell Lumen the URIs it should respond to
|
|
||||||
| and give it the Closure to call when that URI is requested.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
$router->get('/', function () {
|
|
||||||
return redirect('/search');
|
|
||||||
});
|
|
||||||
|
|
||||||
$router->get('/search[/{id}]', function () {
|
|
||||||
return view('app');
|
|
||||||
});
|
|
||||||
|
|
||||||
$router->group(['prefix' => 'api'], function () use ($router) {
|
|
||||||
$router->get('locales', 'ItemController@getLocales');
|
|
||||||
$router->get('refresh', 'ItemController@refreshAllCache');
|
|
||||||
$router->post('search', 'ItemController@search');
|
|
||||||
$router->get('item/hierarchy', 'ItemController@getHierarchy');
|
|
||||||
$router->get('item/names', 'ItemController@getAllItemsName');
|
|
||||||
$router->get('item/nameByID', 'ItemController@getItemNameByID');
|
|
||||||
$router->get('item', 'ItemController@getItem');
|
|
||||||
});
|
|
||||||
|
|
2
items/api/storage/app/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
*
|
|
||||||
!.gitignore
|
|
3
items/api/storage/framework/cache/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
*
|
|
||||||
!data/
|
|
||||||
!.gitignore
|
|
@ -1,2 +0,0 @@
|
|||||||
*
|
|
||||||
!.gitignore
|
|
2
items/api/storage/framework/views/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
*
|
|
||||||
!.gitignore
|
|
2
items/api/storage/logs/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
*
|
|
||||||
!.gitignore
|
|
@ -1,21 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
use Laravel\Lumen\Testing\DatabaseMigrations;
|
|
||||||
use Laravel\Lumen\Testing\DatabaseTransactions;
|
|
||||||
|
|
||||||
class ExampleTest extends TestCase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* A basic test example.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function testExample()
|
|
||||||
{
|
|
||||||
$this->get('/');
|
|
||||||
|
|
||||||
$this->assertEquals(
|
|
||||||
$this->app->version(), $this->response->getContent()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
use Laravel\Lumen\Testing\TestCase as BaseTestCase;
|
|
||||||
|
|
||||||
abstract class TestCase extends BaseTestCase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Creates the application.
|
|
||||||
*
|
|
||||||
* @return \Laravel\Lumen\Application
|
|
||||||
*/
|
|
||||||
public function createApplication()
|
|
||||||
{
|
|
||||||
return require __DIR__.'/../bootstrap/app.php';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,293 +0,0 @@
|
|||||||
# Walkthrough
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
* [Overview](#overview)
|
|
||||||
* [Pipeline definition](#pipeline-definition)
|
|
||||||
* [Pipeline concurrency](#pipeline-concurrency)
|
|
||||||
* [Triggers](#triggers)
|
|
||||||
* [Steps](#steps)
|
|
||||||
* [Replace hosts and user variables](#replace-hosts-and-user-variables)
|
|
||||||
* [Install frontend dependencies and build it](#install-frontend-dependencies-and-build-it)
|
|
||||||
* [Run the frontend](#run-the-frontend)
|
|
||||||
* [Test frontend](#test-frontend)
|
|
||||||
* [Check ansible syntax](#check-ansible-syntax)
|
|
||||||
* [Apply ansible playbook](#apply-ansible-playbook)
|
|
||||||
* [Playbook definition](#playbook-definition)
|
|
||||||
* [Delete old spt-items-api](#delete-old-spt-items-api)
|
|
||||||
* [Copy the project](#copy-the-project)
|
|
||||||
* [Copy PHP env file](#copy-php-env-file)
|
|
||||||
* [Get JavaScript chunks name](#get-javascript-chunks-name)
|
|
||||||
* [Get file names from find output](#get-file-names-from-find-output)
|
|
||||||
* [Copy app.blade.php file](#copy-appbladephp-file)
|
|
||||||
* [Download and install composer dependencies](#download-and-install-composer-dependencies)
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
* The project is split between the frontend and the backend
|
|
||||||
* the backend is a [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) located in [api](../api) that points towards [https://dev.sp-tarkov.com/Rev/spt-items-api.git](https://dev.sp-tarkov.com/Rev/spt-items-api.git)
|
|
||||||
* the frontend is a [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) located in [frontend](../frontend) that points towards [https://dev.sp-tarkov.com/shirito/item-finder-website-frontend.git](https://dev.sp-tarkov.com/shirito/item-finder-website-frontend.git)
|
|
||||||
* There are two Ansible pipelines
|
|
||||||
* A docker pipeline [drone-docker.yml](../../.drone-docker.yml)
|
|
||||||
* A kubernetes pipeline [drone-kubernetes.yml](../../.drone-kubernetes.yml)
|
|
||||||
* All ansible playbook files are located in [.ansible](../../.ansible-items)
|
|
||||||
* The documentation is located in [documentation](../docs)
|
|
||||||
|
|
||||||
## Pipeline definition
|
|
||||||
```yml
|
|
||||||
kind: pipeline
|
|
||||||
type: kubernetes
|
|
||||||
name: default
|
|
||||||
```
|
|
||||||
The pipeline is defined either as Docker or Kubernetes depending on [.drone-docker.yml](../../.drone-docker.yml) or [.drone-kubernetes.yml](../../.drone-kubernetes.yml). The name is set as `default`.
|
|
||||||
|
|
||||||
## Pipeline Concurrency
|
|
||||||
```yml
|
|
||||||
concurrency:
|
|
||||||
limit: 1
|
|
||||||
```
|
|
||||||
The pipeline is set to only one build at a time (every subsequent build with be pending).
|
|
||||||
|
|
||||||
## Triggers
|
|
||||||
```yml
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
```
|
|
||||||
The pipeline is run on every push We want to check that every development on `development` branch is correct and deploy automatically when merged in `master`/`main`.
|
|
||||||
## Steps
|
|
||||||
### Replace hosts and user variables
|
|
||||||
```yml
|
|
||||||
- name: replace hosts and user variables
|
|
||||||
image: ubuntu:impish
|
|
||||||
environment:
|
|
||||||
SPT_ITEMS_HOSTNAME:
|
|
||||||
from_secret: spt_items_hostname
|
|
||||||
DEPLOY_HOSTNAME:
|
|
||||||
from_secret: deploy_hostname
|
|
||||||
DEPLOY_USER:
|
|
||||||
from_secret: deploy_username
|
|
||||||
DEPLOY_PRIVATE_KEY:
|
|
||||||
from_secret: deploy_ssh_key
|
|
||||||
DEPLOY_SSH_KEY_PASSPHRASE:
|
|
||||||
from_secret: deploy_ssh_key_passphrase
|
|
||||||
commands:
|
|
||||||
- sed -i 's/{{ SPT_ITEMS_HOSTNAME }}/'"$SPT_ITEMS_HOSTNAME"'/g' ./items/frontend/.env.example
|
|
||||||
- mv ./items/frontend/.env.example ./items/frontend/.env
|
|
||||||
- echo "$DEPLOY_PRIVATE_KEY" > private.key && chmod 600 private.key
|
|
||||||
- sed -i 's/{{ DEPLOY_HOSTNAME }}/'"$DEPLOY_HOSTNAME"'/g' ./.ansible-items/inventory
|
|
||||||
- sed -i 's/{{ DEPLOY_SSH_KEY_PASSPHRASE }}/'"$DEPLOY_SSH_KEY_PASSPHRASE"'/g' ./.ansible-items/inventory
|
|
||||||
- sed -i 's/{{ DEPLOY_USER }}/'"$DEPLOY_USER"'/g' ./.ansible-items/inventory
|
|
||||||
```
|
|
||||||
Executed on every push. \
|
|
||||||
The following environment variables are injected using Drone secrets:
|
|
||||||
* `SPT_ITEMS_HOSTNAME` is used by the frontend to call the backend.
|
|
||||||
* `DEPLOY_HOSTNAME` is used by Ansible to connect to the remote server via SSH.
|
|
||||||
* `DEPLOY_USER` is used by Ansible to connect to the remote server via SSH.
|
|
||||||
* `DEPLOY_PRIVATE_KEY` is the SSH key used to connect to the remote server via SSH
|
|
||||||
* `DEPLOY_SSH_KEY_PASSPHRASE` is the SSH key passphrase
|
|
||||||
Using `sed` makes temporary changes in the container/pod instead of commiting secrets in the repo in plain text. \
|
|
||||||
The changes are never pushed and are discarded when the container/pod is terminated.
|
|
||||||
|
|
||||||
### Install frontend dependencies and build it
|
|
||||||
```yml
|
|
||||||
- name: install dependencies and build frontend
|
|
||||||
image: node:lts-alpine3.14
|
|
||||||
commands:
|
|
||||||
- node -v
|
|
||||||
- npm -v
|
|
||||||
- yarn --version
|
|
||||||
- yarn --cwd ./items/frontend install
|
|
||||||
- yarn --cwd ./items/frontend build --pure-lockfile
|
|
||||||
- rm -rf ./items/api/public/static/*
|
|
||||||
- cp -r ./items/frontend/build/* ./items/api/public
|
|
||||||
- rm ./items/api/public/index.html
|
|
||||||
depends_on:
|
|
||||||
- replace hosts and user variables
|
|
||||||
```
|
|
||||||
Executed on every push. \
|
|
||||||
Since the PHP backend serves the ReactJS frontend, the former is built and moved in the latter.
|
|
||||||
Notes:
|
|
||||||
* `yarn --cwd <path> <command>` executes the command in the specified file
|
|
||||||
* `rm -rf ./api/public/static/*` deletes the static files to make sure no old JavaScript files remain
|
|
||||||
* `rm ./api/public/invdex.html` ReactJS is bundled with a `index.html`. It is discarded to use [](https://dev.sp-tarkov.com/Rev/spt-items-api/raw/branch/master/resources/views/app.blade.php) instead.
|
|
||||||
|
|
||||||
### Run the frontend
|
|
||||||
```yaml
|
|
||||||
- name: frontend
|
|
||||||
image: nginx:1.21.4-alpine
|
|
||||||
commands:
|
|
||||||
- cp -r ./items/frontend/build/* /usr/share/nginx/html
|
|
||||||
- cp ./items/frontend/src/cypress/nginx_config/default.conf /etc/nginx/conf.d/default.conf
|
|
||||||
- nginx -g "daemon off;"
|
|
||||||
detach: true
|
|
||||||
depends_on:
|
|
||||||
- install dependencies and build frontend
|
|
||||||
```
|
|
||||||
Copies the frontend and the nginx conf in the container to be able to test it.
|
|
||||||
The frontend is run and `detach` is specified so the End-to-End tests (using cypress) can run on it
|
|
||||||
|
|
||||||
### Test frontend
|
|
||||||
```yaml
|
|
||||||
- name: test frontend
|
|
||||||
image: cypress/browsers:node16.5.0-chrome94-ff93
|
|
||||||
commands:
|
|
||||||
- node -v
|
|
||||||
- npm -v
|
|
||||||
- yarn --version
|
|
||||||
- yarn --cwd ./items/frontend cy:ci
|
|
||||||
depends_on:
|
|
||||||
- install dependencies and build frontend
|
|
||||||
```
|
|
||||||
Run frontend tests using Cypress
|
|
||||||
|
|
||||||
### Check ansible syntax
|
|
||||||
```yml
|
|
||||||
- name: check ansible syntax
|
|
||||||
image: plugins/ansible:3
|
|
||||||
settings:
|
|
||||||
playbook: ./.ansible-items/playbook.yml
|
|
||||||
inventory: ./.ansible-items/inventory
|
|
||||||
galaxy: ./.ansible-items/requirements.yml
|
|
||||||
syntax_check: true
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- development
|
|
||||||
```
|
|
||||||
Executed on every push. \
|
|
||||||
Check the Ansible syntax in [playbook.yml](../../.ansible-items/playbook.yml), [inventory](../../.ansible-items/inventory) and [requirements.yml](../../.ansible-items/requirements.yml). The check is executed on every push since we want to detect any error before validating the build using the promotion.
|
|
||||||
|
|
||||||
### Apply ansible playbook
|
|
||||||
```yml
|
|
||||||
- name: apply ansible playbook
|
|
||||||
image: plugins/ansible:3
|
|
||||||
settings:
|
|
||||||
playbook: ./.ansible-items/playbook.yml
|
|
||||||
inventory: ./.ansible-items/inventory
|
|
||||||
galaxy: ./.ansible-items/requirements.yml
|
|
||||||
timeout: 60
|
|
||||||
verbose: 2
|
|
||||||
environment:
|
|
||||||
SPT_ITEMS_HOSTNAME:
|
|
||||||
from_secret: spt_items_hostname
|
|
||||||
DEPLOY_HOSTNAME:
|
|
||||||
from_secret: deploy_hostname
|
|
||||||
DEPLOY_USER:
|
|
||||||
from_secret: deploy_username
|
|
||||||
DEPLOY_USER_GROUP:
|
|
||||||
from_secret: deploy_user_group
|
|
||||||
SPT_ITEMS_PATH:
|
|
||||||
from_secret: deploy_path
|
|
||||||
depends_on:
|
|
||||||
- test frontend
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
- main
|
|
||||||
```
|
|
||||||
Executed only on promotion to production. \
|
|
||||||
This step actually deploys to the server. \
|
|
||||||
This step is [idempotent](https://en.wikipedia.org/wiki/Idempotence). \
|
|
||||||
The following environment variables are injected using Drone secrets:
|
|
||||||
* `SPT_ITEMS_HOSTNAME` is used by the PHP env file.
|
|
||||||
* `DEPLOY_HOSTNAME` is used to connect to the remote server via SSH.
|
|
||||||
* `DEPLOY_USER` is used to connect to the remote server via SSH.
|
|
||||||
* `DEPLOY_USER_GROUP` is the user group, used to give read/write/execute permissions to the whole group. It must be the same as Nginx's user.
|
|
||||||
* `SPT_ITEMS_PATH` is the path on the remote server where the files will be copyed to.
|
|
||||||
|
|
||||||
#### Playbook definition
|
|
||||||
```yml
|
|
||||||
hosts: sptarkov
|
|
||||||
```
|
|
||||||
Uses the host defined in [inventory](../../.ansible-items/inventory). Remember, the step [Replace hosts and user variables](#replace-hosts-and-user-variables) already replaced the variables at this point.
|
|
||||||
|
|
||||||
#### Delete old spt-items-api
|
|
||||||
```yml
|
|
||||||
- name: Delete spt-items-api before adding everything again
|
|
||||||
file:
|
|
||||||
state: absent
|
|
||||||
path: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
|
||||||
```
|
|
||||||
Since the copy does not override the folder, this step takes care of it. \
|
|
||||||
`SPT_ITEMS_PATH` is injected in the environments properties (see [Apply ansible playbook](#apply-ansible-playbook))
|
|
||||||
|
|
||||||
#### Copy the project
|
|
||||||
```yml
|
|
||||||
- name: Copy the project
|
|
||||||
copy:
|
|
||||||
src: ../items/api/
|
|
||||||
dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
|
||||||
```
|
|
||||||
Copies the whole project (frontend and backend) from the [api](../api) folder into the server.
|
|
||||||
|
|
||||||
#### Copy PHP env file
|
|
||||||
```yml
|
|
||||||
- name: Copy PHP .env file
|
|
||||||
template:
|
|
||||||
src: ./templates/.php-env.j2
|
|
||||||
dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}/.env"
|
|
||||||
```
|
|
||||||
Uses [Jinja2](https://jinja2docs.readthedocs.io/en/stable/) to resolve the [template for the PHP .env file](../../.ansible-items/templates/.php-env.j2). \
|
|
||||||
`SPT_ITEMS_PATH` is injected in the environments properties (see [Apply ansible playbook](#apply-ansible-playbook)) \
|
|
||||||
`SPT_ITEMS_HOSTNAME` is injected in the environments properties (see [Apply ansible playbook](#apply-ansible-playbook))
|
|
||||||
|
|
||||||
#### Get JavaScript chunks name
|
|
||||||
```yml
|
|
||||||
- name: Get Chunk 2 name
|
|
||||||
shell:
|
|
||||||
cmd: find "{{ lookup('env', 'SPT_ITEMS_PATH') }}" -type f -name "*chunk.js" -printf "%f\n"
|
|
||||||
register: find_output
|
|
||||||
```
|
|
||||||
Prepare a find of all JavaScript chunk files for the [app.blade.php.j2](../../.ansible-items/templates/app.blade.php.j2) template. \
|
|
||||||
`SPT_ITEMS_PATH` is injected in the environments properties (see [Apply ansible playbook](#apply-ansible-playbook))
|
|
||||||
|
|
||||||
#### Get file names from find output
|
|
||||||
```yml
|
|
||||||
- name: Get file names from find output
|
|
||||||
set_fact:
|
|
||||||
chunk_list: "{{ find_output['stdout'].split('\n') }}"
|
|
||||||
```
|
|
||||||
Splits the string containing the list of all JavaScript chunk files for the [app.blade.php.j2](../../.ansible-items/templates/app.blade.php.j2) template.
|
|
||||||
|
|
||||||
#### Copy app.blade.php file
|
|
||||||
```yml
|
|
||||||
- name: Copy app.blade.php file
|
|
||||||
template:
|
|
||||||
src: ./templates/app.blade.php.j2
|
|
||||||
dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}/resources/views/app.blade.php"
|
|
||||||
```
|
|
||||||
Uses [Jinja2](https://jinja2docs.readthedocs.io/en/stable/) to resolve the [template for the PHP app.blade.php file](../../.ansible-items/templates/app.blade.php.j2). \
|
|
||||||
`SPT_ITEMS_PATH` is injected in the environments properties (see [Apply ansible playbook](#apply-ansible-playbook)).
|
|
||||||
|
|
||||||
#### Download and install composer dependencies
|
|
||||||
```yml
|
|
||||||
- name: Download and installs all composer libs and dependencies
|
|
||||||
community.general.composer:
|
|
||||||
command: install
|
|
||||||
working_dir: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Reset files permissions
|
|
||||||
```yml
|
|
||||||
- name: Reset files permissions
|
|
||||||
file:
|
|
||||||
path: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
|
||||||
owner: "{{ lookup('env', 'DEPLOY_USER') }}"
|
|
||||||
group: "{{ lookup('env', 'DEPLOY_USER_GROUP') }}"
|
|
||||||
mode: 0774
|
|
||||||
recurse: yes
|
|
||||||
```
|
|
||||||
Permissions 0644:
|
|
||||||
* user: read/write/execute
|
|
||||||
* group: read/write/execute (for Nginx to execute the php as well as write in the Laravel logs)
|
|
||||||
* other: read
|
|
||||||
`SPT_ITEMS_PATH` is injected in the environments properties (see [Apply ansible playbook](#apply-ansible-playbook)).
|
|
||||||
|
|
||||||
#### Initialize database
|
|
||||||
```yml
|
|
||||||
- name: Initialize database
|
|
||||||
uri:
|
|
||||||
url: "https://{{ lookup('env', 'SPT_ITEMS_HOSTNAME') }}/api/refresh"
|
|
||||||
method: GET
|
|
||||||
status_code: [200, 204]
|
|
||||||
timeout: 60
|
|
||||||
```
|
|
||||||
The call to `/api/refresh` fetches the data from AKI Server repository, `development` branch.
|
|
@ -1,4 +0,0 @@
|
|||||||
REACT_APP_BACKEND_URL=https://{{ SPT_ITEMS_HOSTNAME }}
|
|
||||||
REACT_APP_SPTARKOV_HOME=https://www.sp-tarkov.com/
|
|
||||||
REACT_APP_SPTARKOV_WORKSHOP=https://mods.sp-tarkov.com/
|
|
||||||
REACT_APP_SPTARKOV_DOCUMENTATION=https://docs.sp-tarkov.com/
|
|
@ -1,9 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) <year> <copyright holders>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,8 +0,0 @@
|
|||||||
# Item Finder Website
|
|
||||||
|
|
||||||
## How to build
|
|
||||||
1. Make sure `Nodejs` and `Npm` (or `Yarn`) are installed
|
|
||||||
1. Run `npm run build` or `yarn build` to build the production project
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
Created based on CRA template typescript (see [cra.md](./documentation/cra.md))
|
|
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"baseUrl": "http://localhost:3000",
|
|
||||||
"video": false,
|
|
||||||
"integrationFolder": "src/cypress/integration",
|
|
||||||
"fixtureFolder": "src/cypress/fixtures",
|
|
||||||
"supportFile": "src/cypress/support/index.js",
|
|
||||||
"pluginsFile": "src/cypress/plugin/index.js",
|
|
||||||
"env": {
|
|
||||||
"REACT_APP_SPTARKOV_HOME": "https://www.sp-tarkov.com/",
|
|
||||||
"REACT_APP_SPTARKOV_WORKSHOP": "https://mods.sp-tarkov.com/",
|
|
||||||
"REACT_APP_SPTARKOV_DOCUMENTATION": "https://docs.sp-tarkov.com/"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
# Getting Started with Create React App
|
|
||||||
|
|
||||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
|
||||||
|
|
||||||
## Available Scripts
|
|
||||||
|
|
||||||
In the project directory, you can run:
|
|
||||||
|
|
||||||
### `yarn start`
|
|
||||||
|
|
||||||
Runs the app in the development mode.\
|
|
||||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
|
||||||
|
|
||||||
The page will reload if you make edits.\
|
|
||||||
You will also see any lint errors in the console.
|
|
||||||
|
|
||||||
### `yarn test`
|
|
||||||
|
|
||||||
Launches the test runner in the interactive watch mode.\
|
|
||||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
|
||||||
|
|
||||||
### `yarn build`
|
|
||||||
|
|
||||||
Builds the app for production to the `build` folder.\
|
|
||||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
|
||||||
|
|
||||||
The build is minified and the filenames include the hashes.\
|
|
||||||
Your app is ready to be deployed!
|
|
||||||
|
|
||||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
|
||||||
|
|
||||||
### `yarn eject`
|
|
||||||
|
|
||||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
|
||||||
|
|
||||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
|
||||||
|
|
||||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
|
||||||
|
|
||||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
|
||||||
|
|
||||||
## Learn More
|
|
||||||
|
|
||||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
|
||||||
|
|
||||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
|
@ -1,77 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "new-item-finder-website",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
|
||||||
"devDependencies": {
|
|
||||||
"@cypress/code-coverage": "^3.9.11",
|
|
||||||
"@cypress/instrument-cra": "^1.4.0",
|
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
|
||||||
"@testing-library/react": "^11.1.0",
|
|
||||||
"@testing-library/user-event": "^12.1.10",
|
|
||||||
"@types/jest": "^26.0.15",
|
|
||||||
"@types/node": "^12.0.0",
|
|
||||||
"@types/react": "^17.0.0",
|
|
||||||
"@types/react-dom": "^17.0.0",
|
|
||||||
"cross-env": "^7.0.3",
|
|
||||||
"cypress": "9.0.0",
|
|
||||||
"start-server-and-test": "^1.14.0",
|
|
||||||
"typescript": "^4.1.2",
|
|
||||||
"wait-on": "^6.0.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@emotion/react": "^11.4.1",
|
|
||||||
"@emotion/styled": "^11.3.0",
|
|
||||||
"@mui/icons-material": "^5.0.3",
|
|
||||||
"@mui/material": "^5.0.3",
|
|
||||||
"@mui/styles": "^5.0.1",
|
|
||||||
"history": "5",
|
|
||||||
"react": "^17.0.2",
|
|
||||||
"react-dom": "^17.0.2",
|
|
||||||
"react-json-view": "^1.21.3",
|
|
||||||
"react-router-dom": "6",
|
|
||||||
"react-scripts": "4.0.3",
|
|
||||||
"web-vitals": "^1.0.1",
|
|
||||||
"zustand": "^3.5.13"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"start": "react-scripts start",
|
|
||||||
"build": "react-scripts build",
|
|
||||||
"test:jest": "react-scripts test",
|
|
||||||
"cy:start:app": "cross-env BROWSER=none react-scripts -r @cypress/instrument-cra start",
|
|
||||||
"cy:start:wait": "start-server-and-test cy:start:app http://localhost:3000",
|
|
||||||
"cy:install": "cypress install",
|
|
||||||
"cy:verify": "cypress verify",
|
|
||||||
"cy:run:ci": "cross-env CYPRESS_BASE_URL=http://frontend cypress run --browser chrome",
|
|
||||||
"cy:open": "yarn run cy:start:wait -- \"cypress open\"",
|
|
||||||
"cy:ci": "yarn run wait-on http-get://frontend && yarn run cy:install && yarn run cy:verify && yarn run cy:run:ci",
|
|
||||||
"eject": "react-scripts eject"
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"extends": [
|
|
||||||
"react-app",
|
|
||||||
"react-app/jest"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"browserslist": {
|
|
||||||
"production": [
|
|
||||||
">0.2%",
|
|
||||||
"not dead",
|
|
||||||
"not op_mini all"
|
|
||||||
],
|
|
||||||
"development": [
|
|
||||||
"last 1 chrome version",
|
|
||||||
"last 1 firefox version",
|
|
||||||
"last 1 safari version"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"nyc": {
|
|
||||||
"include": [
|
|
||||||
"src/**/*.ts",
|
|
||||||
"src/**/*.tsx"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"src/reportWebVitals.ts"
|
|
||||||
],
|
|
||||||
"excludeAfterRemap": true
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 97 KiB |
@ -1,44 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<!-- <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> -->
|
|
||||||
<link rel="icon" href="favicon.ico" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<meta name="theme-color" content="#000000" />
|
|
||||||
<meta name="description" content="item finder" />
|
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
||||||
<!--
|
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
|
||||||
-->
|
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
|
||||||
<!--
|
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
|
||||||
Only files inside the `public` folder can be referenced from the HTML.
|
|
||||||
|
|
||||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
|
||||||
-->
|
|
||||||
<title>Item Finder</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
||||||
<div id="root"></div>
|
|
||||||
<!--
|
|
||||||
This HTML file is a template.
|
|
||||||
If you open it directly in the browser, you will see an empty page.
|
|
||||||
|
|
||||||
You can add webfonts, meta tags, or analytics to this file.
|
|
||||||
The build step will place the bundled scripts into the <body> tag.
|
|
||||||
|
|
||||||
To begin the development, run `npm start` or `yarn start`.
|
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
|
||||||
-->
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 81 KiB |
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"short_name": "items.sp-tarkov",
|
|
||||||
"name": "Items Website for sp-tarkov",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "favicon.ico",
|
|
||||||
"sizes": "32x32",
|
|
||||||
"type": "image/x-icon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "favicon.svg",
|
|
||||||
"type": "image/svg+xml",
|
|
||||||
"sizes": "64x64 96x96 128x128 160x160 192x192 512x512"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": ".",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "#000000",
|
|
||||||
"background_color": "#ffffff"
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
# https://www.robotstxt.org/robotstxt.html
|
|
||||||
User-agent: *
|
|
||||||
Disallow:
|
|
@ -1,38 +0,0 @@
|
|||||||
import {ThemeProvider} from '@mui/material/styles'
|
|
||||||
import {MainPage} from './pages/MainPage'
|
|
||||||
import {ThemeMode} from './state/ThemeMode'
|
|
||||||
import {CssBaseline, useMediaQuery} from '@mui/material'
|
|
||||||
import {useEffect} from "react";
|
|
||||||
import {darkPalette} from "./theme/darkTheme";
|
|
||||||
import {lightPalette} from "./theme/lightTheme";
|
|
||||||
import {getTheme} from "./theme/Theme";
|
|
||||||
import {LocalStorageKeys} from "./dataaccess/SaveKeys";
|
|
||||||
import { useGlobalState } from './state/GlobalState'
|
|
||||||
|
|
||||||
const App = () => {
|
|
||||||
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
|
|
||||||
const [preferedColorScheme ,setPreferedColorScheme] = useGlobalState(state => [state.preferedColorScheme, state.setPreferedColorScheme])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const localPreferedTheme = localStorage.getItem(LocalStorageKeys.PREFERED_COLOR_SCHEME);
|
|
||||||
if (localPreferedTheme) {
|
|
||||||
setPreferedColorScheme(localPreferedTheme as ThemeMode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const preferedTheme = prefersDarkMode ? ThemeMode.DARK_MODE : ThemeMode.LIGHT_MODE;
|
|
||||||
setPreferedColorScheme(preferedTheme)
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, [prefersDarkMode]) // Need to be only used on prefersDarkMode change
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ThemeProvider theme={getTheme(preferedColorScheme === ThemeMode.DARK_MODE ? darkPalette : lightPalette)}>
|
|
||||||
<CssBaseline/>
|
|
||||||
<MainPage/>
|
|
||||||
</ThemeProvider>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App
|
|
@ -1,59 +0,0 @@
|
|||||||
import { Box, IconButton, Theme } from '@mui/material'
|
|
||||||
import { useTheme } from '@mui/material/styles'
|
|
||||||
import Brightness4Icon from '@mui/icons-material/Brightness4'
|
|
||||||
import Brightness7Icon from '@mui/icons-material/Brightness7'
|
|
||||||
import { ThemeMode } from '../state/ThemeMode'
|
|
||||||
import { useGlobalState } from '../state/GlobalState'
|
|
||||||
import { useCallback } from 'react'
|
|
||||||
import { makeStyles } from '@mui/styles'
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
|
||||||
modeToggleButtonHolder: {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
color: 'primary',
|
|
||||||
flexGrow: 1,
|
|
||||||
},
|
|
||||||
iconButton: {
|
|
||||||
ml: 1,
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
export const DarkModeToggle = () => {
|
|
||||||
const theme = useTheme()
|
|
||||||
const classes = useStyles()
|
|
||||||
const [preferedColorScheme, setPreferedColorScheme] = useGlobalState(
|
|
||||||
useCallback(
|
|
||||||
(state) => [state.preferedColorScheme, state.setPreferedColorScheme],
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
const toggleColor = () => {
|
|
||||||
const newTheme =
|
|
||||||
preferedColorScheme === ThemeMode.LIGHT_MODE
|
|
||||||
? ThemeMode.DARK_MODE
|
|
||||||
: ThemeMode.LIGHT_MODE
|
|
||||||
setPreferedColorScheme(newTheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box className={classes.modeToggleButtonHolder} id="modeToggleButtonHolder">
|
|
||||||
{theme.palette.mode} mode
|
|
||||||
<IconButton
|
|
||||||
className={classes.iconButton}
|
|
||||||
sx={{ ml: 1 }}
|
|
||||||
onClick={toggleColor}
|
|
||||||
color="inherit"
|
|
||||||
id="modeToggleButton"
|
|
||||||
>
|
|
||||||
{theme.palette.mode === 'dark' ? (
|
|
||||||
<Brightness7Icon />
|
|
||||||
) : (
|
|
||||||
<Brightness4Icon />
|
|
||||||
)}
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
import {Box, Typography} from '@mui/material'
|
|
||||||
import {makeStyles} from '@mui/styles'
|
|
||||||
|
|
||||||
const useStyles = makeStyles(() => ({
|
|
||||||
footerHolder: {
|
|
||||||
display: 'flex',
|
|
||||||
flex: '0 1 3vh',
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
padding: '0 10vw 0 10vw'
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
export const Footer = () => {
|
|
||||||
const classes = useStyles()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box className={classes.footerHolder}>
|
|
||||||
<Typography id={"footer"}>SPT-Aki ©2021 Created by Rev and Shirito</Typography>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
import { Box, Link, Theme } from '@mui/material'
|
|
||||||
import { makeStyles } from '@mui/styles'
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
import { useGlobalState } from '../state/GlobalState';
|
|
||||||
import { HeaderForm } from './HeaderForm';
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
|
||||||
headerContainer: {
|
|
||||||
display: 'flex',
|
|
||||||
flex: '0 1 3vh',
|
|
||||||
flexDirection: 'row',
|
|
||||||
backgroundColor: theme.palette.background.paper,
|
|
||||||
alignItems: 'center',
|
|
||||||
padding: '0 10vw 0 10vw',
|
|
||||||
},
|
|
||||||
linksContainer: {
|
|
||||||
display: 'flex',
|
|
||||||
flexGrow: 2,
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
height: '100%',
|
|
||||||
},
|
|
||||||
formContainer: {
|
|
||||||
display: 'flex',
|
|
||||||
flexGrow: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
height: '100%',
|
|
||||||
},
|
|
||||||
link: {
|
|
||||||
display: 'flex',
|
|
||||||
padding: '0 1vw 0 1vw',
|
|
||||||
height: '100%',
|
|
||||||
alignItems: 'center',
|
|
||||||
borderBottom: `1px solid transparent`,
|
|
||||||
'&:hover': {
|
|
||||||
borderBottom: `1px solid ${theme.palette.action.hover}`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
export const Header = () => {
|
|
||||||
const classes = useStyles()
|
|
||||||
const websiteLink = useGlobalState(useCallback((state) => state.sptarkovWebsiteUrl,[]))
|
|
||||||
const workshopLink = useGlobalState(useCallback((state) => state.sptarkovWorkshopUrl,[]))
|
|
||||||
const documentationLink = useGlobalState(useCallback((state) => state.sptarkovDocumentationUrl,[]))
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Box className={classes.headerContainer}>
|
|
||||||
<Box className={classes.linksContainer}>
|
|
||||||
<Link
|
|
||||||
underline="hover"
|
|
||||||
color="inherit"
|
|
||||||
id="website-link"
|
|
||||||
href={websiteLink}
|
|
||||||
className={classes.link}
|
|
||||||
>
|
|
||||||
Website
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
underline="hover"
|
|
||||||
color="inherit"
|
|
||||||
id="workshop-link"
|
|
||||||
href={workshopLink}
|
|
||||||
className={classes.link}
|
|
||||||
>
|
|
||||||
Workshop
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
underline="hover"
|
|
||||||
color="inherit"
|
|
||||||
id="documentation-link"
|
|
||||||
href={documentationLink}
|
|
||||||
className={classes.link}
|
|
||||||
>
|
|
||||||
Documentation
|
|
||||||
</Link>
|
|
||||||
</Box>
|
|
||||||
<Box className={classes.formContainer}>
|
|
||||||
<HeaderForm/>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
import { DarkModeToggle } from './DarkModeToggle'
|
|
||||||
import { LocaleSelect } from './LocaleSelect'
|
|
||||||
import { JsonTheme } from './JsonTheme'
|
|
||||||
import { Theme } from '@mui/material'
|
|
||||||
import { makeStyles } from '@mui/styles';
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
|
||||||
form: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
flexGrow: 1,
|
|
||||||
justifyContent: 'flex-end',
|
|
||||||
height: '100%'
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const HeaderForm = () => {
|
|
||||||
const classes = useStyles();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<form className={classes.form}>
|
|
||||||
<DarkModeToggle />
|
|
||||||
<LocaleSelect />
|
|
||||||
<JsonTheme />
|
|
||||||
</form>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
import {
|
|
||||||
Box,
|
|
||||||
FormControl,
|
|
||||||
MenuItem,
|
|
||||||
Select,
|
|
||||||
Theme,
|
|
||||||
} from '@mui/material'
|
|
||||||
import { makeStyles } from '@mui/styles'
|
|
||||||
import { ReactJsonViewThemes } from '../state/ReactJsonViewThemes'
|
|
||||||
import { LocalStorageKeys } from '../dataaccess/SaveKeys'
|
|
||||||
import { useGlobalState } from '../state/GlobalState'
|
|
||||||
import { useCallback } from 'react'
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
|
||||||
jsonHolder: {
|
|
||||||
display: 'flex',
|
|
||||||
flexGrow: 1,
|
|
||||||
padding: '0 0.5vw 0 0.5vw'
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
display: 'flex',
|
|
||||||
flexGrow: 1
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
export const JsonTheme = () => {
|
|
||||||
const classes = useStyles()
|
|
||||||
const [preferedJsonViewerTheme, setPreferedJsonViewerTheme] = useGlobalState(
|
|
||||||
useCallback(
|
|
||||||
(state) => [
|
|
||||||
state.preferedJsonViewerTheme,
|
|
||||||
state.setPreferedJsonViewerTheme,
|
|
||||||
],
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Box className={classes.jsonHolder}>
|
|
||||||
<FormControl fullWidth variant="standard">
|
|
||||||
<Select
|
|
||||||
displayEmpty
|
|
||||||
className={classes.select}
|
|
||||||
labelId="react-json-view-theme"
|
|
||||||
value={preferedJsonViewerTheme}
|
|
||||||
label="JSON theme"
|
|
||||||
onChange={(evt) => {
|
|
||||||
setPreferedJsonViewerTheme(evt.target.value)
|
|
||||||
localStorage.setItem(
|
|
||||||
LocalStorageKeys.PREFERED_JSON_THEME,
|
|
||||||
evt.target.value,
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
id="json-selector"
|
|
||||||
>
|
|
||||||
<MenuItem disabled value="">
|
|
||||||
<em>JSON theme</em>
|
|
||||||
</MenuItem>
|
|
||||||
{ReactJsonViewThemes.map((theme, idx) => (
|
|
||||||
<MenuItem key={idx} value={theme}>
|
|
||||||
{theme}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
import { Select, MenuItem, Theme, Box, FormControl } from '@mui/material'
|
|
||||||
import {makeStyles} from '@mui/styles'
|
|
||||||
import { useCallback, useEffect } from 'react';
|
|
||||||
import { useGlobalState } from '../state/GlobalState'
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
|
||||||
localeHolder: {
|
|
||||||
display: 'flex',
|
|
||||||
flexGrow: 1,
|
|
||||||
padding: '0 0.5vw 0 0.5vw'
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
display: 'flex',
|
|
||||||
flexGrow: 1
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const LocaleSelect = () => {
|
|
||||||
const classes = useStyles()
|
|
||||||
const [preferedLocale, setPreferedLocale] = useGlobalState(useCallback(state => [state.preferedLocale, state.setPreferedLocale],[]))
|
|
||||||
const [localesList, refreshLocalesList] = useGlobalState(useCallback(state => [state.localesList, state.refreshLocalesList],[]))
|
|
||||||
|
|
||||||
useEffect(()=> refreshLocalesList(), [refreshLocalesList])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Box className={classes.localeHolder}>
|
|
||||||
<FormControl fullWidth variant="standard">
|
|
||||||
<Select
|
|
||||||
displayEmpty
|
|
||||||
className={classes.select}
|
|
||||||
labelId="prefered-locale"
|
|
||||||
value={localesList.length > 0 ? preferedLocale : ''}
|
|
||||||
onChange={(evt) => {
|
|
||||||
setPreferedLocale(evt.target.value)
|
|
||||||
}}
|
|
||||||
id="locale-selector"
|
|
||||||
>
|
|
||||||
<MenuItem disabled value="">
|
|
||||||
<em>Language</em>
|
|
||||||
</MenuItem>
|
|
||||||
{localesList.map((locale, idx) => (
|
|
||||||
<MenuItem key={idx} value={locale}>
|
|
||||||
{locale}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
{
|
|
||||||
"item": {
|
|
||||||
"_id": "5734773724597737fd047c14",
|
|
||||||
"_name": "condensed_milk",
|
|
||||||
"_parent": "5448e8d04bdc2ddf718b4569",
|
|
||||||
"_props": {
|
|
||||||
"AllowSpawnOnLocations": [],
|
|
||||||
"AnimationVariantsNumber": 0,
|
|
||||||
"BackgroundColor": "orange",
|
|
||||||
"CanRequireOnRagfair": true,
|
|
||||||
"CanSellOnRagfair": true,
|
|
||||||
"ChangePriceCoef": 1,
|
|
||||||
"ConflictingItems": [],
|
|
||||||
"CreditsPrice": 24943,
|
|
||||||
"Description": "Condensed milk",
|
|
||||||
"DiscardingBlock": false,
|
|
||||||
"ExamineExperience": 20,
|
|
||||||
"ExamineTime": 1,
|
|
||||||
"ExaminedByDefault": false,
|
|
||||||
"ExtraSizeDown": 0,
|
|
||||||
"ExtraSizeForceAdd": false,
|
|
||||||
"ExtraSizeLeft": 0,
|
|
||||||
"ExtraSizeRight": 0,
|
|
||||||
"ExtraSizeUp": 0,
|
|
||||||
"FixedPrice": false,
|
|
||||||
"Height": 1,
|
|
||||||
"HideEntrails": false,
|
|
||||||
"IsAlwaysAvailableForInsurance": false,
|
|
||||||
"IsLockedafterEquip": false,
|
|
||||||
"IsUnbuyable": false,
|
|
||||||
"IsUndiscardable": false,
|
|
||||||
"IsUngivable": false,
|
|
||||||
"IsUnsaleable": false,
|
|
||||||
"ItemSound": "food_tin_can",
|
|
||||||
"LootExperience": 50,
|
|
||||||
"MaxResource": 1,
|
|
||||||
"MergesWithChildren": false,
|
|
||||||
"Name": "Condensed milk",
|
|
||||||
"NotShownInSlot": false,
|
|
||||||
"Prefab": {
|
|
||||||
"path": "assets/content/weapons/usable_items/item_food_condensed_milk/item_food_condensed_milk_loot.bundle",
|
|
||||||
"rcid": ""
|
|
||||||
},
|
|
||||||
"QuestItem": false,
|
|
||||||
"RagFairCommissionModifier": 1,
|
|
||||||
"Rarity": "Rare",
|
|
||||||
"RepairCost": 0,
|
|
||||||
"RepairSpeed": 0,
|
|
||||||
"SendToClient": false,
|
|
||||||
"ShortName": "Condensed milk",
|
|
||||||
"SpawnChance": 9,
|
|
||||||
"StackMaxSize": 1,
|
|
||||||
"StackObjectsCount": 1,
|
|
||||||
"StimulatorBuffs": "Buffs_food_condensed_milk",
|
|
||||||
"Unlootable": false,
|
|
||||||
"UnlootableFromSide": [],
|
|
||||||
"UnlootableFromSlot": "FirstPrimaryWeapon",
|
|
||||||
"UsePrefab": {
|
|
||||||
"path": "assets/content/weapons/usable_items/item_food_condensed_milk/item_food_condensed_milk_container.bundle",
|
|
||||||
"rcid": ""
|
|
||||||
},
|
|
||||||
"Weight": 0.4,
|
|
||||||
"Width": 1,
|
|
||||||
"effects_damage": [],
|
|
||||||
"effects_health": {
|
|
||||||
"Energy": {
|
|
||||||
"value": 75
|
|
||||||
},
|
|
||||||
"Hydration": {
|
|
||||||
"value": -65
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foodEffectType": "afterUse",
|
|
||||||
"foodUseTime": 4
|
|
||||||
},
|
|
||||||
"_proto": "5734770f24597738025ee254",
|
|
||||||
"_type": "Item"
|
|
||||||
},
|
|
||||||
"locale": {
|
|
||||||
"Description": "Condensed milk, also called \"Sguschyonka\" in Russia, once was a part of field ration for the Union soldiers in Civil War, but later reached unprecedented popularity in post-Soviet countries, becoming almost a staple product. Canned, it can be stored for decades and remain just as sweet, tasty and nutritious.",
|
|
||||||
"Name": "Condensed milk",
|
|
||||||
"ShortName": "Cond. milk"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,152 +0,0 @@
|
|||||||
export {};
|
|
||||||
|
|
||||||
describe('Dark Mode Toggle', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.window()
|
|
||||||
.its("sessionStorage")
|
|
||||||
.invoke("removeItem", "db.sp-tarkov.com-locales");
|
|
||||||
|
|
||||||
cy.intercept(
|
|
||||||
'**/api/locales',
|
|
||||||
{ middleware: true },
|
|
||||||
(req) => {
|
|
||||||
req.on('before:response', (res) => {
|
|
||||||
// force all API responses to not be cached
|
|
||||||
res.headers['cache-control'] = 'no-store'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
).as('getLocales');
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(()=> {
|
|
||||||
cy.clearLocalStorage();
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Theme toggle', () => {
|
|
||||||
beforeEach(()=>{
|
|
||||||
cy.intercept({
|
|
||||||
method: 'GET',
|
|
||||||
url: '**/api/locales'}, {
|
|
||||||
statusCode: 200,
|
|
||||||
body: []
|
|
||||||
}).as('getLocalesWithoutData');
|
|
||||||
})
|
|
||||||
describe('If no localStorage is set',() =>{
|
|
||||||
before(() => {
|
|
||||||
cy.visit('/', {
|
|
||||||
onBeforeLoad: function (window) {
|
|
||||||
window.localStorage.removeItem('db.sp-tarkov.com-prefered-color-scheme')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('theme shoud match light', () => {
|
|
||||||
cy.get('#modeToggleButtonHolder')
|
|
||||||
.should('be.visible')
|
|
||||||
.should('have.text', 'light mode');
|
|
||||||
|
|
||||||
cy.get('[data-testid="Brightness4Icon"]')
|
|
||||||
.should('be.visible');
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
describe('If localStorage is set to light',() =>{
|
|
||||||
before(() => {
|
|
||||||
cy.visit('/', {
|
|
||||||
onBeforeLoad: function (window) {
|
|
||||||
window.localStorage.setItem('db.sp-tarkov.com-prefered-color-scheme', 'light')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('theme shoud match light', () => {
|
|
||||||
cy.get('#modeToggleButtonHolder')
|
|
||||||
.should('be.visible')
|
|
||||||
.should('have.text', 'light mode');
|
|
||||||
|
|
||||||
cy.get('[data-testid="Brightness4Icon"]')
|
|
||||||
.should('be.visible');
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('If localStorage is set to dark',() =>{
|
|
||||||
before(() => {
|
|
||||||
cy.visit('/', {
|
|
||||||
onBeforeLoad: function (window) {
|
|
||||||
window.localStorage.setItem('db.sp-tarkov.com-prefered-color-scheme', 'dark')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('theme shoud match dark if localStorage is set to dark', () => {
|
|
||||||
cy.get('#modeToggleButtonHolder')
|
|
||||||
.should('be.visible')
|
|
||||||
.should('have.text', 'dark mode');
|
|
||||||
|
|
||||||
cy.get('[data-testid="Brightness7Icon"]')
|
|
||||||
.should('be.visible');
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Theme toggle',() =>{
|
|
||||||
describe('Toggle from light to dark', ()=>{
|
|
||||||
before(() => {
|
|
||||||
cy.visit('/', {
|
|
||||||
onBeforeLoad: function (window) {
|
|
||||||
window.localStorage.setItem('db.sp-tarkov.com-prefered-color-scheme', 'light')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
it('should be visible and functionnal', () => {
|
|
||||||
cy.get('#modeToggleButtonHolder')
|
|
||||||
.should('be.visible')
|
|
||||||
.should('have.text', 'light mode');
|
|
||||||
|
|
||||||
cy.get('[data-testid="Brightness4Icon"]')
|
|
||||||
.should('be.visible');
|
|
||||||
|
|
||||||
cy.get('#modeToggleButton')
|
|
||||||
.should('be.visible')
|
|
||||||
.click({force: true});
|
|
||||||
|
|
||||||
cy.get('[data-testid="Brightness7Icon"]')
|
|
||||||
.should('be.visible');
|
|
||||||
|
|
||||||
cy.get('#modeToggleButtonHolder')
|
|
||||||
.should('be.visible')
|
|
||||||
.should('have.text', 'dark mode');
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Toggle from dark to light', ()=>{
|
|
||||||
before(() => {
|
|
||||||
cy.visit('/', {
|
|
||||||
onBeforeLoad: function (window) {
|
|
||||||
window.localStorage.setItem('db.sp-tarkov.com-prefered-color-scheme', 'dark')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
it('should be visible and functionnal', () => {
|
|
||||||
cy.get('[data-testid="Brightness7Icon"]')
|
|
||||||
.should('be.visible');
|
|
||||||
|
|
||||||
cy.get('#modeToggleButtonHolder')
|
|
||||||
.should('be.visible')
|
|
||||||
.should('have.text', 'dark mode');
|
|
||||||
|
|
||||||
cy.get('#modeToggleButton')
|
|
||||||
.should('be.visible')
|
|
||||||
.click({force: true});
|
|
||||||
|
|
||||||
cy.get('#modeToggleButtonHolder')
|
|
||||||
.should('be.visible')
|
|
||||||
.should('have.text', 'light mode');
|
|
||||||
|
|
||||||
cy.get('[data-testid="Brightness4Icon"]')
|
|
||||||
.should('be.visible');
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,10 +0,0 @@
|
|||||||
export {};
|
|
||||||
|
|
||||||
describe('Footer', () => {
|
|
||||||
it('footer should be visible', () => {
|
|
||||||
cy.visit('/');
|
|
||||||
|
|
||||||
cy.get('#footer').should('be.visible')
|
|
||||||
cy.get('#footer').should('have.text', 'SPT-Aki ©2021 Created by Rev and Shirito');
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,62 +0,0 @@
|
|||||||
export {};
|
|
||||||
|
|
||||||
describe('Header', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.window()
|
|
||||||
.its("sessionStorage")
|
|
||||||
.invoke("removeItem", "db.sp-tarkov.com-locales");
|
|
||||||
|
|
||||||
cy.intercept(
|
|
||||||
'**/api/locales',
|
|
||||||
{ middleware: true },
|
|
||||||
(req) => {
|
|
||||||
req.on('before:response', (res) => {
|
|
||||||
// force all API responses to not be cached
|
|
||||||
res.headers['cache-control'] = 'no-store'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
).as('getLocales');
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(()=> {
|
|
||||||
cy.clearLocalStorage();
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Test links', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.visit('/');
|
|
||||||
cy.intercept({
|
|
||||||
method: 'GET',
|
|
||||||
url: '**/api/locales'}, {
|
|
||||||
statusCode: 200,
|
|
||||||
body: []
|
|
||||||
});
|
|
||||||
})
|
|
||||||
it('website link shoud be visible and contains the right url', () => {
|
|
||||||
cy.get('#website-link')
|
|
||||||
.should('be.visible')
|
|
||||||
.contains('a', 'Website')
|
|
||||||
.should('have.attr', 'href')
|
|
||||||
.should('not.be.empty')
|
|
||||||
.and('include',Cypress.env('REACT_APP_SPTARKOV_HOME'));
|
|
||||||
})
|
|
||||||
|
|
||||||
it('workshop link shoud be visible and contains the right url', () => {
|
|
||||||
cy.get('#workshop-link')
|
|
||||||
.should('be.visible')
|
|
||||||
.contains('a', 'Workshop')
|
|
||||||
.should('have.attr', 'href')
|
|
||||||
.should('not.be.empty')
|
|
||||||
.and('include',Cypress.env('REACT_APP_SPTARKOV_WORKSHOP'));
|
|
||||||
})
|
|
||||||
|
|
||||||
it('documentation link shoud be visible and contains the right url', () => {
|
|
||||||
cy.get('#documentation-link')
|
|
||||||
.should('be.visible')
|
|
||||||
.contains('a', 'Documentation')
|
|
||||||
.should('have.attr', 'href')
|
|
||||||
.should('not.be.empty')
|
|
||||||
.and('include',Cypress.env('REACT_APP_SPTARKOV_DOCUMENTATION'));
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,69 +0,0 @@
|
|||||||
export {};
|
|
||||||
|
|
||||||
describe('Json Theme', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.window()
|
|
||||||
.its("sessionStorage")
|
|
||||||
.invoke("removeItem", "db.sp-tarkov.com-locales");
|
|
||||||
|
|
||||||
cy.intercept(
|
|
||||||
'**/api/locales',
|
|
||||||
{ middleware: true },
|
|
||||||
(req) => {
|
|
||||||
req.on('before:response', (res) => {
|
|
||||||
// force all API responses to not be cached
|
|
||||||
res.headers['cache-control'] = 'no-store'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
).as('getLocales');
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(()=> {
|
|
||||||
cy.clearLocalStorage();
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Json theme selector', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.intercept({
|
|
||||||
method: 'GET',
|
|
||||||
url: '**/api/locales'}, {
|
|
||||||
statusCode: 200,
|
|
||||||
body: []
|
|
||||||
});
|
|
||||||
})
|
|
||||||
describe('When local storage is not set',() => {
|
|
||||||
before(() => {
|
|
||||||
cy.visit('/', {
|
|
||||||
onBeforeLoad: function (window) {
|
|
||||||
window.localStorage.removeItem('db.sp-tarkov.com-prefered-json-theme')
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
it('json theme selector shoud be visible and functional', () => {
|
|
||||||
cy.get('#json-selector')
|
|
||||||
.should('be.visible')
|
|
||||||
.click({force: true})
|
|
||||||
.then(option =>{
|
|
||||||
cy.get('[role="option"]').contains('mocha').click({force: true});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('When local storage is set', () => {
|
|
||||||
before(() => {
|
|
||||||
cy.visit('/', {
|
|
||||||
onBeforeLoad: function (window) {
|
|
||||||
window.localStorage.setItem('db.sp-tarkov.com-prefered-json-theme', 'eighties')
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
it('json theme selector shoud have the right value', () => {
|
|
||||||
cy.get('#json-selector')
|
|
||||||
.should('be.visible')
|
|
||||||
.contains('eighties');
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,93 +0,0 @@
|
|||||||
export {};
|
|
||||||
|
|
||||||
describe('Locale select', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.window()
|
|
||||||
.its("sessionStorage")
|
|
||||||
.invoke("removeItem", "db.sp-tarkov.com-locales");
|
|
||||||
|
|
||||||
cy.intercept(
|
|
||||||
'**/api/locales',
|
|
||||||
{ middleware: true },
|
|
||||||
(req) => {
|
|
||||||
req.on('before:response', (res) => {
|
|
||||||
// force all API responses to not be cached
|
|
||||||
res.headers['cache-control'] = 'no-store'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
).as('getLocales');
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(()=> {
|
|
||||||
cy.clearLocalStorage();
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Locale selector', () => {
|
|
||||||
describe('when no locale is available', () => {
|
|
||||||
before(() => {
|
|
||||||
cy.intercept({
|
|
||||||
method: 'GET',
|
|
||||||
url: '**/api/locales'},
|
|
||||||
[]).as('getLocalesWithoutData');
|
|
||||||
cy.visit('/', {
|
|
||||||
onBeforeLoad: function (window) {
|
|
||||||
window.localStorage.removeItem('db.sp-tarkov.com-prefered-locale')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('language selector shoud be visible', () => {
|
|
||||||
cy.get('#locale-selector')
|
|
||||||
.should('be.visible')
|
|
||||||
.click({force: true})
|
|
||||||
.then(option =>{
|
|
||||||
cy.get('[role="option"]').should('not.contain', 'locale2');
|
|
||||||
});
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('when locales are available', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.intercept({
|
|
||||||
method: 'GET',
|
|
||||||
url: '**/api/locales'},
|
|
||||||
['locale1', 'locale2', 'locale3'])
|
|
||||||
.as('getLocalesWithData');
|
|
||||||
cy.visit('/');
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('and no local storage is detected', () => {
|
|
||||||
before(() => {
|
|
||||||
cy.visit('/', {
|
|
||||||
onBeforeLoad: function (window) {
|
|
||||||
window.localStorage.removeItem('db.sp-tarkov.com-prefered-locale')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
it('language selector shoud be visible and functional', () => {
|
|
||||||
cy.get('#locale-selector')
|
|
||||||
.should('be.visible')
|
|
||||||
.click({force: true})
|
|
||||||
.then(option =>{
|
|
||||||
cy.get('[data-value="locale3"]').contains('locale3').click({force: true});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('and local storage is detected', () => {
|
|
||||||
before(() => {
|
|
||||||
cy.visit('/', {
|
|
||||||
onBeforeLoad: function (window) {
|
|
||||||
window.localStorage.setItem('db.sp-tarkov.com-prefered-locale', 'locale2')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
it('language selector shoud be visible and functional', () => {
|
|
||||||
cy.get('#locale-selector')
|
|
||||||
.should('be.visible')
|
|
||||||
.contains('locale2');
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
@ -1,78 +0,0 @@
|
|||||||
import condensedMilk from "../fixtures/condensed_milk.json";
|
|
||||||
|
|
||||||
export {};
|
|
||||||
|
|
||||||
describe('Search area', ()=>{
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.intercept({
|
|
||||||
method: 'GET',
|
|
||||||
url: '**/api/locales'
|
|
||||||
}, {
|
|
||||||
statusCode: 200,
|
|
||||||
body: ['locale1', 'locale2']
|
|
||||||
}).as('getLocalesWithoutData');
|
|
||||||
|
|
||||||
cy.intercept({
|
|
||||||
method: 'GET',
|
|
||||||
url: '**/api/item?*'
|
|
||||||
}, {
|
|
||||||
statusCode: 200,
|
|
||||||
body: condensedMilk
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.intercept({
|
|
||||||
method: 'POST',
|
|
||||||
url: '**/api/search*'
|
|
||||||
}, {
|
|
||||||
statusCode: 200,
|
|
||||||
body: []
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.intercept({
|
|
||||||
method: 'GET',
|
|
||||||
url: '**/api/item/hierarchy*'
|
|
||||||
}, {
|
|
||||||
statusCode: 200,
|
|
||||||
body: condensedMilk
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.visit('/');
|
|
||||||
})
|
|
||||||
|
|
||||||
it('default layout', () => {
|
|
||||||
cy.get('#navigation-breadcrumb')
|
|
||||||
.should('be.visible');
|
|
||||||
|
|
||||||
cy.get('#home-breadcrumb')
|
|
||||||
.should('be.visible')
|
|
||||||
.contains('a', 'Home')
|
|
||||||
.should('have.attr', 'href')
|
|
||||||
.should('not.be.empty')
|
|
||||||
.and('include', '/');
|
|
||||||
|
|
||||||
cy.get('#search-autocomplete')
|
|
||||||
.should('be.visible');
|
|
||||||
cy.get('#search-autocomplete-label')
|
|
||||||
.should('be.visible')
|
|
||||||
.contains('label', 'Search by name or ID');
|
|
||||||
|
|
||||||
cy.get('#search-no-data')
|
|
||||||
.should('be.visible')
|
|
||||||
.contains('p', 'No data to display');
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Searching', () => {
|
|
||||||
it('Search using ID displays the json with locale', () => {
|
|
||||||
cy.get('#search-autocomplete')
|
|
||||||
.type(condensedMilk.item._id)
|
|
||||||
.type('{ENTER}')
|
|
||||||
.should('have.value', condensedMilk.locale.Name)
|
|
||||||
cy.get(`.react-json-view .object-key-val > .pushed-content > .object-content > .variable-row > .variable-value > div > .string-value:contains("${condensedMilk.item._id}")`)
|
|
||||||
.should('have.length', 1)
|
|
||||||
.invoke('text')
|
|
||||||
.should('eq', `"${condensedMilk.item._id}"`);
|
|
||||||
cy.get(`.react-json-view .object-key-val > .pushed-content > .object-content > .variable-row > .variable-value > div > .string-value:contains("${condensedMilk.locale.Name}")`)
|
|
||||||
.should('have.length.above', 0);
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|