summary refs log tree commit diff stats
path: root/svc/post_test.go
blob: 99c174a5fd9f1de50cb26b1cf438d184c45d817d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package svc // import "github.com/getwtxt/getwtxt/svc"

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"

	"github.com/getwtxt/registry"
)

var apiPostUserCases = []struct {
	name    string
	nick    string
	uri     string
	wantErr bool
}{
	{
		name:    "Known Good User",
		nick:    "gbmor",
		uri:     "https://gbmor.dev/twtxt.txt",
		wantErr: false,
	},
	{
		name:    "Missing URI",
		nick:    "missinguri",
		uri:     "",
		wantErr: true,
	},
	{
		name:    "Missing Nickname",
		nick:    "",
		uri:     "https://example.com/twtxt.txt",
		wantErr: true,
	},
	{
		name:    "Missing URI and Nickname",
		nick:    "",
		uri:     "",
		wantErr: true,
	},
}

func Test_apiPostUser(t *testing.T) {
	initTestConf()
	portnum := fmt.Sprintf(":%v", confObj.Port)
	twtxtCache = registry.NewIndex()

	for _, tt := range apiPostUserCases {
		t.Run(tt.name, func(t *testing.T) {
			params := url.Values{}
			params.Set("url", tt.uri)
			params.Set("nickname", tt.nick)

			req, err := http.NewRequest("POST", "https://localhost"+portnum+"/api/plain/users", strings.NewReader(params.Encode()))
			if err != nil {
				t.Errorf("%v\n", err)
			}

			req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
			rr := httptest.NewRecorder()
			apiEndpointPOSTHandler(rr, req)

			if !tt.wantErr {
				if rr.Code != http.StatusOK {
					t.Errorf("Received unexpected non-200 response: %v\n", rr.Code)
				}
			} else {
				if rr.Code != http.StatusBadRequest {
					t.Errorf("Expected 400 Bad Request, but received: %v\n", rr.Code)
				}
			}
		})
	}
}
div>

















                                                                                                       
                                                        







                                                                  
 
                                                            
                                                                             
 































































                                                                                                          

                                                                         


                                            

                                                        



                                                                             

                                                              








































                                                                             
                                                                         




                                                    
                                                        


                                            













                                                                        
package svc // import "github.com/getwtxt/getwtxt/svc"

import (
	"html/template"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/spf13/viper"
)

// Configuration object definition
type Configuration struct {
	Mu            sync.RWMutex
	Port          int           `yaml:"ListenPort"`
	LogFile       string        `yaml:"LogFile"`
	DBType        string        `yaml:"DatabaseType"`
	DBPath        string        `yaml:"DatabasePath"`
	AssetsDir     string        `yaml:"-"`
	StdoutLogging bool          `yaml:"StdoutLogging"`
	Version       string        `yaml:"-"`
	CacheInterval time.Duration `yaml:"StatusFetchInterval"`
	DBInterval    time.Duration `yaml:"DatabasePushInterval"`
	LastCache     time.Time     `yaml:"-"`
	LastPush      time.Time     `yaml:"-"`
	Instance      `yaml:"Instance"`
}

// Instance refers to this specific instance of getwtxt
type Instance struct {
	Vers  string `yaml:"-"`
	Name  string `yaml:"Instance.SiteName"`
	URL   string `yaml:"Instance.URL"`
	Owner string `yaml:"Instance.OwnerName"`
	Mail  string `yaml:"Instance.Email"`
	Desc  string `yaml:"Instance.Description"`
}

func initTemplates() *template.Template {
	confObj.Mu.RLock()
	assetsDir := confObj.AssetsDir
	confObj.Mu.RUnlock()

	return template.Must(template.ParseFiles(assetsDir + "/tmpl/index.html"))
}

func initLogging() {

	confObj.Mu.RLock()

	if confObj.StdoutLogging {
		log.SetOutput(os.Stdout)

	} else {

		logfile, err := os.OpenFile(confObj.LogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
		errLog("Could not open log file: ", err)

		// Listen for the signal to close the log file
		// in a separate thread. Passing it as an argument
		// to prevent race conditions when the config is
		// reloaded.
		go func(logfile *os.File) {

			<-closeLog

			log.Printf("Closing log file ...\n")
			errLog("Could not close log file: ", logfile.Close())

		}(logfile)

		log.SetOutput(logfile)
	}

	confObj.Mu.RUnlock()
}

func initConfig() {

	if *flagConfFile == "" {
		viper.SetConfigName("getwtxt")
		viper.SetConfigType("yml")
		viper.AddConfigPath(".")
		viper.AddConfigPath("/usr/local/getwtxt")
		viper.AddConfigPath("/etc")
		viper.AddConfigPath("/usr/local/etc")

	} else {
		path, file := filepath.Split(*flagConfFile)
		if path == "" {
			path = "."
		}
		if file == "" {
			file = *flagConfFile
		}
		filename := strings.Split(file, ".")
		viper.SetConfigName(filename[0])
		viper.SetConfigType(filename[1])
		viper.AddConfigPath(path)
	}

	log.Printf("Loading configuration ...\n")
	if err := viper.ReadInConfig(); err != nil {
		log.Printf("%v\n", err.Error())
		log.Printf("Using defaults ...\n")
	} else {
		viper.WatchConfig()
		viper.OnConfigChange(func(e fsnotify.Event) {
			log.Printf("Config file change detected. Reloading...\n")
			rebindConfig()
		})
	}

	viper.SetDefault("ListenPort", 9001)
	viper.SetDefault("LogFile", "getwtxt.log")
	viper.SetDefault("DatabasePath", "getwtxt.db")
	viper.SetDefault("AssetsDirectory", "assets")
	viper.SetDefault("DatabaseType", "leveldb")
	viper.SetDefault("StdoutLogging", false)
	viper.SetDefault("ReCacheInterval", "1h")
	viper.SetDefault("DatabasePushInterval", "5m")

	viper.SetDefault("Instance.SiteName", "getwtxt")
	viper.SetDefault("Instance.OwnerName", "Anonymous Microblogger")
	viper.SetDefault("Instance.Email", "nobody@knows")
	viper.SetDefault("Instance.URL", "https://twtxt.example.com")
	viper.SetDefault("Instance.Description", "A fast, resilient twtxt registry server written in Go!")

	confObj.Mu.Lock()

	confObj.Port = viper.GetInt("ListenPort")
	confObj.LogFile = viper.GetString("LogFile")

	confObj.DBType = strings.ToLower(viper.GetString("DatabaseType"))
	if *flagDBType != "" {
		confObj.DBType = *flagDBType
	}

	confObj.DBPath = viper.GetString("DatabasePath")
	if *flagDBPath != "" {
		confObj.DBPath = *flagDBPath
	}
	log.Printf("Using %v database: %v\n", confObj.DBType, confObj.DBPath)

	confObj.AssetsDir = viper.GetString("AssetsDirectory")
	if *flagAssets != "" {
		confObj.AssetsDir = *flagAssets
	}

	confObj.StdoutLogging = viper.GetBool("StdoutLogging")
	if confObj.StdoutLogging {
		log.Printf("Logging to stdout\n")
	} else {
		log.Printf("Logging to %v\n", confObj.LogFile)
	}

	confObj.CacheInterval = viper.GetDuration("StatusFetchInterval")
	log.Printf("User status fetch interval: %v\n", confObj.CacheInterval)

	confObj.DBInterval = viper.GetDuration("DatabasePushInterval")
	log.Printf("Database push interval: %v\n", confObj.DBInterval)

	confObj.LastCache = time.Now()
	confObj.LastPush = time.Now()
	confObj.Version = getwtxt

	confObj.Instance.Vers = getwtxt
	confObj.Instance.Name = viper.GetString("Instance.SiteName")
	confObj.Instance.URL = viper.GetString("Instance.URL")
	confObj.Instance.Owner = viper.GetString("Instance.OwnerName")
	confObj.Instance.Mail = viper.GetString("Instance.Email")
	confObj.Instance.Desc = viper.GetString("Instance.Description")

	confObj.Mu.Unlock()

}

func rebindConfig() {

	confObj.Mu.RLock()
	if !confObj.StdoutLogging {
		closeLog <- true
	}
	confObj.Mu.RUnlock()

	confObj.Mu.Lock()

	confObj.DBType = strings.ToLower(viper.GetString("DatabaseType"))
	if *flagDBType != "" {
		confObj.DBType = *flagDBType
	}

	confObj.LogFile = viper.GetString("LogFile")
	confObj.DBPath = viper.GetString("DatabasePath")
	if *flagDBPath != "" {
		confObj.DBPath = *flagDBPath
	}
	confObj.StdoutLogging = viper.GetBool("StdoutLogging")
	confObj.CacheInterval = viper.GetDuration("StatusFetchInterval")
	confObj.DBInterval = viper.GetDuration("DatabasePushInterval")

	confObj.Instance.Name = viper.GetString("Instance.SiteName")
	confObj.Instance.URL = viper.GetString("Instance.URL")
	confObj.Instance.Owner = viper.GetString("Instance.OwnerName")
	confObj.Instance.Mail = viper.GetString("Instance.Email")
	confObj.Instance.Desc = viper.GetString("Instance.Description")

	confObj.Mu.Unlock()

	initLogging()
}