Browse Source

rewritten config to use JSON files, listing of login tokens on User-edit page, white logo variant

master
Stefan Naumann 1 year ago
parent
commit
47dbef9b6c
15 changed files with 331 additions and 268 deletions
  1. +14
    -16
      build/worker.go
  2. +108
    -147
      core/config.go
  3. +41
    -0
      core/log.go
  4. +24
    -21
      core/models.go
  5. +21
    -24
      main.go
  6. +36
    -20
      mvo.cfg
  7. +5
    -5
      static/img/logo.svg
  8. +4
    -4
      views/install2.html
  9. +33
    -1
      views/user_edit.html
  10. +2
    -0
      web/auth.go
  11. +1
    -1
      web/build.go
  12. +2
    -2
      web/index.go
  13. +10
    -11
      web/install.go
  14. +26
    -16
      web/routing.go
  15. +4
    -0
      web/user.go

+ 14
- 16
build/worker.go View File

@@ -19,7 +19,7 @@ import (
)

type BuilderConfig struct {
cfg core.KeyValueStore;
cfg *core.Config;
db *gorm.DB;
};

@@ -46,14 +46,12 @@ func (m* mockerBuilder) append ( args... string) {
}

// sets up the worker threads in the given quantitiy
func SetupWorkerThreads ( cfg core.KeyValueStore, db* gorm.DB ) {
func SetupWorkerThreads ( cfg *core.Config, db* gorm.DB ) {
c.cfg = cfg
c.db = db
numThreads, err := strconv.Atoi ( cfg.Get("parallel_builds") )
if err != nil {
panic ("Config statement about parallel builds could not be parsed");
}
os.RemoveAll ( c.cfg.Get("dir_repo") )
numThreads := cfg.ParallelBuilds

os.RemoveAll ( c.cfg.Directory.Repo )
InChannel = make( chan core.Build, 50 )
Workers = make(map[int]core.Thread)
for i:=0; i < numThreads; i++ {
@@ -106,11 +104,11 @@ func WorkerGoGet ( b *core.Build ) (*mockerBuilder, bool) {
var log mockerBuilder
log.append( "> Started build of ", b.Repository.Name, "\n" )

err := os.Mkdir ( c.cfg.Get("dir_repo"), 0777 );
err = os.Mkdir ( c.cfg.Get("dir_build"), 0777 );
err := os.Mkdir ( c.cfg.Directory.Repo, 0777 );
err = os.Mkdir ( c.cfg.Directory.Build, 0777 );

log.append ( "> Cloning repository\n" )
dir := c.cfg.Get("dir_repo") + dirNameFromRepo ( b.Repository )
dir := c.cfg.Directory.Repo + dirNameFromRepo ( b.Repository )
out, err := exec.Command ("git", "clone", b.Repository.CloneUrl, dir ).CombinedOutput()
log.append( string(out) )
if err != nil {
@@ -160,7 +158,7 @@ func WorkerGoBuild ( b *core.Build, log *mockerBuilder ) (*mockerBuilder, bool)
log.append ( "> Starting Build ... \n")

buildScript := []byte(strings.ReplaceAll ( b.Repository.BuildScript, "\r", "\n") )
dir := c.cfg.Get("dir_repo") + dirNameFromRepo ( b.Repository )
dir := c.cfg.Directory.Repo + dirNameFromRepo ( b.Repository )
buildScriptName := dir + "/mvoBuild.sh"
err := ioutil.WriteFile ( buildScriptName, buildScript, 0777 )
if err != nil {
@@ -181,16 +179,16 @@ func WorkerGoBuild ( b *core.Build, log *mockerBuilder ) (*mockerBuilder, bool)
func WorkerGoZip ( b *core.Build, log *mockerBuilder ) (*mockerBuilder, bool) {
var ZipName string = dirNameFromRepo ( b.Repository) + "_" +
strconv.Itoa(int(b.ID)) + ".tar." +
c.cfg.Get("compression_method");
c.cfg.CompressionMethod;

var ZipFile string = "../" + c.cfg.Get("dir_build") + ZipName
var ZipFile string = "../" + c.cfg.Directory.Build + ZipName
var inFile string = dirNameFromRepo ( b.Repository )

os.RemoveAll ( c.cfg.Get("dir_repo") + inFile + "/.git" )
os.RemoveAll ( c.cfg.Directory.Repo + inFile + "/.git" )

log.append ( "> Zipping directory ", inFile, " to ", ZipFile, "\n" )
cmd := exec.Command ( "tar", "-cavf", ZipFile, inFile )
cmd.Dir = c.cfg.Get("dir_repo")
cmd.Dir = c.cfg.Directory.Repo
out, err := cmd.CombinedOutput ()
log.append( string(out) )
if err != nil {
@@ -205,7 +203,7 @@ func WorkerGoZip ( b *core.Build, log *mockerBuilder ) (*mockerBuilder, bool) {
// removes the repo directory
func WorkerGoCleanup ( b *core.Build, log *mockerBuilder ) (*mockerBuilder, bool) {
log.append ( "> Cleaning up build directory\n" )
var inFile string = c.cfg.Get("dir_repo") +
var inFile string = c.cfg.Directory.Repo +
dirNameFromRepo ( b.Repository )
os.RemoveAll ( inFile );
return log, true;


+ 108
- 147
core/config.go View File

@@ -4,185 +4,146 @@
package core

import (
"fmt"
"io"
"os"
"bufio"
"strings"
"sort"
"strconv"
"reflect"
"io/ioutil"
"encoding/json"
)
// configuration file
const ConfigFile string = "mvo.cfg"

// global config structure
type Config struct {
filename string // the filename of the configuration file
storage map[string]string // the global persistent part of the configuration values
temp map[string]string // the global temporary part of the configuration values
type ConfigDirectory struct {
Repo string
Build string
}

// a simple key-value store for storing and retrieving configuration values,
// can only store string:string
type KeyValueStore interface {
// retrieves a value by its key, in the persistent part (persisting needs Persist call!).
// the temporary part takes precendence. If the key is not found there, the
// persistent part is looked into
Get ( key string ) string
// sets a value by its key, in the persistent memory (needs Persist call!)
Set ( key string, value string )
// removes a key + its value from the Key-value store
// returns whether it s was successfull
Remove ( key string ) bool
// sets a temporary key + value
SetTemp ( key string, value string )
// gets a temporary _only_ key+value, will never look into the persistent part
GetTemp ( key string) string
// persists the structure to the file
Persist ( filename string ) error
// prints everything in a more or less human readable fashion for debugging
Debug ()
// returns true if the configuration is set to be debug
isDebug() bool
type ConfigAuthor struct {
Email string
Name string
Street string
Zip string
City string
Privacy string
}

func (s *Config) Get ( key string ) string {
var x string
x = s.temp[key]
if x == "" {
return s.storage[key]
}
return x;
}

func (s *Config) Sanitize () {
if s.Get ( "http_port") == "" {
s.Set ( "http_post", "4042" );
}
type ConfigDatabase struct {
Provider string
Username string
Password string
Port int
Host string
Dbname string
Filename string
PostgresSSL string
}

func (s *Config) Set ( key string, value string ) {
s.storage[key] = value
// global config structure
type Config struct {
AppTitle string
Install bool
Debug bool
HttpPort int
HttpHost string
LogFile string
LogServer string
LogFileEnable bool
LogMode string
LoginTokenDuration int
ParallelBuilds int
CompressionMethod string
Directory ConfigDirectory
Author ConfigAuthor
Database ConfigDatabase
}

func (s *Config) GetTemp ( key string ) string {
return s.temp[key]
func ConfigDefault () Config {
return Config{
AppTitle: "mvoCI",
Install: false,
Debug: false,
HttpPort: 4042,
HttpHost: "localhost",
LoginTokenDuration: 300,
CompressionMethod: "gz",
ParallelBuilds: 2,
LogFile: "log/godate.log",
LogFileEnable: true,
LogMode: "stderr",
LogServer: "log/server.log",
Directory: ConfigDirectory{
Repo: "repo/",
Build: "build/",
},
// Author -> empty strings
// Database -> empty strings
}
}

func (s *Config) SetTemp ( key string, value string ) {
s.temp[key] = value
}
var cfg Config

func (s *Config) isDebug () bool {
if s.Get("Debug") == "True" ||
s.Get("Debug") == "1" ||
s.Get("Debug") == "true" ||
s.Get("debug") == "True" ||
s.Get("debug") == "1" ||
s.Get("debug") == "true" {
return true
} else {
return false
func (cfg Config) recurseReflect ( current reflect.Value, parts []string ) string {
var v reflect.Value = current.FieldByName ( parts[0] )
if v.Type().Name() == "int" {
return strconv.FormatInt( v.Int(), 10 )
} else if v.Type().Name() == "string" {
return v.String()
}
if len (parts) > 1 {
return cfg.recurseReflect ( v, parts[1:] )
}
return "Invalid Type"
}

func (s *Config) Remove ( key string ) bool {
var x string
x = s.storage[key]
if x == "" {
x = s.temp[key]
if x == "" {
return false;
}
delete ( s.temp, key )
return true;
} else {
x = s.temp[key]
if x != "" {
delete ( s.temp, key )
}
delete ( s.storage, key )
return true;
}
func (cfg Config) Reflect ( q string ) string {
var parts []string = strings.Split ( q, "." )
current := reflect.Indirect(reflect.ValueOf(cfg))
var r string = cfg.recurseReflect ( current, parts )
Console.Log ("Found value", r, "in reflecting cfg for", q)
return r
}

func (s *Config) Persist ( filename string ) error {
f, err := os.Create ( filename )
func ConfigWrite ( cfg *Config, filename string ) error {
b, err := json.MarshalIndent ( cfg, "", " " )
if err != nil {
Console.Log ( "Could not open file ", filename, " for persisting configs, creating" );
f2, err2 := os.Create ( s.filename );
if err2 != nil {
Console.Fail ("Could not create config-file, aborting")
return err2
}
f = f2;
return err
}
defer f.Close();
w := bufio.NewWriter ( f );

var keys []string
for k := range s.storage {
keys = append(keys, k);
}

sort.Strings ( keys );

for _,k := range keys {
_, err = fmt.Fprintf ( w, "%s=%s\n", k, s.storage[k] )
if err != nil {
Console.Log ( "Could not write to config file" )
}
}
w.Flush()
return nil
err = ioutil.WriteFile ( filename, b, 0644 )
return err
}

func (s *Config) Debug () {
Console.Log ("Global:")
var keys []string
for k := range s.storage {
keys = append(keys, k);
}
sort.Strings ( keys );
for _,k := range keys {
Console.Log ( " -- ",k, " = ", s.storage[k] )
}

Console.Log("")
Console.Log("Local:")
var keyt []string
for k := range s.temp {
keyt = append(keyt, k);
}
sort.Strings ( keyt );
for _,k := range keyt {
Console.Log ( " -- ",k, " = ", s.temp[k] )
}
func GenericJSONDecode ( rd io.Reader, pl interface{} ) error {
dec := json.NewDecoder( rd )
for dec.More() {
err := dec.Decode( pl )
if err != nil {
// early debug
//Console.Log ( "JSON-decode error: ", err )
return err
}
}
return nil
}

// reads the configuration from file to a KeyValueStore-object
func ConfigRead ( filename string ) KeyValueStore {
var kv KeyValueStore
kv = &Config {
filename: filename,
storage: make(map[string]string),
temp: make(map[string]string),
}

func ConfigRead ( filename string ) (Config, error) {
cfg = ConfigDefault()
f, err := os.Open ( filename )
if err != nil {
Console.Fail ("Cannot open config file")
panic ("")
// Todo: early console
//Console.Fail ("Cannot open config file")
return cfg, err;
}
defer f.Close();

s := bufio.NewScanner ( f )
for s.Scan () {
if s.Text()[0] != '#' {
v := strings.Split ( s.Text(), "=" )
kv.Set ( v[0], v[1] );
}
}
err = s.Err()
err = GenericJSONDecode ( f, &cfg )
if err != nil {
Console.Log ( "Fatal error", err )
// TODO: early config
//Console.Log ( "Fatal error", err )
//panic("")
return cfg, err;
}

return kv;
return cfg, nil;
}

+ 41
- 0
core/log.go View File

@@ -6,6 +6,8 @@ import (
)

type LogWrapper interface {
Debug ( ...interface{} )
Debugf ( string, ...interface{} )
Log ( ...interface{} )
Logf ( string, ...interface{} )
Warn ( ...interface{} )
@@ -25,6 +27,45 @@ var LogStderrEnable bool = false;
var f *os.File
var Console lwrapper

type ServerLogger struct {
f *os.File
}

func ServerLoggerNew ( logfile string ) ( *ServerLogger, error ) {
var err error;
f, err = os.OpenFile( logfile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644 )
if err != nil {
return nil, err
}

var s ServerLogger
s.f = f;
return &s, nil;
}

func (s ServerLogger) Close () {
s.f.Close()
}

func (s ServerLogger) Write ( p []byte ) ( n int, err error ) {
s.f.Write ( p );
s.f.Close();
return os.Stdout.Write ( p );
}

func (lw lwrapper) Debug ( args ...interface{} ) {
if cfg.Debug {
args = append ( []interface{}{"[DBUG]"}, args...)
lw.l.Println ( args... )
}
}

func (lw lwrapper) Debugf ( format string, args ...interface{} ) {
if cfg.Debug {
lw.l.Printf ( "[DBUG]" + format, args... );
}
}

func (lw lwrapper) Log ( args ...interface{} ) {
args = append ( []interface{}{"[INFO]"}, args...)
lw.l.Println ( args... )


+ 24
- 21
core/models.go View File

@@ -43,9 +43,12 @@ type Group struct {
// structure for LoginTokens, used for authentification after login
type LoginToken struct {
ID uint `gorm:"primary_key"` // database ID
CreatedAt time.Time // date of creation
ExpiresAt time.Time // Expiration Date of the loginToken
Name string
UserID uint // Link to the user holding the Token
User User
Type string
Secret string // a secret string
}

@@ -85,32 +88,32 @@ type Build struct {
// parses the database configuration values and sets a configuration string
// for GORM accordingly, so it can set up the database connection.
// used by DBConnect
func connection_settings ( cfg KeyValueStore ) string {
func connection_settings ( cfg *Config ) string {
var x string
switch cfg.Get("database_provider") {
switch cfg.Database.Provider {
case "sqlite3":
x = cfg.Get("database_filename")
x = cfg.Database.Filename
case "mysql":
x = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True",
cfg.Get("database_username"),
cfg.Get("database_password"),
cfg.Get("database_host"),
cfg.Get("database_dbname"))
cfg.Database.Username,
cfg.Database.Password,
cfg.Database.Host,
cfg.Database.Dbname)
case "postgres":
x = fmt.Sprintf("user=%s password=%s host=%s dbname=%s port=%s sslmode=%s",
cfg.Get("database_username"),
cfg.Get("database_password"),
cfg.Get("database_host"),
cfg.Get("database_dbname"),
cfg.Get("database_port"),
cfg.Get("database_postgres_ssl"))
cfg.Database.Username,
cfg.Database.Password,
cfg.Database.Host,
cfg.Database.Dbname,
cfg.Database.Port,
cfg.Database.PostgresSSL)
case "mssql":
x = fmt.Sprintf("sqlserver://%s:%s@%s:%s?database=%s",
cfg.Get("database_username"),
cfg.Get("database_password"),
cfg.Get("database_host"),
cfg.Get("database_port"),
cfg.Get("database_dbname"))
cfg.Database.Username,
cfg.Database.Password,
cfg.Database.Host,
cfg.Database.Port,
cfg.Database.Dbname)
}
return x;
}
@@ -125,11 +128,11 @@ func connection_settings ( cfg KeyValueStore ) string {
// - database_filename : only sqlite3 - file (can be :memory)
// - database_port : communication port of the DB
// - database_dbname : the name of the database to use
func DBConnect ( cfg KeyValueStore ) (*gorm.DB, error) {
db,err := gorm.Open( cfg.Get("database_provider"), connection_settings ( cfg ) );
func DBConnect ( cfg *Config ) (*gorm.DB, error) {
db,err := gorm.Open( cfg.Database.Provider, connection_settings ( cfg ) );
if err == nil {
db.AutoMigrate ( &User{}, &Group{}, &LoginToken{}, &Repository{}, &Build{} );
if cfg.isDebug() {
if cfg.Debug {
db.Debug();
db.LogMode ( true );
}


+ 21
- 24
main.go View File

@@ -22,7 +22,7 @@ import (
)

// send a webhook to the local instance of mvoCI to trigger a build event
func SendLocalBuild ( db *gorm.DB, port string, rID uint, secret string ) error {
func SendLocalBuild ( db *gorm.DB, port int, rID uint, secret string ) error {
var r core.Repository
db.Where("id = ?", rID).First( &r )
if r.ID != rID {
@@ -49,7 +49,7 @@ func SendLocalBuild ( db *gorm.DB, port string, rID uint, secret string ) error
}

// send JSON data to the local instance (find out the correct URL)
url := "http://127.0.0.1:" + port + "/push/hook/local"
url := "http://127.0.0.1:" + strconv.FormatInt ( int64(port), 10 ) + "/push/hook/local"
req, _ := http.NewRequest( "POST", url, bytes.NewBuffer( b ) )
req.Header.Set("Content-Type", "application/json")

@@ -72,7 +72,6 @@ func SendLocalBuild ( db *gorm.DB, port string, rID uint, secret string ) error
// entry point of mvoCI, sets up the config data structure for the rest of the
// system and bootstraps the database and the web-server
func main() {
ConfigFile := "mvo.cfg"

// set up command line parameters
var install bool;
@@ -82,6 +81,7 @@ func main() {
var localhook bool;
var repositoryID int;
var localSecret string;
var err error
flag.BoolVar ( &w, "web", false, "Starts the complete web-application, if not: only webhook and local hook");
flag.BoolVar ( &install, "install", false, "Starts " + os.Args[0] + " with installer enabled")
flag.IntVar ( &port, "port", 0, "Specify the port to bind to")
@@ -92,19 +92,17 @@ func main() {
flag.Parse()

// read the config
var cfg core.KeyValueStore;
cfg = core.ConfigRead ( ConfigFile )
var cfg core.Config;
cfg, err = core.ConfigRead ( core.ConfigFile )
if ( install ) {
cfg.SetTemp ("install", "true");
} else {
cfg.SetTemp ("install", "false");
cfg.Install = true
}

os.Mkdir ( "log", 0777 )
lf := cfg.Get ( "LogFile" );
lfenable := cfg.Get ( "LogFileEnable");
lmode := cfg.Get ( "LogMode" );
if lfenable == "true" && len(lf) > 0 {
lf := cfg.LogFile;
lfenable := cfg.LogFileEnable;
lmode := cfg.LogMode;
if lfenable == true && len(lf) > 0 {
core.LogFileEnable = true;
}
if lmode == "stdout" {
@@ -116,11 +114,11 @@ func main() {
}

if ( d ) {
cfg.SetTemp ("debug", "true");
cfg.Debug = true;
}

if ( port != 0 ) {
cfg.SetTemp ( "http_port", strconv.Itoa( port ) )
cfg.HttpPort = port
}

core.LogInit ( lf )
@@ -128,27 +126,26 @@ func main() {
Console.Log ("Read Configuration (", ConfigFile, ") successfully" )
// set up gorm database connection
var db *gorm.DB;
var err error
if cfg.GetTemp ("install") == "true" {
if cfg.Install == true {
Console.Log ("Installation mode: Skipping Database connection");
db = nil
} else {
Console.Log ("Connecting to database: ", cfg.Get("database_provider") );
db, err = core.DBConnect ( cfg );
Console.Log ("Connecting to database: ", cfg.Database.Provider );
db, err = core.DBConnect ( &cfg );
if err != nil {
Console.Log ("Cannot connect to database: ", err )
}
}

// send build hook locally
if cfg.GetTemp("install") != "true" && localhook {
if cfg.Install != true && localhook {
if repositoryID < 0 {
fmt.Println ("[FAIL] No valid repository ID given")
} else {
if db == nil {
fmt.Println ("[FAIL] Cannot procede with no database connection")
}
err = SendLocalBuild( db, cfg.Get("http_port"), uint(repositoryID), localSecret )
err = SendLocalBuild( db, cfg.HttpPort, uint(repositoryID), localSecret )
if err == nil {
fmt.Println ("[ OK ] Sending hook for R (", repositoryID, ") successful")
} else {
@@ -158,14 +155,14 @@ func main() {
return;
}

if cfg.GetTemp ("install") != "true" {
if cfg.Install == false {
Console.Log ( "Setting up build workers" )
build.SetupWorkerThreads ( cfg, db );
build.SetupWorkerThreads ( &cfg, db );
}

Console.Log ( "Starting: %s", cfg.Get("app_title") )
Console.Log ( "Starting: %s", cfg.AppTitle )

// start eh web server and hand everything over to it
server := web.NewServer ( cfg, db )
server := web.NewServer ( &cfg, db )
server.Start();
}

+ 36
- 20
mvo.cfg View File

@@ -1,20 +1,36 @@
LogFile=log/mvo.log
LogFileEnable=true
LogMode=stderr
app_title=mvoCI
compression_method=gz
database_filename=db2.sqlite
database_provider=sqlite3
debug=true
dir_build=builds/
dir_repo=repo/
host=localhost
http_port=4042
impress_city=myCity
impress_mail=example@test.org
impress_owner=Mr LDR
impress_privacy=Hello.!<br />This is an example text for privacy information.
impress_street=Fake Street 42
impress_zip=09126
login_token_duration=300
parallel_builds=2
{
"AppTitle": "mvoCI",
"Install": false,
"Debug": false,
"HttpPort": 4042,
"HttpHost": "localhost",
"LogFile": "log/godate.log",
"LogServer": "log/server.log",
"LogFileEnable": true,
"LogMode": "stderr",
"LoginTokenDuration": 300,
"ParallelBuilds": 2,
"CompressionMethod": "gz",
"Directory": {
"Repo": "repo/",
"Build": "build/"
},
"Author": {
"Email": "",
"Name": "",
"Street": "",
"Zip": "",
"City": "",
"Privacy": ""
},
"Database": {
"Provider": "sqlite3",
"Username": "",
"Password": "",
"Port": 0,
"Host": "",
"Dbname": "",
"Filename": "db.sqlite",
"PostgresSSL": ""
}
}

+ 5
- 5
static/img/logo.svg View File

@@ -45,15 +45,15 @@
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1034"
inkscape:window-height="1080"
id="namedview64"
showgrid="false"
inkscape:zoom="5.6568543"
inkscape:cx="76.569969"
inkscape:cy="29.805114"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="1"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="g6"
showguides="true"
inkscape:guide-bbox="true">
@@ -83,7 +83,7 @@
style="fill:#353535;stroke-width:33.80500031;stroke-linecap:round;fill-opacity:1"
id="g6">
<path
style="fill:#353535;fill-opacity:1;stroke:#ffffff;stroke-width:2.1381135;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2.1381135;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 186.58107,8.5597827 c -3.04998,0.079472 -6.07866,0.2239661 -9.11359,0.4472892 -0.96501,0.070979 -1.92647,0.2099576 -2.88928,0.2955303 L 161.23814,53.656116 c -9.68153,2.005241 -19.12496,4.955927 -28.20038,8.817986 L 95.508887,34.167088 c -1.253868,0.720875 -2.51125,1.406712 -3.74891,2.156573 -2.589209,1.568291 -5.141566,3.202838 -7.657004,4.896219 -2.515254,1.693476 -5.006233,3.446696 -7.4421,5.263636 -2.435966,1.816841 -4.828196,3.708443 -7.179437,5.647023 -0.934332,0.770462 -1.819936,1.595788 -2.738055,2.388207 l 15.664223,43.219315 c -0.03383,0.0367 -0.05376,0.08306 -0.08755,0.11981 l 0.111431,0.359431 C 75.843973,105.42222 69.917931,113.37201 64.801273,122.03545 L 17.63349,120.74948 c -0.935446,2.12699 -1.898035,4.25384 -2.746014,6.39784 -1.155324,2.92093 -2.220691,5.83814 -3.215625,8.78604 -0.994646,2.94799 -1.9341111,5.93615 -2.7698925,8.90584 -0.7487438,2.66062 -1.3998712,5.30742 -2.0217039,7.97932 l 38.5954424,25.91082 c -0.04692,0.42482 -0.01272,0.85256 -0.0557,1.27798 l -0.01592,0.66295 c -0.443811,4.51732 -0.402606,8.55682 -0.405932,13.05924 9.54e-4,4.51982 0.205298,9.05974 0.652676,13.5944 l -39.07303,26.24628 c 0.011456,0.0505 0.044232,0.0932 0.055716,0.14378 0.678641,2.98625 1.4326661,5.96799 2.2684473,8.9378 0.8357814,2.9697 1.7434107,5.926 2.7380557,8.87389 0.994646,2.9479 2.060397,5.86511 3.215624,8.78603 0.838716,2.12048 1.755927,4.242 2.682339,6.34193 l 46.817566,-1.28596 c 0.313061,0.53094 0.690855,1.02231 1.010852,1.54954 h 0.294502 c 2.29473,3.78049 4.738219,7.43976 7.330667,10.94261 2.592445,3.50228 5.324803,6.85765 8.190288,10.07201 l -15.903009,43.81036 c 1.381851,1.20939 2.748737,2.42044 4.162798,3.5863 2.351241,1.93859 4.743569,3.83019 7.179438,5.64703 2.435964,1.81694 4.934898,3.57025 7.450058,5.26364 2.515254,1.69348 5.059934,3.32792 7.649043,4.89622 0.740751,0.44874 1.485074,0.89016 2.23661,1.3179 l 37.162724,-28.0674 c 0.54098,0.23805 1.09713,0.40007 1.63967,0.63101 l 0.0875,-0.0638 c 4.10287,1.75096 8.3006,3.28976 12.54413,4.66458 4.24381,1.37483 8.53694,2.56102 12.89433,3.55437 l 0.0557,0.15175 c 0.0212,0.005 0.0426,0.0271 0.0636,0.032 l 13.37189,44.52923 c 1.88066,0.1947 3.73916,0.42818 5.62734,0.56711 3.03493,0.22345 6.0874,0.33592 9.13747,0.41534 1.29385,0.0335 2.59423,0.026 3.89218,0.0239 V 253.36275 c -0.21419,0.009 -0.43102,0.0217 -0.64472,0.0159 -0.32826,-0.009 -0.65129,-0.0422 -0.97901,-0.0559 -1.63696,-0.0546 -3.27278,-0.20559 -4.91099,-0.39137 -7.03162,-0.79745 -14.05958,-2.72811 -20.73441,-6.06238 -21.07751,-10.52922 -33.18202,-31.43363 -33.3183,-53.13954 0.017,-2.71331 0.24239,-5.43019 0.62878,-8.09912 0.38641,-2.66885 0.95166,-5.3043 1.69537,-7.89147 0.74372,-2.58716 1.64897,-5.11963 2.73806,-7.58795 1.08908,-2.46828 2.3584,-4.86036 3.78076,-7.17259 1.42235,-2.31223 2.99235,-4.55035 4.73586,-6.6694 1.74353,-2.11904 3.67028,-4.11798 5.72286,-6.00645 2.05256,-1.88857 4.23286,-3.66671 6.58248,-5.2876 2.34962,-1.62089 4.8711,-3.10877 7.50577,-4.42498 1.88018,-0.93926 3.75695,-1.74359 5.68307,-2.47605 1.92609,-0.73246 3.88402,-1.38705 5.84223,-1.91696 1.95824,-0.52991 3.91349,-0.95409 5.89001,-1.28595 0.92866,-0.15591 1.87131,-0.2146 2.80174,-0.32747 0.31813,-0.0412 0.63616,-0.0836 0.95512,-0.11982 0.73312,-0.0774 1.47163,-0.21244 2.20477,-0.26359 0.71669,-0.0501 1.4251,-0.007 2.1411,-0.032 0.65965,-0.0285 1.32098,-0.0399 1.98191,-0.0479 V 8.56777 c -0.36379,0.00137 -0.72659,-0.00918 -1.09044,-0.00798 -1.02504,0.010922 -2.04106,-0.026644 -3.0644,0 z"
id="path2-3"
inkscape:connector-curvature="0" />


+ 4
- 4
views/install2.html View File

@@ -16,10 +16,10 @@
Select which database you are running.
</div>
<select id="db_type" name="db_type" size="1">
<option value="sqlite3"{{ if eq (config "database_provider") "sqlite3"}} selected{{else}}{{end}}>SQLite</option>
<option value="postgres"{{ if eq (config "database_provider") "postgres"}} selected{{else}}{{end}}>PostgreSQL</option>
<option value="mysql"{{ if eq (config "database_provider") "mysql"}} selected{{else}}{{end}}>MySQL / MariaDB</option>
<option value="mssql"{{ if eq (config "database_provider") "mssql"}} selected{{else}}{{end}}>MSSQL</option>
<option value="sqlite3"{{ if eq (config "Database.Provider") "sqlite3"}} selected{{else}}{{end}}>SQLite</option>
<option value="postgres"{{ if eq (config "Database.Provider") "postgres"}} selected{{else}}{{end}}>PostgreSQL</option>
<option value="mysql"{{ if eq (config "Database.Provider") "mysql"}} selected{{else}}{{end}}>MySQL / MariaDB</option>
<option value="mssql"{{ if eq (config "Database.Provider") "mssql"}} selected{{else}}{{end}}>MSSQL</option>
</select>
</div>
<div class="group" id="sqlite3_group">


+ 33
- 1
views/user_edit.html View File

@@ -51,7 +51,7 @@
<label class="checkbox">
<input type="checkbox" name="user_superuser" {{ if .u.Superuser }}checked{{ end }} />
Superuser
</label>
</label>
</div>

<button class="btn-primary">
@@ -63,5 +63,37 @@
Delete User
</a>
{{ end }}

<h2>Login Credentials</h2>

{{ range .LoginToken }}
<div class="block-link">
<span class="block">
<span class="left-side">
<span class="block-item block-icon">
<i class="fa fa-{{ if eq .Type "login" }}user{{else}}share-alt{{end}} black" title="{{ if eq .Type "login" }}Login{{else}}API{{end}}"></i>
</span>
<span class="block-item">
<span class="block-top-item">
{{ .Name }}
</span>
<span class="block-bottom-item block-fa">
{{ if ne .Type "login" }}{{.Secret}}{{ end }}
</span>
</span>
</span>
<span class="right-side">
<span class="block-item">
<span class="block-top-item block-fa">
<i class="fa fa-calendar-o"></i>{{ render_time .CreatedAt }}
</span>
</span>
<span class="block-item dashboard-icons">
<a href="/user/token/invalidate/{{ .ID }}" title="Invalidate" class="fa fa-trash"></a>
</span>
</span>
</span>
</div>
{{ end }}
</form>
{{ end }}

+ 2
- 0
web/auth.go View File

@@ -136,6 +136,8 @@ func NewLoginToken ( l *core.LoginToken, ctx echo.Context, u core.User ) bool {
return false
}
l.Secret = secret
l.Name = "Login"
l.Type = "login"
l.ExpiresAt = time.Now().Add ( 24*time.Hour )
l.User = u



+ 1
- 1
web/build.go View File

@@ -103,7 +103,7 @@ func buildDelete ( id uint ) uint {

s.db.Select("id, repository_id, zip").Where("id = ?", id).First ( &b )
rID = b.RepositoryID
os.Remove ( s.cfg.Get("dir_build") + b.Zip )
os.Remove ( s.cfg.Directory.Build + b.Zip )
s.db.Delete( &b )
return rID
}


+ 2
- 2
web/index.go View File

@@ -48,7 +48,7 @@ func dashboardHandler ( ctx echo.Context, user core.User ) error {

// redirects either to the dashboard, login-form or installation pages if enabled
func indexHandler ( ctx echo.Context ) error {
if s.cfg.Get("install") == "true" {
if s.cfg.Install == true {
return ctx.Redirect(http.StatusFound, "/install/1")
}
u := core.User{};
@@ -94,7 +94,7 @@ func doLoginHandler ( ctx echo.Context ) error {

// Renders the login form
func loginHandler ( ctx echo.Context ) error {
if s.cfg.Get("install") == "true" {
if s.cfg.Install == true {
return ctx.Redirect(http.StatusFound, "/install/1")
}
return ctx.Render (http.StatusOK, "login", echo.Map{});


+ 10
- 11
web/install.go View File

@@ -24,12 +24,11 @@ func dbConfigurationHandler ( ctx echo.Context ) ([]string,[]string) {
switch provider {
case "sqlite3":
file = ctx.FormValue ("sqlite3_file")
s.cfg.Set ( "database_provider", provider )
s.cfg.Set ( "database_filename", file )

s.cfg.Database.Provider = provider
s.cfg.Database.Filename = file
case "postgres":
sslrequired = ctx.FormValue ( "postgres_ssl" )
s.cfg.Set ( "database_postgres_ssl", sslrequired )
s.cfg.Database.PostgresSSL = sslrequired
fallthrough
case "mssql":
fallthrough
@@ -40,12 +39,12 @@ func dbConfigurationHandler ( ctx echo.Context ) ([]string,[]string) {
port, _ = strconv.Atoi ( ctx.FormValue ( provider + "_port" ))
dbname = ctx.FormValue ( provider + "_database" )

s.cfg.Set ( "database_provider", provider )
s.cfg.Set ( "database_host", host)
s.cfg.Set ( "database_port", strconv.Itoa ( port ))
s.cfg.Set ( "database_username", user )
s.cfg.Set ( "database_password", passwd )
s.cfg.Set ( "database_dbname", dbname )
s.cfg.Database.Provider = provider
s.cfg.Database.Host = host
s.cfg.Database.Port = port
s.cfg.Database.Username = user
s.cfg.Database.Password = passwd
s.cfg.Database.Dbname = dbname
default:
return []string{ "Invalid provider selected"}, []string{}
}
@@ -53,7 +52,7 @@ func dbConfigurationHandler ( ctx echo.Context ) ([]string,[]string) {
var err error
s.db, err = core.DBConnect ( s.cfg );
if s.db != nil {
s.cfg.Persist ("");
core.ConfigWrite ( s.cfg, core.ConfigFile );
return []string {}, []string{"Database configuration saved"}
} else {
return []string{"Cannot connect to the database: "+err.Error()}, []string{}


+ 26
- 16
web/routing.go View File

@@ -7,6 +7,7 @@ import (
"os"
"fmt"
"time"
"strconv"
"net/http"
"html/template"
"mvoCI/core"
@@ -23,7 +24,7 @@ const Version = "0.4.5"
// structure for globally known values inside the web-package
type Server struct {
echo *echo.Echo // global reference to the echo object
cfg core.KeyValueStore // global reference to the config object
cfg *core.Config // global reference to the config object
db *gorm.DB // global reference to the database connection
}
var s *Server
@@ -35,13 +36,13 @@ func dbErrorHandler ( ctx echo.Context ) error {

// the routing "table"
func (s *Server) Routes () {
if s.db != nil || (s.db == nil && s.cfg.Get("install") != "false") {
if s.db != nil || (s.db == nil && s.cfg.Install != false) {
s.echo.GET ( "/", indexHandler )
s.echo.GET ( "/login", loginHandler )
s.echo.GET ( "/impress", impressHandler )
s.echo.GET ( "/dashboard", indexHandler )

if s.cfg.Get("install") == "false" {
if s.cfg.Install == false {
s.echo.POST ( "/login", doLoginHandler )
s.echo.GET ( "/install/:id", loginHandler )
s.echo.GET ( "/install/", loginHandler )
@@ -74,7 +75,7 @@ func (s *Server) Routes () {
// WEBHOOK!
s.echo.POST ( "/push/hook/:api", webhook )
}
if s.cfg.Get("install") == "true" {
if s.cfg.Install == true {
s.echo.GET ( "/install/:id", installHandler )
s.echo.GET ( "/install/", installHandler )
s.echo.GET ( "/install", installHandler )
@@ -120,32 +121,39 @@ func RenderTime ( t time.Time ) string {
}

// sets up the echo server
func NewServer ( c core.KeyValueStore, d *gorm.DB ) Server {
func NewServer ( c *core.Config, d *gorm.DB ) Server {
server := Server{}
server.cfg = c
server.db = d

server.echo = echo.New()

server.echo.Use(middleware.Logger())
server.echo.Use(middleware.Recover())
var serverlogger *core.ServerLogger;
serverlogger, err := core.ServerLoggerNew ( c.LogServer );
if err == nil {
server.echo.Use(middleware.LoggerWithConfig(
middleware.LoggerConfig{Output: serverlogger}))
} else {
server.echo.Use(middleware.Logger())
}

server.echo.Use(middleware.Recover())
server.echo.Renderer = echoview.New(goview.Config{
Root: "views",
Extension: ".html",
Master: "layouts/master",
Funcs: template.FuncMap{
"app_title": func() string {
return server.cfg.Get("app_title")
return server.cfg.AppTitle
},
"app_version": func() string{
return Version
},
"config": func( q string ) string {
return server.cfg.Get( q )
return server.cfg.Reflect( q )
},
"config_html": func ( q string ) template.HTML {
return template.HTML ( server.cfg.Get ( q ) )
return template.HTML ( server.cfg.Reflect ( q ) )
},
"render_error_list": render_error_list,
"build_status": BuildStatus,
@@ -168,8 +176,9 @@ func NewServer ( c core.KeyValueStore, d *gorm.DB ) Server {
func HTTPError ( code int, ctx echo.Context ) error {
errorPage := fmt.Sprintf("/error/HTTP%d.html", code)
m := echo.Map{
"author_email": s.cfg.Get("author_email"),
"author_name": s.cfg.Get("author_name")}
"author_email": s.cfg.Author.Email,
"author_name": s.cfg.Author.Name,
}
if _, err := os.Stat ( "/view"+errorPage ); err != nil {
return ctx.Render(code, errorPage, m)
} else {
@@ -186,8 +195,9 @@ func customHTTPErrorHandler(err error, ctx echo.Context) {
}
errorPage := fmt.Sprintf("/error/HTTP%d.html", code)
m := echo.Map{
"author_email": s.cfg.Get("author_email"),
"author_name": s.cfg.Get("author_name")}
"author_email": s.cfg.Author.Email,
"author_name": s.cfg.Author.Name,
}
if err := ctx.Render(code, errorPage, m); err != nil {
ctx.Logger().Error(err)
}
@@ -196,6 +206,6 @@ func customHTTPErrorHandler(err error, ctx echo.Context) {

// starts the server and prints minor debug information
func (s* Server) Start() {
fmt.Print ("Starting server at "+s.cfg.Get("http_host") + ":" + s.cfg.Get("http_port"))
s.echo.Start(s.cfg.Get("http_host") + ":" + s.cfg.Get("http_port"))
fmt.Print ("Starting server at "+s.cfg.HttpHost + ":" + strconv.FormatInt( int64(s.cfg.HttpPort), 10 ))
s.echo.Start(s.cfg.HttpHost + ":" + strconv.FormatInt ( int64(s.cfg.HttpPort), 10 ))
}

+ 4
- 0
web/user.go View File

@@ -73,6 +73,9 @@ func userEditHandler ( ctx echo.Context ) error {
HTTPError ( 401, ctx );
}
}

var token []core.LoginToken
s.db.Where ( "user_id = ?", u.ID).Find ( &token );
return ctx.Render ( http.StatusOK, "user_edit", echo.Map{
"m": "edit",
"navPage": navPage,
@@ -80,6 +83,7 @@ func userEditHandler ( ctx echo.Context ) error {
"u": u,
"errors": []string{},
"success": []string{},
"LoginToken": token,
})
}



Loading…
Cancel
Save