initial commit of functioning opening tracking

This commit is contained in:
Sheldan
2024-01-06 23:29:25 +01:00
parent b72c68dfe5
commit 45e7982330
176 changed files with 37635 additions and 0 deletions

2
.env Normal file
View File

@@ -0,0 +1,2 @@
REGISTRY_PREFIX=harbor.sheldan.dev/gw2/
VERSION=0.0.1

35
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Execute Build
on:
push:
branches:
- master
- feature/**
- hotfix/**
- bugfix/**
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'corretto'
java-version: 21
- name: Build with Maven
run: mvn -B install --file gw2-tools-backend/pom.xml
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '20.x'
- name: Install dependencies
run: cd gw2-tools-ui && npm ci

86
.github/workflows/release.yaml vendored Normal file
View File

@@ -0,0 +1,86 @@
name: Publishes a new version of the application
on: workflow_dispatch
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: master
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'corretto'
java-version: 21
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: '20.x'
- name: Install node dependencies and build
working-directory: ./gw2-tools-ui
run: npm ci
- name: Build ui application
working-directory: ./gw2-tools-ui
run: npm run build
- name: Copy built UI
run: cp -R gw2-tools-ui/build/* gw2-tools-frontend/resources
- name: Load current version
id: version
working-directory: ./gw2-tools-backend
run: echo "version=$(mvn -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive exec:exec | cut -d- -f1)" >> $GITHUB_ENV
- name: Release maven packages
uses: qcastel/github-actions-maven-release@v1.12.41
env:
JAVA_HOME: /usr/lib/jvm/java-21-openjdk/
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
maven-project-folder: "gw2-tools-backend/"
git-release-bot-name: "release-bot"
git-release-bot-email: "release-bot@sheldan.dev"
release-branch-name: master
maven-args: "-Dmaven.javadoc.skip=true -s settings.xml -DskipTests"
access-token: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Harbor
uses: docker/login-action@v2
with:
registry: harbor.sheldan.dev
username: ${{ secrets.HARBOR_USERNAME }}
password: ${{ secrets.HARBOR_TOKEN }}
- name: Update Chart version file
uses: fjogeleit/yaml-update-action@v0.13.2
with:
valueFile: 'ci/gw2-tools/Chart.yaml'
propertyPath: 'version'
value: ${{ env.version }}
commitChange: false
- name: Update .env version
run:
sed -i '2s/.*/VERSION=${{ env.version }}/' .env
- name: Update version in package.json
working-directory: ./gw2-tools-ui
run:
npm version ${{ env.version }}
- name: Load env file
id: dotenv
uses: falti/dotenv-action@v1.0.4
with:
path: .env
- name: Build and push Docker containers
run: docker-compose build && docker-compose push
env:
REGISTRY_PREFIX: ${{ steps.dotenv.outputs.registry_prefix }}
VERSION: ${{ steps.dotenv.outputs.version }}
- name: Helm package and push
working-directory: ./ci/
run: |-
helm registry login -u '${{ secrets.HARBOR_USERNAME }}' -p '${{ secrets.HARBOR_TOKEN }}' harbor.sheldan.dev
helm package gw2-tools
helm push gw2-tools*.tgz oci://harbor.sheldan.dev/gw2
- name: Fix file permissions
run:
sudo chmod -R ugo+rwX . # https://github.com/actions/checkout/issues/164
- name: Commit updated versions
uses: EndBug/add-and-commit@v9
with:
author_name: "release-bot"
author_email: "release-bot@sheldan.dev"

12
.gitignore vendored
View File

@@ -18,7 +18,19 @@
*.zip
*.tar.gz
*.rar
.idea
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
/gw2-tools-frontend/resources/static/
/gw2-tools-frontend/resources/favicon.ico
/gw2-tools-frontend/resources/asset-manifest.json
/gw2-tools-frontend/resources/manifest.json
/gw2-tools-frontend/resources/robots.txt
/gw2-tools-frontend/resources/logo512.png
/gw2-tools-frontend/resources/index.html
/gw2-tools-frontend/resources/logo192.png
/tilt/gw2-tools-dev/Chart.lock
/target/
*.tgz

3
.tiltignore Normal file
View File

@@ -0,0 +1,3 @@
*.tgz
tilt/gw2-tools-dev/tmpcharts/
tilt/gw2-tools-dev/charts/*.tgz

9
README Normal file
View File

@@ -0,0 +1,9 @@
Just some tools that I created thinking what could help:
for now its a tracker to track openings of containers, to help with drop rate research on the wiki
Notice required by Guild wars 2:
© ArenaNet LLC. All rights reserved. NCSOFT, ArenaNet, Guild Wars, Guild Wars 2, GW2, Guild Wars 2: Heart of Thorns, Guild Wars 2: Path of Fire, Guild Wars 2: End of Dragons, and Guild Wars 2: Secrets of the Obscure and all associated logos, designs, and composite marks are trademarks or registered trademarks of NCSOFT Corporation.
As taken from Guild Wars 2 Content Terms of Use on 2024-01-08.

44
Tiltfile Normal file
View File

@@ -0,0 +1,44 @@
allow_k8s_contexts('k8s-cluster')
load('ext://restart_process', 'docker_build_with_restart')
registry = 'harbor.sheldan.dev/gw2/'
local_resource(
'java-backend-compile',
' cd gw2-tools-backend && mvn install && ' +
' rm -rf executable/target/jar-staging && ' +
' unzip -o executable/target/gw2-tools-exec.jar -d executable/target/jar-staging && ' +
' rsync --delete --delete-excluded --inplace --checksum --exclude="*-SNAPSHOT.jar" -r executable/target/jar-staging/ executable/target/jar && ' +
' rm -rf executable/target/jar/snapshots && ' +
' mkdir executable/target/jar/snapshots && ' +
' rsync --delete --delete-excluded --inplace --checksum --include="*/" --include="*-SNAPSHOT.jar" --exclude="*" -r executable/target/jar-staging/BOOT-INF/lib/ executable/target/jar/snapshots',
deps=['gw2-tools-backend/pom.xml'], auto_init=False, trigger_mode = TRIGGER_MODE_MANUAL, labels=['compilation'])
docker_build_with_restart(
registry + 'gw2-tools-backend',
'./gw2-tools-backend/executable/target/jar',
entrypoint=['java', '-noverify', '-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005', '-cp', '.:./lib/*', 'dev.sheldan.gw2.tools.ToolApplicationKt'],
dockerfile='./gw2-tools-backend/executable/Dockerfile',
live_update=[
sync('./gw2-tools-backend/executable/target/jar/BOOT-INF/lib', '/app/lib'),
sync('./gw2-tools-backend/executable/target/jar/META-INF', '/app/META-INF'),
sync('./gw2-tools-backend/executable/target/jar/BOOT-INF/classes', '/app'),
sync('./gw2-tools-backend/executable/target/jar/snapshots', '/app/lib')
],
)
docker_build(registry + 'gw2-tools-frontend', 'gw2-tools-frontend', dockerfile='gw2-tools-frontend/docker/Dockerfile')
docker_build(registry + 'gw2-tools-database', 'gw2-tools-backend/database/src/main', dockerfile='gw2-tools-backend/database/src/main/docker/Dockerfile')
local('cd tilt/gw2-tools-dev && helm dep up')
k8s_yaml(helm('tilt/gw2-tools-dev', values=[
'./../drr-environments/argocd/apps/gw2-tools/values/local/values.yaml',
'./../drr-environments/argocd/apps/gw2-tools/values/local/values.secrets.yaml'
]))
k8s_resource('backend', port_forwards='5005:5005', labels=['applications'])
k8s_resource('frontend', labels=['applications'])
k8s_resource('chart-postgresql', port_forwards='5432:5432', labels=['applications'])
k8s_resource('db-config-deployment-job', auto_init=False, trigger_mode = TRIGGER_MODE_MANUAL, labels=['deployment'])
local_resource('ui-build', 'rm -rf gw2-tools-frontend/resources/static && cd gw2-tools-ui && npm run build && cp -R build/* ../gw2-tools-frontend/resources', auto_init=False, trigger_mode = TRIGGER_MODE_MANUAL, labels=['compilation'])

23
ci/gw2-tools/.helmignore Normal file
View File

@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

8
ci/gw2-tools/Chart.yaml Normal file
View File

@@ -0,0 +1,8 @@
apiVersion: v2
name: gw2-tools
description: A Helm chart for Kubernetes
type: application
version: 0.0.1

View File

@@ -0,0 +1,7 @@
liquibase.secureParsing=false
liquibase.liquibaseSchemaName={{ .Values.db.schemaName }}
liquibase.command.defaultSchemaName={{ .Values.db.schemaName }}
liquibase.command.password={{ .Values.dbCredentials.password }}
liquibase.command.username={{ .Values.dbCredentials.userName }}
driver=org.postgresql.Driver
liquibase.command.url=jdbc:postgresql://{{ $.Values.dbCredentials.host }}:{{ $.Values.dbCredentials.port }}/{{ $.Values.dbCredentials.name }}

View File

@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "gw2Tools.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "gw2Tools.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "gw2Tools.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "gw2Tools.labels" -}}
helm.sh/chart: {{ include "gw2Tools.chart" . }}
{{ include "gw2Tools.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "gw2Tools.selectorLabels" -}}
app.kubernetes.io/name: {{ include "gw2Tools.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "gw2Tools.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "gw2Tools.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,102 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
labels:
app: backend
spec:
{{- if not .Values.backend.autoscaling.enabled }}
replicas: {{ .Values.backend.replicaCount }}
{{- end }}
selector:
matchLabels:
app: backend
template:
metadata:
{{- with .Values.backend.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
app: backend
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "gw2Tools.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.backend.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.backend.securityContext | nindent 12 }}
image: "{{ .Values.backend.image.repository }}/{{ .Values.backend.image.image }}:{{ .Values.backend.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.backend.image.pullPolicy }}
env:
- name: DB_PASS
valueFrom:
secretKeyRef:
name: db-credentials
key: dbPassword
- name: DB_HOST
valueFrom:
secretKeyRef:
name: db-credentials
key: dbHost
- name: DB_PORT
valueFrom:
secretKeyRef:
name: db-credentials
key: dbPort
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: dbUser
- name: DB_NAME
valueFrom:
secretKeyRef:
name: db-credentials
key: dbName
{{- range $key, $value := .Values.backend.propertyConfig }}
- name: {{ $key | quote }}
value: {{ $value | quote}}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.backend.service.port }}
protocol: TCP
{{- if .Values.backend.debug.enabled }}
- name: debug
containerPort: {{ .Values.backend.debug.port }}
protocol: TCP
{{- end }}
readinessProbe:
httpGet:
path: /health-check
port: {{ .Values.backend.service.port }}
initialDelaySeconds: {{ $.Values.backend.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ $.Values.backend.readinessProbe.periodSeconds }}
failureThreshold: {{ $.Values.backend.readinessProbe.failureThreshold }}
livenessProbe:
httpGet:
path: /health-check
port: {{ .Values.backend.service.port }}
initialDelaySeconds: {{ $.Values.backend.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ $.Values.backend.livenessProbe.periodSeconds }}
failureThreshold: {{ $.Values.backend.livenessProbe.failureThreshold }}
resources:
{{- toYaml .Values.backend.resources | nindent 12 }}
{{- with .Values.backend.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.backend.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.backend.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,28 @@
{{- if .Values.backend.autoscaling.enabled }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: backend
labels:
{{- include "gw2Tools.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: backend
minReplicas: {{ .Values.backend.autoscaling.minReplicas }}
maxReplicas: {{ .Values.backend.autoscaling.maxReplicas }}
metrics:
{{- if .Values.backend.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
targetAverageUtilization: {{ .Values.backend.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.backend.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
targetAverageUtilization: {{ .Values.backend.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.backend.service.name }}
labels:
{{- include "gw2Tools.labels" . | nindent 4 }}
spec:
type: {{ .Values.backend.service.type }}
ports:
- port: {{ .Values.backend.service.port }}
targetPort: {{ .Values.backend.service.port }}
protocol: TCP
name: http
selector:
app: backend

View File

@@ -0,0 +1,38 @@
{{- if .Values.cacheJob.enabled -}}
apiVersion: batch/v1
kind: CronJob
metadata:
name: cache-job
spec:
schedule: {{ .Values.cacheJob.cronExpression | quote }}
jobTemplate:
spec:
template:
spec:
containers:
- name: item-cache
image: {{ .Values.cacheJob.image.repository }}{{ .Values.cacheJob.image.image }}:{{ .Values.cacheJob.image.tag }}
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- curl -X POST "${BACKEND_HOST}:${BACKEND_PORT}/item-cache"
env:
- name: BACKEND_HOST
value: "{{ .Values.backend.service.name }}.{{ .Release.Namespace }}.svc.cluster.local"
- name: BACKEND_PORT
value: "{{ .Values.backend.service.port }}"
- name: currency-cache
image: {{ .Values.cacheJob.image.repository }}{{ .Values.cacheJob.image.image }}:{{ .Values.cacheJob.image.tag }}
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- curl -X POST "${BACKEND_HOST}:${BACKEND_PORT}/currency-cache"
env:
- name: BACKEND_HOST
value: "{{ .Values.backend.service.name }}.{{ .Release.Namespace }}.svc.cluster.local"
- name: BACKEND_PORT
value: "{{ .Values.backend.service.port }}"
restartPolicy: Never
{{- end }}

View File

@@ -0,0 +1,44 @@
{{- if .Values.dbDeployment.enabled -}}
apiVersion: batch/v1
kind: Job
metadata:
name: db-config-deployment-job
annotations:
"helm.sh/hook": pre-upgrade,pre-install
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": hook-succeeded
spec:
template:
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: db-config-deployment-job
image: "{{ $.Values.dbDeployment.image.repository }}/{{ $.Values.dbDeployment.image.image }}:{{ $.Values.dbDeployment.image.tag | default .Chart.AppVersion}}"
imagePullPolicy: {{ $.Values.dbDeployment.image.pullPolicy }}
args:
- "--changelog-file=changeLog.xml"
- "--defaultsFile=/liquibase/config/liquibase.properties"
- "update"
volumeMounts:
- mountPath: "/liquibase/config/"
name: liquibase-config
readOnly: true
restartPolicy: Never
volumes:
- name: liquibase-config
secret:
secretName: liquibase-config
backoffLimit: 4
---
apiVersion: v1
kind: Secret
metadata:
name: liquibase-config
data:
liquibase.properties: {{ (tpl (.Files.Get "configuration/db/liquibase.properties") . ) | b64enc }}
{{- end }}

View File

@@ -0,0 +1,13 @@
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
annotations:
"helm.sh/hook": pre-upgrade,pre-install
"helm.sh/hook-weight": "-6"
data:
dbPassword: {{ $.Values.dbCredentials.password | b64enc }} # b64enc is needed, because a stringData secret field cannot hold numeric values
dbUser: {{ $.Values.dbCredentials.userName | b64enc }}
dbHost: {{ $.Values.dbCredentials.host | b64enc }}
dbPort: {{ $.Values.dbCredentials.port | b64enc }}
dbName: {{ $.Values.dbCredentials.name | b64enc }}

View File

@@ -0,0 +1,72 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
labels:
app: frontend
spec:
{{- if not .Values.frontend.autoscaling.enabled }}
replicas: {{ .Values.frontend.replicaCount }}
{{- end }}
selector:
matchLabels:
app: frontend
template:
metadata:
{{- with .Values.frontend.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
app: frontend
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "gw2Tools.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.frontend.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.frontend.securityContext | nindent 12 }}
image: "{{ .Values.frontend.image.repository }}/{{ .Values.frontend.image.image }}:{{ .Values.frontend.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.frontend.image.pullPolicy }}
env:
- name: BACKEND_HOST
value: "{{ .Values.backend.service.name }}.{{ .Release.Namespace }}.svc.cluster.local"
- name: BACKEND_PORT
value: "{{ .Values.backend.service.port }}"
ports:
- name: http
containerPort: {{ .Values.frontend.service.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /health
port: {{ .Values.frontend.service.port }}
initialDelaySeconds: {{ $.Values.frontend.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ $.Values.frontend.livenessProbe.periodSeconds }}
failureThreshold: {{ $.Values.frontend.livenessProbe.failureThreshold }}
readinessProbe:
httpGet:
path: /health
port: {{ .Values.frontend.service.port }}
initialDelaySeconds: {{ $.Values.frontend.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ $.Values.frontend.readinessProbe.periodSeconds }}
failureThreshold: {{ $.Values.frontend.readinessProbe.failureThreshold }}
resources:
{{- toYaml .Values.frontend.resources | nindent 12 }}
{{- with .Values.frontend.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.frontend.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.frontend.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,56 @@
{{- if .Values.frontend.ingress.enabled -}}
{{- $fullName := include "gw2Tools.fullname" . -}}
{{- $svcPort := .Values.frontend.service.port -}}
{{- $servicePort := .Values.frontend.service.port -}}
{{- $serviceName := .Values.frontend.service.name -}}
{{- $ingressPath := .Values.frontend.ingress.path -}}
{{- $ingressPathType := .Values.frontend.ingress.pathType -}}
{{- $extraPaths := .Values.frontend.ingress.extraPaths -}}
{{- if and .Values.frontend.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.frontend.ingress.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.frontend.ingress.annotations "kubernetes.io/ingress.class" .Values.frontend.ingress.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: frontend-ingress
labels:
{{- include "gw2Tools.labels" . | nindent 4 }}
{{- with .Values.frontend.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.frontend.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.frontend.ingress.className }}
{{- end }}
{{- with .Values.frontend.ingress.tls }}
tls:
{{- tpl (toYaml .) $ | nindent 4 }}
{{- end }}
rules:
{{- if .Values.frontend.ingress.hosts }}
{{- range .Values.frontend.ingress.hosts }}
- host: {{ tpl . $ }}
http:
paths:
{{- with $extraPaths }}
{{- toYaml . | nindent 10 }}
{{- end }}
- path: {{ $ingressPath }}
pathType: {{ $ingressPathType }}
backend:
service:
name: {{ $serviceName }}
port:
number: {{ $servicePort }}
{{- end }}
{{- end -}}
{{- end }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.frontend.service.name }}
labels:
{{- include "gw2Tools.labels" . | nindent 4 }}
spec:
type: {{ .Values.frontend.service.type }}
ports:
- port: {{ .Values.frontend.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
app: frontend

View File

@@ -0,0 +1,12 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "gw2Tools.serviceAccountName" . }}
labels:
{{- include "gw2Tools.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

175
ci/gw2-tools/values.yaml Normal file
View File

@@ -0,0 +1,175 @@
# Default values for gw2-tools.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
backend:
image:
repository: harbor.sheldan.dev/gw2
pullPolicy: Always
image: gw2-tools-backend
# Overrides the image tag whose default is the chart appVersion.
tag: 0.0.1
debug:
enabled: true
port: 5005
service:
type: ClusterIP
port: 8080
name: backend-service
nodeSelector: {}
tolerations: []
affinity: {}
resources:
podAnnotations: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
ingress:
enabled: false
annotations: {}
labels: {}
path: /
pathType: Prefix
hosts:
extraPaths: []
tls: []
replicaCount: 1
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
livenessProbe:
initialDelaySeconds: 20
periodSeconds: 5
failureThreshold: 3
readinessProbe:
initialDelaySeconds: 20
periodSeconds: 5
failureThreshold: 3
propertyConfig:
hikariPoolSize: 3
dbDeployment:
enabled: true
image:
repository: harbor.sheldan.dev/gw2
image: gw2-tools-database
tag: 0.0.1
pullPolicy: Always
cacheJob:
enabled: true
image:
repository:
pullPolicy: Always
image: curlimages/curl
tag: 8.5.0
cronExpression: "@weekly"
dbCredentials:
password:
userName:
host:
port:
name:
db:
schemaName: gw2
frontend:
image:
repository: harbor.sheldan.dev/gw2
pullPolicy: IfNotPresent
image: gw2-tools-frontend
# Overrides the image tag whose default is the chart appVersion.
tag: 0.0.1
port: 8080
service:
type: ClusterIP
port: 8080
name: frontend
nodeSelector: {}
livenessProbe:
initialDelaySeconds: 2
periodSeconds: 5
failureThreshold: 3
readinessProbe:
initialDelaySeconds: 2
periodSeconds: 5
failureThreshold: 3
tolerations: []
affinity: {}
resources:
podAnnotations: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
ingress:
enabled: false
annotations: {}
labels: {}
path: /
pathType: Prefix
hosts:
extraPaths: []
tls: []
replicaCount: 1
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""

17
docker-compose.yaml Normal file
View File

@@ -0,0 +1,17 @@
version: "3.7"
services:
gw2-tools-backend:
build:
context: gw2-tools-backend/packaging/src/main/docker
image: ${REGISTRY_PREFIX}gw2-tools-backend:${VERSION:-latest}
gw2-tools-frontend:
build:
context: gw2-tools-frontend
dockerfile: docker/Dockerfile
image: ${REGISTRY_PREFIX}gw2-tools-frontend:${VERSION:-latest}
database:
build:
context: gw2-tools-backend/database/src/main
dockerfile: docker/Dockerfile
image: ${REGISTRY_PREFIX}gw2-tools-database:${VERSION:-latest}

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>gw2-tools</artifactId>
<version>0.0.9-SNAPSHOT</version>
</parent>
<artifactId>database</artifactId>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,2 @@
FROM liquibase/liquibase:4.25.1-alpine
ADD resources/changeLog/ /liquibase/

View File

@@ -0,0 +1,27 @@
package dev.sheldan.gw2.tools.entity
import jakarta.persistence.*
@Entity(name = "currency")
class Currency(@Column(name="name", nullable = false)
val name: String,
@Column(name="description")
val description: String,
@Column(name="icon_url", nullable = false)
val iconUrl: String,
@OneToMany(
fetch = FetchType.LAZY,
cascade = [CascadeType.PERSIST, CascadeType.MERGE],
mappedBy = "currency"
)
private var openings: List<OpeningCurrency>? = null,
@Id
@Column(name="id", nullable = false)
val id: Int) {
}

View File

@@ -0,0 +1,36 @@
package dev.sheldan.gw2.tools.entity
import jakarta.persistence.*
@Entity(name = "item")
class Item(@Column(name="name", nullable = false)
val name: String,
@Column(name="description")
val description: String,
@Column(name="icon_url", nullable = false)
val iconUrl: String,
@Column(name="type", nullable = false)
val type: String,
@Column(name="rarity", nullable = false)
val rarity: String,
@OneToMany(cascade = [CascadeType.MERGE, CascadeType.PERSIST], orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "item")
var submissionTemplates: List<SubmissionTemplate>? = null,
@OneToMany(
fetch = FetchType.LAZY,
cascade = [CascadeType.PERSIST, CascadeType.MERGE],
mappedBy = "item"
)
private var openings: List<OpeningItem>? = null,
@Id
@Column(name="id", nullable = false)
val id: Int) {
}

View File

@@ -0,0 +1,25 @@
package dev.sheldan.gw2.tools.entity
import jakarta.persistence.*
import java.time.Instant
@Entity(name = "opening")
class Opening(
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", referencedColumnName = "id")
val user: User,
@OneToMany(cascade = [CascadeType.MERGE, CascadeType.PERSIST], orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "opening")
var currencies: List<OpeningCurrency>? = null,
@OneToMany(cascade = [CascadeType.MERGE, CascadeType.PERSIST], orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "opening")
var items: List<OpeningItem>? = null,
@Column(name = "description")
var description: String?=null,
@Column(name = "created", insertable = false, updatable = false)
var creationDate: Instant?=null,
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Int?=null
) {
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.gw2.tools.entity
import jakarta.persistence.*
@Entity(name = "opening_currency")
class OpeningCurrency(
@ManyToOne(cascade = [CascadeType.PERSIST, CascadeType.MERGE])
@JoinColumn(name = "currency_id", nullable = false)
val currency: Currency,
@ManyToOne(cascade = [CascadeType.PERSIST, CascadeType.MERGE])
@JoinColumn(name = "opening_id", nullable = false)
val opening: Opening,
@Column(name = "amount")
val amount: Int,
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Int?=null
) {
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.gw2.tools.entity
import jakarta.persistence.*
@Entity(name = "opening_item")
class OpeningItem(
@ManyToOne(cascade = [CascadeType.PERSIST, CascadeType.MERGE])
@JoinColumn(name = "item_id", nullable = false)
val item: Item,
@ManyToOne(cascade = [CascadeType.PERSIST, CascadeType.MERGE])
@JoinColumn(name = "opening_id", nullable = false)
val opening: Opening,
@Column(name = "count")
val count: Int,
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Int?=null
) {
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.gw2.tools.entity
import jakarta.persistence.*
@Entity(name = "submission_template")
class SubmissionTemplate(
@ManyToOne(cascade = [CascadeType.PERSIST, CascadeType.MERGE])
@JoinColumn(name = "item_id", nullable = false)
val item: Item,
@Column(name = "template_text")
val templateText: String,
@Column(name = "name")
val name: String,
@Column(name = "description")
val description: String,
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Int?=null
) {
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.gw2.tools.entity
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.Id
@Entity(name = "gw2_user")
class User( @Id
@Column(name="id", nullable = false)
val id: String) {
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.gw2.tools.repo
import dev.sheldan.gw2.tools.entity.Currency
import org.springframework.data.repository.CrudRepository
interface CurrencyRepository : CrudRepository<Currency, Int> {
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.gw2.tools.repo
import dev.sheldan.gw2.tools.entity.Item
import org.springframework.data.repository.CrudRepository
interface ItemRepository : CrudRepository<Item, Int> {
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.gw2.tools.repo
import dev.sheldan.gw2.tools.entity.Opening
import dev.sheldan.gw2.tools.entity.User
import org.springframework.data.repository.CrudRepository
interface OpeningRepository : CrudRepository<Opening, Int> {
fun getAllByUser(user: User): List<Opening>
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.gw2.tools.repo
import dev.sheldan.gw2.tools.entity.Item
import dev.sheldan.gw2.tools.entity.SubmissionTemplate
import org.springframework.data.repository.CrudRepository
interface SubmissionTemplateRepository : CrudRepository<SubmissionTemplate, Int> {
fun getSubmissionTemplateByItem(item: Item): List<SubmissionTemplate>
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.gw2.tools.repo
import dev.sheldan.gw2.tools.entity.User
import org.springframework.data.repository.CrudRepository
interface UserRepository : CrudRepository<User, String> {
}

View File

@@ -0,0 +1,37 @@
package dev.sheldan.gw2.tools.service
import dev.sheldan.gw2.tools.entity.Currency
import dev.sheldan.gw2.tools.repo.CurrencyRepository
import org.springframework.stereotype.Component
@Component
class CurrencyManagement(val currencyRepository: CurrencyRepository) {
fun getCurrencies(currencyIds: List<Int>): List<Currency> {
return currencyRepository.findAllById(currencyIds).toList()
}
fun getCurrenciesAsMap(currencyIds: List<Int>): Map<Int, Currency> {
return getCurrencies(currencyIds).associateBy { it.id }
}
fun getCurrencies() : List<Currency> {
return currencyRepository.findAll().toList()
}
fun createAndSaveCurrency(id: Int, name: String, description: String, iconUrl: String): Currency {
val currency = createCurrency(id, name, description, iconUrl)
return currencyRepository.save(currency)
}
fun createCurrency(id: Int, name: String, description: String, iconUrl: String): Currency {
return Currency(name, description, iconUrl, null, id)
}
fun saveCurrency(currency: Currency): Currency {
return currencyRepository.save(currency)
}
fun saveCurrencies(currencies: List<Currency>) {
currencyRepository.saveAll(currencies)
}
}

View File

@@ -0,0 +1,42 @@
package dev.sheldan.gw2.tools.service
import dev.sheldan.gw2.tools.entity.Item
import dev.sheldan.gw2.tools.repo.ItemRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Component
@Component
class ItemManagement(val itemRepository: ItemRepository) {
fun getItems(itemIds: List<Int>) : List<Item> {
return itemRepository.findAllById(itemIds).toList()
}
fun getItem(itemId: Int) : Item? {
return itemRepository.findByIdOrNull(itemId)
}
fun getItemsAsMap(itemIds: List<Int>) : Map<Int, Item> {
return getItems(itemIds).associateBy { it.id }
}
fun getItems() : List<Item> {
return itemRepository.findAll().toList()
}
fun createItem(id: Int, name: String, description: String, iconUrl: String, type: String, rarity: String): Item {
return Item(name, description, iconUrl, type, rarity, null, null, id)
}
fun createAndSaveItem(id: Int, name: String, description: String, iconUrl: String, type: String, rarity: String): Item {
val item = createItem(id, name, description, iconUrl, type, rarity)
return saveItem(item)
}
fun saveItems(items: List<Item>) {
itemRepository.saveAll(items)
}
fun saveItem(item: Item): Item {
return itemRepository.save(item)
}
}

View File

@@ -0,0 +1,37 @@
package dev.sheldan.gw2.tools.service
import dev.sheldan.gw2.tools.entity.*
import dev.sheldan.gw2.tools.entity.Currency
import dev.sheldan.gw2.tools.repo.OpeningRepository
import org.springframework.stereotype.Component
import java.util.*
@Component
class OpeningManagement(
val openingRepository: OpeningRepository
) {
fun createOpening(user: User, items: Map<Item, Int>, currencies: Map<Currency, Int>, description: String?){
val opening = Opening(user, description = description)
val openingItems: List<OpeningItem> = items.map { OpeningItem(it.key, opening, it.value) }
val openingCurrencies: List<OpeningCurrency> = currencies.map { OpeningCurrency(it.key, opening, it.value) }
opening.currencies = openingCurrencies
opening.items = openingItems
openingRepository.save(opening)
}
fun getOpeningsByUser(user: User): List<Opening> {
return openingRepository.getAllByUser(user)
}
fun getAllOpenings(): List<Opening> {
return openingRepository.findAll().toList()
}
fun getOpening(id: Int): Optional<Opening> {
return openingRepository.findById(id)
}
fun deleteOpening(opening: Opening) {
openingRepository.delete(opening)
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.gw2.tools.service
import dev.sheldan.gw2.tools.entity.Item
import dev.sheldan.gw2.tools.entity.SubmissionTemplate
import dev.sheldan.gw2.tools.repo.SubmissionTemplateRepository
import org.springframework.stereotype.Component
@Component
class SubmissionTemplateManagement(
val submissionTemplateRepository: SubmissionTemplateRepository
) {
fun getSubmissionTemplatesForItem(item: Item): List<SubmissionTemplate> {
return submissionTemplateRepository.getSubmissionTemplateByItem(item)
}
fun getAllSubmissionTemplates(): List<SubmissionTemplate> {
return submissionTemplateRepository.findAll().toList()
}
}

View File

@@ -0,0 +1,39 @@
package dev.sheldan.gw2.tools.service
import dev.sheldan.gw2.tools.entity.User
import dev.sheldan.gw2.tools.repo.UserRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Component
import java.security.MessageDigest
@Component
class UserManagement(val userRepository: UserRepository) {
fun getUser(id: String): User? {
return userRepository.findByIdOrNull(id)
}
fun getOrCreateUser(token: String): User {
val userId = createUserId(token)
val possibleUser = getUser(userId)
return possibleUser ?: createUser(userId)
}
fun createUserWithId(id: String): User {
val userObj = User(id)
return userRepository.save(userObj)
}
fun createUser(token: String): User {
val hashed = createUserId(token)
val userObj = User(hashed)
return userRepository.save(userObj)
}
private fun createUserId(token: String): String {
val md = MessageDigest.getInstance("SHA-256")
val digest = md.digest(token.toByteArray())
val hashed = digest.fold("") { str, it -> str + "%02x".format(it) }
return hashed
}
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<include file="tables/tables.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,34 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<changeSet author="Sheldan" id="currency-table">
<createTable tableName="currency">
<column name="id" type="INTEGER">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="VARCHAR(64)">
<constraints nullable="false"/>
</column>
<column name="description" type="VARCHAR(255)">
<constraints nullable="true"/>
</column>
<column name="icon_url" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<sql>
DROP TRIGGER IF EXISTS currency_update_trigger ON currency;
CREATE TRIGGER currency_update_trigger BEFORE UPDATE ON currency FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS currency_insert_trigger ON currency;
CREATE TRIGGER currency_insert_trigger BEFORE INSERT ON currency FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,44 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<changeSet author="Sheldan" id="item-table">
<createTable tableName="item">
<column name="id" type="INTEGER">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="VARCHAR(128)">
<constraints nullable="false"/>
</column>
<column name="description" type="VARCHAR(1024)">
<constraints nullable="true"/>
</column>
<column name="icon_url" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="type" type="VARCHAR(64)">
<constraints nullable="true"/>
</column>
<column name="rarity" type="VARCHAR(32)">
<constraints nullable="true"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<sql>
ALTER TABLE item ADD CONSTRAINT check_item_rarity CHECK (rarity IN ('JUNK', 'BASIC', 'FINE', 'MASTERWORK', 'RARE', 'EXOTIC', 'ASCENDED', 'LEGENDARY'));
ALTER TABLE item ADD CONSTRAINT check_item_type CHECK (type IN ('CONTAINER', 'ARMOR', 'BACK', 'BAG', 'CONSUMABLE', 'CRAFTING_MATERIAL', 'GATHERING', 'GIZMO', 'JADE_TECH_MODULE', 'KEY', 'MINI_PET', 'POWER_CORE', 'TOOL', 'TRAIT', 'TRINKET', 'TROPHY', 'UPGRADE_COMPONENT', 'WEAPON', 'RELIC'));
</sql>
<sql>
DROP TRIGGER IF EXISTS item_update_trigger ON item;
CREATE TRIGGER item_update_trigger BEFORE UPDATE ON item FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS item_insert_trigger ON item;
CREATE TRIGGER item_insert_trigger BEFORE INSERT ON item FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,82 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<changeSet author="Sheldan" id="opening-table">
<createTable tableName="opening">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_opening"/>
</column>
<column name="user_id" type="VARCHAR(255)">
<constraints nullable="false" />
</column>
<column name="description" type="VARCHAR(1024)"/>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="user_id" baseTableName="opening" constraintName="fk_opening_user" deferrable="false"
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
referencedTableName="gw2_user" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS opening_update_trigger ON opening;
CREATE TRIGGER opening_update_trigger BEFORE UPDATE ON opening FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS opening_insert_trigger ON opening;
CREATE TRIGGER opening_insert_trigger BEFORE INSERT ON opening FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
<changeSet author="Sheldan" id="opening_currency-table">
<createTable tableName="opening_currency">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_opening_currency"/>
</column>
<column name="opening_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="currency_id" type="INT">
<constraints nullable="false"/>
</column>
<column name="amount" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
<addForeignKeyConstraint baseColumnNames="opening_id" baseTableName="opening_currency"
constraintName="fk_opening_currency_opening" deferrable="false"
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="opening"
validate="true"/>
<addForeignKeyConstraint baseColumnNames="currency_id" baseTableName="opening_currency"
constraintName="fk_opening_currency_currency" deferrable="false"
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
referencedTableName="currency" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="opening_item-table">
<createTable tableName="opening_item">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_opening_item"/>
</column>
<column name="opening_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="item_id" type="INT">
<constraints nullable="false"/>
</column>
<column name="count" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
<addForeignKeyConstraint baseColumnNames="opening_id" baseTableName="opening_item"
constraintName="fk_opening_item_opening" deferrable="false"
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="opening"
validate="true"/>
<addForeignKeyConstraint baseColumnNames="item_id" baseTableName="opening_item"
constraintName="fk_opening_item_item" deferrable="false"
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
referencedTableName="item" validate="true"/>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,8 @@
CREATE OR REPLACE FUNCTION insert_trigger_procedure() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.created := CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$;

View File

@@ -0,0 +1,8 @@
CREATE OR REPLACE FUNCTION update_trigger_procedure() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.updated := CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$;

View File

@@ -0,0 +1,34 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<changeSet author="Sheldan" id="submission_template-table">
<createTable tableName="submission_template">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_submission_template"/>
</column>
<column name="item_id" type="INTEGER">
<constraints nullable="false" />
</column>
<column name="description" type="VARCHAR(1024)"/>
<column name="template_text" type="VARCHAR(2048)"/>
<column name="name" type="VARCHAR(128)"/>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="item_id" baseTableName="submission_template" constraintName="fk_submission_template_item" deferrable="false"
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
referencedTableName="item" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS submission_template_update_trigger ON submission_template;
CREATE TRIGGER submission_template_update_trigger BEFORE UPDATE ON submission_template FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS submission_template_insert_trigger ON submission_template;
CREATE TRIGGER submission_template_insert_trigger BEFORE INSERT ON submission_template FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,11 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<include file="trigger_functions.xml" relativeToChangelogFile="true"/>
<include file="item.xml" relativeToChangelogFile="true"/>
<include file="currency.xml" relativeToChangelogFile="true"/>
<include file="user.xml" relativeToChangelogFile="true"/>
<include file="opening.xml" relativeToChangelogFile="true"/>
<include file="submission_template.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,15 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<changeSet author="Sheldan" id="insert_trigger" dbms="postgresql">
<sqlFile encoding="utf8" path="sql/insert_trigger.sql"
relativeToChangelogFile="true"
splitStatements="false"/>
</changeSet>
<changeSet author="Sheldan" id="update_trigger" dbms="postgresql">
<sqlFile encoding="utf8" path="sql/update_trigger.sql"
relativeToChangelogFile="true"
splitStatements="false"/>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,25 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<changeSet author="Sheldan" id="user-table">
<createTable tableName="gw2_user">
<column name="id" type="VARCHAR(255)">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<sql>
DROP TRIGGER IF EXISTS gw2_user_update_trigger ON gw2_user;
CREATE TRIGGER gw2_user_update_trigger BEFORE UPDATE ON gw2_user FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS gw2_user_insert_trigger ON gw2_user;
CREATE TRIGGER gw2_user_insert_trigger BEFORE INSERT ON gw2_user FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,6 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd">
<include file="0.0.1/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
FROM amazoncorretto:21.0.1-alpine3.18
RUN apk add entr
WORKDIR /app
ADD BOOT-INF/lib/ /app/lib
ADD snapshots/ /app/lib
ADD META-INF /app/META-INF
ADD BOOT-INF/classes /app
ENTRYPOINT java -cp .:./lib/* dev.sheldan.gw2.tools.ToolApplicationKt

View File

@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>gw2-tools</artifactId>
<version>0.0.9-SNAPSHOT</version>
</parent>
<artifactId>executable</artifactId>
<properties>
<main.class>dev.sheldan.gw2.tools.ToolApplicationKt</main.class>
</properties>
<build>
<finalName>gw2-tools</finalName>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>${main.class}</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>${maven-assembly-plugin.version}</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals> <goal>single</goal> </goals>
<configuration>
<archive>
<manifest>
<mainClass>${main.class}</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
<dependency>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>rest-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,13 @@
package dev.sheldan.gw2.tools
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
@SpringBootApplication(exclude = [org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration::class])
@EnableJpaRepositories(basePackages = ["dev.sheldan.gw2"])
class ToolApplication
fun main(args: Array<String>) {
runApplication<ToolApplication>(*args)
}

View File

@@ -0,0 +1,18 @@
management.endpoints.web.exposure.include=mappings
security.basic.enabled=false
management.security.enabled=false
spring.datasource.url=jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_NAME}
spring.datasource.username= ${DB_USER}
spring.datasource.password= ${DB_PASS}
spring.datasource.hikari.maximum-pool-size=${hikariPoolSize}
spring.jpa.hibernate.default_schema=gw2
spring.jpa.properties.hibernate.default_schema=gw2
spring.quartz.jdbc.initialize-schema=never
management.metrics.tags.application=GW2-Tools
management.endpoint.health.probes.enabled=true
management.health.livenessState.enabled=true
management.health.readinessState.enabled=true
spring.application.name=GW2-Tools

View File

@@ -0,0 +1,11 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] [%-5level] [%logger{36}]: %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>gw2-tools</artifactId>
<version>0.0.9-SNAPSHOT</version>
</parent>
<artifactId>gw2-api-client</artifactId>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
<dependency>
<groupId>io.github.kryszak</groupId>
<artifactId>gwatlin</artifactId>
</dependency>
<dependency>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>database</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,6 @@
package dev.sheldan.gw2.tools.config
interface ApiKey {
fun getApiKey(): String
fun setApiKey(key: String)
}

View File

@@ -0,0 +1,38 @@
package dev.sheldan.gw2.tools.config
import io.github.kryszak.gwatlin.api.account.GWAccountClient
import io.github.kryszak.gwatlin.api.characters.GWCharactersClient
import io.github.kryszak.gwatlin.api.items.GWItemsClient
import io.github.kryszak.gwatlin.api.miscellaneous.GWMiscellaneousClient
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.ScopedProxyMode
import org.springframework.web.context.annotation.RequestScope
@Configuration
class BeanConfig(val apiKey: ApiKey) {
@Bean
@RequestScope(proxyMode = ScopedProxyMode.DEFAULT)
fun gwCharacterClient(): GWCharactersClient {
return GWCharactersClient(apiKey.getApiKey())
}
@Bean
@RequestScope(proxyMode = ScopedProxyMode.DEFAULT)
fun getAccountClient(): GWAccountClient {
return GWAccountClient(apiKey.getApiKey())
}
@Bean
fun getMiscellaneousClient(): GWMiscellaneousClient {
return GWMiscellaneousClient()
}
@Bean
fun gwItemClient(): GWItemsClient {
return GWItemsClient()
}
}

View File

@@ -0,0 +1,69 @@
package dev.sheldan.gw2.tools.loader
import dev.sheldan.gw2.tools.models.*
import dev.sheldan.gw2.tools.service.ItemManagement
import io.github.kryszak.gwatlin.api.account.GWAccountClient
import io.github.kryszak.gwatlin.api.characters.GWCharactersClient
import io.github.kryszak.gwatlin.api.miscellaneous.GWMiscellaneousClient
import org.springframework.stereotype.Component
import org.springframework.web.context.annotation.RequestScope
@Component
@RequestScope
class AccountLoader(
val charClient: GWCharactersClient,
val accountClient: GWAccountClient,
val miscellaneousClient: GWMiscellaneousClient,
val itemManagement: ItemManagement
) {
fun getCharacters(): List<String>? {
return charClient.getCharacters()
}
fun getWallet(): AccountWalletView {
val wallet = accountClient.getWallet()
val currencies = miscellaneousClient.getCurrencies().associateBy { it.id }
val enrichedCurrencies = wallet.mapNotNull { walletCurrency ->
val currency = currencies[walletCurrency.id]
currency?.let {
EnrichedCurrency(walletCurrency.id, walletCurrency.value, it.name, it.description, it.icon)
}
}
return AccountWalletView(enrichedCurrencies)
}
fun getBank(): AccountVaultView {
val bank = accountClient.getAccountVault()
val itemIds = mutableSetOf<Int>()
val items = bank.filterNotNull().mapNotNull {
itemIds.add(it.id)
EnrichedItem(it.id, it.count, it.boundTo)
}
val itemStats = itemManagement.getItemsAsMap(itemIds.toList())
items.forEach { item ->
itemStats[item.id]?.let {
item.setValuesFromDbItem(it)
}
}
return AccountVaultView(items)
}
fun getMaterials(): AccountMaterialView{
val materials = accountClient.getMaterials()
val itemIds = mutableSetOf<Int>()
val items = materials
.filter { it.count > 0 }
.map {
itemIds.add(it.id)
EnrichedItem(it.id, it.count)
}
val actualMaterials = itemManagement.getItemsAsMap(itemIds.toList())
items.forEach { item ->
actualMaterials[item.id]?.let {
item.setValuesFromDbItem(it)
}
}
return AccountMaterialView(items)
}
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.gw2.tools.loader
import dev.sheldan.gw2.tools.models.EnrichedCurrency
import io.github.kryszak.gwatlin.api.miscellaneous.GWMiscellaneousClient
import org.springframework.stereotype.Component
@Component
class CurrencyLoader(
var miscellaneousClient: GWMiscellaneousClient,
) {
fun getAllCurrencies(): List<EnrichedCurrency> {
return miscellaneousClient.getCurrencies().map { EnrichedCurrency(it.id, 0, it.name, it.description, it.icon) }
}
}

View File

@@ -0,0 +1,98 @@
package dev.sheldan.gw2.tools.loader
import dev.sheldan.gw2.tools.entity.Item
import dev.sheldan.gw2.tools.models.*
import dev.sheldan.gw2.tools.service.ItemManagement
import io.github.kryszak.gwatlin.api.account.GWAccountClient
import io.github.kryszak.gwatlin.api.account.model.InventoryItem
import io.github.kryszak.gwatlin.api.characters.GWCharactersClient
import io.github.kryszak.gwatlin.api.characters.model.character.inventory.Bag
import io.github.kryszak.gwatlin.api.characters.model.character.inventory.InventorySlot
import org.springframework.stereotype.Component
import org.springframework.web.context.annotation.RequestScope
@Component
@RequestScope
class InventoryLoader(
val charClient: GWCharactersClient,
val accountClient: GWAccountClient,
val itemManagement: ItemManagement
) {
fun getInventory(characterName: String): List<Bag?>? {
return charClient.getInventory(characterName)
}
fun getFullCharacterInventory(characterName: String): CharacterInventory {
val itemIds = mutableSetOf<Int>()
val characterInventory = getCharacterInventory(characterName, itemIds)
val itemStats = getFullItemInfos(itemIds)
enrichCharacterInventory(characterInventory, itemStats)
return characterInventory
}
private fun getCharacterInventory(characterName: String, itemIds: MutableSet<Int>): CharacterInventory {
val characterInventory = getInventory(characterName)
val convertedBags = mutableListOf<InventoryBag>()
characterInventory?.let {
for (bag in characterInventory.filterNotNull()) {
convertedBags.addLast(convertApiBagToCharacterInventory(bag, itemIds))
}
}
return CharacterInventory(characterName, convertedBags)
}
fun getAccountInventory(): AccountInventoryView {
val characters = charClient.getCharacters()
val itemIds = mutableSetOf<Int>()
val inventories = mutableListOf<CharacterInventory>()
for (characterName in characters) {
inventories.addLast(getCharacterInventory(characterName, itemIds))
}
val itemStats = getFullItemInfos(itemIds)
inventories.forEach { enrichCharacterInventory(it, itemStats) }
return AccountInventoryView(inventories)
}
private fun enrichCharacterInventory(characterInventory: CharacterInventory, itemStats: Map<Int, Item>) {
return characterInventory.bags.forEach { bag ->
bag.items.forEach { item ->
itemStats[item.id]?.let {
item.setValuesFromDbItem(it)
}
}
}
}
private fun convertApiBagToCharacterInventory(bag: Bag, itemIds: MutableSet<Int>): InventoryBag {
val items = bag.inventory.filterNotNull().map { convertInventorySlot(it, itemIds) }
return InventoryBag(bag.id, bag.size, items)
}
private fun convertInventorySlot(slot: InventorySlot, itemIds: MutableSet<Int>): EnrichedItem {
itemIds.add(slot.id)
return EnrichedItem(slot.id, slot.count, slot.boundTo)
}
private fun convertInventoryItem(item: InventoryItem, itemIds: MutableSet<Int>): EnrichedItem {
itemIds.add(item.id)
return EnrichedItem(item.id, item.count, item.binding)
}
fun getSharedInventory(): AccountInventory {
val itemIds = mutableSetOf<Int>()
val sharedInventorySlots = accountClient.getInventory().filterNotNull()
val sharedItems = sharedInventorySlots.map { convertInventoryItem(it, itemIds) }
val fullItemInfo = getFullItemInfos(itemIds)
sharedItems.forEach { item ->
fullItemInfo[item.id]?.let {
item.setValuesFromDbItem(it)
}
}
return AccountInventory(sharedItems)
}
private fun getFullItemInfos(itemIds: MutableSet<Int>) = itemManagement.getItemsAsMap(itemIds.toList())
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.gw2.tools.loader
import dev.sheldan.gw2.tools.models.EnrichedItem
import io.github.kryszak.gwatlin.api.items.GWItemsClient
import org.springframework.stereotype.Component
@Component
class ItemLoader(
val itemClient: GWItemsClient
) {
fun getAllItems(): List<EnrichedItem> {
return itemClient.getItemIds().chunked(200)
.flatMap { (itemClient.getItems(it)) }
.map {
val item = EnrichedItem(it.id, 1)
item.setValuesFromItem(it)
item
}
}
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.gw2.tools.models
class AccountInventory(
val slots: List<EnrichedItem>
) {
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.gw2.tools.models
data class AccountInventoryView(
val inventories: List<CharacterInventory>
) {
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.gw2.tools.models
class AccountMaterialView(
val slots: List<EnrichedItem>
) {
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.gw2.tools.models
class AccountVaultView(
val slots: List<EnrichedItem>
) {
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.gw2.tools.models
class AccountWalletView(
val currencies: List<EnrichedCurrency>
) {
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.gw2.tools.models
data class CharacterInventory(
val name: String,
val bags: List<InventoryBag>
)
{
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.gw2.tools.models
class EnrichedCurrency(
val id: Int,
var amount: Int,
val name: String,
val description: String,
val iconUrl: String
) {
}

View File

@@ -0,0 +1,33 @@
package dev.sheldan.gw2.tools.models
import io.github.kryszak.gwatlin.api.items.model.item.Item
data class EnrichedItem(
val id: Int,
var count: Int,
val boundTo: String? = null,
var iconUrl: String? = null,
var name: String? = null,
var description: String? = null,
var type: Gw2TItemType? = null,
var level: Int? = null,
var rarity: Gw2TItemRarity? = null
) {
fun setValuesFromItem(item: Item) {
this.name = item.name
this.level = item.level
this.description = item.description
this.type = Gw2TItemType.convertFromAPI(item.type)
this.rarity = Gw2TItemRarity.convertFromAPI(item.rarity)
this.iconUrl = item.icon.ifBlank { "https://render.guildwars2.com/file/4BC52199DBDEEF5D4D90736B582DDA0F092B0DE4/434780.png" } // default icon if nothing is shown
}
fun setValuesFromDbItem(item: dev.sheldan.gw2.tools.entity.Item) {
this.name = item.name
this.description = item.description
this.type = Gw2TItemType.valueOf(item.type)
this.rarity = Gw2TItemRarity.valueOf(item.rarity)
this.iconUrl = item.iconUrl
}
}

View File

@@ -0,0 +1,28 @@
package dev.sheldan.gw2.tools.models
import io.github.kryszak.gwatlin.api.items.model.item.ItemRarity
enum class Gw2TItemRarity {
JUNK,
BASIC,
FINE,
MASTERWORK,
RARE,
EXOTIC,
ASCENDED,
LEGENDARY;
companion object {
fun convertFromAPI(rarity: ItemRarity): Gw2TItemRarity = when (rarity) {
ItemRarity.FINE -> FINE
ItemRarity.MASTERWORK -> MASTERWORK
ItemRarity.RARE -> RARE
ItemRarity.EXOTIC -> EXOTIC
ItemRarity.JUNK -> JUNK
ItemRarity.BASIC -> BASIC
ItemRarity.ASCENDED -> ASCENDED
ItemRarity.LEGENDARY -> LEGENDARY
}
}
}

View File

@@ -0,0 +1,51 @@
package dev.sheldan.gw2.tools.models
import io.github.kryszak.gwatlin.api.items.model.item.ItemType
enum class Gw2TItemType {
CONTAINER,
ARMOR,
BACK,
BAG,
CONSUMABLE,
CRAFTING_MATERIAL,
GATHERING,
GIZMO,
JADE_TECH_MODULE,
KEY,
MINI_PET,
POWER_CORE,
TOOL,
TRAIT,
TRINKET,
TROPHY,
UPGRADE_COMPONENT,
WEAPON,
RELIC;
companion object {
fun convertFromAPI(type: ItemType): Gw2TItemType = when (type) {
ItemType.CONTAINER -> CONTAINER
ItemType.ARMOR -> ARMOR
ItemType.BACK -> BACK
ItemType.BAG -> BAG
ItemType.CONSUMABLE -> CONSUMABLE
ItemType.CRAFTING_MATERIAL -> CRAFTING_MATERIAL
ItemType.GATHERING -> GATHERING
ItemType.GIZMO -> GIZMO
ItemType.JADE_TECH_MODULE -> JADE_TECH_MODULE
ItemType.KEY -> KEY
ItemType.MINI_PET -> MINI_PET
ItemType.POWER_CORE -> POWER_CORE
ItemType.TOOL -> TOOL
ItemType.TRAIT -> TRAIT
ItemType.TRINKET -> TRINKET
ItemType.TROPHY -> TROPHY
ItemType.UPGRADE_COMPONENT -> UPGRADE_COMPONENT
ItemType.WEAPON -> WEAPON
ItemType.RELIC -> RELIC
}
}
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.gw2.tools.models
data class InventoryBag(
val id: Int,
val size: Int,
val items: List<EnrichedItem>
) {
}

308
gw2-tools-backend/mvnw vendored Executable file
View File

@@ -0,0 +1,308 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.2.0
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /usr/local/etc/mavenrc ] ; then
. /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "$(uname)" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
else
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=$(java-config --jre-home)
fi
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="$(which javac)"
if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=$(which readlink)
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
if $darwin ; then
javaHome="$(dirname "\"$javaExecutable\"")"
javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
else
javaExecutable="$(readlink -f "\"$javaExecutable\"")"
fi
javaHome="$(dirname "\"$javaExecutable\"")"
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=$(cd "$wdir/.." || exit 1; pwd)
fi
# end of workaround
done
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
# Remove \r in case we run on Windows within Git Bash
# and check out the repository with auto CRLF management
# enabled. Otherwise, we may read lines that are delimited with
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
# splitting rules.
tr -s '\r\n' ' ' < "$1"
fi
}
log() {
if [ "$MVNW_VERBOSE" = true ]; then
printf '%s\n' "$1"
fi
}
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
log "$MAVEN_PROJECTBASEDIR"
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
if [ -r "$wrapperJarPath" ]; then
log "Found $wrapperJarPath"
else
log "Couldn't find $wrapperJarPath, downloading it ..."
if [ -n "$MVNW_REPOURL" ]; then
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
else
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
fi
while IFS="=" read -r key value; do
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
safeValue=$(echo "$value" | tr -d '\r')
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
log "Downloading from: $wrapperUrl"
if $cygwin; then
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
fi
if command -v wget > /dev/null; then
log "Found wget ... using wget"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
log "Found curl ... using curl"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
else
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
fi
else
log "Falling back to using Java to download"
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaSource=$(cygpath --path --windows "$javaSource")
javaClass=$(cygpath --path --windows "$javaClass")
fi
if [ -e "$javaSource" ]; then
if [ ! -e "$javaClass" ]; then
log " - Compiling MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/javac" "$javaSource")
fi
if [ -e "$javaClass" ]; then
log " - Running MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
wrapperSha256Sum=""
while IFS="=" read -r key value; do
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
if [ -n "$wrapperSha256Sum" ]; then
wrapperSha256Result=false
if command -v sha256sum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
elif command -v shasum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
exit 1
fi
if [ $wrapperSha256Result = false ]; then
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
exit 1
fi
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
# shellcheck disable=SC2086 # safe args
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

205
gw2-tools-backend/mvnw.cmd vendored Normal file
View File

@@ -0,0 +1,205 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM https://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.2.0
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %WRAPPER_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
SET WRAPPER_SHA_256_SUM=""
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
)
IF NOT %WRAPPER_SHA_256_SUM%=="" (
powershell -Command "&{"^
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
" exit 1;"^
"}"^
"}"
if ERRORLEVEL 1 goto error
)
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%"=="on" pause
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
cmd /C exit /B %ERROR_CODE%

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>gw2-tools</artifactId>
<version>0.0.9-SNAPSHOT</version>
</parent>
<artifactId>packaging</artifactId>
<packaging>pom</packaging>
<properties>
<file.basedir>${project.basedir}/src/main/docker/</file.basedir>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<!-- backend jar -->
<artifactItem>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>executable</artifactId>
<version>${project.version}</version>
<classifier>exec</classifier>
<type>jar</type>
<overWrite>true</overWrite>
<outputDirectory>${file.basedir}/tools</outputDirectory>
<destFileName>app.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,8 @@
FROM amazoncorretto:21.0.1-alpine3.18
MAINTAINER Sheldan
VOLUME /tmp
ADD config/* /config/
ADD wrapper/*.sh /
RUN chmod +x /start.sh
ADD tools/app.jar /app.jar
CMD ["/start.sh"]

View File

@@ -0,0 +1 @@
logging.config=config/logback.xml

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<property name="LOG_PATH" value="logs"/>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<appender name="logFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/log.log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>
%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</Pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>
${LOG_PATH}/archived/log_%d{dd-MM-yyyy}.log
</fileNamePattern>
<maxHistory>10</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
</appender>
<logger name="dev.sheldan.gw2" level="INFO"/>
<root level="info">
<appender-ref ref="logFileAppender"/>
<appender-ref ref="stdout"/>
</root>
</configuration>

View File

@@ -0,0 +1,7 @@
#!/bin/sh
DEBUG_PARAMS=""
if [ "x$REMOTE_DEBUG" = 'xtrue' ]; then
DEBUG_PARAMS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
echo "Starting with remote debugging on port 5005"
fi;
java ${DEBUG_PARAMS} -jar app.jar

176
gw2-tools-backend/pom.xml Normal file
View File

@@ -0,0 +1,176 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.1</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>gw2-tools</artifactId>
<version>0.0.9-SNAPSHOT</version>
<packaging>pom</packaging>
<name>gw2-tools</name>
<description>Tools for GW2</description>
<modules>
<module>executable</module>
<module>gw2-api-client</module>
<module>rest-api</module>
<module>packaging</module>
<module>database</module>
</modules>
<scm>
<connection>scm:git:${project.scm.url}</connection>
<developerConnection>scm:git:${project.scm.url}</developerConnection>
<url>https://github.com/Sheldan/gw2-tools.git</url>
<tag>HEAD</tag>
</scm>
<distributionManagement>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/Sheldan/gw2-tools</url>
</repository>
</distributionManagement>
<properties>
<java.version>21</java.version>
<kotlin.version>1.9.21</kotlin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.code.style>official</kotlin.code.style>
<kotlin.compiler.jvmTarget>21</kotlin.compiler.jvmTarget>
<maven-surefire-plugin.version>3.2.5</maven-surefire-plugin.version>
<maven-failsafe-plugin.version>3.2.5</maven-failsafe-plugin.version>
<exec-maven-plugin.version>3.1.1</exec-maven-plugin.version>
<junit-jupiter-engine.version>5.10.1</junit-jupiter-engine.version>
<maven-jar-plugin.version>3.3.0</maven-jar-plugin.version>
<maven-assembly-plugin.version>3.6.0</maven-assembly-plugin.version>
<kotlin-logging.version>2.0.11</kotlin-logging.version>
<gwatlin.version>1.9.5</gwatlin.version>
<maven-release-plugin.version>2.5.3</maven-release-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
<plugin>jpa</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-noarg</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<artifactId>maven-release-plugin</artifactId>
<version>${maven-release-plugin.version}</version>
<configuration>
<scmCommentPrefix>[RELEASE]</scmCommentPrefix>
<tagNameFormat>@{project.version}</tagNameFormat>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter-engine.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>io.github.kryszak</groupId>
<artifactId>gwatlin</artifactId>
<version>${gwatlin.version}</version>
</dependency>
<dependency>
<groupId>io.github.microutils</groupId>
<artifactId>kotlin-logging-jvm</artifactId>
<version>${kotlin-logging.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>gw2-tools</artifactId>
<version>0.0.9-SNAPSHOT</version>
</parent>
<artifactId>rest-api</artifactId>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${maven-failsafe-plugin.version}</version>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
<dependency>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>gw2-api-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.gw2.tools</groupId>
<artifactId>database</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.github.microutils</groupId>
<artifactId>kotlin-logging-jvm</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,4 @@
package dev.sheldan.gw2.tools
class ItemNotFoundException(message: String): Throwable(message) {
}

View File

@@ -0,0 +1,33 @@
package dev.sheldan.gw2.tools.api
import dev.sheldan.gw2.tools.loader.AccountLoader
import dev.sheldan.gw2.tools.models.AccountMaterialView
import dev.sheldan.gw2.tools.models.AccountVaultView
import dev.sheldan.gw2.tools.models.AccountWalletView
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.context.annotation.RequestScope
@RestController
@RequestScope
class AccountController(var accountLoader: AccountLoader) {
@GetMapping("/characters")
fun inventory(): List<String>? {
return accountLoader.getCharacters()
}
@GetMapping("/wallet")
fun wallet(): AccountWalletView {
return accountLoader.getWallet()
}
@GetMapping("/bank")
fun bank(): AccountVaultView {
return accountLoader.getBank()
}
@GetMapping("/materials")
fun materials(): AccountMaterialView {
return accountLoader.getMaterials()
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.gw2.tools.api
import dev.sheldan.gw2.tools.service.CacheService
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class CurrencyController(val cacheService: CacheService) {
@PostMapping("/currency-cache")
fun updateCurrencyCache() {
cacheService.reloadCurrencyCache()
}
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.gw2.tools.api
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class Health {
@GetMapping("/health-check")
fun healCheck(): String {
return "yes"
}
}

View File

@@ -0,0 +1,31 @@
package dev.sheldan.gw2.tools.api
import dev.sheldan.gw2.tools.loader.InventoryLoader
import dev.sheldan.gw2.tools.models.AccountInventory
import dev.sheldan.gw2.tools.models.AccountInventoryView
import dev.sheldan.gw2.tools.models.CharacterInventory
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.context.annotation.RequestScope
@RestController
@RequestScope
class InventoryController(var inventoryLoader: InventoryLoader) {
@GetMapping("/inventory/{name}")
fun characterInventory(@PathVariable("name") characterName: String): CharacterInventory {
return inventoryLoader.getFullCharacterInventory(characterName)
}
@GetMapping("/inventory")
fun completeCharacterInventory(): AccountInventoryView {
return inventoryLoader.getAccountInventory()
}
@GetMapping("/sharedInventory")
fun accountInventory(): AccountInventory {
return inventoryLoader.getSharedInventory()
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.gw2.tools.api
import dev.sheldan.gw2.tools.service.CacheService
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class ItemController(val cacheService: CacheService) {
@PostMapping("/item-cache")
fun updateItemCache() {
cacheService.reloadItemCache()
}
}

View File

@@ -0,0 +1,46 @@
package dev.sheldan.gw2.tools.api
import dev.sheldan.gw2.tools.config.ApiKeyInterceptor
import dev.sheldan.gw2.tools.model.ItemRates
import dev.sheldan.gw2.tools.model.OpeningRequest
import dev.sheldan.gw2.tools.model.OpeningsView
import dev.sheldan.gw2.tools.service.OpeningService
import jakarta.servlet.http.HttpServletRequest
import mu.KotlinLogging
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.*
import org.springframework.web.context.annotation.RequestScope
@RestController
@RequestScope
class OpeningController(val openingService: OpeningService) {
private val logger = KotlinLogging.logger {}
@PostMapping("/openings")
@ResponseStatus(HttpStatus.CREATED)
fun createOpenings(request: HttpServletRequest, @RequestBody openingRequest: OpeningRequest) {
val apiKey: String = request.getHeader(ApiKeyInterceptor.API_KEY_HEADER_NAME) ?: throw IllegalArgumentException("API key not provided.")
openingService.createOpening(openingRequest, apiKey)
}
@GetMapping("/openings")
fun getOpenings(request: HttpServletRequest, @RequestParam("showOwnOnly") ownOnly: String?): OpeningsView {
val apiKey: String? = request.getHeader(ApiKeyInterceptor.API_KEY_HEADER_NAME)
val showOnOnly = ownOnly.toBoolean()
logger.info { "Retrieving openings" }
val loadedOpenings = openingService.loadOpenings(apiKey, showOnOnly)
logger.info { "Loaded ${loadedOpenings.openings.size} openings" }
return loadedOpenings
}
@DeleteMapping("/openings/{id}")
fun deleteOpening(request: HttpServletRequest, @PathVariable("id") openingId: Int) {
val apiKey: String = request.getHeader(ApiKeyInterceptor.API_KEY_HEADER_NAME) ?: throw IllegalArgumentException("API key not provided.")
openingService.deleteOpening(apiKey, openingId)
}
@GetMapping("/itemRates")
fun getItemRates(): ItemRates {
return openingService.loadItemRates()
}
}

View File

@@ -0,0 +1,23 @@
package dev.sheldan.gw2.tools.api
import dev.sheldan.gw2.tools.ItemNotFoundException
import dev.sheldan.gw2.tools.service.SubmissionTemplateService
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
@RestController
class SubmissionTemplateController(
val submissionTemplateService: SubmissionTemplateService
) {
@GetMapping("/submissionTemplates")
@ResponseBody
fun getSubmissionTemplatesForItem(@RequestParam("itemId") itemId: Int?): ResponseEntity<Any> {
try {
val responseObj = itemId?.let { submissionTemplateService.getSubmissionTemplateForItem(itemId) } ?: submissionTemplateService.getSubmissionTemplates()
return ResponseEntity(responseObj, HttpStatus.OK)
} catch (e: ItemNotFoundException) {
return ResponseEntity(e.message, HttpStatus.BAD_REQUEST)
}
}
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.gw2.tools.config
open class ApiKeyContainer : ApiKey {
var apiKeyValue: String = "";
override fun getApiKey(): String {
return apiKeyValue
}
override fun setApiKey(key: String) {
this.apiKeyValue = key
}
}

View File

@@ -0,0 +1,18 @@
package dev.sheldan.gw2.tools.config
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.web.servlet.HandlerInterceptor
class ApiKeyInterceptor(private val apiKeyContainer: ApiKey) : HandlerInterceptor {
companion object {
const val API_KEY_HEADER_NAME = "api-key"
}
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
val apiKey: String? = request.getHeader(API_KEY_HEADER_NAME)
apiKey?.let { apiKeyContainer.setApiKey(it) }
return true
}
}

View File

@@ -0,0 +1,26 @@
package dev.sheldan.gw2.tools.config
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.context.annotation.RequestScope
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@Configuration
class HeaderInterceptorConfig : WebMvcConfigurer {
override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(apiKeyInterceptor())
}
@Bean
fun apiKeyInterceptor(): ApiKeyInterceptor {
return ApiKeyInterceptor(apiContainer())
}
@Bean
@RequestScope
fun apiContainer(): ApiKeyContainer {
return ApiKeyContainer()
}
}

View File

@@ -0,0 +1,5 @@
package dev.sheldan.gw2.tools.model
data class DisplayOpeningItem(val itemId: Int, val change: Int, val itemType: OpeningItemType) {
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.gw2.tools.model
class ItemDisplay(
val id: Int,
val name: String,
val iconUrl: String,
val description: String,
val rarity: String
) {
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.gw2.tools.model
import dev.sheldan.gw2.tools.models.EnrichedCurrency
import dev.sheldan.gw2.tools.models.EnrichedItem
class ItemRate(val item: EnrichedItem, val receivedItems: List<EnrichedItem>, val receivedCurrencies: List<EnrichedCurrency>) {
}

Some files were not shown because too many files have changed in this diff Show More