Uploading is now drag & drop (or click to browse) instead of a form; folders can be created any time, not just during upload; files can be dragged onto a folder to move them, or dropped elsewhere in the tree to move back to the top level; files and folders can be deleted. Both drag sources (an OS file drag and dragging an existing tree entry) land on the same drop targets, told apart by a custom drag payload type versus the browser's own "Files" type. Also sort folders before files in the tree (previously interleaved alphabetically).
114 lines
2.8 KiB
Ruby
114 lines
2.8 KiB
Ruby
require "sinatra/base"
|
|
require "sinatra/json"
|
|
require_relative "lib/maptool/gpx"
|
|
require_relative "lib/maptool/gpx_store"
|
|
|
|
module Maptool
|
|
class App < Sinatra::Base
|
|
set :public_folder, File.join(__dir__, "public")
|
|
set :views, File.join(__dir__, "views")
|
|
|
|
OFFLINE_PMTILES_PATH = File.join(__dir__, "data", "offline.pmtiles")
|
|
GPX_STORE = GpxStore.new(File.join(__dir__, "data", "gpx"))
|
|
|
|
get "/" do
|
|
erb :index
|
|
end
|
|
|
|
get "/api/health" do
|
|
json status: "ok"
|
|
end
|
|
|
|
get "/api/offline-tiles" do
|
|
json available: File.exist?(OFFLINE_PMTILES_PATH)
|
|
end
|
|
|
|
# Served as a plain range-request-capable file, not decoded server-side:
|
|
# pmtiles.js reads the archive's directory and fetches only the byte
|
|
# ranges it needs for the tiles currently on screen.
|
|
get "/tiles/offline.pmtiles" do
|
|
halt 404 unless File.exist?(OFFLINE_PMTILES_PATH)
|
|
|
|
send_file OFFLINE_PMTILES_PATH, type: "application/octet-stream"
|
|
end
|
|
|
|
get "/api/gpx/tree" do
|
|
json GPX_STORE.tree
|
|
end
|
|
|
|
post "/api/gpx" do
|
|
file = params[:file]
|
|
halt 400, json(error: "file is required") unless file && file[:tempfile]
|
|
|
|
begin
|
|
path = GPX_STORE.save(folder: params[:folder].to_s, filename: file[:filename], io: file[:tempfile])
|
|
rescue GpxStore::InvalidPath
|
|
halt 400, json(error: "invalid folder")
|
|
end
|
|
|
|
json path: path, tree: GPX_STORE.tree
|
|
end
|
|
|
|
get "/api/gpx/file/*" do
|
|
begin
|
|
path = GPX_STORE.resolve_file(params[:splat].first)
|
|
rescue GpxStore::InvalidPath
|
|
halt 400
|
|
end
|
|
halt 404 unless File.file?(path)
|
|
|
|
begin
|
|
json Gpx.to_geojson(path)
|
|
rescue Gpx::ParseError
|
|
halt 422, json(error: "could not parse GPX file")
|
|
end
|
|
end
|
|
|
|
post "/api/gpx/folder" do
|
|
begin
|
|
GPX_STORE.create_folder(params[:path].to_s)
|
|
rescue GpxStore::InvalidPath
|
|
halt 400, json(error: "invalid path")
|
|
end
|
|
|
|
json tree: GPX_STORE.tree
|
|
end
|
|
|
|
delete "/api/gpx/file/*" do
|
|
begin
|
|
GPX_STORE.delete_file(params[:splat].first)
|
|
rescue GpxStore::InvalidPath
|
|
halt 400
|
|
rescue Errno::ENOENT
|
|
halt 404
|
|
end
|
|
|
|
json tree: GPX_STORE.tree
|
|
end
|
|
|
|
delete "/api/gpx/folder/*" do
|
|
begin
|
|
GPX_STORE.delete_folder(params[:splat].first)
|
|
rescue GpxStore::InvalidPath
|
|
halt 400
|
|
rescue Errno::ENOENT
|
|
halt 404
|
|
end
|
|
|
|
json tree: GPX_STORE.tree
|
|
end
|
|
|
|
post "/api/gpx/move" do
|
|
begin
|
|
path = GPX_STORE.move(from: params[:from].to_s, to_folder: params[:to_folder].to_s)
|
|
rescue GpxStore::InvalidPath
|
|
halt 400, json(error: "invalid path")
|
|
rescue Errno::ENOENT
|
|
halt 404
|
|
end
|
|
|
|
json path: path, tree: GPX_STORE.tree
|
|
end
|
|
end
|
|
end
|