Move all sync pages to authenticatedHandler
Still need to fix many templates
This commit is contained in:
parent
85d2d0b95b
commit
dac52a879a
23 changed files with 409 additions and 476 deletions
|
|
@ -51,7 +51,7 @@ func (ts templateSystemDisk) renderOrError(w http.ResponseWriter, template_name
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
err = t.Execute(buf, context)
|
err = t.Execute(buf, context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to render template")
|
log.Error().Err(err).Str("name", template_name).Msg("Failed to render template")
|
||||||
RespondError(w, "Failed to render template", err, http.StatusInternalServerError)
|
RespondError(w, "Failed to render template", err, http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
<div
|
<div
|
||||||
class="col-md-6 text-md-end d-flex align-items-center justify-content-md-end"
|
class="col-md-6 text-md-end d-flex align-items-center justify-content-md-end"
|
||||||
>
|
>
|
||||||
{{ if .IsSyncOngoing }}
|
{{ if .C.IsSyncOngoing }}
|
||||||
<p class="last-refreshed mb-0">
|
<p class="last-refreshed mb-0">
|
||||||
<i class="fas fa-sync-alt me-2 syncing"></i>Syncing now...
|
<i class="fas fa-sync-alt me-2 syncing"></i>Syncing now...
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
<p class="last-refreshed mb-0">
|
<p class="last-refreshed mb-0">
|
||||||
<i class="fas fa-sync-alt me-2"></i>Last updated:
|
<i class="fas fa-sync-alt me-2"></i>Last updated:
|
||||||
<span id="last-refreshed-time"
|
<span id="last-refreshed-time"
|
||||||
>{{ .LastSync | timeRelativePtr }}</span
|
>{{ .C.LastSync | timeRelativePtr }}</span
|
||||||
>
|
>
|
||||||
<button class="btn btn-sm btn-outline-primary ms-3">
|
<button class="btn btn-sm btn-outline-primary ms-3">
|
||||||
Refresh Data
|
Refresh Data
|
||||||
|
|
@ -57,7 +57,7 @@
|
||||||
<i class="fas fa-clock"></i>
|
<i class="fas fa-clock"></i>
|
||||||
</div>
|
</div>
|
||||||
<h5 class="card-title">Last Data Refresh</h5>
|
<h5 class="card-title">Last Data Refresh</h5>
|
||||||
<p class="metric-value">{{ .LastSync | timeRelativePtr }}</p>
|
<p class="metric-value">{{ .C.LastSync | timeRelativePtr }}</p>
|
||||||
<!-- <p class="card-text text-muted">Last sync: 12:45 PM</p> -->
|
<!-- <p class="card-text text-muted">Last sync: 12:45 PM</p> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -71,13 +71,13 @@
|
||||||
<i class="fas fa-ticket-alt"></i>
|
<i class="fas fa-ticket-alt"></i>
|
||||||
</div>
|
</div>
|
||||||
<h5 class="card-title">Service Requests</h5>
|
<h5 class="card-title">Service Requests</h5>
|
||||||
{{ if .IsSyncOngoing }}
|
{{ if .C.IsSyncOngoing }}
|
||||||
<p class="metric-value">
|
<p class="metric-value">
|
||||||
{{ .CountServiceRequests | bigNumber }}...?
|
{{ .C.CountServiceRequests | bigNumber }}...?
|
||||||
</p>
|
</p>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<p class="metric-value">
|
<p class="metric-value">
|
||||||
{{ .CountServiceRequests | bigNumber }}
|
{{ .C.CountServiceRequests | bigNumber }}
|
||||||
</p>
|
</p>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<!--<p class="card-text text-muted">
|
<!--<p class="card-text text-muted">
|
||||||
|
|
@ -97,13 +97,13 @@
|
||||||
<i class="fas fa-bug"></i>
|
<i class="fas fa-bug"></i>
|
||||||
</div>
|
</div>
|
||||||
<h5 class="card-title">Mosquito Sources</h5>
|
<h5 class="card-title">Mosquito Sources</h5>
|
||||||
{{ if .IsSyncOngoing }}
|
{{ if .C.IsSyncOngoing }}
|
||||||
<p class="metric-value">
|
<p class="metric-value">
|
||||||
{{ .CountMosquitoSources | bigNumber }}..?
|
{{ .C.CountMosquitoSources | bigNumber }}..?
|
||||||
</p>
|
</p>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<p class="metric-value">
|
<p class="metric-value">
|
||||||
{{ .CountMosquitoSources | bigNumber }}
|
{{ .C.CountMosquitoSources | bigNumber }}
|
||||||
</p>
|
</p>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<!-- <p class="card-text text-muted">
|
<!-- <p class="card-text text-muted">
|
||||||
|
|
@ -123,10 +123,10 @@
|
||||||
<i class="fas fa-clipboard-check"></i>
|
<i class="fas fa-clipboard-check"></i>
|
||||||
</div>
|
</div>
|
||||||
<h5 class="card-title">Traps</h5>
|
<h5 class="card-title">Traps</h5>
|
||||||
{{ if .IsSyncOngoing }}
|
{{ if .C.IsSyncOngoing }}
|
||||||
<p class="metric-value">{{ .CountTraps | bigNumber }}...?</p>
|
<p class="metric-value">{{ .C.CountTraps | bigNumber }}...?</p>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<p class="metric-value">{{ .CountTraps | bigNumber }}</p>
|
<p class="metric-value">{{ .C.CountTraps | bigNumber }}</p>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<!-- <p class="card-text text-muted">
|
<!-- <p class="card-text text-muted">
|
||||||
<span class="text-success">
|
<span class="text-success">
|
||||||
|
|
@ -142,6 +142,8 @@
|
||||||
<h3 class="section-title">Mosquito Activity Heatmap</h3>
|
<h3 class="section-title">Mosquito Activity Heatmap</h3>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
{{ if not (eq .Organization nil) }}
|
||||||
|
{{ if not (eq .Organization.ServiceAreaCentroidGeojson nil) }}
|
||||||
<map-aggregate
|
<map-aggregate
|
||||||
centroid="{{ if .Organization.ServiceAreaCentroidGeojson.IsValue }}
|
centroid="{{ if .Organization.ServiceAreaCentroidGeojson.IsValue }}
|
||||||
{{ .Organization.ServiceAreaCentroidGeojson.MustGet|json }}
|
{{ .Organization.ServiceAreaCentroidGeojson.MustGet|json }}
|
||||||
|
|
@ -153,6 +155,8 @@
|
||||||
xmax="{{ .Organization.ServiceAreaXmax.GetOr 0 }}"
|
xmax="{{ .Organization.ServiceAreaXmax.GetOr 0 }}"
|
||||||
ymax="{{ .Organization.ServiceAreaYmax.GetOr 0 }}"
|
ymax="{{ .Organization.ServiceAreaYmax.GetOr 0 }}"
|
||||||
/>
|
/>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -174,7 +178,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{ range $i, $sr := .RecentRequests }}
|
{{ range $i, $sr := .C.RecentRequests }}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ $sr.Date | timeRelativePtr }}</td>
|
<td>{{ $sr.Date | timeRelativePtr }}</td>
|
||||||
<td>Service Request</td>
|
<td>Service Request</td>
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,9 @@
|
||||||
setTooltipsForSidebar();
|
setTooltipsForSidebar();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
{{ if not .Config.IsProductionEnvironment }}
|
||||||
|
<script src="/.flogo/injector.js"></script>
|
||||||
|
{{ end }}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{{ template "sync/component/icons.html" }}
|
{{ template "sync/component/icons.html" }}
|
||||||
|
|
@ -60,6 +63,7 @@
|
||||||
<div id="content">
|
<div id="content">
|
||||||
{{ template "content" . }}
|
{{ template "content" . }}
|
||||||
</div>
|
</div>
|
||||||
|
<div id="flogo"></div>
|
||||||
<script src="/static/vendor/js/bootstrap.bundle.min.js"></script>
|
<script src="/static/vendor/js/bootstrap.bundle.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
document.getElementById("sidebarToggle").addEventListener("click", () => {
|
document.getElementById("sidebarToggle").addEventListener("click", () => {
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,13 @@
|
||||||
<!-- favicon -->
|
<!-- favicon -->
|
||||||
<link rel="icon" href="/static/favicon-sync.ico" type="image/x-icon" />
|
<link rel="icon" href="/static/favicon-sync.ico" type="image/x-icon" />
|
||||||
{{ block "extraheader" . }}{{ end }}
|
{{ block "extraheader" . }}{{ end }}
|
||||||
|
{{ if not .Config.IsProductionEnvironment }}
|
||||||
|
<script src="/.flogo/injector.js"></script>
|
||||||
|
{{ end }}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{{ template "content" . }}
|
{{ template "content" . }}
|
||||||
|
<div id="flogo"></div>
|
||||||
<script src="/static/vendor/js/bootstrap.bundle.min.js"></script>
|
<script src="/static/vendor/js/bootstrap.bundle.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
7
main.go
7
main.go
|
|
@ -75,7 +75,12 @@ func main() {
|
||||||
defer sentryWriter.Close()
|
defer sentryWriter.Close()
|
||||||
|
|
||||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||||
log.Logger = log.Output(zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr}, sentryWriter)).Level(zerolog.InfoLevel)
|
log.Logger = log.Output(zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr}, sentryWriter))
|
||||||
|
if os.Getenv("VERBOSE") != "" {
|
||||||
|
log.Logger = log.Logger.Level(zerolog.DebugLevel)
|
||||||
|
} else {
|
||||||
|
log.Logger = log.Logger.Level(zerolog.InfoLevel)
|
||||||
|
}
|
||||||
|
|
||||||
err = db.InitializeDatabase(context.TODO(), config.PGDSN)
|
err = db.InitializeDatabase(context.TODO(), config.PGDSN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@ package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type contentAdminDash struct{}
|
type contentAdminDash struct{}
|
||||||
|
|
||||||
func getAdminDash(ctx context.Context, user *models.User) (string, interface{}, *errorWithStatus) {
|
func getAdminDash(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentAdminDash], *errorWithStatus) {
|
||||||
content := contentAdminDash{}
|
content := contentAdminDash{}
|
||||||
return "sync/admin-dash.html", content, nil
|
return newResponse("sync/admin-dash.html", content), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
52
sync/cell.go
52
sync/cell.go
|
|
@ -1,12 +1,11 @@
|
||||||
package sync
|
package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/h3utils"
|
"github.com/Gleipnir-Technology/nidus-sync/h3utils"
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/html"
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/uber/h3-go/v4"
|
"github.com/uber/h3-go/v4"
|
||||||
)
|
)
|
||||||
|
|
@ -18,70 +17,48 @@ type contentCell struct {
|
||||||
MapData ComponentMap
|
MapData ComponentMap
|
||||||
Traps []TrapSummary
|
Traps []TrapSummary
|
||||||
Treatments []Treatment
|
Treatments []Treatment
|
||||||
URL ContentURL
|
|
||||||
User User
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCellDetails(w http.ResponseWriter, r *http.Request, user *models.User) {
|
func getCellDetails(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentCell], *errorWithStatus) {
|
||||||
cell_str := chi.URLParam(r, "cell")
|
cell_str := chi.URLParam(r, "cell")
|
||||||
if cell_str == "" {
|
if cell_str == "" {
|
||||||
respondError(w, "There should always be a cell", nil, http.StatusBadRequest)
|
return nil, newErrorStatus(http.StatusBadRequest, "There should always be a cell")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
c, err := HexToInt64(cell_str)
|
c, err := HexToInt64(cell_str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "Cannot convert provided cell to uint64", err, http.StatusBadRequest)
|
return nil, newErrorStatus(http.StatusBadRequest, "Cannot convert provided cell to uint64")
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx := r.Context()
|
|
||||||
org, err := user.Organization().One(ctx, db.PGInstance.BobDB)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get org", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
userContent, err := contentForUser(ctx, user)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get user", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
center, err := h3.Cell(c).LatLng()
|
center, err := h3.Cell(c).LatLng()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "Failed to get center", err, http.StatusInternalServerError)
|
return nil, newError("Failed to get center: %w", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
boundary, err := h3.Cell(c).Boundary()
|
boundary, err := h3.Cell(c).Boundary()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "Failed to get boundary", err, http.StatusInternalServerError)
|
return nil, newError("Failed to get boundary: %w", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
inspections, err := inspectionsByCell(ctx, org, h3.Cell(c))
|
inspections, err := inspectionsByCell(ctx, org, h3.Cell(c))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "Failed to get inspections by cell", err, http.StatusInternalServerError)
|
return nil, newError("Failed to get inspections by cell: %w", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
geojson, err := h3utils.H3ToGeoJSON([]h3.Cell{h3.Cell(c)})
|
geojson, err := h3utils.H3ToGeoJSON([]h3.Cell{h3.Cell(c)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "Failed to get boundaries", err, http.StatusInternalServerError)
|
return nil, newError("Failed to get boundaries: %w", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
resolution := h3.Cell(c).Resolution()
|
resolution := h3.Cell(c).Resolution()
|
||||||
sources, err := breedingSourcesByCell(ctx, org, h3.Cell(c))
|
sources, err := breedingSourcesByCell(ctx, org, h3.Cell(c))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "Failed to get sources", err, http.StatusInternalServerError)
|
return nil, newError("Failed to get sources: %w", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
traps, err := trapsByCell(ctx, org, h3.Cell(c))
|
traps, err := trapsByCell(ctx, org, h3.Cell(c))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "Failed to get traps", err, http.StatusInternalServerError)
|
return nil, newError("Failed to get traps: %w", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
treatments, err := treatmentsByCell(ctx, org, h3.Cell(c))
|
treatments, err := treatmentsByCell(ctx, org, h3.Cell(c))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "Failed to get treatments", err, http.StatusInternalServerError)
|
return nil, newError("Failed to get treatments: %w", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
data := contentCell{
|
return newResponse("sync/cell.html", contentCell{
|
||||||
BreedingSources: sources,
|
BreedingSources: sources,
|
||||||
CellBoundary: boundary,
|
CellBoundary: boundary,
|
||||||
Inspections: inspections,
|
Inspections: inspections,
|
||||||
|
|
@ -95,8 +72,5 @@ func getCellDetails(w http.ResponseWriter, r *http.Request, user *models.User) {
|
||||||
},
|
},
|
||||||
Traps: traps,
|
Traps: traps,
|
||||||
Treatments: treatments,
|
Treatments: treatments,
|
||||||
URL: newContentURL(),
|
}), nil
|
||||||
User: userContent,
|
|
||||||
}
|
|
||||||
html.RenderOrError(w, "sync/cell.html", &data)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
sync/config.go
Normal file
15
sync/config.go
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
package sync
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Gleipnir-Technology/nidus-sync/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type contentConfig struct {
|
||||||
|
IsProductionEnvironment bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContentConfig() contentConfig {
|
||||||
|
return contentConfig{
|
||||||
|
IsProductionEnvironment: config.IsProductionEnvironment(),
|
||||||
|
}
|
||||||
|
}
|
||||||
264
sync/dash.go
264
sync/dash.go
|
|
@ -21,10 +21,7 @@ import (
|
||||||
// Authenticated pages
|
// Authenticated pages
|
||||||
var ()
|
var ()
|
||||||
|
|
||||||
type Config struct {
|
type contentSource struct {
|
||||||
}
|
|
||||||
|
|
||||||
type ContentSource struct {
|
|
||||||
Inspections []Inspection
|
Inspections []Inspection
|
||||||
MapData ComponentMap
|
MapData ComponentMap
|
||||||
Source *BreedingSourceDetail
|
Source *BreedingSourceDetail
|
||||||
|
|
@ -34,12 +31,12 @@ type ContentSource struct {
|
||||||
TreatmentModels []TreatmentModel
|
TreatmentModels []TreatmentModel
|
||||||
User User
|
User User
|
||||||
}
|
}
|
||||||
type ContentTrap struct {
|
type contentTrap struct {
|
||||||
MapData ComponentMap
|
MapData ComponentMap
|
||||||
Trap Trap
|
Trap Trap
|
||||||
User User
|
User User
|
||||||
}
|
}
|
||||||
type ContentDashboard struct {
|
type contentDashboard struct {
|
||||||
CountTraps int
|
CountTraps int
|
||||||
CountMosquitoSources int
|
CountMosquitoSources int
|
||||||
CountServiceRequests int
|
CountServiceRequests int
|
||||||
|
|
@ -47,13 +44,10 @@ type ContentDashboard struct {
|
||||||
IsSyncOngoing bool
|
IsSyncOngoing bool
|
||||||
LastSync *time.Time
|
LastSync *time.Time
|
||||||
MapData ComponentMap
|
MapData ComponentMap
|
||||||
Organization *models.Organization
|
|
||||||
RecentRequests []ServiceRequestSummary
|
RecentRequests []ServiceRequestSummary
|
||||||
URL ContentURL
|
|
||||||
User User
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContentLayoutTest struct {
|
type contentLayoutTest struct {
|
||||||
User User
|
User User
|
||||||
}
|
}
|
||||||
type ContentDistrict struct {
|
type ContentDistrict struct {
|
||||||
|
|
@ -67,16 +61,12 @@ func getDistrict(w http.ResponseWriter, r *http.Request) {
|
||||||
html.RenderOrError(w, "sync/district.html", &context)
|
html.RenderOrError(w, "sync/district.html", &context)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLayoutTest(w http.ResponseWriter, r *http.Request, u *models.User) {
|
func getLayoutTest(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentLayoutTest], *errorWithStatus) {
|
||||||
userContent, err := contentForUser(r.Context(), u)
|
return newResponse("sync/layout-test.html", contentLayoutTest{}), nil
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get user", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
html.RenderOrError(w, "sync/layout-test.html", &ContentLayoutTest{User: userContent})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRoot(w http.ResponseWriter, r *http.Request) {
|
func getRoot(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
user, err := auth.GetAuthenticatedUser(r)
|
user, err := auth.GetAuthenticatedUser(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// No credentials or user not found: go to login
|
// No credentials or user not found: go to login
|
||||||
|
|
@ -93,77 +83,135 @@ func getRoot(w http.ResponseWriter, r *http.Request) {
|
||||||
signin(w, errorCode, "/")
|
signin(w, errorCode, "/")
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
has, err := background.HasFieldseekerConnection(r.Context(), user)
|
has, err := background.HasFieldseekerConnection(ctx, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "Failed to check for ArcGIS connection", err, http.StatusInternalServerError)
|
respondError(w, "Failed to check for ArcGIS connection", err, http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if has {
|
if has {
|
||||||
dashboard(r.Context(), w, user)
|
org, err := user.Organization().One(ctx, db.PGInstance.BobDB)
|
||||||
|
if err != nil {
|
||||||
|
respondError(w, "Failed to get organization", err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dashboard(ctx, w, org, user)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
oauthPrompt(w, r, user)
|
oauthPrompt(w, r, user)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to render root", err, http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSource(w http.ResponseWriter, r *http.Request, u *models.User) {
|
func getSource(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentSource], *errorWithStatus) {
|
||||||
globalid_s := chi.URLParam(r, "globalid")
|
globalid_s := chi.URLParam(r, "globalid")
|
||||||
if globalid_s == "" {
|
if globalid_s == "" {
|
||||||
respondError(w, "No globalid provided", nil, http.StatusBadRequest)
|
return nil, newError("No globalid provided: %w", nil)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
globalid, err := uuid.Parse(globalid_s)
|
globalid, err := uuid.Parse(globalid_s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "globalid is not a UUID", nil, http.StatusBadRequest)
|
return nil, newError("globalid is not a UUID: %w", nil)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
source(w, r, u, globalid)
|
userContent, err := contentForUser(r.Context(), user)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Failed to get user content: %w", err)
|
||||||
|
}
|
||||||
|
s, err := sourceByGlobalId(r.Context(), org, globalid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Failed to get source: %w", err)
|
||||||
|
}
|
||||||
|
inspections, err := inspectionsBySource(r.Context(), org, globalid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Failed to get inspections: %w", err)
|
||||||
|
}
|
||||||
|
traps, err := trapsBySource(r.Context(), org, globalid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Failed to get traps: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStadia(w http.ResponseWriter, r *http.Request, u *models.User) {
|
treatments, err := treatmentsBySource(r.Context(), org, globalid)
|
||||||
userContent, err := contentForUser(r.Context(), u)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "Failed to get user content", err, http.StatusInternalServerError)
|
return nil, newError("Failed to get treatments: %w", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
data := ContentDashboard{
|
treatment_models := modelTreatment(treatments)
|
||||||
|
latlng, err := s.H3Cell.LatLng()
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Failed to get latlng: %w", err)
|
||||||
|
}
|
||||||
|
data := contentSource{
|
||||||
|
Inspections: inspections,
|
||||||
|
MapData: ComponentMap{
|
||||||
|
Center: latlng,
|
||||||
|
//GeoJSON:
|
||||||
|
MapboxToken: config.MapboxToken,
|
||||||
|
Markers: []MapMarker{
|
||||||
|
MapMarker{
|
||||||
|
LatLng: latlng,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Zoom: 13,
|
||||||
|
},
|
||||||
|
Source: s,
|
||||||
|
Traps: traps,
|
||||||
|
Treatments: treatments,
|
||||||
|
TreatmentModels: treatment_models,
|
||||||
|
User: userContent,
|
||||||
|
}
|
||||||
|
|
||||||
|
return newResponse("sync/source.html", data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStadia(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentDashboard], *errorWithStatus) {
|
||||||
|
data := contentDashboard{
|
||||||
MapData: ComponentMap{
|
MapData: ComponentMap{
|
||||||
MapboxToken: config.MapboxToken,
|
MapboxToken: config.MapboxToken,
|
||||||
},
|
},
|
||||||
URL: newContentURL(),
|
|
||||||
User: userContent,
|
|
||||||
}
|
}
|
||||||
html.RenderOrError(w, "sync/stadia.html", data)
|
return newResponse("sync/stadia.html", data), nil
|
||||||
|
|
||||||
}
|
}
|
||||||
func getTemplateTest(w http.ResponseWriter, r *http.Request) {
|
func getTemplateTest(w http.ResponseWriter, r *http.Request) {
|
||||||
html.RenderOrError(w, "sync/template-test.html", nil)
|
html.RenderOrError(w, "sync/template-test.html", nil)
|
||||||
}
|
}
|
||||||
func getTrap(w http.ResponseWriter, r *http.Request, u *models.User) {
|
func getTrap(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentTrap], *errorWithStatus) {
|
||||||
globalid_s := chi.URLParam(r, "globalid")
|
globalid_s := chi.URLParam(r, "globalid")
|
||||||
if globalid_s == "" {
|
if globalid_s == "" {
|
||||||
respondError(w, "No globalid provided", nil, http.StatusBadRequest)
|
return nil, newError("No globalid provided: %w", nil)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
globalid, err := uuid.Parse(globalid_s)
|
globalid, err := uuid.Parse(globalid_s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "globalid is not a UUID", nil, http.StatusBadRequest)
|
return nil, newError("globalid is not a UUID: %w", nil)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
trap(w, r, u, globalid)
|
userContent, err := contentForUser(r.Context(), user)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Failed to get user content: %w", err)
|
||||||
|
}
|
||||||
|
t, err := trapByGlobalId(r.Context(), org, globalid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Failed to get trap: %w", err)
|
||||||
|
}
|
||||||
|
latlng, err := t.H3Cell.LatLng()
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Failed to get latlng: %w", err)
|
||||||
|
}
|
||||||
|
data := contentTrap{
|
||||||
|
MapData: ComponentMap{
|
||||||
|
Center: latlng,
|
||||||
|
//GeoJSON:
|
||||||
|
MapboxToken: config.MapboxToken,
|
||||||
|
Markers: []MapMarker{
|
||||||
|
MapMarker{
|
||||||
|
LatLng: latlng,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Zoom: 13,
|
||||||
|
},
|
||||||
|
Trap: t,
|
||||||
|
User: userContent,
|
||||||
|
}
|
||||||
|
return newResponse("sync/trap.html", data), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func dashboard(ctx context.Context, w http.ResponseWriter, user *models.User) {
|
func dashboard(ctx context.Context, w http.ResponseWriter, org *models.Organization, user *models.User) {
|
||||||
org, err := user.Organization().One(ctx, db.PGInstance.BobDB)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get org", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var lastSync *time.Time
|
var lastSync *time.Time
|
||||||
sync, err := org.FieldseekerSyncs(sm.OrderBy("created").Desc()).One(ctx, db.PGInstance.BobDB)
|
sync, err := org.FieldseekerSyncs(sm.OrderBy("created").Desc()).One(ctx, db.PGInstance.BobDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -204,12 +252,7 @@ func dashboard(ctx context.Context, w http.ResponseWriter, user *models.User) {
|
||||||
Status: "Completed",
|
Status: "Completed",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
userContent, err := contentForUser(ctx, user)
|
content := contentDashboard{
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get user context", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data := ContentDashboard{
|
|
||||||
CountTraps: int(trapCount),
|
CountTraps: int(trapCount),
|
||||||
CountMosquitoSources: int(sourceCount),
|
CountMosquitoSources: int(sourceCount),
|
||||||
CountServiceRequests: int(serviceCount),
|
CountServiceRequests: int(serviceCount),
|
||||||
|
|
@ -218,111 +261,22 @@ func dashboard(ctx context.Context, w http.ResponseWriter, user *models.User) {
|
||||||
MapData: ComponentMap{
|
MapData: ComponentMap{
|
||||||
MapboxToken: config.MapboxToken,
|
MapboxToken: config.MapboxToken,
|
||||||
},
|
},
|
||||||
Organization: org,
|
|
||||||
RecentRequests: requests,
|
RecentRequests: requests,
|
||||||
|
}
|
||||||
|
userContent, err := contentForUser(ctx, user)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
html.RenderOrError(w, "sync/dashboard.html", contentAuthenticated[contentDashboard]{
|
||||||
|
C: content,
|
||||||
URL: newContentURL(),
|
URL: newContentURL(),
|
||||||
User: userContent,
|
User: userContent,
|
||||||
}
|
})
|
||||||
html.RenderOrError(w, "sync/dashboard.html", data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func source(w http.ResponseWriter, r *http.Request, user *models.User, id uuid.UUID) {
|
func source(w http.ResponseWriter, r *http.Request, org *models.Organization, user *models.User, id uuid.UUID) {
|
||||||
org, err := user.Organization().One(r.Context(), db.PGInstance.BobDB)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get org", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
userContent, err := contentForUser(r.Context(), user)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get user content", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s, err := sourceByGlobalId(r.Context(), org, id)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get source", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
inspections, err := inspectionsBySource(r.Context(), org, id)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get inspections", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
traps, err := trapsBySource(r.Context(), org, id)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get traps", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
treatments, err := treatmentsBySource(r.Context(), org, id)
|
func trap(w http.ResponseWriter, r *http.Request, org *models.Organization, user *models.User, id uuid.UUID) {
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get treatments", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
treatment_models := modelTreatment(treatments)
|
|
||||||
latlng, err := s.H3Cell.LatLng()
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get latlng", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data := ContentSource{
|
|
||||||
Inspections: inspections,
|
|
||||||
MapData: ComponentMap{
|
|
||||||
Center: latlng,
|
|
||||||
//GeoJSON:
|
|
||||||
MapboxToken: config.MapboxToken,
|
|
||||||
Markers: []MapMarker{
|
|
||||||
MapMarker{
|
|
||||||
LatLng: latlng,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Zoom: 13,
|
|
||||||
},
|
|
||||||
Source: s,
|
|
||||||
Traps: traps,
|
|
||||||
Treatments: treatments,
|
|
||||||
TreatmentModels: treatment_models,
|
|
||||||
User: userContent,
|
|
||||||
}
|
|
||||||
|
|
||||||
html.RenderOrError(w, "sync/source.html", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func trap(w http.ResponseWriter, r *http.Request, user *models.User, id uuid.UUID) {
|
|
||||||
org, err := user.Organization().One(r.Context(), db.PGInstance.BobDB)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get org", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
userContent, err := contentForUser(r.Context(), user)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get user content", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t, err := trapByGlobalId(r.Context(), org, id)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get trap", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
latlng, err := t.H3Cell.LatLng()
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get latlng", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data := ContentTrap{
|
|
||||||
MapData: ComponentMap{
|
|
||||||
Center: latlng,
|
|
||||||
//GeoJSON:
|
|
||||||
MapboxToken: config.MapboxToken,
|
|
||||||
Markers: []MapMarker{
|
|
||||||
MapMarker{
|
|
||||||
LatLng: latlng,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Zoom: 13,
|
|
||||||
},
|
|
||||||
Trap: t,
|
|
||||||
User: userContent,
|
|
||||||
}
|
|
||||||
|
|
||||||
html.RenderOrError(w, "sync/trap.html", data)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@ package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type contentDownloadPlaceholder struct{}
|
type contentDownloadPlaceholder struct{}
|
||||||
|
|
||||||
func getDownloadList(ctx context.Context, user *models.User) (string, interface{}, *errorWithStatus) {
|
func getDownloadList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentDownloadPlaceholder], *errorWithStatus) {
|
||||||
content := contentSettingPlaceholder{}
|
content := contentDownloadPlaceholder{}
|
||||||
return "sync/download-list.html", content, nil
|
return newResponse("sync/download-list.html", content), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@ package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type contentMessageList struct{}
|
type contentMessageList struct{}
|
||||||
|
|
||||||
func getMessageList(ctx context.Context, user *models.User) (string, interface{}, *errorWithStatus) {
|
func getMessageList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentMessageList], *errorWithStatus) {
|
||||||
content := contentMessageList{}
|
content := contentMessageList{}
|
||||||
return "sync/message-list.html", content, nil
|
return newResponse("sync/message-list.html", content), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
package sync
|
package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
//"context"
|
"context"
|
||||||
//"fmt"
|
//"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
//"strings"
|
//"strings"
|
||||||
//"time"
|
//"time"
|
||||||
|
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/html"
|
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/notification"
|
"github.com/Gleipnir-Technology/nidus-sync/notification"
|
||||||
//"github.com/Gleipnir-Technology/bob"
|
//"github.com/Gleipnir-Technology/bob"
|
||||||
//"github.com/Gleipnir-Technology/bob/dialect/psql"
|
//"github.com/Gleipnir-Technology/bob/dialect/psql"
|
||||||
|
|
@ -21,22 +20,14 @@ import (
|
||||||
|
|
||||||
type contentNotificationList struct {
|
type contentNotificationList struct {
|
||||||
Notifications []notification.Notification
|
Notifications []notification.Notification
|
||||||
User User
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNotificationList(w http.ResponseWriter, r *http.Request, u *models.User) {
|
func getNotificationList(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentNotificationList], *errorWithStatus) {
|
||||||
userContent, err := contentForUser(r.Context(), u)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get user", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx := r.Context()
|
|
||||||
notifications, err := notification.ForUser(ctx, u)
|
notifications, err := notification.ForUser(ctx, u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "Failed to get notifications", err, http.StatusInternalServerError)
|
return nil, newError("Failed to get notifications: %w", err)
|
||||||
}
|
}
|
||||||
html.RenderOrError(w, "sync/notification-list.html", &contentNotificationList{
|
return newResponse("sync/notification-list.html", contentNotificationList{
|
||||||
Notifications: notifications,
|
Notifications: notifications,
|
||||||
User: userContent,
|
}), nil
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,7 @@ import (
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ContextOauthPrompt struct {
|
type ContextOauthPrompt struct{}
|
||||||
User User
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the ArcGIS authorization URL with PKCE
|
// Build the ArcGIS authorization URL with PKCE
|
||||||
func buildArcGISAuthURL(clientID string) string {
|
func buildArcGISAuthURL(clientID string) string {
|
||||||
|
|
@ -77,13 +75,6 @@ func getOAuthRefresh(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func oauthPrompt(w http.ResponseWriter, r *http.Request, user *models.User) {
|
func oauthPrompt(w http.ResponseWriter, r *http.Request, user *models.User) {
|
||||||
userContent, err := contentForUser(r.Context(), user)
|
data := ContextOauthPrompt{}
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get user content", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data := ContextOauthPrompt{
|
|
||||||
User: userContent,
|
|
||||||
}
|
|
||||||
html.RenderOrError(w, "sync/oauth-prompt.html", data)
|
html.RenderOrError(w, "sync/oauth-prompt.html", data)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
119
sync/pool.go
119
sync/pool.go
|
|
@ -1,123 +1,20 @@
|
||||||
package sync
|
package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/html"
|
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/platform"
|
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/userfile"
|
|
||||||
"github.com/go-chi/chi/v5"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type contentPoolDetail struct {
|
type contentPoolList struct{}
|
||||||
CSVFileID int32
|
|
||||||
Organization *models.Organization
|
|
||||||
Upload platform.UploadPoolDetail
|
|
||||||
URL ContentURL
|
|
||||||
User User
|
|
||||||
}
|
|
||||||
type ContentPoolList struct {
|
|
||||||
Uploads []platform.PoolUpload
|
|
||||||
URL ContentURL
|
|
||||||
User User
|
|
||||||
}
|
|
||||||
type ContentPoolUpload struct {
|
|
||||||
URL ContentURL
|
|
||||||
User User
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPoolList(w http.ResponseWriter, r *http.Request, u *models.User) {
|
func getPoolList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentPoolList], *errorWithStatus) {
|
||||||
ctx := r.Context()
|
return newResponse("sync/pool-list.html", contentPoolList{}), nil
|
||||||
userContent, err := contentForUser(ctx, u)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get user", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
uploads, err := platform.PoolUploadList(ctx, u.OrganizationID)
|
func getPoolCreate(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentPoolList], *errorWithStatus) {
|
||||||
if err != nil {
|
return newResponse("sync/pool-upload.html", contentPoolList{}), nil
|
||||||
respondError(w, "Failed to get uploads", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
data := ContentPoolList{
|
func getPoolByID(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentPoolList], *errorWithStatus) {
|
||||||
Uploads: uploads,
|
return newResponse("sync/pool-by-id.html", contentPoolList{}), nil
|
||||||
URL: newContentURL(),
|
|
||||||
User: userContent,
|
|
||||||
}
|
|
||||||
html.RenderOrError(w, "sync/pool-list.html", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPoolUpload(w http.ResponseWriter, r *http.Request, u *models.User) {
|
|
||||||
userContent, err := contentForUser(r.Context(), u)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get user", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data := ContentPoolUpload{
|
|
||||||
URL: newContentURL(),
|
|
||||||
User: userContent,
|
|
||||||
}
|
|
||||||
html.RenderOrError(w, "sync/pool-csv-upload.html", data)
|
|
||||||
}
|
|
||||||
func getPoolUploadByID(w http.ResponseWriter, r *http.Request, u *models.User) {
|
|
||||||
ctx := r.Context()
|
|
||||||
userContent, err := contentForUser(ctx, u)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get user", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
org, err := u.Organization().One(ctx, db.PGInstance.BobDB)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get organization", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
file_id_str := chi.URLParam(r, "id")
|
|
||||||
file_id, err := strconv.ParseInt(file_id_str, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to parse file_id", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
detail, err := platform.GetUploadPoolDetail(ctx, u.OrganizationID, int32(file_id))
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get pool", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data := contentPoolDetail{
|
|
||||||
CSVFileID: int32(file_id),
|
|
||||||
Organization: org,
|
|
||||||
Upload: detail,
|
|
||||||
URL: newContentURL(),
|
|
||||||
User: userContent,
|
|
||||||
}
|
|
||||||
html.RenderOrError(w, "sync/pool-by-id.html", data)
|
|
||||||
}
|
|
||||||
func postPoolUpload(w http.ResponseWriter, r *http.Request, u *models.User) {
|
|
||||||
err := r.ParseMultipartForm(32 << 10) // 32 MB buffer
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to parse form", err, http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uploads, err := userfile.SaveFileUpload(r, "csvfile", userfile.CollectionCSV)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to extract image uploads", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(uploads) == 0 {
|
|
||||||
respondError(w, "No upload found", nil, http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(uploads) != 1 {
|
|
||||||
respondError(w, "You must only submit one file at a time", nil, http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
upload := uploads[0]
|
|
||||||
pool_upload, err := platform.NewPoolUpload(r.Context(), u, upload)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to create new pool", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
http.Redirect(w, r, fmt.Sprintf("/pool/upload/%d", pool_upload.ID), http.StatusFound)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||||
|
|
@ -11,13 +12,13 @@ type contentRadar struct {
|
||||||
Organization *models.Organization
|
Organization *models.Organization
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRadar(ctx context.Context, user *models.User) (string, contentRadar, *errorWithStatus) {
|
func getRadar(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentRadar], *errorWithStatus) {
|
||||||
org, err := user.Organization().One(ctx, db.PGInstance.BobDB)
|
org, err := user.Organization().One(ctx, db.PGInstance.BobDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", contentRadar{}, newError("get org: %w", err)
|
return nil, newError("get org: %w", err)
|
||||||
}
|
}
|
||||||
data := contentRadar{
|
data := contentRadar{
|
||||||
Organization: org,
|
Organization: org,
|
||||||
}
|
}
|
||||||
return "sync/radar.html", data, nil
|
return newResponse("sync/radar.html", data), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/api"
|
"github.com/Gleipnir-Technology/nidus-sync/api"
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/auth"
|
"github.com/Gleipnir-Technology/nidus-sync/auth"
|
||||||
|
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/html"
|
"github.com/Gleipnir-Technology/nidus-sync/html"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
|
|
@ -46,34 +47,37 @@ func Router() chi.Router {
|
||||||
// Authenticated endpoints
|
// Authenticated endpoints
|
||||||
r.Route("/api", api.AddRoutes)
|
r.Route("/api", api.AddRoutes)
|
||||||
r.Method("GET", "/admin", authenticatedHandler(getAdminDash))
|
r.Method("GET", "/admin", authenticatedHandler(getAdminDash))
|
||||||
r.Method("GET", "/cell/{cell}", auth.NewEnsureAuth(getCellDetails))
|
r.Method("GET", "/cell/{cell}", authenticatedHandler(getCellDetails))
|
||||||
r.Method("GET", "/download", authenticatedHandler(getDownloadList))
|
r.Method("GET", "/download", authenticatedHandler(getDownloadList))
|
||||||
r.Method("GET", "/layout-test", auth.NewEnsureAuth(getLayoutTest))
|
r.Method("GET", "/layout-test", authenticatedHandler(getLayoutTest))
|
||||||
r.Method("GET", "/message", authenticatedHandler(getMessageList))
|
r.Method("GET", "/message", authenticatedHandler(getMessageList))
|
||||||
r.Method("GET", "/notification", auth.NewEnsureAuth(getNotificationList))
|
r.Method("GET", "/notification", authenticatedHandler(getNotificationList))
|
||||||
r.Method("GET", "/pool", auth.NewEnsureAuth(getPoolList))
|
r.Method("GET", "/pool", authenticatedHandler(getPoolList))
|
||||||
r.Method("GET", "/pool/upload", auth.NewEnsureAuth(getPoolUpload))
|
r.Method("GET", "/pool/create", authenticatedHandler(getPoolCreate))
|
||||||
r.Method("GET", "/pool/upload/{id}", auth.NewEnsureAuth(getPoolUploadByID))
|
r.Method("GET", "/pool/{id}", authenticatedHandler(getPoolByID))
|
||||||
r.Method("POST", "/pool/upload", auth.NewEnsureAuth(postPoolUpload))
|
|
||||||
r.Method("GET", "/radar", authenticatedHandler(getRadar))
|
r.Method("GET", "/radar", authenticatedHandler(getRadar))
|
||||||
r.Method("GET", "/service-request", authenticatedHandler(getServiceRequestList))
|
r.Method("GET", "/service-request", authenticatedHandler(getServiceRequestList))
|
||||||
r.Method("GET", "/service-request/{id}", authenticatedHandler(getServiceRequestDetail))
|
r.Method("GET", "/service-request/{id}", authenticatedHandler(getServiceRequestDetail))
|
||||||
r.Method("GET", "/setting", auth.NewEnsureAuth(getSetting))
|
r.Method("GET", "/setting", authenticatedHandler(getSetting))
|
||||||
r.Method("GET", "/setting/organization", auth.NewEnsureAuth(getSettingOrganization))
|
r.Method("GET", "/setting/organization", authenticatedHandler(getSettingOrganization))
|
||||||
r.Method("GET", "/setting/integration", auth.NewEnsureAuth(getSettingIntegration))
|
r.Method("GET", "/setting/integration", authenticatedHandler(getSettingIntegration))
|
||||||
r.Method("GET", "/setting/pesticide", authenticatedHandler(getSettingPesticide))
|
r.Method("GET", "/setting/pesticide", authenticatedHandler(getSettingPesticide))
|
||||||
r.Method("GET", "/setting/pesticide/add", authenticatedHandler(getSettingPesticideAdd))
|
r.Method("GET", "/setting/pesticide/add", authenticatedHandler(getSettingPesticideAdd))
|
||||||
r.Method("GET", "/setting/user", authenticatedHandler(getSettingUserList))
|
r.Method("GET", "/setting/user", authenticatedHandler(getSettingUserList))
|
||||||
r.Method("GET", "/setting/user/add", authenticatedHandler(getSettingUserAdd))
|
r.Method("GET", "/setting/user/add", authenticatedHandler(getSettingUserAdd))
|
||||||
r.Method("GET", "/signout", auth.NewEnsureAuth(getSignout))
|
r.Method("GET", "/signout", auth.NewEnsureAuth(getSignout))
|
||||||
r.Method("GET", "/source/{globalid}", auth.NewEnsureAuth(getSource))
|
r.Method("GET", "/source/{globalid}", authenticatedHandler(getSource))
|
||||||
r.Method("GET", "/stadia", auth.NewEnsureAuth(getStadia))
|
r.Method("GET", "/stadia", authenticatedHandler(getStadia))
|
||||||
r.Method("GET", "/sudo", authenticatedHandler(getSudo))
|
r.Method("GET", "/sudo", authenticatedHandler(getSudo))
|
||||||
r.Method("POST", "/sudo/email", authenticatedHandlerPost(postSudoEmail))
|
r.Method("POST", "/sudo/email", authenticatedHandlerPost(postSudoEmail))
|
||||||
r.Method("POST", "/sudo/sms", authenticatedHandlerPost(postSudoSMS))
|
r.Method("POST", "/sudo/sms", authenticatedHandlerPost(postSudoSMS))
|
||||||
r.Method("GET", "/trap/{globalid}", auth.NewEnsureAuth(getTrap))
|
r.Method("GET", "/trap/{globalid}", authenticatedHandler(getTrap))
|
||||||
r.Method("GET", "/text/{destination}", auth.NewEnsureAuth(getTextMessages))
|
r.Method("GET", "/text/{destination}", authenticatedHandler(getTextMessages))
|
||||||
r.Method("GET", "/upload", authenticatedHandler(getUploadList))
|
r.Method("GET", "/upload", authenticatedHandler(getUploadList))
|
||||||
|
r.Method("GET", "/upload/pool", authenticatedHandler(getUploadPoolList))
|
||||||
|
r.Method("GET", "/upload/pool/create", authenticatedHandler(getUploadPoolCreate))
|
||||||
|
r.Method("POST", "/upload/pool/create", authenticatedHandlerPostMultipart(postUploadPoolCreate))
|
||||||
|
r.Method("GET", "/upload/pool/upload/{id}", authenticatedHandler(getUploadPoolByID))
|
||||||
|
|
||||||
html.AddStaticRoute(r, "/static")
|
html.AddStaticRoute(r, "/static")
|
||||||
return r
|
return r
|
||||||
|
|
@ -88,17 +92,34 @@ func (e *errorWithStatus) Error() string {
|
||||||
return e.Message
|
return e.Message
|
||||||
}
|
}
|
||||||
func newError(mesg_format string, args ...interface{}) *errorWithStatus {
|
func newError(mesg_format string, args ...interface{}) *errorWithStatus {
|
||||||
|
return newErrorStatus(http.StatusInternalServerError, mesg_format, args...)
|
||||||
|
}
|
||||||
|
func newErrorStatus(status int, mesg_format string, args ...interface{}) *errorWithStatus {
|
||||||
w := fmt.Errorf(mesg_format, args...)
|
w := fmt.Errorf(mesg_format, args...)
|
||||||
return &errorWithStatus{
|
return &errorWithStatus{
|
||||||
Message: w.Error(),
|
Message: w.Error(),
|
||||||
Status: http.StatusInternalServerError,
|
Status: status,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type handlerFunctionGet[T any] func(context.Context, *models.User) (string, T, *errorWithStatus)
|
type response[T any] struct {
|
||||||
|
content T
|
||||||
|
template string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newResponse[T any](template string, content T) *response[T] {
|
||||||
|
return &response[T]{
|
||||||
|
content: content,
|
||||||
|
template: template,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type handlerFunctionGet[T any] func(context.Context, *http.Request, *models.Organization, *models.User) (*response[T], *errorWithStatus)
|
||||||
type wrappedHandler func(http.ResponseWriter, *http.Request)
|
type wrappedHandler func(http.ResponseWriter, *http.Request)
|
||||||
type contentAuthenticated[T any] struct {
|
type contentAuthenticated[T any] struct {
|
||||||
C T
|
C T
|
||||||
|
Config contentConfig
|
||||||
|
Organization *models.Organization
|
||||||
URL ContentURL
|
URL ContentURL
|
||||||
User User
|
User User
|
||||||
}
|
}
|
||||||
|
|
@ -112,22 +133,32 @@ func authenticatedHandler[T any](f handlerFunctionGet[T]) http.Handler {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
template, content, e := f(ctx, u)
|
org, err := u.Organization().One(ctx, db.PGInstance.BobDB)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, e := f(ctx, r, org, u)
|
||||||
//log.Info().Str("template", template).Err(e).Msg("handler done")
|
//log.Info().Str("template", template).Err(e).Msg("handler done")
|
||||||
if e != nil {
|
if e != nil {
|
||||||
log.Warn().Int("status", e.Status).Err(e).Str("user message", e.Message).Msg("Responding with an error from sync pages")
|
log.Warn().Int("status", e.Status).Err(e).Str("user message", e.Message).Msg("Responding with an error from sync pages")
|
||||||
http.Error(w, e.Error(), e.Status)
|
http.Error(w, e.Error(), e.Status)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
html.RenderOrError(w, template, contentAuthenticated[T]{
|
if org == nil {
|
||||||
C: content,
|
http.Error(w, "nil org", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
html.RenderOrError(w, resp.template, contentAuthenticated[T]{
|
||||||
|
C: resp.content,
|
||||||
|
Organization: org,
|
||||||
URL: newContentURL(),
|
URL: newContentURL(),
|
||||||
User: userContent,
|
User: userContent,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type handlerFunctionPost[T any] func(context.Context, *models.User, T) (string, *errorWithStatus)
|
type handlerFunctionPost[T any] func(context.Context, *http.Request, *models.User, T) (string, *errorWithStatus)
|
||||||
|
|
||||||
func authenticatedHandlerPost[T any](f handlerFunctionPost[T]) http.Handler {
|
func authenticatedHandlerPost[T any](f handlerFunctionPost[T]) http.Handler {
|
||||||
return auth.NewEnsureAuth(func(w http.ResponseWriter, r *http.Request, u *models.User) {
|
return auth.NewEnsureAuth(func(w http.ResponseWriter, r *http.Request, u *models.User) {
|
||||||
|
|
@ -145,7 +176,31 @@ func authenticatedHandlerPost[T any](f handlerFunctionPost[T]) http.Handler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
path, e := f(ctx, u, content)
|
path, e := f(ctx, r, u, content)
|
||||||
|
if e != nil {
|
||||||
|
http.Error(w, e.Error(), e.Status)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, path, http.StatusFound)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
func authenticatedHandlerPostMultipart[T any](f handlerFunctionPost[T]) http.Handler {
|
||||||
|
return auth.NewEnsureAuth(func(w http.ResponseWriter, r *http.Request, u *models.User) {
|
||||||
|
err := r.ParseMultipartForm(32 << 10) // 32 MB buffer
|
||||||
|
if err != nil {
|
||||||
|
respondError(w, "Failed to parse form", err, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var content T
|
||||||
|
|
||||||
|
err = decoder.Decode(&content, r.PostForm)
|
||||||
|
if err != nil {
|
||||||
|
respondError(w, "Failed to decode form", err, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx := r.Context()
|
||||||
|
path, e := f(ctx, r, u, content)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
http.Error(w, e.Error(), e.Status)
|
http.Error(w, e.Error(), e.Status)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/config"
|
"github.com/Gleipnir-Technology/nidus-sync/config"
|
||||||
|
|
@ -31,11 +32,11 @@ type contentServiceRequestList struct {
|
||||||
ClosedRequests []contentClosedServiceRequest
|
ClosedRequests []contentClosedServiceRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServiceRequestDetail(ctx context.Context, user *models.User) (string, interface{}, *errorWithStatus) {
|
func getServiceRequestDetail(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentServiceRequestDetail], *errorWithStatus) {
|
||||||
content := contentServiceRequestDetail{}
|
content := contentServiceRequestDetail{}
|
||||||
return "sync/service-request-detail.html", content, nil
|
return newResponse("sync/service-request-detail.html", content), nil
|
||||||
}
|
}
|
||||||
func getServiceRequestList(ctx context.Context, user *models.User) (string, interface{}, *errorWithStatus) {
|
func getServiceRequestList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentServiceRequestList], *errorWithStatus) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
content := contentServiceRequestList{
|
content := contentServiceRequestList{
|
||||||
ActiveRequests: []contentActiveServiceRequest{
|
ActiveRequests: []contentActiveServiceRequest{
|
||||||
|
|
@ -111,5 +112,5 @@ func getServiceRequestList(ctx context.Context, user *models.User) (string, inte
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return "sync/service-request-list.html", content, nil
|
return newResponse("sync/service-request-list.html", content), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,42 +7,29 @@ import (
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/arcgis"
|
"github.com/Gleipnir-Technology/nidus-sync/arcgis"
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/html"
|
|
||||||
//"github.com/rs/zerolog/log"
|
//"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type contentSettingOrganization struct {
|
type contentSettingOrganization struct {
|
||||||
Organization *models.Organization
|
Organization *models.Organization
|
||||||
URL ContentURL
|
|
||||||
User User
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type contentSettingIntegration struct {
|
type contentSettingIntegration struct {
|
||||||
ArcGISOAuth *models.OauthToken
|
ArcGISOAuth *models.OauthToken
|
||||||
URL ContentURL
|
|
||||||
User User
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSetting(w http.ResponseWriter, r *http.Request, u *models.User) {
|
type contentAuthenticatedPlaceholder struct {
|
||||||
userContent, err := contentForUser(r.Context(), u)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get user content", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
data := ContentAuthenticatedPlaceholder{
|
|
||||||
URL: newContentURL(),
|
func getSetting(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentAuthenticatedPlaceholder], *errorWithStatus) {
|
||||||
User: userContent,
|
data := contentAuthenticatedPlaceholder{}
|
||||||
}
|
return newResponse("sync/settings.html", data), nil
|
||||||
html.RenderOrError(w, "sync/settings.html", data)
|
|
||||||
}
|
|
||||||
func getSettingOrganization(w http.ResponseWriter, r *http.Request, u *models.User) {
|
|
||||||
ctx := r.Context()
|
|
||||||
userContent, err := contentForUser(ctx, u)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get user content", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
func getSettingOrganization(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentSettingOrganization], *errorWithStatus) {
|
||||||
org, err := u.Organization().One(ctx, db.PGInstance.BobDB)
|
org, err := u.Organization().One(ctx, db.PGInstance.BobDB)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("get organization: %w", err)
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
var district contentDistrict
|
var district contentDistrict
|
||||||
district, err = bob.One[contentDistrict](ctx, db.PGInstance.BobDB, psql.Select(
|
district, err = bob.One[contentDistrict](ctx, db.PGInstance.BobDB, psql.Select(
|
||||||
|
|
@ -76,46 +63,35 @@ func getSettingOrganization(w http.ResponseWriter, r *http.Request, u *models.Us
|
||||||
*/
|
*/
|
||||||
data := contentSettingOrganization{
|
data := contentSettingOrganization{
|
||||||
Organization: org,
|
Organization: org,
|
||||||
URL: newContentURL(),
|
|
||||||
User: userContent,
|
|
||||||
}
|
}
|
||||||
html.RenderOrError(w, "sync/setting-organization.html", data)
|
return newResponse("sync/setting-organization.html", data), nil
|
||||||
}
|
|
||||||
func getSettingIntegration(w http.ResponseWriter, r *http.Request, u *models.User) {
|
|
||||||
ctx := r.Context()
|
|
||||||
userContent, err := contentForUser(ctx, u)
|
|
||||||
if err != nil {
|
|
||||||
respondError(w, "Failed to get user content", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
func getSettingIntegration(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentSettingIntegration], *errorWithStatus) {
|
||||||
oauth, err := arcgis.GetOAuthForUser(ctx, u)
|
oauth, err := arcgis.GetOAuthForUser(ctx, u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "Failed to get oauth", err, http.StatusInternalServerError)
|
return nil, newError("Failed to get oauth: %w", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
data := contentSettingIntegration{
|
data := contentSettingIntegration{
|
||||||
ArcGISOAuth: oauth,
|
ArcGISOAuth: oauth,
|
||||||
URL: newContentURL(),
|
|
||||||
User: userContent,
|
|
||||||
}
|
}
|
||||||
html.RenderOrError(w, "sync/setting-integration.html", data)
|
return newResponse("sync/setting-integration.html", data), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type contentSettingPlaceholder struct{}
|
type contentSettingPlaceholder struct{}
|
||||||
|
|
||||||
func getSettingPesticide(ctx context.Context, user *models.User) (string, interface{}, *errorWithStatus) {
|
func getSettingPesticide(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentSettingPlaceholder], *errorWithStatus) {
|
||||||
content := contentSettingPlaceholder{}
|
content := contentSettingPlaceholder{}
|
||||||
return "sync/setting-pesticide.html", content, nil
|
return newResponse("sync/setting-pesticide.html", content), nil
|
||||||
}
|
}
|
||||||
func getSettingPesticideAdd(ctx context.Context, user *models.User) (string, interface{}, *errorWithStatus) {
|
func getSettingPesticideAdd(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentSettingPlaceholder], *errorWithStatus) {
|
||||||
content := contentSettingPlaceholder{}
|
content := contentSettingPlaceholder{}
|
||||||
return "sync/setting-pesticide-add.html", content, nil
|
return newResponse("sync/setting-pesticide-add.html", content), nil
|
||||||
}
|
}
|
||||||
func getSettingUserAdd(ctx context.Context, user *models.User) (string, interface{}, *errorWithStatus) {
|
func getSettingUserAdd(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentSettingPlaceholder], *errorWithStatus) {
|
||||||
content := contentSettingPlaceholder{}
|
content := contentSettingPlaceholder{}
|
||||||
return "sync/setting-user-add.html", content, nil
|
return newResponse("sync/setting-user-add.html", content), nil
|
||||||
}
|
}
|
||||||
func getSettingUserList(ctx context.Context, user *models.User) (string, interface{}, *errorWithStatus) {
|
func getSettingUserList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentSettingPlaceholder], *errorWithStatus) {
|
||||||
content := contentSettingPlaceholder{}
|
content := contentSettingPlaceholder{}
|
||||||
return "sync/setting-user-list.html", content, nil
|
return newResponse("sync/setting-user-list.html", content), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,8 @@ func getSignout(w http.ResponseWriter, r *http.Request, user *models.User) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSignup(w http.ResponseWriter, r *http.Request) {
|
func getSignup(w http.ResponseWriter, r *http.Request) {
|
||||||
signup(w, r.URL.Path)
|
data := ContentSignup{}
|
||||||
|
html.RenderOrError(w, "sync/signup.html", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func postSignin(w http.ResponseWriter, r *http.Request) {
|
func postSignin(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
@ -105,8 +106,3 @@ func signin(w http.ResponseWriter, errorCode string, next string) {
|
||||||
}
|
}
|
||||||
html.RenderOrError(w, "sync/signin.html", data)
|
html.RenderOrError(w, "sync/signin.html", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func signup(w http.ResponseWriter, path string) {
|
|
||||||
data := ContentSignup{}
|
|
||||||
html.RenderOrError(w, "sync/signup.html", data)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
10
sync/sudo.go
10
sync/sudo.go
|
|
@ -19,9 +19,9 @@ type contentSudo struct {
|
||||||
ForwardEmailNidusAddress string
|
ForwardEmailNidusAddress string
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSudo(ctx context.Context, user *models.User) (string, interface{}, *errorWithStatus) {
|
func getSudo(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentSudo], *errorWithStatus) {
|
||||||
if user.Role != enums.UserroleRoot {
|
if user.Role != enums.UserroleRoot {
|
||||||
return "", nil, &errorWithStatus{
|
return nil, &errorWithStatus{
|
||||||
Message: "You have to be a root user to access this",
|
Message: "You have to be a root user to access this",
|
||||||
Status: http.StatusForbidden,
|
Status: http.StatusForbidden,
|
||||||
}
|
}
|
||||||
|
|
@ -30,7 +30,7 @@ func getSudo(ctx context.Context, user *models.User) (string, interface{}, *erro
|
||||||
ForwardEmailRMOAddress: config.ForwardEmailRMOAddress,
|
ForwardEmailRMOAddress: config.ForwardEmailRMOAddress,
|
||||||
ForwardEmailNidusAddress: config.ForwardEmailNidusAddress,
|
ForwardEmailNidusAddress: config.ForwardEmailNidusAddress,
|
||||||
}
|
}
|
||||||
return "sync/sudo.html", content, nil
|
return newResponse("sync/sudo.html", content), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var decoder = schema.NewDecoder()
|
var decoder = schema.NewDecoder()
|
||||||
|
|
@ -42,7 +42,7 @@ type FormEmail struct {
|
||||||
To string `schema:"emailTo"`
|
To string `schema:"emailTo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func postSudoEmail(ctx context.Context, u *models.User, e FormEmail) (string, *errorWithStatus) {
|
func postSudoEmail(ctx context.Context, r *http.Request, u *models.User, e FormEmail) (string, *errorWithStatus) {
|
||||||
if u.Role != enums.UserroleRoot {
|
if u.Role != enums.UserroleRoot {
|
||||||
return "", &errorWithStatus{
|
return "", &errorWithStatus{
|
||||||
Message: "You must have sudo powers to do this",
|
Message: "You must have sudo powers to do this",
|
||||||
|
|
@ -71,7 +71,7 @@ type FormSMS struct {
|
||||||
Phone string `schema:"smsPhone"`
|
Phone string `schema:"smsPhone"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func postSudoSMS(ctx context.Context, u *models.User, sms FormSMS) (string, *errorWithStatus) {
|
func postSudoSMS(ctx context.Context, r *http.Request, u *models.User, sms FormSMS) (string, *errorWithStatus) {
|
||||||
if u.Role != enums.UserroleRoot {
|
if u.Role != enums.UserroleRoot {
|
||||||
return "", &errorWithStatus{
|
return "", &errorWithStatus{
|
||||||
Message: "You must have sudo powers to do this",
|
Message: "You must have sudo powers to do this",
|
||||||
|
|
|
||||||
19
sync/text.go
19
sync/text.go
|
|
@ -1,24 +1,15 @@
|
||||||
package sync
|
package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/html"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ContentTextMessages struct {
|
type contentTextMessages struct{}
|
||||||
User User
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTextMessages(w http.ResponseWriter, r *http.Request, u *models.User) {
|
func getTextMessages(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentTextMessages], *errorWithStatus) {
|
||||||
userContent, err := contentForUser(r.Context(), u)
|
content := contentTextMessages{}
|
||||||
if err != nil {
|
return newResponse("sync/text-messages.html", content), nil
|
||||||
respondError(w, "Failed to get user", err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
content := ContentTextMessages{
|
|
||||||
User: userContent,
|
|
||||||
}
|
|
||||||
html.RenderOrError(w, "sync/text-messages.html", content)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,6 @@ type ComponentMap struct {
|
||||||
Markers []MapMarker
|
Markers []MapMarker
|
||||||
Zoom int
|
Zoom int
|
||||||
}
|
}
|
||||||
type ContentAuthenticatedPlaceholder struct {
|
|
||||||
URL ContentURL
|
|
||||||
User User
|
|
||||||
}
|
|
||||||
type ContentMockURLs struct {
|
type ContentMockURLs struct {
|
||||||
Dispatch string
|
Dispatch string
|
||||||
DispatchResults string
|
DispatchResults string
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,88 @@ package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||||
|
"github.com/Gleipnir-Technology/nidus-sync/platform"
|
||||||
|
"github.com/Gleipnir-Technology/nidus-sync/userfile"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
type contentUploadPlaceholder struct{}
|
type contentUploadPlaceholder struct{}
|
||||||
|
|
||||||
func getUploadList(ctx context.Context, user *models.User) (string, interface{}, *errorWithStatus) {
|
func getUploadList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentUploadPlaceholder], *errorWithStatus) {
|
||||||
content := contentUploadPlaceholder{}
|
content := contentUploadPlaceholder{}
|
||||||
return "sync/upload-list.html", content, nil
|
return newResponse("sync/upload-list.html", content), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type contentPoolDetail struct {
|
||||||
|
CSVFileID int32
|
||||||
|
Organization *models.Organization
|
||||||
|
Upload platform.UploadPoolDetail
|
||||||
|
}
|
||||||
|
type contentUploadPoolList struct {
|
||||||
|
Uploads []platform.PoolUpload
|
||||||
|
}
|
||||||
|
type contentUploadPoolCreate struct{}
|
||||||
|
|
||||||
|
func getUploadPoolList(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentUploadPoolList], *errorWithStatus) {
|
||||||
|
uploads, err := platform.PoolUploadList(ctx, u.OrganizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Failed to get uploads: %w", err)
|
||||||
|
}
|
||||||
|
data := contentUploadPoolList{
|
||||||
|
Uploads: uploads,
|
||||||
|
}
|
||||||
|
return newResponse("sync/pool-list.html", data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUploadPoolCreate(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentUploadPoolCreate], *errorWithStatus) {
|
||||||
|
data := contentUploadPoolCreate{}
|
||||||
|
return newResponse("sync/pool-csv-upload.html", data), nil
|
||||||
|
}
|
||||||
|
func getUploadPoolByID(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentPoolDetail], *errorWithStatus) {
|
||||||
|
org, err := u.Organization().One(ctx, db.PGInstance.BobDB)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Failed to get organization: %w", err)
|
||||||
|
}
|
||||||
|
file_id_str := chi.URLParam(r, "id")
|
||||||
|
file_id, err := strconv.ParseInt(file_id_str, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Failed to parse file_id: %w", err)
|
||||||
|
}
|
||||||
|
detail, err := platform.GetUploadPoolDetail(ctx, u.OrganizationID, int32(file_id))
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Failed to get pool: %w", err)
|
||||||
|
}
|
||||||
|
data := contentPoolDetail{
|
||||||
|
CSVFileID: int32(file_id),
|
||||||
|
Organization: org,
|
||||||
|
Upload: detail,
|
||||||
|
}
|
||||||
|
return newResponse("sync/pool-by-id.html", data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type FormUploadPool struct{}
|
||||||
|
|
||||||
|
func postUploadPoolCreate(ctx context.Context, r *http.Request, u *models.User, f FormUploadPool) (string, *errorWithStatus) {
|
||||||
|
uploads, err := userfile.SaveFileUpload(r, "csvfile", userfile.CollectionCSV)
|
||||||
|
if err != nil {
|
||||||
|
return "", newError("Failed to extract image uploads: %s", err)
|
||||||
|
}
|
||||||
|
if len(uploads) == 0 {
|
||||||
|
return "", newErrorStatus(http.StatusBadRequest, "No upload found")
|
||||||
|
}
|
||||||
|
if len(uploads) != 1 {
|
||||||
|
return "", newErrorStatus(http.StatusBadRequest, "You must only submit one file at a time")
|
||||||
|
}
|
||||||
|
upload := uploads[0]
|
||||||
|
pool_upload, err := platform.NewPoolUpload(r.Context(), u, upload)
|
||||||
|
if err != nil {
|
||||||
|
return "", newError("Failed to create new pool: %w", err)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("/pool/upload/%d", pool_upload.ID), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue