- Added a new AGENTS.md file to document the project structure and conventions. - Updated .gitignore to include node_modules and refined cursor rules. - Introduced new backend and frontend components for improved map interactions, including context menus and controls. - Enhanced API composables for better admin and authentication functionalities. - Refactored existing components for cleaner code and improved user experience. - Updated README.md to clarify production asset serving and user setup instructions.
135 lines
2.9 KiB
Go
135 lines
2.9 KiB
Go
package app
|
|
|
|
import (
|
|
"archive/zip"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
|
|
"github.com/andyleap/hnh-map/internal/app/store"
|
|
"go.etcd.io/bbolt"
|
|
)
|
|
|
|
type mapData struct {
|
|
Grids map[string]string
|
|
Markers map[string][]Marker
|
|
}
|
|
|
|
func (a *App) export(rw http.ResponseWriter, req *http.Request) {
|
|
s := a.getSession(req)
|
|
if s == nil || !s.Auths.Has(AUTH_ADMIN) {
|
|
http.Error(rw, "Unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
rw.Header().Set("Content-Type", "application/zip")
|
|
rw.Header().Set("Content-Disposition", "attachment; filename=\"griddata.zip\"")
|
|
|
|
zw := zip.NewWriter(rw)
|
|
defer zw.Close()
|
|
|
|
err := a.db.Update(func(tx *bbolt.Tx) error {
|
|
maps := map[int]mapData{}
|
|
gridMap := map[string]int{}
|
|
|
|
grids := tx.Bucket(store.BucketGrids)
|
|
if grids == nil {
|
|
return nil
|
|
}
|
|
tiles := tx.Bucket(store.BucketTiles)
|
|
if tiles == nil {
|
|
return nil
|
|
}
|
|
|
|
err := grids.ForEach(func(k, v []byte) error {
|
|
gd := GridData{}
|
|
err := json.Unmarshal(v, &gd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
md, ok := maps[gd.Map]
|
|
if !ok {
|
|
md = mapData{
|
|
Grids: map[string]string{},
|
|
Markers: map[string][]Marker{},
|
|
}
|
|
maps[gd.Map] = md
|
|
}
|
|
md.Grids[gd.Coord.Name()] = gd.ID
|
|
gridMap[gd.ID] = gd.Map
|
|
mapb := tiles.Bucket([]byte(strconv.Itoa(gd.Map)))
|
|
if mapb == nil {
|
|
return nil
|
|
}
|
|
zoom := mapb.Bucket([]byte("0"))
|
|
if zoom == nil {
|
|
return nil
|
|
}
|
|
tdraw := zoom.Get([]byte(gd.Coord.Name()))
|
|
if tdraw == nil {
|
|
return nil
|
|
}
|
|
td := TileData{}
|
|
err = json.Unmarshal(tdraw, &td)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
w, err := zw.Create(fmt.Sprintf("%d/%s.png", gd.Map, gd.ID))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f, err := os.Open(filepath.Join(a.gridStorage, td.File))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = io.Copy(w, f)
|
|
f.Close()
|
|
return err
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = func() error {
|
|
markersb := tx.Bucket(store.BucketMarkers)
|
|
if markersb == nil {
|
|
return nil
|
|
}
|
|
markersgrid := markersb.Bucket(store.BucketMarkersGrid)
|
|
if markersgrid == nil {
|
|
return nil
|
|
}
|
|
return markersgrid.ForEach(func(k, v []byte) error {
|
|
marker := Marker{}
|
|
err := json.Unmarshal(v, &marker)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
if _, ok := maps[gridMap[marker.GridID]]; ok {
|
|
maps[gridMap[marker.GridID]].Markers[marker.GridID] = append(maps[gridMap[marker.GridID]].Markers[marker.GridID], marker)
|
|
}
|
|
return nil
|
|
})
|
|
}()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for mapid, mapdata := range maps {
|
|
w, err := zw.Create(fmt.Sprintf("%d/grids.json", mapid))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
json.NewEncoder(w).Encode(mapdata)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
}
|