mirror of
https://github.com/justinian/menagerie.git
synced 2025-12-11 00:54:33 -08:00
Separate wild searches out by world
This commit is contained in:
148
api_handler.go
148
api_handler.go
@@ -2,67 +2,40 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type apiHandler struct {
|
||||
lock sync.Mutex
|
||||
loader *Loader
|
||||
tamedStmt *sqlx.Stmt
|
||||
wildStmt *sqlx.Stmt
|
||||
type api struct {
|
||||
lock sync.Mutex
|
||||
loader *Loader
|
||||
tamedStmt *sqlx.Stmt
|
||||
wildStmt *sqlx.Stmt
|
||||
worldsStmt *sqlx.Stmt
|
||||
}
|
||||
|
||||
func (ah *apiHandler) getDinos(tamed bool) ([]dinoResult, error) {
|
||||
func (ah *api) getTames(w http.ResponseWriter, r *http.Request) {
|
||||
ah.loader.lock.Lock()
|
||||
defer ah.loader.lock.Unlock()
|
||||
|
||||
db := ah.loader.db
|
||||
|
||||
query := getWildDinos
|
||||
stmt := ah.wildStmt
|
||||
if tamed {
|
||||
query = getTamedDinos
|
||||
stmt = ah.tamedStmt
|
||||
}
|
||||
|
||||
var err error
|
||||
if stmt == nil {
|
||||
stmt, err = db.Preparex(query)
|
||||
if ah.tamedStmt == nil {
|
||||
ah.tamedStmt, err = db.Preparex(getTamedDinos)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Database Error: %w", err)
|
||||
}
|
||||
|
||||
if tamed {
|
||||
ah.tamedStmt = stmt
|
||||
} else {
|
||||
ah.wildStmt = stmt
|
||||
log.Printf("Error preparing statement: %s", err)
|
||||
http.Error(w, "Database Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var result []dinoResult
|
||||
err = stmt.Select(&result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Database Error: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (ah *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
log.Printf("Error parsing query: %s", err)
|
||||
http.Error(w, "Bad Request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
tamed := r.Form.Get("type") != "wild"
|
||||
result, err := ah.getDinos(tamed)
|
||||
err = ah.tamedStmt.Select(&result)
|
||||
if err != nil {
|
||||
log.Printf("Error loading dinos: %s", err)
|
||||
http.Error(w, "Database Error", http.StatusInternalServerError)
|
||||
@@ -79,21 +52,94 @@ func (ah *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func runServer(loader *Loader, addr string) {
|
||||
apiHandler := &apiHandler{loader: loader}
|
||||
func (ah *api) getWorlds(w http.ResponseWriter, r *http.Request) {
|
||||
ah.loader.lock.Lock()
|
||||
defer ah.loader.lock.Unlock()
|
||||
|
||||
sm := http.NewServeMux()
|
||||
sm.Handle("/api/dinos", apiHandler)
|
||||
db := ah.loader.db
|
||||
|
||||
fs := http.FileServer(http.Dir("static"))
|
||||
sm.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
http.Redirect(w, r, "/tamed.html", http.StatusFound)
|
||||
var err error
|
||||
if ah.worldsStmt == nil {
|
||||
ah.worldsStmt, err = db.Preparex(getWorlds)
|
||||
if err != nil {
|
||||
log.Printf("Error preparing statement: %s", err)
|
||||
http.Error(w, "Database Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
fs.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
var result []struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
err = ah.worldsStmt.Select(&result)
|
||||
if err != nil {
|
||||
log.Printf("Error loading dinos: %s", err)
|
||||
http.Error(w, "Database Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
log.Printf("Error marshalling result: %s", err)
|
||||
http.Error(w, "JSON Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func (ah *api) getWild(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
worldId := vars["world"]
|
||||
|
||||
ah.loader.lock.Lock()
|
||||
defer ah.loader.lock.Unlock()
|
||||
|
||||
db := ah.loader.db
|
||||
|
||||
var err error
|
||||
if ah.wildStmt == nil {
|
||||
ah.wildStmt, err = db.Preparex(getWildDinos)
|
||||
if err != nil {
|
||||
log.Printf("Error preparing statement: %s", err)
|
||||
http.Error(w, "Database Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var result []dinoResult
|
||||
err = ah.wildStmt.Select(&result, worldId)
|
||||
if err != nil {
|
||||
log.Printf("Error loading dinos: %s", err)
|
||||
http.Error(w, "Database Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
log.Printf("Error marshalling result: %s", err)
|
||||
http.Error(w, "JSON Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(data)
|
||||
}
|
||||
func runServer(loader *Loader, addr string) {
|
||||
api := &api{loader: loader}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/api/tames", api.getTames)
|
||||
r.HandleFunc("/api/worlds", api.getWorlds)
|
||||
r.HandleFunc("/api/worlds/{world}/dinos", api.getWild)
|
||||
|
||||
fs := http.FileServer(http.Dir("static"))
|
||||
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", fs))
|
||||
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/static/tamed.html", http.StatusFound)
|
||||
})
|
||||
|
||||
log.Printf("Listening on: %s", addr)
|
||||
log.Fatal(http.ListenAndServe(addr, loggingWrapper(sm)))
|
||||
log.Fatal(http.ListenAndServe(addr, loggingWrapper(r)))
|
||||
}
|
||||
|
||||
@@ -103,5 +103,8 @@ FROM
|
||||
LEFT JOIN worlds w ON d.world == w.id
|
||||
LEFT JOIN classes c1 ON d.class == c1.id
|
||||
WHERE
|
||||
d.is_tamed = 0
|
||||
d.is_tamed = 0 AND
|
||||
d.world = ?
|
||||
`
|
||||
|
||||
const getWorlds = `SELECT id, name FROM worlds`
|
||||
|
||||
1
go.mod
1
go.mod
@@ -4,6 +4,7 @@ go 1.16
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.5.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/jmoiron/sqlx v1.3.4
|
||||
github.com/justinian/ark v1.0.0
|
||||
github.com/mattn/go-sqlite3 v1.14.8
|
||||
|
||||
2
go.sum
2
go.sum
@@ -4,6 +4,8 @@ github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gG
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/jmoiron/sqlx v1.3.4 h1:wv+0IJZfL5z0uZoUjlpKgHkgaFSYD+r9CfrXjEXsO7w=
|
||||
github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
|
||||
github.com/justinian/ark v1.0.0 h1:cD7SHtxXhNJFE+9KX/d9/Ax2o6ILcV15zquhCErRfFc=
|
||||
|
||||
6
static/images/icon/about.txt
Normal file
6
static/images/icon/about.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
This favicon was generated using the following graphics from Twitter Twemoji:
|
||||
|
||||
- Graphics Title: 1f995.svg
|
||||
- Graphics Author: Copyright 2020 Twitter, Inc and other contributors (https://github.com/twitter/twemoji)
|
||||
- Graphics Source: https://github.com/twitter/twemoji/blob/master/assets/svg/1f995.svg
|
||||
- Graphics License: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
|
||||
BIN
static/images/icon/android-chrome-192x192.png
Normal file
BIN
static/images/icon/android-chrome-192x192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.3 KiB |
BIN
static/images/icon/android-chrome-512x512.png
Normal file
BIN
static/images/icon/android-chrome-512x512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
static/images/icon/apple-touch-icon.png
Normal file
BIN
static/images/icon/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
BIN
static/images/icon/favicon-16x16.png
Normal file
BIN
static/images/icon/favicon-16x16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 492 B |
BIN
static/images/icon/favicon-32x32.png
Normal file
BIN
static/images/icon/favicon-32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 983 B |
BIN
static/images/icon/favicon.ico
Normal file
BIN
static/images/icon/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
10
static/images/icon/site.webmanifest
Normal file
10
static/images/icon/site.webmanifest
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name":"Menagerie: ARK Creature Explorer",
|
||||
"short_name":"Menagerie",
|
||||
"icons": [
|
||||
{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},
|
||||
{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],
|
||||
"theme_color":"#ffffff",
|
||||
"background_color":"#ffffff",
|
||||
"display":"standalone"
|
||||
}
|
||||
@@ -1,3 +1,20 @@
|
||||
var populateWildLinks = function (listElement) {
|
||||
$.ajax("/api/worlds", {
|
||||
dataType: "json",
|
||||
success: function(data, statusString, xhr) {
|
||||
for (i = 0; i < data.length; i++) {
|
||||
var item = document.createElement("li");
|
||||
var link = document.createElement("a");
|
||||
var worldName = document.createTextNode(data[i]["name"]);
|
||||
link.classList.add("dropdown-item");
|
||||
link.href = "wild.html#" + data[i]["id"]
|
||||
link.appendChild(worldName);
|
||||
item.appendChild(link);
|
||||
listElement.appendChild(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var colorFunc = function (data, type, row, meta) {
|
||||
var c = null;
|
||||
@@ -78,12 +78,13 @@ var columns = [
|
||||
];
|
||||
|
||||
var tableOptions = {
|
||||
"ajax": {"url":"api/dinos?type=tamed", "dataSrc":""},
|
||||
"columns": tamedColumns,
|
||||
"ajax": {"url":"/api/tames", "dataSrc":""},
|
||||
"columns": columns,
|
||||
|
||||
"dom": "rtpil",
|
||||
"pageLength": 50,
|
||||
"scrollX": true,
|
||||
"processing": true,
|
||||
|
||||
"language": {
|
||||
"searchBuilder": {
|
||||
@@ -7,7 +7,7 @@ var showStats = function () {
|
||||
table.columns([12, 13, 14, 15, 16, 17, 18, 19]).visible(showBase);
|
||||
};
|
||||
|
||||
var wildColumns = [
|
||||
var columns = [
|
||||
{"data": "world", "title": "World", "visible": false},
|
||||
{"data": "class_name", "title": "Class"},
|
||||
{"data": "levels_wild", "title": "Level"},
|
||||
@@ -33,13 +33,14 @@ var wildColumns = [
|
||||
{"data": "speed_wild", "title": "Sp", "searchBuilderTitle": "Base Speed"}
|
||||
];
|
||||
|
||||
var wildTableOptions = {
|
||||
"ajax": {"url":"api/dinos?type=wild", "dataSrc":""},
|
||||
"columns": wildColumns,
|
||||
var tableOptions = {
|
||||
"ajax": {"dataSrc":""},
|
||||
"columns": columns,
|
||||
|
||||
"dom": "rtpil",
|
||||
"pageLength": 50,
|
||||
"scrollX": true,
|
||||
"processing": true,
|
||||
|
||||
"language": {
|
||||
"searchBuilder": {
|
||||
@@ -55,7 +56,15 @@ var wildTableOptions = {
|
||||
"searchBuilder": {
|
||||
"columns": [0, 1, 2,
|
||||
6, 7, 8, 9, 10, 11,
|
||||
12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27]
|
||||
12, 13, 14, 15, 16, 17, 18, 19]
|
||||
}
|
||||
};
|
||||
|
||||
var setTableWorld = function (table) {
|
||||
var id = window.location.hash.substr(1);
|
||||
if (id.length > 0) {
|
||||
table.rows({selected: true}).deselect();
|
||||
url = "/api/worlds/" + id + "/dinos";
|
||||
table.ajax.url(url).load();
|
||||
}
|
||||
};
|
||||
@@ -11,9 +11,9 @@
|
||||
<script type="text/javascript"
|
||||
src="https://cdn.datatables.net/v/bs5/jq-3.3.1/dt-1.10.25/sb-1.1.0/sl-1.3.3/datatables.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="colors.js"></script>
|
||||
<script type="text/javascript" src="main.js"></script>
|
||||
<script type="text/javascript" src="tamed.js"></script>
|
||||
<script type="text/javascript" src="js/colors.js"></script>
|
||||
<script type="text/javascript" src="js/main.js"></script>
|
||||
<script type="text/javascript" src="js/tamed.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready( function () {
|
||||
@@ -30,6 +30,9 @@
|
||||
$( '#showCurrent' ).on( 'click', showStats );
|
||||
$( '#showColor' ).on( 'click', showStats );
|
||||
|
||||
var wilds = document.getElementById("wildDropdown");
|
||||
populateWildLinks(wilds);
|
||||
|
||||
table.on( "select", function () {
|
||||
row = table.row({"selected":true}).data();
|
||||
if (row.name) {
|
||||
@@ -96,7 +99,11 @@
|
||||
<span class="navbar-brand">Menagerie: ARK Creature Explorer</span>
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item"><a class="nav-link active" href="#">Tamed</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="wild.html">Wild</a></li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">Wild</a>
|
||||
<ul class="dropdown-menu" id="wildDropdown">
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/static/images/icon/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/static/images/icon/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/static/images/icon/favicon-16x16.png">
|
||||
<link rel="manifest" href="/static/images/icon/site.webmanifest">
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="https://cdn.jsdelivr.net/npm/bootswatch@5/dist/yeti/bootstrap.min.css">
|
||||
@@ -11,15 +15,20 @@
|
||||
<script type="text/javascript"
|
||||
src="https://cdn.datatables.net/v/bs5/jq-3.3.1/dt-1.10.25/sb-1.1.0/sl-1.3.3/datatables.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="colors.js"></script>
|
||||
<script type="text/javascript" src="main.js"></script>
|
||||
<script type="text/javascript" src="wild.js"></script>
|
||||
<script type="text/javascript" src="js/colors.js"></script>
|
||||
<script type="text/javascript" src="js/main.js"></script>
|
||||
<script type="text/javascript" src="js/wild.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready( function () {
|
||||
table = $('#dinos').DataTable(tableOptions);
|
||||
|
||||
table.searchBuilder.container().prependTo( $('#searchBuilderDiv') );
|
||||
|
||||
setTableWorld(table);
|
||||
window.addEventListener("hashchange", function () {
|
||||
setTableWorld(table);
|
||||
}, {passive: true});
|
||||
|
||||
$( '#searchInput' ).on( 'keyup', function () {
|
||||
table.search( this.value ).draw();
|
||||
} );
|
||||
@@ -27,6 +36,9 @@
|
||||
$( '#showBase' ).on( 'click', showStats );
|
||||
$( '#showColor' ).on( 'click', showStats );
|
||||
|
||||
var wilds = document.getElementById("wildDropdown");
|
||||
populateWildLinks(wilds);
|
||||
|
||||
table.on( "select", function () {
|
||||
row = table.row({"selected":true}).data();
|
||||
|
||||
@@ -69,6 +81,14 @@
|
||||
.dinoInfo {
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
#dinos_processing {
|
||||
padding: 10px;
|
||||
border: 3px solid black;
|
||||
z-index: 1000;
|
||||
color: #fff;
|
||||
background: rgba(0.6, 0.6, 0.6, 0.25);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -78,7 +98,11 @@
|
||||
<span class="navbar-brand">Menagerie: ARK Creature Explorer</span>
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item"><a class="nav-link" href="tamed.html">Tamed</a></li>
|
||||
<li class="nav-item"><a class="nav-link active" href="#">Wild</a></li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">Wild</a>
|
||||
<ul class="dropdown-menu" id="wildDropdown">
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
Reference in New Issue
Block a user