mirror of
https://github.com/arcan1s/ffxivbis.git
synced 2025-07-06 18:35:53 +00:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
0ab9162cb5 | |||
d3018998cd | |||
d4553b2e50 | |||
8496d105c0 | |||
ec2cfaea38 | |||
963e84f792 | |||
feea01a47e | |||
fcacd9f15c | |||
b2256784dd | |||
fee87ddbc8 | |||
dc882b74bf | |||
7a6cd84ce3 | |||
33b750123d | |||
d049238dcf | |||
5d72852420 | |||
78a00e2cab | |||
786c3d7d48 | |||
8a1d99b319 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -75,6 +75,7 @@ lib_managed/
|
||||
src_managed/
|
||||
project/boot/
|
||||
project/plugins/project/
|
||||
.bsp/
|
||||
|
||||
# Scala-IDE specific
|
||||
.scala_dependencies
|
||||
|
11
build.sbt
11
build.sbt
@ -1,3 +1,5 @@
|
||||
organization := "me.arcanis"
|
||||
|
||||
name := "ffxivbis"
|
||||
|
||||
scalaVersion := "2.13.6"
|
||||
@ -5,12 +7,3 @@ scalaVersion := "2.13.6"
|
||||
scalacOptions ++= Seq("-deprecation", "-feature")
|
||||
|
||||
enablePlugins(JavaAppPackaging)
|
||||
|
||||
assemblyMergeStrategy in assembly := {
|
||||
case PathList("META-INF", xs @ _*) => MergeStrategy.discard
|
||||
case "application.conf" => MergeStrategy.concat
|
||||
case "module-info.class" => MergeStrategy.first
|
||||
case x =>
|
||||
val oldStrategy = (assemblyMergeStrategy in assembly).value
|
||||
oldStrategy(x)
|
||||
}
|
@ -12,6 +12,7 @@ libraryDependencies += "com.typesafe.akka" %% "akka-actor-typed" % AkkaVersion
|
||||
libraryDependencies += "com.typesafe.akka" %% "akka-stream" % AkkaVersion
|
||||
libraryDependencies += "com.github.swagger-akka-http" %% "swagger-akka-http" % "2.6.0"
|
||||
libraryDependencies += "jakarta.platform" % "jakarta.jakartaee-web-api" % "9.1.0"
|
||||
libraryDependencies += "ch.megard" %% "akka-http-cors" % "1.1.2"
|
||||
|
||||
libraryDependencies += "io.spray" %% "spray-json" % "1.3.6"
|
||||
|
||||
|
@ -1 +1 @@
|
||||
sbt.version = 1.3.3
|
||||
sbt.version = 1.5.8
|
||||
|
@ -1,4 +1,4 @@
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")
|
||||
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.3")
|
||||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.9.3")
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.4")
|
||||
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.10.0-RC1")
|
||||
addDependencyTreePlugin
|
||||
|
23
src/main/resources/html/api.html
Normal file
23
src/main/resources/html/api.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>FFXIV loot helper API</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<!-- Embed elements Elements via Web Component -->
|
||||
<script src="https://unpkg.com/@stoplight/elements/web-components.min.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/@stoplight/elements/styles.min.css" type="text/css">
|
||||
|
||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<elements-api
|
||||
apiDescriptionUrl="/api-docs/swagger.json"
|
||||
router="hash"
|
||||
layout="sidebar"
|
||||
/>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -6,17 +6,17 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link href="/static/favicon.ico" rel="shortcut icon">
|
||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" type="text/css">
|
||||
|
||||
<link href="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.css" type="text/css">
|
||||
|
||||
|
||||
<link href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" type="text/css">
|
||||
|
||||
<link href="/static/styles.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="/static/styles.css" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
data-single-select="true"
|
||||
data-sortable="true"
|
||||
data-sort-name="nick"
|
||||
data-sort-order="desc"
|
||||
data-sort-order="asc"
|
||||
data-sort-reset="true"
|
||||
data-toolbar="#toolbar">
|
||||
<thead class="table-primary">
|
||||
@ -86,23 +86,24 @@
|
||||
|
||||
<div id="update-bis-dialog" tabindex="-1" role="dialog" class="modal fade">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content">
|
||||
<div class="modal-header">
|
||||
<form class="modal-content" action="javascript:" onsubmit="updateBis()">
|
||||
<div class="modal-header form-group row">
|
||||
<div class="btn-group" role="group" aria-label="Update bis">
|
||||
<input id="add-piece-btn" name="update-bis" type="radio" class="btn-check" autocomplete="off" checked>
|
||||
<label class="btn btn-outline-primary" for="add-piece-btn">add piece</label>
|
||||
|
||||
<input id="update-bis-btn" name="update-bis" type="radio" class="btn-check" autocomplete="off">
|
||||
<label class="btn btn-outline-primary" for="update-bis-btn">update bis</label>
|
||||
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
</div>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label" for="player">player</label>
|
||||
<div class="col-sm-8">
|
||||
<select id="player" name="player" class="form-control" title="player"></select>
|
||||
<select id="player" name="player" class="form-control" title="player" required></select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="piece-row" class="form-group row">
|
||||
@ -120,14 +121,14 @@
|
||||
<div id="bis-link-row" class="form-group row" style="display: none">
|
||||
<label class="col-sm-4 col-form-label" for="bis-link">link</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="bis-link" name="link" class="form-control" placeholder="link to bis" onkeyup="disableSubmitBisButton()">
|
||||
<input id="bis-link" name="link" class="form-control" placeholder="link to bis">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">close</button>
|
||||
<button id="submit-add-bis-btn" type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="addPiece()" disabled>add</button>
|
||||
<button id="submit-update-bis-btn" type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="setBis()" style="display: none" disabled>set</button>
|
||||
<button id="submit-add-bis-btn" type="submit" class="btn btn-primary">add</button>
|
||||
<button id="submit-set-bis-btn" type="submit" class="btn btn-primary" style="display: none">set</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -138,6 +139,7 @@
|
||||
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
||||
<ul class="nav">
|
||||
<li><a class="nav-link" href="/" title="home">home</a></li>
|
||||
<li><a class="nav-link" href="/api-docs" title="api">api</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav">
|
||||
@ -171,7 +173,7 @@
|
||||
const updateButton = $("#update-btn");
|
||||
|
||||
const submitAddBisButton = $("#submit-add-bis-btn");
|
||||
const submitUpdateBisButton = $("#submit-update-bis-btn");
|
||||
const submitSetBisButton = $("#submit-set-bis-btn");
|
||||
const updateBisDialog = $("#update-bis-dialog");
|
||||
|
||||
const addPieceButton = $("#add-piece-btn");
|
||||
@ -208,12 +210,8 @@
|
||||
success: function (_) { reload(); },
|
||||
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); },
|
||||
});
|
||||
}
|
||||
|
||||
function disableSubmitBisButton() {
|
||||
const nonEmpty = (playerInput.val() !== null); // well lol
|
||||
submitUpdateBisButton.attr("disabled", !(nonEmpty && linkInput.val()));
|
||||
submitAddBisButton.attr("disabled", !(nonEmpty));
|
||||
updateBisDialog.modal("hide");
|
||||
return true; // action expects boolean result
|
||||
}
|
||||
|
||||
function hideControls() {
|
||||
@ -222,20 +220,24 @@
|
||||
}
|
||||
|
||||
function hideLinkPart() {
|
||||
disableSubmitBisButton();
|
||||
bisLinkRow.hide();
|
||||
submitUpdateBisButton.hide();
|
||||
linkInput.prop("required", false);
|
||||
submitSetBisButton.hide();
|
||||
pieceRow.show();
|
||||
pieceTypeRow.show();
|
||||
pieceInput.prop("required", true);
|
||||
pieceTypeInput.prop("required", true);
|
||||
submitAddBisButton.show();
|
||||
}
|
||||
|
||||
function hidePiecePart() {
|
||||
disableSubmitBisButton();
|
||||
bisLinkRow.show();
|
||||
submitUpdateBisButton.show();
|
||||
linkInput.prop("required", true);
|
||||
submitSetBisButton.show();
|
||||
pieceRow.hide();
|
||||
pieceTypeRow.hide();
|
||||
pieceInput.prop("required", false);
|
||||
pieceTypeInput.prop("required", false);
|
||||
submitAddBisButton.hide();
|
||||
}
|
||||
|
||||
@ -269,7 +271,6 @@
|
||||
return option;
|
||||
});
|
||||
playerInput.empty().append(options);
|
||||
disableSubmitBisButton();
|
||||
},
|
||||
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); },
|
||||
});
|
||||
@ -327,6 +328,18 @@
|
||||
success: function (_) { reload(); },
|
||||
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); },
|
||||
});
|
||||
updateBisDialog.modal("hide");
|
||||
return true; // action expects boolean result
|
||||
}
|
||||
|
||||
function updateBis() {
|
||||
if (updateBisButton.is(":checked")) {
|
||||
return setBis();
|
||||
}
|
||||
if (addPieceButton.is(":checked")) {
|
||||
return addPiece();
|
||||
}
|
||||
return false; // should not happen
|
||||
}
|
||||
|
||||
$(function () {
|
||||
@ -344,6 +357,7 @@
|
||||
|
||||
table.bootstrapTable({});
|
||||
reload();
|
||||
reset();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -6,11 +6,11 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link href="/static/favicon.ico" rel="shortcut icon">
|
||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
||||
|
||||
<link href="/static/styles.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="/static/styles.css" type="text/css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -71,7 +71,9 @@
|
||||
|
||||
<div class="container">
|
||||
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
||||
<ul class="nav"></ul>
|
||||
<ul class="nav">
|
||||
<li><a class="nav-link" href="/api-docs" title="api">api</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav">
|
||||
<li><a class="nav-link" href="https://github.com/arcan1s/ffxivbis" title="sources">ffxivbis</a></li>
|
||||
@ -122,7 +124,7 @@
|
||||
password: passwordInput.val(),
|
||||
permission: "admin",
|
||||
}),
|
||||
type: "PUT",
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
|
@ -6,17 +6,17 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link href="/static/favicon.ico" rel="shortcut icon">
|
||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" type="text/css">
|
||||
|
||||
<link href="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.css" type="text/css">
|
||||
|
||||
|
||||
<link href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" type="text/css">
|
||||
|
||||
<link href="/static/styles.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="/static/styles.css" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@ -88,35 +88,35 @@
|
||||
|
||||
<div id="add-loot-dialog" tabindex="-1" role="dialog" class="modal fade">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<form class="modal-content" action="javascript:" onsubmit="addLoot()">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">add looted piece</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
</div>
|
||||
|
||||
<form class="modal-body">
|
||||
<div class="modal-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label" for="player">player</label>
|
||||
<div class="col-sm-8">
|
||||
<select id="player" name="player" class="form-control" title="player"></select>
|
||||
<select id="player" name="player" class="form-control" title="player" required></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label" for="piece">piece</label>
|
||||
<div class="col-sm-8">
|
||||
<select id="piece" name="piece" class="form-control" title="piece"></select>
|
||||
<select id="piece" name="piece" class="form-control" title="piece" required></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label" for="piece-type">piece type</label>
|
||||
<div class="col-sm-8">
|
||||
<select id="piece-type" name="pieceType" class="form-control" title="pieceType"></select>
|
||||
<select id="piece-type" name="pieceType" class="form-control" title="pieceType" required></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label" for="job">job</label>
|
||||
<div class="col-sm-8">
|
||||
<select id="job" name="job" class="form-control" title="job"></select>
|
||||
<select id="job" name="job" class="form-control" title="job" required></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
@ -141,14 +141,14 @@
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">close</button>
|
||||
<button type="button" class="btn btn-secondary" onclick="suggestLoot()">suggest</button>
|
||||
<button id="submit-btn" type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="addLoot()" disabled>add</button>
|
||||
<button type="submit" class="btn btn-primary">add</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -156,6 +156,7 @@
|
||||
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
||||
<ul class="nav">
|
||||
<li><a class="nav-link" href="/" title="home">home</a></li>
|
||||
<li><a class="nav-link" href="/api-docs" title="api">api</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav">
|
||||
@ -189,7 +190,6 @@
|
||||
const addButton = $("#add-btn");
|
||||
const removeButton = $("#remove-btn");
|
||||
|
||||
const submitLootButton = $("#submit-btn");
|
||||
const addLootDialog = $("#add-loot-dialog");
|
||||
|
||||
const freeLootInput = $("#free-loot");
|
||||
@ -221,6 +221,8 @@
|
||||
success: function (_) { reload(); },
|
||||
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); },
|
||||
});
|
||||
addLootDialog.modal("hide");
|
||||
return true; // action expects boolean result
|
||||
}
|
||||
|
||||
function hideControls() {
|
||||
@ -260,7 +262,6 @@
|
||||
return option;
|
||||
});
|
||||
playerInput.empty().append(options);
|
||||
submitLootButton.attr("disabled", options.length === 0);
|
||||
},
|
||||
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); },
|
||||
});
|
||||
@ -310,7 +311,7 @@
|
||||
return {
|
||||
nick: stat.nick,
|
||||
job: stat.job,
|
||||
isRequired: stat.isRequired,
|
||||
isRequired: stat.isRequired ? "yes" : "no",
|
||||
lootCount: stat.lootCount,
|
||||
lootCountBiS: stat.lootCountBiS,
|
||||
lootCountTotal: stat.lootCountTotal,
|
||||
|
@ -6,17 +6,17 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link href="/static/favicon.ico" rel="shortcut icon">
|
||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" type="text/css">
|
||||
|
||||
<link href="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.css" type="text/css">
|
||||
|
||||
|
||||
<link href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" type="text/css">
|
||||
|
||||
<link href="/static/styles.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="/static/styles.css" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@ -65,7 +65,7 @@
|
||||
data-single-select="true"
|
||||
data-sortable="true"
|
||||
data-sort-name="nick"
|
||||
data-sort-order="desc"
|
||||
data-sort-order="asc"
|
||||
data-sort-reset="true"
|
||||
data-toolbar="#toolbar">
|
||||
<thead class="table-primary">
|
||||
@ -84,23 +84,23 @@
|
||||
|
||||
<div id="add-player-dialog" tabindex="-1" role="dialog" class="modal fade">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<form class="modal-content" action="javascript:" onsubmit="addPlayer()">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">add new player</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
</div>
|
||||
|
||||
<form class="modal-body">
|
||||
<div class="modal-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label" for="nick">player name</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="nick" name="nick" class="form-control" placeholder="nick" onkeyup="disableAddPlayerForm()">
|
||||
<input id="nick" name="nick" class="form-control" placeholder="nick" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label" for="job">player job</label>
|
||||
<div class="col-sm-8">
|
||||
<select id="job" name="job" class="form-control" title="job"></select>
|
||||
<select id="job" name="job" class="form-control" title="job" required></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
@ -115,13 +115,13 @@
|
||||
<input id="priority" name="priority" type="number" class="form-control" value="0">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">close</button>
|
||||
<button id="submit-player-btn" type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="addPlayer()" disabled>add</button>
|
||||
<button type="submit" class="btn btn-primary">add</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -129,6 +129,7 @@
|
||||
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
||||
<ul class="nav">
|
||||
<li><a class="nav-link" href="/" title="home">home</a></li>
|
||||
<li><a class="nav-link" href="/api-docs" title="api">api</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav">
|
||||
@ -162,7 +163,6 @@
|
||||
const removeButton = $("#remove-btn");
|
||||
|
||||
const addPlayerDialog = $("#add-player-dialog");
|
||||
const submitPlayerButton = $("#submit-player-btn");
|
||||
|
||||
const jobInput = $("#job");
|
||||
const linkInput = $("#link");
|
||||
@ -187,6 +187,8 @@
|
||||
success: function (_) { reload(); },
|
||||
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); },
|
||||
});
|
||||
addPlayerDialog.modal("hide");
|
||||
return true; // action expects boolean result
|
||||
}
|
||||
|
||||
function bisLinkFormatter(link, row) {
|
||||
@ -197,10 +199,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
function disableAddPlayerForm() {
|
||||
submitPlayerButton.attr("disabled", !nickInput.val());
|
||||
}
|
||||
|
||||
function hideControls() {
|
||||
addButton.attr("hidden", isReadOnly);
|
||||
removeButton.attr("hidden", isReadOnly);
|
||||
|
@ -1,19 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>FFXIV loot helper API</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet" type="text/css">
|
||||
|
||||
<link href="/static/favicon.ico" rel="shortcut icon">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<redoc spec-url="/api-docs/swagger.json"></redoc>
|
||||
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -6,17 +6,17 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link href="/static/favicon.ico" rel="shortcut icon">
|
||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" type="text/css">
|
||||
|
||||
<link href="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.css" type="text/css">
|
||||
|
||||
|
||||
<link href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" type="text/css">
|
||||
|
||||
<link href="/static/styles.css" rel="stylesheet" type="text/css">
|
||||
<link rel="stylesheet" href="/static/styles.css" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
data-single-select="true"
|
||||
data-sortable="true"
|
||||
data-sort-name="username"
|
||||
data-sort-order="desc"
|
||||
data-sort-order="asc"
|
||||
data-sort-reset="true"
|
||||
data-toolbar="#toolbar">
|
||||
<thead class="table-primary">
|
||||
@ -84,38 +84,38 @@
|
||||
|
||||
<div id="add-user-dialog" tabindex="-1" role="dialog" class="modal fade">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<form class="modal-content" action="javascript:" onsubmit="addUser()">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">add new user</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
</div>
|
||||
|
||||
<form class="modal-body">
|
||||
<div class="modal-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label" for="username">login</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="username" name="username" class="form-control" placeholder="username" onkeyup="disableAddUserForm()">
|
||||
<input id="username" name="username" class="form-control" placeholder="username" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label" for="password">password</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="password" name="password" type="password" class="form-control" placeholder="password" onkeyup="disableAddUserForm()">
|
||||
<input id="password" name="password" type="password" class="form-control" placeholder="password" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label" for="permission">permission</label>
|
||||
<div class="col-sm-8">
|
||||
<select id="permission" name="permission" class="form-control" title="permission"></select>
|
||||
<select id="permission" name="permission" class="form-control" title="permission" required></select>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">close</button>
|
||||
<button id="submit-btn" type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="addUser()" disabled>add</button>
|
||||
<button type="submit" class="btn btn-primary">add</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -123,6 +123,7 @@
|
||||
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
||||
<ul class="nav">
|
||||
<li><a class="nav-link" href="/" title="home">home</a></li>
|
||||
<li><a class="nav-link" href="/api-docs" title="api">api</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav">
|
||||
@ -156,7 +157,6 @@
|
||||
const removeButton = $("#remove-btn");
|
||||
|
||||
const addUserDialog = $("#add-user-dialog");
|
||||
const submitUserButton = $("#submit-btn");
|
||||
|
||||
const usernameInput = $("#username");
|
||||
const passwordInput = $("#password");
|
||||
@ -176,10 +176,8 @@
|
||||
success: function (_) { reload(); },
|
||||
error: function (jqXHR, _, errorThrown) { requestAlert(jqXHR, errorThrown); },
|
||||
});
|
||||
}
|
||||
|
||||
function disableAddUserForm() {
|
||||
submitUserButton.attr("disabled", !(usernameInput.val() && passwordInput.val()));
|
||||
addUserDialog.modal("hide");
|
||||
return true; // action expects boolean result
|
||||
}
|
||||
|
||||
function hideControls() {
|
||||
|
@ -11,6 +11,6 @@
|
||||
<appender-ref ref="http" />
|
||||
</logger>
|
||||
<logger name="org.flywaydb.core.internal" level="INFO" />
|
||||
<logger name="com.zaxxer.hikari.pool.HikariPool" level="INFO" />
|
||||
<logger name="com.zaxxer.hikari.pool" level="INFO" />
|
||||
|
||||
</configuration>
|
||||
|
@ -50,6 +50,7 @@ me.arcanis.ffxivbis {
|
||||
#hostname = "127.0.0.1:8000"
|
||||
# enable head requests for GET requests
|
||||
enable-head-requests = yes
|
||||
schemes = ["http"]
|
||||
|
||||
authorization-cache {
|
||||
# maximum amount of cached logins
|
||||
@ -57,14 +58,14 @@ me.arcanis.ffxivbis {
|
||||
# ttl of cached logins
|
||||
cache-timeout = 1m
|
||||
}
|
||||
}
|
||||
|
||||
default-dispatcher {
|
||||
type = Dispatcher
|
||||
executor = "thread-pool-executor"
|
||||
thread-pool-executor {
|
||||
fixed-pool-size = 16
|
||||
}
|
||||
throughput = 1
|
||||
}
|
||||
|
||||
default-dispatcher {
|
||||
type = Dispatcher
|
||||
executor = "thread-pool-executor"
|
||||
thread-pool-executor {
|
||||
fixed-pool-size = 16
|
||||
}
|
||||
throughput = 1
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ function loadTypes(url, selector) {
|
||||
}
|
||||
|
||||
function setupFormClear(dialog, reset) {
|
||||
dialog.on("shown.bs.modal", function () {
|
||||
dialog.on("hide.bs.modal", function () {
|
||||
$(this).find("form").trigger("reset");
|
||||
$(this).find("table").bootstrapTable("removeAll");
|
||||
if (reset) {
|
||||
|
@ -50,7 +50,7 @@ class Application(context: ActorContext[Nothing]) extends AbstractBehavior[Nothi
|
||||
val party = context.spawn(PartyService(storage), "party")
|
||||
val http = new RootEndpoint(context.system, party, bisProvider)
|
||||
|
||||
val flow = Route.toFlow(http.route)(context.system)
|
||||
val flow = Route.toFlow(http.routes)(context.system)
|
||||
Http(context.system).newServerAt(host, port).bindFlow(flow)
|
||||
|
||||
case Success(result) =>
|
||||
|
@ -13,8 +13,8 @@ import akka.http.scaladsl.server.Directive0
|
||||
import akka.http.scaladsl.server.Directives.{extractClientIP, extractRequestContext, mapResponse, optionalHeaderValueByType}
|
||||
import com.typesafe.scalalogging.Logger
|
||||
|
||||
import java.time.{Instant, ZoneId}
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.{Instant, ZoneId}
|
||||
import java.util.Locale
|
||||
|
||||
trait HttpLog {
|
||||
@ -68,7 +68,7 @@ object HttpLog {
|
||||
|
||||
val httpLogDatetimeFormatter: DateTimeFormatter =
|
||||
DateTimeFormatter
|
||||
.ofPattern("dd/MMM/uuuu:HH:mm:ss xx ")
|
||||
.ofPattern("dd/MMM/uuuu:HH:mm:ss xx")
|
||||
.withLocale(Locale.UK)
|
||||
.withZone(ZoneId.systemDefault())
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import akka.actor.typed.{ActorRef, ActorSystem, Scheduler}
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.http.scaladsl.server._
|
||||
import akka.util.Timeout
|
||||
import ch.megard.akka.http.cors.scaladsl.CorsDirectives.cors
|
||||
import com.typesafe.scalalogging.StrictLogging
|
||||
import me.arcanis.ffxivbis.http.api.v1.RootApiV1Endpoint
|
||||
import me.arcanis.ffxivbis.http.view.RootView
|
||||
@ -25,7 +26,7 @@ class RootEndpoint(system: ActorSystem[Nothing], storage: ActorRef[Message], pro
|
||||
private val config = system.settings.config
|
||||
|
||||
implicit val scheduler: Scheduler = system.scheduler
|
||||
implicit val timeout: Timeout = config.getDuration("me.arcanis.ffxivbis.settings.request-timeout")
|
||||
implicit val timeout: Timeout = config.getTimeout("me.arcanis.ffxivbis.settings.request-timeout")
|
||||
|
||||
private val auth = AuthorizationProvider(config, storage, timeout, scheduler)
|
||||
|
||||
@ -33,28 +34,30 @@ class RootEndpoint(system: ActorSystem[Nothing], storage: ActorRef[Message], pro
|
||||
private val rootView = new RootView(auth)
|
||||
private val swagger = new Swagger(config)
|
||||
|
||||
def route: Route =
|
||||
def routes: Route =
|
||||
withHttpLog {
|
||||
ignoreTrailingSlash {
|
||||
apiRoute ~ htmlRoute ~ swagger.routes ~ swaggerUIRoute
|
||||
cors() {
|
||||
apiRoutes ~ htmlRoutes ~ swagger.routes ~ swaggerUIRoutes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def apiRoute: Route =
|
||||
private def apiRoutes: Route =
|
||||
pathPrefix("api") {
|
||||
pathPrefix(Segment) {
|
||||
case "v1" => rootApiV1Endpoint.route
|
||||
case "v1" => rootApiV1Endpoint.routes
|
||||
case _ => reject
|
||||
}
|
||||
}
|
||||
|
||||
private def htmlRoute: Route =
|
||||
private def htmlRoutes: Route =
|
||||
pathPrefix("static") {
|
||||
getFromResourceDirectory("static")
|
||||
} ~ rootView.route
|
||||
} ~ rootView.routes
|
||||
|
||||
private def swaggerUIRoute: Route =
|
||||
private def swaggerUIRoutes: Route =
|
||||
path("api-docs") {
|
||||
getFromResource("html/redoc.html")
|
||||
getFromResource("html/api.html")
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import com.typesafe.config.Config
|
||||
import io.swagger.v3.oas.models.security.SecurityScheme
|
||||
|
||||
import scala.io.Source
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
class Swagger(config: Config) extends SwaggerHttpService {
|
||||
|
||||
@ -22,6 +23,7 @@ class Swagger(config: Config) extends SwaggerHttpService {
|
||||
classOf[api.v1.LootEndpoint],
|
||||
classOf[api.v1.PartyEndpoint],
|
||||
classOf[api.v1.PlayerEndpoint],
|
||||
classOf[api.v1.StatusEndpoint],
|
||||
classOf[api.v1.TypesEndpoint],
|
||||
classOf[api.v1.UserEndpoint]
|
||||
)
|
||||
@ -35,15 +37,17 @@ class Swagger(config: Config) extends SwaggerHttpService {
|
||||
|
||||
override val host: String =
|
||||
if (config.hasPath("me.arcanis.ffxivbis.web.hostname")) config.getString("me.arcanis.ffxivbis.web.hostname")
|
||||
else s"${config.getString("me.arcanis.ffxivbis.web.host")}:${config.getString("me.arcanis.ffxivbis.web.port")}"
|
||||
else s"${config.getString("me.arcanis.ffxivbis.web.host")}:${config.getInt("me.arcanis.ffxivbis.web.port")}"
|
||||
|
||||
override val schemes: List[String] = config.getStringList("me.arcanis.ffxivbis.web.schemes").asScala.toList
|
||||
|
||||
private val basicAuth = new SecurityScheme()
|
||||
.description("basic http auth")
|
||||
.`type`(SecurityScheme.Type.HTTP)
|
||||
.in(SecurityScheme.In.HEADER)
|
||||
.scheme("bearer")
|
||||
override val securitySchemes: Map[String, SecurityScheme] = Map("basic auth" -> basicAuth)
|
||||
.scheme("basic")
|
||||
override val securitySchemes: Map[String, SecurityScheme] = Map("basic" -> basicAuth)
|
||||
|
||||
override val unwantedDefinitions: Seq[String] =
|
||||
Seq("Function1", "Function1RequestContextFutureRouteResult")
|
||||
Seq("Function1", "Function1RequestContextFutureRouteResult", "SeqLootModel", "SeqPieceModel")
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ class BiSEndpoint(
|
||||
with Authorization
|
||||
with JsonSupport {
|
||||
|
||||
def route: Route = createBiS ~ getBiS ~ modifyBiS
|
||||
def routes: Route = createBiS ~ getBiS ~ modifyBiS
|
||||
|
||||
@PUT
|
||||
@Path("party/{partyId}/bis")
|
||||
@ -79,7 +79,7 @@ class BiSEndpoint(
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))),
|
||||
security = Array(new SecurityRequirement(name = "basic", scopes = Array("post"))),
|
||||
tags = Array("best in slot"),
|
||||
)
|
||||
def createBiS: Route =
|
||||
@ -140,7 +140,7 @@ class BiSEndpoint(
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))),
|
||||
security = Array(new SecurityRequirement(name = "basic", scopes = Array("get"))),
|
||||
tags = Array("best in slot"),
|
||||
)
|
||||
def getBiS: Route =
|
||||
@ -197,7 +197,7 @@ class BiSEndpoint(
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))),
|
||||
security = Array(new SecurityRequirement(name = "basic", scopes = Array("post"))),
|
||||
tags = Array("best in slot"),
|
||||
)
|
||||
def modifyBiS: Route =
|
||||
|
@ -11,6 +11,7 @@ package me.arcanis.ffxivbis.http.api.v1
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.http.scaladsl.server._
|
||||
import ch.megard.akka.http.cors.scaladsl.CorsDirectives.corsRejectionHandler
|
||||
import com.typesafe.scalalogging.StrictLogging
|
||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||
import spray.json._
|
||||
@ -27,9 +28,26 @@ trait HttpHandler extends StrictLogging { this: JsonSupport =>
|
||||
}
|
||||
|
||||
def rejectionHandler: RejectionHandler =
|
||||
RejectionHandler.default
|
||||
RejectionHandler
|
||||
.newBuilder()
|
||||
.handleAll[MethodRejection] { rejections =>
|
||||
val (methods, names) = rejections.map(r => r.supported -> r.supported.name).unzip
|
||||
|
||||
respondWithHeader(headers.Allow(methods)) {
|
||||
options {
|
||||
complete(StatusCodes.OK, HttpEntity.Empty)
|
||||
} ~
|
||||
complete(
|
||||
StatusCodes.MethodNotAllowed,
|
||||
s"HTTP method not allowed, supported methods: ${names.mkString(", ")}"
|
||||
)
|
||||
}
|
||||
}
|
||||
.result()
|
||||
.withFallback(corsRejectionHandler)
|
||||
.seal
|
||||
.mapRejectionResponse {
|
||||
case response @ HttpResponse(_, _, entity: HttpEntity.Strict, _) =>
|
||||
case response @ HttpResponse(_, _, entity: HttpEntity.Strict, _) if entity.data.nonEmpty =>
|
||||
val message = ErrorModel(entity.data.utf8String).toJson
|
||||
response.withEntity(HttpEntity(ContentTypes.`application/json`, message.compactPrint))
|
||||
case other => other
|
||||
|
@ -37,7 +37,7 @@ class LootEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
with JsonSupport
|
||||
with HttpHandler {
|
||||
|
||||
def route: Route = getLoot ~ modifyLoot ~ suggestLoot
|
||||
def routes: Route = getLoot ~ modifyLoot ~ suggestLoot
|
||||
|
||||
@GET
|
||||
@Path("party/{partyId}/loot")
|
||||
@ -81,7 +81,7 @@ class LootEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))),
|
||||
security = Array(new SecurityRequirement(name = "basic", scopes = Array("get"))),
|
||||
tags = Array("loot"),
|
||||
)
|
||||
def getLoot: Route =
|
||||
@ -137,7 +137,7 @@ class LootEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))),
|
||||
security = Array(new SecurityRequirement(name = "basic", scopes = Array("post"))),
|
||||
tags = Array("loot"),
|
||||
)
|
||||
def modifyLoot: Route =
|
||||
@ -202,7 +202,7 @@ class LootEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))),
|
||||
security = Array(new SecurityRequirement(name = "basic", scopes = Array("get"))),
|
||||
tags = Array("loot"),
|
||||
)
|
||||
def suggestLoot: Route =
|
||||
|
@ -40,7 +40,7 @@ class PartyEndpoint(
|
||||
with JsonSupport
|
||||
with HttpHandler {
|
||||
|
||||
def route: Route = getPartyDescription ~ modifyPartyDescription
|
||||
def routes: Route = getPartyDescription ~ modifyPartyDescription
|
||||
|
||||
@GET
|
||||
@Path("party/{partyId}/description")
|
||||
@ -73,7 +73,7 @@ class PartyEndpoint(
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))),
|
||||
security = Array(new SecurityRequirement(name = "basic", scopes = Array("get"))),
|
||||
tags = Array("party"),
|
||||
)
|
||||
def getPartyDescription: Route =
|
||||
@ -126,7 +126,7 @@ class PartyEndpoint(
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))),
|
||||
security = Array(new SecurityRequirement(name = "basic", scopes = Array("post"))),
|
||||
tags = Array("party"),
|
||||
)
|
||||
def modifyPartyDescription: Route =
|
||||
|
@ -41,7 +41,7 @@ class PlayerEndpoint(
|
||||
with JsonSupport
|
||||
with HttpHandler {
|
||||
|
||||
def route: Route = getParty ~ getPartyStats ~ modifyParty
|
||||
def routes: Route = getParty ~ getPartyStats ~ modifyParty
|
||||
|
||||
@GET
|
||||
@Path("party/{partyId}")
|
||||
@ -85,7 +85,7 @@ class PlayerEndpoint(
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))),
|
||||
security = Array(new SecurityRequirement(name = "basic", scopes = Array("get"))),
|
||||
tags = Array("party"),
|
||||
)
|
||||
def getParty: Route =
|
||||
@ -146,7 +146,7 @@ class PlayerEndpoint(
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))),
|
||||
security = Array(new SecurityRequirement(name = "basic", scopes = Array("get"))),
|
||||
tags = Array("party"),
|
||||
)
|
||||
def getPartyStats: Route =
|
||||
@ -202,7 +202,7 @@ class PlayerEndpoint(
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))),
|
||||
security = Array(new SecurityRequirement(name = "basic", scopes = Array("post"))),
|
||||
tags = Array("party"),
|
||||
)
|
||||
def modifyParty: Route =
|
||||
|
@ -32,14 +32,15 @@ class RootApiV1Endpoint(
|
||||
private val lootEndpoint = new LootEndpoint(storage, auth)
|
||||
private val partyEndpoint = new PartyEndpoint(storage, provider, auth)
|
||||
private val playerEndpoint = new PlayerEndpoint(storage, provider, auth)
|
||||
private val statusEndpoint = new StatusEndpoint
|
||||
private val typesEndpoint = new TypesEndpoint(config)
|
||||
private val userEndpoint = new UserEndpoint(storage, auth)
|
||||
|
||||
def route: Route =
|
||||
def routes: Route =
|
||||
handleExceptions(exceptionHandler) {
|
||||
handleRejections(rejectionHandler) {
|
||||
biSEndpoint.route ~ lootEndpoint.route ~ partyEndpoint.route ~
|
||||
playerEndpoint.route ~ typesEndpoint.route ~ userEndpoint.route
|
||||
biSEndpoint.routes ~ lootEndpoint.routes ~ partyEndpoint.routes ~ playerEndpoint.routes ~
|
||||
statusEndpoint.routes ~ typesEndpoint.routes ~ userEndpoint.routes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 Evgeniy Alekseev.
|
||||
*
|
||||
* This file is part of ffxivbis
|
||||
* (see https://github.com/arcan1s/ffxivbis).
|
||||
*
|
||||
* License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
package me.arcanis.ffxivbis.http.api.v1
|
||||
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.http.scaladsl.server._
|
||||
import io.swagger.v3.oas.annotations.Operation
|
||||
import io.swagger.v3.oas.annotations.media.{Content, Schema}
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse
|
||||
import jakarta.ws.rs._
|
||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||
|
||||
@Path("/api/v1")
|
||||
class StatusEndpoint extends JsonSupport {
|
||||
|
||||
def routes: Route = getServerStatus
|
||||
|
||||
@GET
|
||||
@Path("status")
|
||||
@Produces(value = Array("application/json"))
|
||||
@Operation(
|
||||
summary = "server status",
|
||||
description = "Returns the server status descriptor",
|
||||
responses = Array(
|
||||
new ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "Service status descriptor",
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[StatusModel])))
|
||||
),
|
||||
new ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "Internal server error",
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
tags = Array("status"),
|
||||
)
|
||||
def getServerStatus: Route =
|
||||
path("status") {
|
||||
get {
|
||||
complete {
|
||||
StatusModel(
|
||||
version = Option(getClass.getPackage.getImplementationVersion),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ import me.arcanis.ffxivbis.models._
|
||||
@Path("/api/v1")
|
||||
class TypesEndpoint(config: Config) extends JsonSupport {
|
||||
|
||||
def route: Route = getAllJobs ~ getJobs ~ getPermissions ~ getPieces ~ getPieceTypes ~ getPriority
|
||||
def routes: Route = getAllJobs ~ getJobs ~ getPermissions ~ getPieces ~ getPieceTypes ~ getPriority
|
||||
|
||||
@GET
|
||||
@Path("types/jobs/all")
|
||||
|
@ -36,9 +36,9 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
with Authorization
|
||||
with JsonSupport {
|
||||
|
||||
def route: Route = createParty ~ createUser ~ deleteUser ~ getUsers ~ getUsersCurrent
|
||||
def routes: Route = createParty ~ createUser ~ deleteUser ~ getUsers ~ getUsersCurrent
|
||||
|
||||
@PUT
|
||||
@POST
|
||||
@Path("party")
|
||||
@Consumes(value = Array("application/json"))
|
||||
@Operation(
|
||||
@ -76,7 +76,7 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
def createParty: Route =
|
||||
path("party") {
|
||||
extractExecutionContext { implicit executionContext =>
|
||||
put {
|
||||
post {
|
||||
entity(as[UserModel]) { user =>
|
||||
onSuccess(newPartyId) { partyId =>
|
||||
val admin = user.toUser.copy(partyId = partyId, permission = Permission.admin)
|
||||
@ -126,7 +126,7 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("admin"))),
|
||||
security = Array(new SecurityRequirement(name = "basic", scopes = Array("admin"))),
|
||||
tags = Array("users"),
|
||||
)
|
||||
def createUser: Route =
|
||||
@ -172,7 +172,7 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("admin"))),
|
||||
security = Array(new SecurityRequirement(name = "basic", scopes = Array("admin"))),
|
||||
tags = Array("users"),
|
||||
)
|
||||
def deleteUser: Route =
|
||||
@ -223,7 +223,7 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))),
|
||||
security = Array(new SecurityRequirement(name = "basic", scopes = Array("get"))),
|
||||
tags = Array("users"),
|
||||
)
|
||||
def getUsers: Route =
|
||||
@ -270,7 +270,7 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||
),
|
||||
),
|
||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("admin"))),
|
||||
security = Array(new SecurityRequirement(name = "basic", scopes = Array("admin"))),
|
||||
tags = Array("users"),
|
||||
)
|
||||
def getUsersCurrent: Route =
|
||||
|
@ -52,5 +52,6 @@ trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
|
||||
implicit val playerBiSLinkFormat: RootJsonFormat[PlayerBiSLinkModel] = jsonFormat2(PlayerBiSLinkModel.apply)
|
||||
implicit val playerIdWithCountersFormat: RootJsonFormat[PlayerIdWithCountersModel] =
|
||||
jsonFormat9(PlayerIdWithCountersModel.apply)
|
||||
implicit val statusFormat: RootJsonFormat[StatusModel] = jsonFormat1(StatusModel.apply)
|
||||
implicit val userFormat: RootJsonFormat[UserModel] = jsonFormat4(UserModel.apply)
|
||||
}
|
||||
|
@ -19,7 +19,10 @@ case class PlayerModel(
|
||||
@Schema(description = "looted pieces") loot: Option[Seq[LootModel]],
|
||||
@Schema(description = "link to best in slot", example = "https://ffxiv.ariyala.com/19V5R") link: Option[String],
|
||||
@Schema(description = "player loot priority", `type` = "number") priority: Option[Int],
|
||||
@Schema(description = "count of looted pieces which are parts of best in slot") lootCountBiS: Option[Int],
|
||||
@Schema(
|
||||
description = "count of looted pieces which are parts of best in slot",
|
||||
`type` = "number"
|
||||
) lootCountBiS: Option[Int],
|
||||
@Schema(description = "total count of looted pieces", `type` = "number") lootCountTotal: Option[Int],
|
||||
) {
|
||||
|
||||
|
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 Evgeniy Alekseev.
|
||||
*
|
||||
* This file is part of ffxivbis
|
||||
* (see https://github.com/arcan1s/ffxivbis).
|
||||
*
|
||||
* License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
package me.arcanis.ffxivbis.http.api.v1.json
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
|
||||
case class StatusModel(@Schema(description = "server version") version: Option[String])
|
@ -15,7 +15,7 @@ import me.arcanis.ffxivbis.http.{Authorization, AuthorizationProvider}
|
||||
|
||||
class RootView(override val auth: AuthorizationProvider) extends Authorization {
|
||||
|
||||
def route: Route = getBiS ~ getIndex ~ getLoot ~ getParty ~ getUsers
|
||||
def routes: Route = getBiS ~ getIndex ~ getLoot ~ getParty ~ getUsers
|
||||
|
||||
def getBiS: Route =
|
||||
path("party" / Segment / "bis") { partyId: String =>
|
||||
|
@ -24,14 +24,14 @@ class PartyService(context: ActorContext[Message], storage: ActorRef[DatabaseMes
|
||||
with StrictLogging {
|
||||
import me.arcanis.ffxivbis.utils.Implicits._
|
||||
|
||||
private val cacheTimeout: FiniteDuration =
|
||||
context.system.settings.config.getDuration("me.arcanis.ffxivbis.settings.cache-timeout")
|
||||
private val cacheTimeout =
|
||||
context.system.settings.config.getFiniteDuration("me.arcanis.ffxivbis.settings.cache-timeout")
|
||||
implicit private val executionContext: ExecutionContext = {
|
||||
val selector = DispatcherSelector.fromConfig("me.arcanis.ffxivbis.default-dispatcher")
|
||||
context.system.dispatchers.lookup(selector)
|
||||
}
|
||||
implicit private val timeout: Timeout =
|
||||
context.system.settings.config.getDuration("me.arcanis.ffxivbis.settings.request-timeout")
|
||||
context.system.settings.config.getTimeout("me.arcanis.ffxivbis.settings.request-timeout")
|
||||
implicit private val scheduler: Scheduler = context.system.scheduler
|
||||
|
||||
override def onMessage(msg: Message): Behavior[Message] = handle(Map.empty)(msg)
|
||||
|
@ -8,30 +8,32 @@
|
||||
*/
|
||||
package me.arcanis.ffxivbis.service.database.impl
|
||||
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import me.arcanis.ffxivbis.messages._
|
||||
import me.arcanis.ffxivbis.service.database.Database
|
||||
|
||||
trait DatabaseBiSHandler { this: Database =>
|
||||
|
||||
def bisHandler: DatabaseMessage.Handler = {
|
||||
case AddPieceToBis(playerId, piece, client) =>
|
||||
run(profile.insertPieceBiS(playerId, piece))(_ => client ! ())
|
||||
Behaviors.same
|
||||
def bisHandler(msg: BisDatabaseMessage): Behavior[DatabaseMessage] =
|
||||
msg match {
|
||||
case AddPieceToBis(playerId, piece, client) =>
|
||||
run(profile.insertPieceBiS(playerId, piece))(_ => client ! ())
|
||||
Behaviors.same
|
||||
|
||||
case GetBiS(partyId, maybePlayerId, client) =>
|
||||
run {
|
||||
getParty(partyId, withBiS = true, withLoot = false)
|
||||
.map(filterParty(_, maybePlayerId))
|
||||
}(client ! _)
|
||||
Behaviors.same
|
||||
case GetBiS(partyId, maybePlayerId, client) =>
|
||||
run {
|
||||
getParty(partyId, withBiS = true, withLoot = false)
|
||||
.map(filterParty(_, maybePlayerId))
|
||||
}(client ! _)
|
||||
Behaviors.same
|
||||
|
||||
case RemovePieceFromBiS(playerId, piece, client) =>
|
||||
run(profile.deletePieceBiS(playerId, piece))(_ => client ! ())
|
||||
Behaviors.same
|
||||
case RemovePieceFromBiS(playerId, piece, client) =>
|
||||
run(profile.deletePieceBiS(playerId, piece))(_ => client ! ())
|
||||
Behaviors.same
|
||||
|
||||
case RemovePiecesFromBiS(playerId, client) =>
|
||||
run(profile.deletePiecesBiS(playerId))(_ => client ! ())
|
||||
Behaviors.same
|
||||
}
|
||||
case RemovePiecesFromBiS(playerId, client) =>
|
||||
run(profile.deletePiecesBiS(playerId))(_ => client ! ())
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ package me.arcanis.ffxivbis.service.database.impl
|
||||
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext}
|
||||
import akka.actor.typed.{Behavior, DispatcherSelector}
|
||||
import com.typesafe.config.Config
|
||||
import me.arcanis.ffxivbis.messages.DatabaseMessage
|
||||
import me.arcanis.ffxivbis.messages.{BisDatabaseMessage, DatabaseMessage, LootDatabaseMessage, PartyDatabaseMessage, UserDatabaseMessage}
|
||||
import me.arcanis.ffxivbis.service.database.Database
|
||||
import me.arcanis.ffxivbis.storage.DatabaseProfile
|
||||
|
||||
@ -32,8 +32,12 @@ class DatabaseImpl(context: ActorContext[DatabaseMessage])
|
||||
override val config: Config = context.system.settings.config
|
||||
override val profile: DatabaseProfile = new DatabaseProfile(executionContext, config)
|
||||
|
||||
override def onMessage(msg: DatabaseMessage): Behavior[DatabaseMessage] = handle(msg)
|
||||
override def onMessage(msg: DatabaseMessage): Behavior[DatabaseMessage] =
|
||||
msg match {
|
||||
case msg: BisDatabaseMessage => bisHandler(msg)
|
||||
case msg: LootDatabaseMessage => lootHandler(msg)
|
||||
case msg: PartyDatabaseMessage => partyHandler(msg)
|
||||
case msg: UserDatabaseMessage => userHandler(msg)
|
||||
}
|
||||
|
||||
private def handle: DatabaseMessage.Handler =
|
||||
bisHandler.orElse(lootHandler).orElse(partyHandler).orElse(userHandler)
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
*/
|
||||
package me.arcanis.ffxivbis.service.database.impl
|
||||
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import me.arcanis.ffxivbis.messages._
|
||||
import me.arcanis.ffxivbis.models.Loot
|
||||
@ -17,28 +18,29 @@ import java.time.Instant
|
||||
|
||||
trait DatabaseLootHandler { this: Database =>
|
||||
|
||||
def lootHandler: DatabaseMessage.Handler = {
|
||||
case AddPieceTo(playerId, piece, isFreeLoot, client) =>
|
||||
val loot = Loot(-1, piece, Instant.now, isFreeLoot)
|
||||
run(profile.insertPiece(playerId, loot))(_ => client ! ())
|
||||
Behaviors.same
|
||||
def lootHandler(msg: LootDatabaseMessage): Behavior[DatabaseMessage] =
|
||||
msg match {
|
||||
case AddPieceTo(playerId, piece, isFreeLoot, client) =>
|
||||
val loot = Loot(-1, piece, Instant.now, isFreeLoot)
|
||||
run(profile.insertPiece(playerId, loot))(_ => client ! ())
|
||||
Behaviors.same
|
||||
|
||||
case GetLoot(partyId, maybePlayerId, client) =>
|
||||
run {
|
||||
getParty(partyId, withBiS = false, withLoot = true)
|
||||
.map(filterParty(_, maybePlayerId))
|
||||
}(client ! _)
|
||||
Behaviors.same
|
||||
case GetLoot(partyId, maybePlayerId, client) =>
|
||||
run {
|
||||
getParty(partyId, withBiS = false, withLoot = true)
|
||||
.map(filterParty(_, maybePlayerId))
|
||||
}(client ! _)
|
||||
Behaviors.same
|
||||
|
||||
case RemovePieceFrom(playerId, piece, isFreeLoot, client) =>
|
||||
run(profile.deletePiece(playerId, piece, isFreeLoot))(_ => client ! ())
|
||||
Behaviors.same
|
||||
case RemovePieceFrom(playerId, piece, isFreeLoot, client) =>
|
||||
run(profile.deletePiece(playerId, piece, isFreeLoot))(_ => client ! ())
|
||||
Behaviors.same
|
||||
|
||||
case SuggestLoot(partyId, piece, client) =>
|
||||
run {
|
||||
getParty(partyId, withBiS = true, withLoot = true)
|
||||
.map(_.suggestLoot(piece))
|
||||
}(client ! _)
|
||||
Behaviors.same
|
||||
}
|
||||
case SuggestLoot(partyId, piece, client) =>
|
||||
run {
|
||||
getParty(partyId, withBiS = true, withLoot = true)
|
||||
.map(_.suggestLoot(piece))
|
||||
}(client ! _)
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
*/
|
||||
package me.arcanis.ffxivbis.service.database.impl
|
||||
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import me.arcanis.ffxivbis.messages._
|
||||
import me.arcanis.ffxivbis.models.{BiS, Player}
|
||||
@ -17,50 +18,51 @@ import scala.concurrent.Future
|
||||
|
||||
trait DatabasePartyHandler { this: Database =>
|
||||
|
||||
def partyHandler: DatabaseMessage.Handler = {
|
||||
case AddPlayer(player, client) =>
|
||||
run(profile.insertPlayer(player))(_ => client ! ())
|
||||
Behaviors.same
|
||||
def partyHandler(msg: PartyDatabaseMessage): Behavior[DatabaseMessage] =
|
||||
msg match {
|
||||
case AddPlayer(player, client) =>
|
||||
run(profile.insertPlayer(player))(_ => client ! ())
|
||||
Behaviors.same
|
||||
|
||||
case GetParty(partyId, client) =>
|
||||
run(getParty(partyId, withBiS = true, withLoot = true))(client ! _)
|
||||
Behaviors.same
|
||||
case GetParty(partyId, client) =>
|
||||
run(getParty(partyId, withBiS = true, withLoot = true))(client ! _)
|
||||
Behaviors.same
|
||||
|
||||
case GetPartyDescription(partyId, client) =>
|
||||
run(profile.getPartyDescription(partyId))(client ! _)
|
||||
Behaviors.same
|
||||
case GetPartyDescription(partyId, client) =>
|
||||
run(profile.getPartyDescription(partyId))(client ! _)
|
||||
Behaviors.same
|
||||
|
||||
case GetPlayer(playerId, client) =>
|
||||
run {
|
||||
profile
|
||||
.getPlayerFull(playerId)
|
||||
.flatMap { maybePlayerData =>
|
||||
Future.traverse(maybePlayerData.toSeq) { playerData =>
|
||||
for {
|
||||
bis <- profile.getPiecesBiS(playerId)
|
||||
loot <- profile.getPieces(playerId)
|
||||
} yield Player(
|
||||
playerData.id,
|
||||
playerId.partyId,
|
||||
playerId.job,
|
||||
playerId.nick,
|
||||
BiS(bis.map(_.piece)),
|
||||
loot,
|
||||
playerData.link,
|
||||
playerData.priority
|
||||
)
|
||||
case GetPlayer(playerId, client) =>
|
||||
run {
|
||||
profile
|
||||
.getPlayerFull(playerId)
|
||||
.flatMap { maybePlayerData =>
|
||||
Future.traverse(maybePlayerData.toSeq) { playerData =>
|
||||
for {
|
||||
bis <- profile.getPiecesBiS(playerId)
|
||||
loot <- profile.getPieces(playerId)
|
||||
} yield Player(
|
||||
playerData.id,
|
||||
playerId.partyId,
|
||||
playerId.job,
|
||||
playerId.nick,
|
||||
BiS(bis.map(_.piece)),
|
||||
loot,
|
||||
playerData.link,
|
||||
playerData.priority
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.map(_.headOption)
|
||||
}(client ! _)
|
||||
Behaviors.same
|
||||
.map(_.headOption)
|
||||
}(client ! _)
|
||||
Behaviors.same
|
||||
|
||||
case RemovePlayer(playerId, client) =>
|
||||
run(profile.deletePlayer(playerId))(_ => client ! ())
|
||||
Behaviors.same
|
||||
case RemovePlayer(playerId, client) =>
|
||||
run(profile.deletePlayer(playerId))(_ => client ! ())
|
||||
Behaviors.same
|
||||
|
||||
case UpdateParty(description, client) =>
|
||||
run(profile.insertPartyDescription(description))(_ => client ! ())
|
||||
Behaviors.same
|
||||
}
|
||||
case UpdateParty(description, client) =>
|
||||
run(profile.insertPartyDescription(description))(_ => client ! ())
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
|
@ -8,32 +8,34 @@
|
||||
*/
|
||||
package me.arcanis.ffxivbis.service.database.impl
|
||||
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import me.arcanis.ffxivbis.messages._
|
||||
import me.arcanis.ffxivbis.service.database.Database
|
||||
|
||||
trait DatabaseUserHandler { this: Database =>
|
||||
|
||||
def userHandler: DatabaseMessage.Handler = {
|
||||
case AddUser(user, isHashedPassword, client) =>
|
||||
val toInsert = if (isHashedPassword) user else user.withHashedPassword
|
||||
run(profile.insertUser(toInsert))(_ => client ! ())
|
||||
Behaviors.same
|
||||
def userHandler(msg: UserDatabaseMessage): Behavior[DatabaseMessage] =
|
||||
msg match {
|
||||
case AddUser(user, isHashedPassword, client) =>
|
||||
val toInsert = if (isHashedPassword) user else user.withHashedPassword
|
||||
run(profile.insertUser(toInsert))(_ => client ! ())
|
||||
Behaviors.same
|
||||
|
||||
case DeleteUser(partyId, username, client) =>
|
||||
run(profile.deleteUser(partyId, username))(_ => client ! ())
|
||||
Behaviors.same
|
||||
case DeleteUser(partyId, username, client) =>
|
||||
run(profile.deleteUser(partyId, username))(_ => client ! ())
|
||||
Behaviors.same
|
||||
|
||||
case Exists(partyId, client) =>
|
||||
run(profile.exists(partyId))(client ! _)
|
||||
Behaviors.same
|
||||
case Exists(partyId, client) =>
|
||||
run(profile.exists(partyId))(client ! _)
|
||||
Behaviors.same
|
||||
|
||||
case GetUser(partyId, username, client) =>
|
||||
run(profile.getUser(partyId, username))(client ! _)
|
||||
Behaviors.same
|
||||
case GetUser(partyId, username, client) =>
|
||||
run(profile.getUser(partyId, username))(client ! _)
|
||||
Behaviors.same
|
||||
|
||||
case GetUsers(partyId, client) =>
|
||||
run(profile.getUsers(partyId))(client ! _)
|
||||
Behaviors.same
|
||||
}
|
||||
case GetUsers(partyId, client) =>
|
||||
run(profile.getUsers(partyId))(client ! _)
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ package me.arcanis.ffxivbis.utils
|
||||
import akka.util.Timeout
|
||||
import com.typesafe.config.Config
|
||||
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.TimeUnit
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
import scala.language.implicitConversions
|
||||
@ -19,20 +18,14 @@ import scala.util.Try
|
||||
|
||||
object Implicits {
|
||||
|
||||
implicit def getBooleanFromOptionString(maybeYes: Option[String]): Boolean = maybeYes.map(_.toLowerCase) match {
|
||||
case Some("yes" | "on") => true
|
||||
case _ => false
|
||||
}
|
||||
|
||||
implicit def getFiniteDuration(duration: Duration): FiniteDuration =
|
||||
FiniteDuration(duration.toNanos, TimeUnit.NANOSECONDS)
|
||||
|
||||
implicit def getTimeout(duration: Duration): Timeout =
|
||||
FiniteDuration(duration.toNanos, TimeUnit.NANOSECONDS)
|
||||
|
||||
implicit class ConfigExtension(config: Config) {
|
||||
|
||||
def getFiniteDuration(path: String): FiniteDuration =
|
||||
FiniteDuration(config.getDuration(path, TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS)
|
||||
|
||||
def getOptString(path: String): Option[String] =
|
||||
Try(config.getString(path)).toOption.filter(_.nonEmpty)
|
||||
|
||||
def getTimeout(path: String): Timeout = getFiniteDuration(path)
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ class BiSEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRouteT
|
||||
private val storage = testKit.spawn(Database())
|
||||
private val provider = testKit.spawn(BisProvider())
|
||||
private val party = testKit.spawn(PartyService(storage))
|
||||
private val route = new BiSEndpoint(party, provider, Fixtures.authProvider)(askTimeout, testKit.scheduler).route
|
||||
private val route = new BiSEndpoint(party, provider, Fixtures.authProvider)(askTimeout, testKit.scheduler).routes
|
||||
|
||||
override def beforeAll(): Unit = {
|
||||
super.beforeAll()
|
||||
|
@ -34,7 +34,7 @@ class LootEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRoute
|
||||
|
||||
private val storage = testKit.spawn(Database())
|
||||
private val party = testKit.spawn(PartyService(storage))
|
||||
private val route = new LootEndpoint(party, Fixtures.authProvider)(askTimeout, testKit.scheduler).route
|
||||
private val route = new LootEndpoint(party, Fixtures.authProvider)(askTimeout, testKit.scheduler).routes
|
||||
|
||||
override def beforeAll(): Unit = {
|
||||
super.beforeAll()
|
||||
|
@ -35,7 +35,7 @@ class PartyEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRout
|
||||
private val storage = testKit.spawn(Database())
|
||||
private val provider = testKit.spawn(BisProvider())
|
||||
private val party = testKit.spawn(PartyService(storage))
|
||||
private val route = new PartyEndpoint(party, provider, Fixtures.authProvider)(askTimeout, testKit.scheduler).route
|
||||
private val route = new PartyEndpoint(party, provider, Fixtures.authProvider)(askTimeout, testKit.scheduler).routes
|
||||
|
||||
override def beforeAll(): Unit = {
|
||||
super.beforeAll()
|
||||
|
@ -34,7 +34,7 @@ class PlayerEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRou
|
||||
private val storage = testKit.spawn(Database())
|
||||
private val provider = testKit.spawn(BisProvider())
|
||||
private val party = testKit.spawn(PartyService(storage))
|
||||
private val route = new PlayerEndpoint(party, provider, Fixtures.authProvider)(askTimeout, testKit.scheduler).route
|
||||
private val route = new PlayerEndpoint(party, provider, Fixtures.authProvider)(askTimeout, testKit.scheduler).routes
|
||||
|
||||
override def beforeAll(): Unit = {
|
||||
super.beforeAll()
|
||||
|
@ -0,0 +1,29 @@
|
||||
package me.arcanis.ffxivbis.http.api.v1
|
||||
|
||||
import akka.http.scaladsl.model.StatusCodes
|
||||
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||
import com.typesafe.config.Config
|
||||
import me.arcanis.ffxivbis.Settings
|
||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpecLike
|
||||
|
||||
import scala.language.postfixOps
|
||||
|
||||
class StatusEndpointTest extends AnyWordSpecLike
|
||||
with Matchers with ScalatestRouteTest with JsonSupport {
|
||||
|
||||
override val testConfig: Config = Settings.withRandomDatabase
|
||||
|
||||
private val route = new StatusEndpoint().routes
|
||||
|
||||
"api v1 status endpoint" must {
|
||||
|
||||
"return server status" in {
|
||||
Get("/status") ~> route ~> check {
|
||||
status shouldEqual StatusCodes.OK
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ class TypesEndpointTest extends AnyWordSpecLike
|
||||
|
||||
override val testConfig: Config = Settings.withRandomDatabase
|
||||
|
||||
private val route = new TypesEndpoint(testConfig).route
|
||||
private val route = new TypesEndpoint(testConfig).routes
|
||||
|
||||
"api v1 types endpoint" must {
|
||||
|
||||
|
@ -30,7 +30,7 @@ class UserEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRoute
|
||||
private var partyId = Fixtures.partyId
|
||||
private val storage = testKit.spawn(Database())
|
||||
private val party = testKit.spawn(PartyService(storage))
|
||||
private val route = new UserEndpoint(party, Fixtures.authProvider)(askTimeout, testKit.scheduler).route
|
||||
private val route = new UserEndpoint(party, Fixtures.authProvider)(askTimeout, testKit.scheduler).routes
|
||||
|
||||
override def beforeAll(): Unit = {
|
||||
super.beforeAll()
|
||||
@ -50,7 +50,7 @@ class UserEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRoute
|
||||
val uri = Uri(s"/party")
|
||||
val entity = UserModel.fromUser(Fixtures.userAdmin).copy(password = Fixtures.userPassword)
|
||||
|
||||
Put(uri, entity) ~> route ~> check {
|
||||
Post(uri, entity) ~> route ~> check {
|
||||
status shouldEqual StatusCodes.OK
|
||||
partyId = responseAs[PartyIdModel].partyId
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
version := "0.11.0"
|
||||
version := "0.13.2"
|
||||
|
Reference in New Issue
Block a user