feat: shot-crafter-calculator with H2 persistence and production history

Quarkus 3.20.1 monolith serving React 18 + TypeScript + Tailwind SPA.

Features:
- Three-tab calculator (Insumos, Fórmulas, Calculadora) for
  Soulshot, Spiritshot and Blessed Spiritshot crafting in Lineage 2
  Interlude/Clásico with all 15 grades and pre-loaded recipes
- Real-time profitability computation (cristales → ore →
  crafteos → shots → cost → sale → ganancia)
- Multi-user auth with JWT in httpOnly cookie (bcrypt + RSA 2048)
- H2 file-based persistence in ./data/shots.mv.db (file-based, H2)
- Auto-save on state changes (debounced 500ms)
- Production history with stats (total/avg/best/worst/last5avg)
  and per-run detail modal with snapshot of insumos+formulas

Stack:
- Backend: Quarkus REST + Hibernate ORM Panache + smallrye-jwt
- Frontend: React 18 + TypeScript + Vite + Tailwind 3
- Build: Maven runs frontend-maven-plugin (Node 22 + npm ci)
  then copies dist to META-INF/resources for Quarkus to serve

Verified:
- 5 backend endpoints + 5 history endpoints with curl
- 35/35 browser tests via Playwright + Chromium
- All TS strict, all builds green
This commit is contained in:
2026-08-12 16:03:51 -04:00
commit 9c9fb3a2ca
57 changed files with 6260 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
quarkus.http.port=8080
quarkus.http.host=0.0.0.0
quarkus.application.name=shot-crafter-calculator
# H2 file-based
quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.url=jdbc:h2:file:./data/shots;DB_CLOSE_DELAY=-1
quarkus.datasource.username=sa
quarkus.datasource.password=
quarkus.hibernate-orm.database.generation=update
quarkus.hibernate-orm.log.sql=false
# JWT
mp.jwt.verify.issuer=shot-crafter-calculator
mp.jwt.verify.publickey.location=publicKey.pem
smallrye.jwt.sign.key.location=privateKey.pem
# Cookie auth
app.auth.cookie-name=auth-token
app.auth.cookie-max-age-seconds=86400
# Security
quarkus.http.auth.proactive=false
%native.quarkus.native.resources.includes=META-INF/resources/.*,publicKey.pem,privateKey.pem
+28
View File
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4Rey1Bjlao2e9
6AT++5zUVYZC+g3UIL29Nd/FG64+YZublZ8z9BEIG2IMm39B6XwgpyTIhVvW/lR1
qSEcBaVPjcJVx3grx3GCbqZ+00BlJM/jwRUFMRybNZ9pCmWcWW2JTBhHPjGtfFBd
kZS3kr0htccsWbILJUJlfwSyt2+rNwGNLBJfMoJBjmWjytK5wtgOTxReaUELHRqf
hn6EukIbmyQtATDXF0Xor/MWquGrYK29oNT/R5w2oMKV4IQtDPn/Es0xkFl+nrh9
FfgjOhOEctrf9KvPdwQIKdfhrQ/TH3iVzUxijNUOIemKoxmjf4KCz5boJHPPgSsq
A49fQ5xFAgMBAAECggEACugB51brzGxbxqwgUOfFyPVvjFruDZjgfKiJysnldAYP
5n33sw9Qy2jffT0zeMxfud5apCRBzXA2bH5VminQC8IpTIK79I9fLafsjRi7Y3Zx
zW+PQG7p3AUo9FfBWtHE1LmfEucLRqgoaNlQctWprFXsvg29psDjn0viFHfHPAax
iDe3l0ftILoUF62UEisJ8aAbd+tRPnN6uGD+f6b95+wCeRdK6WlA/ZATxie+O7A5
u/P0ID2WdVAiANB4MVo71zMEr3fXkHWX4NerlA3MGLGYCmkN28xFpvifPh1v84OX
oNHu0v7o88xxfHPmP/Zn138Uzad8N3uKb9n8XYzdMQKBgQDZUJNW/s6eQcq83Lpb
yRjMyB+uq4M1XVTLX9wENdG3UxI0Vfv7n5WWz+Gd2telyRryrDjzGWupTm0/HsuO
BrNfX2pwwuCdrbnTtdixMRImTipRxRXiRSMSn6pFqfpt3zrt+JzD1GHAtY3BcEi5
dvoLnTiSzGZw2pb7rwuyeVsdWQKBgQDZE5VpX2NYvdm3BwoOp/KllJxgttFVRbKs
KbfRVF1sQ68dBU/w2aUOF8dNxXzFc+nU+M8F4zz0YGbcsEhzkjKV8S/gRxiaso6c
Qqeba8/OxqeztQnHbVh6bbSFfG9i0+FXlJVqKttjitVB0QZB3gyLdkmL0frksrvL
F3UddwZ8zQKBgHTwWPjdUM30VWZf2KB/jCrWHcZeYNKckH6H7NsPIvTlbMxg4KG8
dECdSKkrFBQQLcIcTuDx8u8+Vqc6qQqaLHfL3nkjRL9UtsRn/F0NLNkUAs3Roj8K
OR9Sb8vg9fOdxhY8TA9M//U1PTy0cU3r6g3J4qGMACwGVGzG+yJlD1SxAoGBALyp
756QX+jdwB35yTzprNNKMQtBePhSxjIpY/BUEYop3UUsu8jJcFGqSvcF4CZAUwdd
Y5hrYivGqT+/GokPlFWLNKAJSpIRBC89IyzKa+b78v8WJjSkjVSCinXFq41KNzyG
D8IhE2IVZLl6MKUIlwCSwuL5kcQ4r0yYy5nbO9E1AoGAVATm7YeZxFd3WIZwMtk1
RkqMMGM6jZHk5aaHMR0YIsja4jSQjWu86y7y653nnxsVr08PnMESTREmEiyWNUC6
94VxRSqc0HSUXHsE5w1ig2rRJg4fhrgDiQeabXUHuldYIc4Dyfps92QXguVLBhde
o1gonQ1xfEz2HcfnDFwytGM=
-----END PRIVATE KEY-----
+9
View File
@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuEXstQY5WqNnvegE/vuc
1FWGQvoN1CC9vTXfxRuuPmGbm5WfM/QRCBtiDJt/Qel8IKckyIVb1v5UdakhHAWl
T43CVcd4K8dxgm6mftNAZSTP48EVBTEcmzWfaQplnFltiUwYRz4xrXxQXZGUt5K9
IbXHLFmyCyVCZX8EsrdvqzcBjSwSXzKCQY5lo8rSucLYDk8UXmlBCx0an4Z+hLpC
G5skLQEw1xdF6K/zFqrhq2CtvaDU/0ecNqDCleCELQz5/xLNMZBZfp64fRX4IzoT
hHLa3/Srz3cECCnX4a0P0x94lc1MYozVDiHpiqMZo3+Cgs+W6CRzz4ErKgOPX0Oc
RQIDAQAB
-----END PUBLIC KEY-----