view pkg/controllers/system.go @ 3628:6693be57b7a2 configuration

Re-worked the Go part a little bit.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Tue, 11 Jun 2019 12:07:55 +0200
parents a688a478e35f
children 1825a1bc9fb1
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"
	"database/sql"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/gorilla/mux"

	"gemma.intevation.de/gemma/pkg/config"
	"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`

	updateSettingSQL = `
INSERT INTO system_admin.system_config (config_key, config_val)
VALUES ($1, $2)
ON CONFLICT (key) DO UPDATE SET config_val = $2`
)

// 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
}

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()

	for key, value := range *settings {
		if _, err = setStmt.ExecContext(
			ctx,
			updateSettingSQL,
			key, value,
		); err != nil {
			return
		}
	}

	if err = tx.Commit(); err != nil {
		return
	}

	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
}