Mercurial > gemma
view pkg/controllers/system.go @ 3936:d859ec6cf2f0
Fairwayprofile enhanced
Added getDimensions to mixins.
Use getDimensions to render fairwayprofile.
author | Thomas Junk <thomas.junk@intevation.de> |
---|---|
date | Fri, 12 Jul 2019 09:55:03 +0200 |
parents | 45be361f2d48 |
children | 49564382ffff |
line wrap: on
line source
// This is Free Software under GNU Affero General Public License v >= 3.0 // without warranty, see README.md and license for details. // // SPDX-License-Identifier: AGPL-3.0-or-later // License-Filename: LICENSES/AGPL-3.0.txt // // Copyright (C) 2018 by via donau // – Österreichische Wasserstraßen-Gesellschaft mbH // Software engineering by Intevation GmbH // // Author(s): // * Sascha Wilde <sascha.wilde@intevation.de> package controllers import ( "bytes" "context" "database/sql" "fmt" "io/ioutil" "log" "net/http" "strings" "sync" "github.com/gorilla/mux" "gemma.intevation.de/gemma/pkg/auth" "gemma.intevation.de/gemma/pkg/config" "gemma.intevation.de/gemma/pkg/geoserver" "gemma.intevation.de/gemma/pkg/models" ) const ( getFeatureColourSQL = `SELECT r,g,b,a FROM systemconf.feature_colours WHERE feature_name = $1 AND style_attr = $2` setFeatureColourSQL = `UPDATE systemconf.feature_colours SET (r, g, b, a) = ($3, $4, $5, $6) WHERE feature_name = $1 AND style_attr = $2` getSettingsSQL = ` SELECT config_key, config_val FROM sys_admin.system_config` getConfigSQL = ` SELECT config_val FROM sys_admin.system_config WHERE config_key = $1` updateSettingSQL = ` INSERT INTO sys_admin.system_config (config_key, config_val) VALUES ($1, $2) ON CONFLICT (config_key) DO UPDATE SET config_val = $2` deleteSoundingDiffsSQL = ` DELETE FROM caching.sounding_differences` ) // System status end points func showSystemLog( _ interface{}, req *http.Request, _ *sql.Conn, ) (jr JSONResult, err error) { serviceName := mux.Vars(req)["service"] fileName := mux.Vars(req)["file"] // The following check is currently most likely unnecessary as I wasn't // able to inject a verbatim '/' via the middleware, but better be on // the safe site... if strings.Contains(fileName, "/") { err = JSONError{http.StatusBadRequest, "error: no slashes allowed in file name"} return } var path string switch serviceName { case "apache2", "postgresql": path = "/var/log/" + serviceName + "/" + fileName default: err = JSONError{http.StatusBadRequest, "error: invalid service: " + serviceName} return } var txt []byte if txt, err = ioutil.ReadFile(path); err != nil { return } jr = JSONResult{ Result: struct { Path string `json:"path"` Content string `json:"content"` }{path, string(txt)}, } return } func getSystemConfig( _ interface{}, req *http.Request, _ *sql.Conn, ) (jr JSONResult, err error) { cfg := config.PublishedConfig() if cfg == "" { jr = JSONResult{Result: strings.NewReader("{}")} return } var data []byte if data, err = ioutil.ReadFile(cfg); err != nil { return } jr = JSONResult{Result: bytes.NewReader(data)} return } func getSystemSettings( _ interface{}, req *http.Request, conn *sql.Conn, ) (jr JSONResult, err error) { var rows *sql.Rows if rows, err = conn.QueryContext(req.Context(), getSettingsSQL); err != nil { return } defer rows.Close() settings := map[string]string{} for rows.Next() { var key, val string if err = rows.Scan(&key, &val); err != nil { return } settings[key] = val } if err = rows.Err(); err != nil { return } jr = JSONResult{Result: settings} return } type reconfFunc func(sql.NullString, string) (func(), error) var ( reconfigureFuncsMu sync.Mutex reconfigureFuncs = map[string]reconfFunc{} ) func registerReconfigureFunc(key string, fn reconfFunc) { reconfigureFuncsMu.Lock() defer reconfigureFuncsMu.Unlock() reconfigureFuncs[key] = fn } func reconfigureFunc(key string) reconfFunc { reconfigureFuncsMu.Lock() defer reconfigureFuncsMu.Unlock() return reconfigureFuncs[key] } func reconfigureClassBreaks(old sql.NullString, curr, which string, recalc func()) (func(), error) { // If new values are broken, don't proceed. currCVs, err := models.ParseColorValues(curr) if err != nil { return nil, err } doBoth := func() { log.Printf("info: Trigger re-calculation of %s.", which) geoserver.ReconfigureStyle(which) recalc() } if !old.Valid { return doBoth, nil } oldCVs, err := models.ParseColorValues(old.String) if err != nil { log.Printf("warn: old config value is broken: %v\n", err) return doBoth, nil } if len(currCVs) != len(oldCVs) { return doBoth, nil } colorChanged := false for i := range currCVs { if currCVs[i].Value != oldCVs[i].Value { return doBoth, nil } if currCVs[i].Color != oldCVs[i].Color { colorChanged = true } } // Only the color changed -> no expensive recalc needed. if colorChanged { log.Println("info: Only colors changed.") return func() { geoserver.ReconfigureStyle(which) }, nil } return nil, nil } func init() { registerReconfigureFunc("morphology_classbreaks", func(old sql.NullString, curr string) (func(), error) { return reconfigureClassBreaks( old, curr, "sounding_results_contour_lines_geoserver", func() { log.Println( "todo: Trigger expensive recalculation of sounding result contours.") }) }) registerReconfigureFunc("morphology_classbreaks_compare", func(old sql.NullString, curr string) (func(), error) { return reconfigureClassBreaks( old, curr, "sounding_differences", func() { go deleteSoundingDiffs() }) }) } func deleteSoundingDiffs() { // TODO: Better do that in import queue? ctx := context.Background() if err := auth.RunAs(ctx, "sys_admin", func(conn *sql.Conn) error { _, err := conn.ExecContext(ctx, deleteSoundingDiffsSQL) return err }, ); err != nil { log.Printf("error: Cleaning sounding diffs cache failed: %v\n", err) } } func setSystemSettings( input interface{}, req *http.Request, conn *sql.Conn, ) (jr JSONResult, err error) { settings := input.(*map[string]string) ctx := req.Context() var tx *sql.Tx if tx, err = conn.BeginTx(ctx, nil); err != nil { return } defer tx.Rollback() var setStmt *sql.Stmt if setStmt, err = tx.PrepareContext(ctx, updateSettingSQL); err != nil { return } defer setStmt.Close() var getStmt *sql.Stmt if getStmt, err = tx.PrepareContext(ctx, getConfigSQL); err != nil { return } defer getStmt.Close() reconfigure := map[string]func(){} for key, value := range *settings { var old sql.NullString err = getStmt.QueryRowContext(ctx, key).Scan(&old) switch { case err == sql.ErrNoRows: old.Valid, err = false, nil case err != nil: return } if cmp := reconfigureFunc(key); cmp != nil { var fn func() if fn, err = cmp(old, value); err != nil { return } if fn != nil { reconfigure[key] = fn } } if _, err = setStmt.ExecContext(ctx, key, value); err != nil { return } } if err = tx.Commit(); err != nil { return } for _, fn := range reconfigure { fn() } jr = JSONResult{ Code: http.StatusCreated, Result: struct { Result string `json:"result"` }{"success"}, } return } // Map/Feature style end points func getFeatureStyle( _ interface{}, req *http.Request, db *sql.Conn, ) (jr JSONResult, err error) { feature := mux.Vars(req)["feature"] attr := mux.Vars(req)["attr"] c := models.Colour{} err = db.QueryRowContext( req.Context(), getFeatureColourSQL, feature, attr, ).Scan(&c.R, &c.G, &c.B, &c.A) switch { case err == sql.ErrNoRows: err = JSONError{ Code: http.StatusNotFound, Message: "Requestes style not found.", } return case err != nil: return } jr.Result = &struct { Colour models.Colour `json:"colour"` Code string `json:"code"` }{c, fmt.Sprintf("rgba(%d, %d, %d, %g)", c.R, c.G, c.B, c.A)} return } func setFeatureStyle( input interface{}, req *http.Request, db *sql.Conn, ) (jr JSONResult, err error) { feature := mux.Vars(req)["feature"] attr := mux.Vars(req)["attr"] c := input.(*models.Colour) if !c.IsValid() { err = JSONError{http.StatusBadRequest, "error: invalid colours"} return } var res sql.Result res, err = db.ExecContext( req.Context(), setFeatureColourSQL, feature, attr, c.R, c.G, c.B, c.A) if err != nil { return } if n, err2 := res.RowsAffected(); err2 == nil && n == 0 { err = JSONError{ Code: http.StatusNotFound, Message: "Requestes style not found.", } return } jr = JSONResult{ Code: http.StatusCreated, Result: struct { Result string `json:"result"` }{"success"}, } return }