Browse Source

Refactor to use instances

Toni Fadjukoff 6 years ago
parent
commit
dcd28bbfa6
5 changed files with 227 additions and 211 deletions
  1. 4 5
      auth.go
  2. 55 67
      bot.go
  3. 142 6
      db.go
  4. 0 47
      songs.go
  5. 26 86
      web.go

+ 4 - 5
auth.go View File

118
 	return pbkdf2.Key(password, salt, 4096, 32, sha1.New)
118
 	return pbkdf2.Key(password, salt, 4096, 32, sha1.New)
119
 }
119
 }
120
 
120
 
121
-func userOk(username, password string) (bool, error) {
122
-	var hash string
123
-	err := getDb().QueryRow("SELECT u.password FROM public.user u WHERE lower(u.username) = lower($1)", username).Scan(&hash)
121
+func userOk(db *DB, username, password string) (bool, error) {
122
+	hash, err := db.FindHashForUser(username)
124
 	if err != nil {
123
 	if err != nil {
125
 		if err.Error() == "sql: no rows in result set" {
124
 		if err.Error() == "sql: no rows in result set" {
126
 			return false, nil
125
 			return false, nil
136
 	return ok, nil
135
 	return ok, nil
137
 }
136
 }
138
 
137
 
139
-func tryLogin(username, password string, longerTime bool) (http.Cookie, error) {
140
-	if exists, err := userOk(username, password); !exists {
138
+func tryLogin(db *DB, username, password string, longerTime bool) (http.Cookie, error) {
139
+	if exists, err := userOk(db, username, password); !exists {
141
 		if err != nil {
140
 		if err != nil {
142
 			return http.Cookie{}, err
141
 			return http.Cookie{}, err
143
 		}
142
 		}

+ 55 - 67
bot.go View File

3
 import (
3
 import (
4
 	"encoding/json"
4
 	"encoding/json"
5
 	"errors"
5
 	"errors"
6
+	"flag"
6
 	"fmt"
7
 	"fmt"
7
 	"github.com/jasonlvhit/gocron"
8
 	"github.com/jasonlvhit/gocron"
8
 	"github.com/lamperi/e4bot/spotify"
9
 	"github.com/lamperi/e4bot/spotify"
15
 	"time"
16
 	"time"
16
 )
17
 )
17
 
18
 
18
-var songs SongPriorityQueue
19
+type App struct {
20
+	db          *DB
21
+	credentials Credentials
22
+}
19
 
23
 
20
-var credentials struct {
24
+type Credentials struct {
21
 	APIURL              string
25
 	APIURL              string
22
 	UserName            string
26
 	UserName            string
23
 	Password            string
27
 	Password            string
26
 	ListenAddr          string
30
 	ListenAddr          string
27
 }
31
 }
28
 
32
 
33
+func (app *App) CreateSpotifyClient() *spotify.SpotifyClient {
34
+	spotifyClient := spotify.NewClient(app.credentials.SpotifyClientID, app.credentials.SpotifyClientSecret)
35
+	return spotifyClient
36
+}
37
+
38
+func (app *App) LaunchWeb() {
39
+	spotifyClient := app.CreateSpotifyClient()
40
+	go func() {
41
+		webStart(app.credentials.ListenAddr, app.db, spotifyClient)
42
+	}()
43
+}
44
+
29
 func appendSong(wikiText string, author string, newSong string) string {
45
 func appendSong(wikiText string, author string, newSong string) string {
30
 	const AUTHOR_MARK = "<!-- Lisääjä -->"
46
 	const AUTHOR_MARK = "<!-- Lisääjä -->"
31
 	const SONG_MARK = "<!-- Kappale -->"
47
 	const SONG_MARK = "<!-- Kappale -->"
46
 	return strings.Join(changedLines, "\n")
62
 	return strings.Join(changedLines, "\n")
47
 }
63
 }
48
 
64
 
49
-func addSong(updateTitle, updateSection, author, song string) (bool, error) {
50
-	wiki := CreateWikiClient(credentials.APIURL, credentials.UserName, credentials.Password)
65
+func (app *App) wikiClient() *WikiClient {
66
+	wiki := CreateWikiClient(app.credentials.APIURL, app.credentials.UserName, app.credentials.Password)
67
+	return wiki
68
+}
69
+
70
+func (app *App) AddSong(updateTitle, updateSection, author, song string) (bool, error) {
71
+	wiki := app.wikiClient()
51
 
72
 
52
 	sections, err := wiki.GetWikiPageSections(updateTitle)
73
 	sections, err := wiki.GetWikiPageSections(updateTitle)
53
 	if err != nil {
74
 	if err != nil {
76
 	return false, errors.New("Could not find matching section")
97
 	return false, errors.New("Could not find matching section")
77
 }
98
 }
78
 
99
 
79
-func songSynced(userId, roundId int) error {
80
-	query := `UPDATE public.entry SET synced = true WHERE user_id = $1 AND round_id = $2`
81
-	res, err := getDb().Exec(query, userId, roundId)
82
-
83
-	if err != nil {
84
-		return err
85
-	}
86
-
87
-	affected, err := res.RowsAffected()
88
-	if err != nil {
89
-		return err
90
-	}
91
-
92
-	if affected != 1 {
93
-		return errors.New("Unknown entry ID")
94
-	}
95
-	return nil
100
+func (app *App) SongSynced(userId, roundId int) error {
101
+	_, err := app.db.EntrySynced(userId, roundId)
102
+	return err
96
 }
103
 }
97
 
104
 
98
-func submitSong() {
99
-	query := `
100
-	SELECT e.user_id, e.round_id, e.artist, e.title, e.spotify_url, p.article, u.username, r.section
101
-	FROM public.entry e 
102
-	JOIN public."user" u ON u.id = e.user_id
103
-	JOIN public.round r ON r.id = e.round_id
104
-	JOIN public.panel p ON p.id = r.panel_id
105
-	WHERE r.start < current_timestamp AND e.synced = false`
106
-	rows, err := getDb().Query(query)
105
+func (app *App) SubmitSongs() {
106
+	entries, err := app.db.FindEntriesToSync()
107
 	if err != nil {
107
 	if err != nil {
108
-		log.Println("Error while reading songs from database:", err)
108
+		log.Println("Error while finding entries to sync:", err)
109
 		return
109
 		return
110
 	}
110
 	}
111
-	defer rows.Close()
112
-	for rows.Next() {
113
-		var (
114
-			userId, roundId                                       int
115
-			artist, title, spotifyURL, article, username, section string
116
-		)
117
-		err := rows.Scan(&userId, &roundId, &artist, &title, &spotifyURL, &article, &username, &section)
118
-		if err != nil {
119
-			log.Println("Error while scanning row:", err)
120
-			continue
121
-		}
122
-		song := songWikiText(spotifyURL, artist, title)
111
+
112
+	for _, entry := range entries {
113
+		song := songWikiText(entry.spotifyURL, entry.artist, entry.title)
123
 
114
 
124
 		fmt.Println("Time has passed for " + song)
115
 		fmt.Println("Time has passed for " + song)
125
 
116
 
126
-		success, err := addSong(article, section, username, song)
117
+		success, err := app.AddSong(entry.article, entry.section, entry.username, song)
127
 		if err != nil {
118
 		if err != nil {
128
 			log.Println("Error while adding song:", err)
119
 			log.Println("Error while adding song:", err)
129
 		}
120
 		}
130
 		if success {
121
 		if success {
131
-			err = songSynced(userId, roundId)
122
+			err = app.SongSynced(entry.userId, entry.roundId)
132
 			if err != nil {
123
 			if err != nil {
133
 				fmt.Println("Error received:", err)
124
 				fmt.Println("Error received:", err)
134
 			}
125
 			}
135
 		}
126
 		}
136
 	}
127
 	}
137
-	err = rows.Err()
138
-	if err != nil {
139
-		log.Println("Error after reading cursor:", err)
140
-		return
141
-	}
142
 }
128
 }
143
 
129
 
144
 func isCurrentAuthor(line, author string) bool {
130
 func isCurrentAuthor(line, author string) bool {
229
 	return strings.Join(changedLines, "\n")
215
 	return strings.Join(changedLines, "\n")
230
 }
216
 }
231
 
217
 
232
-func fixAverages(title string) error {
233
-	wiki := CreateWikiClient(credentials.APIURL, credentials.UserName, credentials.Password)
218
+func (app *App) FixAverages(title string) error {
219
+	wiki := app.wikiClient()
234
 
220
 
235
 	sections, err := wiki.GetWikiPageSections(title)
221
 	sections, err := wiki.GetWikiPageSections(title)
236
 	if err != nil {
222
 	if err != nil {
270
 	return nil
256
 	return nil
271
 }
257
 }
272
 
258
 
273
-func fixAveragesTask() {
274
-	err := fixAverages("Levyraati 2018")
259
+func (app *App) FixAveragesTask() {
260
+	err := app.FixAverages("Levyraati 2018")
275
 	if err != nil {
261
 	if err != nil {
276
 		fmt.Println("Error while calculating averages:", err)
262
 		fmt.Println("Error while calculating averages:", err)
277
 	}
263
 	}
278
 }
264
 }
279
 
265
 
280
-func initCreds() error {
281
-	f, err := os.Open("credentials.json")
266
+func initCreds() (Credentials, error) {
267
+	var credsFile string
268
+	flag.StringVar(&credsFile, "credentials", "credentials.json", "JSON config to hold app credentials")
269
+	flag.Parse()
270
+
271
+	var credentials Credentials
272
+	f, err := os.Open(credsFile)
282
 	if err != nil {
273
 	if err != nil {
283
-		return err
274
+		return credentials, err
284
 	}
275
 	}
285
 	defer f.Close()
276
 	defer f.Close()
286
 
277
 
287
 	if err != nil {
278
 	if err != nil {
288
 		log.Fatal(err)
279
 		log.Fatal(err)
289
-		return err
280
+		return credentials, err
290
 	}
281
 	}
291
 	dec := json.NewDecoder(f)
282
 	dec := json.NewDecoder(f)
292
 	for {
283
 	for {
296
 			log.Fatal(err)
287
 			log.Fatal(err)
297
 		}
288
 		}
298
 	}
289
 	}
299
-	return nil
290
+	return credentials, nil
300
 }
291
 }
301
 
292
 
302
 func songWikiText(url string, artist string, title string) string {
293
 func songWikiText(url string, artist string, title string) string {
304
 }
295
 }
305
 
296
 
306
 func main() {
297
 func main() {
307
-	err := initCreds()
298
+	creds, err := initCreds()
308
 	if err != nil {
299
 	if err != nil {
309
 		panic(err)
300
 		panic(err)
310
 	}
301
 	}
311
 
302
 
312
-	spotifyClient := spotify.NewClient(credentials.SpotifyClientID, credentials.SpotifyClientSecret)
313
-	go func() {
314
-		webStart(credentials.ListenAddr, spotifyClient)
315
-	}()
316
-
317
-	gocron.Every(1).Hour().Do(fixAveragesTask)
303
+	a := App{InitDatabase(), creds}
304
+	a.LaunchWeb()
318
 
305
 
319
-	gocron.Every(1).Second().Do(submitSong)
306
+	gocron.Every(1).Hour().Do(a.FixAveragesTask)
307
+	gocron.Every(1).Second().Do(a.SubmitSongs)
320
 	<-gocron.Start()
308
 	<-gocron.Start()
321
 
309
 
322
 }
310
 }

+ 142 - 6
db.go View File

2
 
2
 
3
 import (
3
 import (
4
 	"database/sql"
4
 	"database/sql"
5
+	"errors"
5
 	_ "github.com/lib/pq"
6
 	_ "github.com/lib/pq"
6
 	"log"
7
 	"log"
7
 )
8
 )
8
 
9
 
9
-var (
10
+type DB struct {
10
 	database *sql.DB
11
 	database *sql.DB
11
-)
12
+}
12
 
13
 
13
-func init() {
14
+func InitDatabase() *DB {
14
 	connStr := "user=levyraati dbname=levyraati sslmode=disable"
15
 	connStr := "user=levyraati dbname=levyraati sslmode=disable"
15
 	var err error
16
 	var err error
16
-	database, err = sql.Open("postgres", connStr)
17
+	database, err := sql.Open("postgres", connStr)
17
 	if err != nil {
18
 	if err != nil {
18
 		log.Fatal(err)
19
 		log.Fatal(err)
19
 	}
20
 	}
21
 	if err != nil {
22
 	if err != nil {
22
 		log.Fatal(err)
23
 		log.Fatal(err)
23
 	}
24
 	}
25
+	return &DB{database}
26
+}
27
+
28
+func (db *DB) FindHashForUser(username string) (string, error) {
29
+	var hash string
30
+	err := db.database.QueryRow("SELECT u.password FROM public.user u WHERE lower(u.username) = lower($1)", username).Scan(&hash)
31
+	return hash, err
32
+}
33
+
34
+func (db *DB) EntrySynced(userId, roundId int) (bool, error) {
35
+	query := `UPDATE public.entry SET synced = true WHERE user_id = $1 AND round_id = $2`
36
+	res, err := db.database.Exec(query, userId, roundId)
37
+
38
+	if err != nil {
39
+		return false, err
40
+	}
41
+
42
+	affected, err := res.RowsAffected()
43
+	if err != nil {
44
+		return false, err
45
+	}
46
+
47
+	if affected != 1 {
48
+		return false, errors.New("Unknown entry ID")
49
+	}
50
+	return true, nil
51
+
52
+}
53
+
54
+func (db *DB) FindEntriesToSync() ([]*EntryToSync, error) {
55
+	query := `
56
+	SELECT e.user_id, e.round_id, e.artist, e.title, e.spotify_url, p.article, u.username, r.section
57
+	FROM public.entry e 
58
+	JOIN public."user" u ON u.id = e.user_id
59
+	JOIN public.round r ON r.id = e.round_id
60
+	JOIN public.panel p ON p.id = r.panel_id
61
+	WHERE r.start < current_timestamp AND e.synced = false`
62
+	rows, err := db.database.Query(query)
63
+
64
+	if err != nil {
65
+		log.Println("Error while reading songs from database:", err)
66
+		return nil, err
67
+	}
68
+	defer rows.Close()
69
+	var entries []*EntryToSync
70
+	for rows.Next() {
71
+		var (
72
+			userId, roundId                                       int
73
+			artist, title, spotifyURL, article, username, section string
74
+		)
75
+		err := rows.Scan(&userId, &roundId, &artist, &title, &spotifyURL, &article, &username, &section)
76
+		if err != nil {
77
+			log.Println("Error while scanning row:", err)
78
+			return nil, err
79
+		}
80
+		entries = append(entries, &EntryToSync{userId, roundId, artist, title, spotifyURL, article, username, section})
81
+	}
82
+	err = rows.Err()
83
+	if err != nil {
84
+		log.Println("Error after reading cursor:", err)
85
+		return nil, err
86
+	}
87
+	return entries, nil
88
+}
89
+
90
+type EntryToSync struct {
91
+	userId, roundId                                       int
92
+	artist, title, spotifyURL, article, username, section string
24
 }
93
 }
25
 
94
 
26
-func getDb() *sql.DB {
27
-	return database
95
+func (db *DB) FindAllEntries(username string) ([]*Song, error) {
96
+	var songs []*Song
97
+	query := `
98
+		SELECT r.id, r.section, e.artist, e.title, e.spotify_url, e.synced
99
+		FROM public.round r
100
+		LEFT JOIN public.entry e ON r.id = e.round_id
101
+		LEFT JOIN public."user" u ON u.id = e.user_id AND lower(u.username) = lower($1)
102
+		ORDER BY r.start ASC`
103
+	rows, err := db.database.Query(query, username)
104
+	if err != nil {
105
+		return nil, err
106
+	}
107
+	defer rows.Close()
108
+	for i := 0; rows.Next(); i++ {
109
+		song := &Song{}
110
+		songs = append(songs, song)
111
+		var (
112
+			artist, title, url *string
113
+			sync               *bool
114
+		)
115
+		err = rows.Scan(&songs[i].RoundID, &songs[i].RoundName, &artist, &title, &url, &sync)
116
+		if err != nil {
117
+			return nil, err
118
+		}
119
+		if artist != nil {
120
+			song.Artist = *artist
121
+		}
122
+		if title != nil {
123
+			song.Title = *title
124
+		}
125
+		if url != nil {
126
+			song.URL = *url
127
+		}
128
+		if sync != nil {
129
+			song.Sync = *sync
130
+		}
131
+	}
132
+	return songs, nil
133
+}
134
+
135
+type Song struct {
136
+	RoundID   int
137
+	RoundName string
138
+	Title     string
139
+	Artist    string
140
+	URL       string
141
+	Sync      bool
142
+}
143
+
144
+func (db *DB) UpdateEntry(username, round, artist, title, url string) (bool, error) {
145
+	query := `
146
+	INSERT INTO public.entry
147
+	SELECT id, $2, $3, $4, $5, false
148
+	FROM public."user" u
149
+	WHERE lower(u.username) = lower($1)
150
+	ON CONFLICT (user_id, round_id) DO UPDATE SET artist = EXCLUDED.artist, title = EXCLUDED.title, spotify_url = EXCLUDED.spotify_url, synced = EXCLUDED.synced`
151
+	res, err := db.database.Exec(query, username, round, artist, title, url)
152
+
153
+	if err != nil {
154
+		return false, err
155
+	}
156
+	affected, err := res.RowsAffected()
157
+	if err != nil {
158
+		return false, err
159
+	}
160
+	if affected != 1 {
161
+		return false, nil
162
+	}
163
+	return true, nil
28
 }
164
 }

+ 0 - 47
songs.go View File

1
-package main
2
-
3
-import (
4
-	"time"
5
-)
6
-
7
-// From https://golang.org/pkg/container/heap/
8
-
9
-type Song struct {
10
-	time  time.Time
11
-	song  string
12
-	week  int
13
-	sync  bool
14
-	index int
15
-}
16
-
17
-type SongPriorityQueue []*Song
18
-
19
-func (pq SongPriorityQueue) Len() int {
20
-	return len(pq)
21
-}
22
-
23
-func (pq SongPriorityQueue) Less(i, j int) bool {
24
-	return pq[i].time.Before(pq[j].time)
25
-
26
-}
27
-func (pq SongPriorityQueue) Swap(i, j int) {
28
-	pq[i], pq[j] = pq[j], pq[i]
29
-	pq[i].index = i
30
-	pq[j].index = j
31
-}
32
-
33
-func (pq *SongPriorityQueue) Push(x interface{}) {
34
-	n := len(*pq)
35
-	item := x.(*Song)
36
-	item.index = n
37
-	*pq = append(*pq, item)
38
-}
39
-
40
-func (pq *SongPriorityQueue) Pop() interface{} {
41
-	old := *pq
42
-	n := len(old)
43
-	item := old[n-1]
44
-	item.index = -1 // for safety
45
-	*pq = old[0 : n-1]
46
-	return item
47
-}

+ 26 - 86
web.go View File

10
 
10
 
11
 var (
11
 var (
12
 	cachedTemplates = template.Must(template.ParseFiles("web/index.html"))
12
 	cachedTemplates = template.Must(template.ParseFiles("web/index.html"))
13
-	spotifyClient   *spotify.SpotifyClient
14
 )
13
 )
15
 
14
 
16
-func indexHandler(w http.ResponseWriter, r *http.Request) {
15
+func (s *WebService) IndexHandler(w http.ResponseWriter, r *http.Request) {
17
 	if r.URL.Path != "/" {
16
 	if r.URL.Path != "/" {
18
 		http.Error(w, "Not Found", http.StatusNotFound)
17
 		http.Error(w, "Not Found", http.StatusNotFound)
19
 		return
18
 		return
21
 
20
 
22
 	data := struct {
21
 	data := struct {
23
 		Username string
22
 		Username string
24
-		Songs    []*struct {
25
-			RoundID   int
26
-			RoundName string
27
-			Title     string
28
-			Artist    string
29
-			URL       string
30
-			Sync      bool
31
-		}
23
+		Songs    []*Song
32
 	}{
24
 	}{
33
 		"",
25
 		"",
34
-		make([]*struct {
35
-			RoundID   int
36
-			RoundName string
37
-			Title     string
38
-			Artist    string
39
-			URL       string
40
-			Sync      bool
41
-		}, 0),
26
+		make([]*Song, 0),
42
 	}
27
 	}
43
 
28
 
44
 	session, err := getSession(r)
29
 	session, err := getSession(r)
45
 	if session != nil {
30
 	if session != nil {
46
-		query := `
47
-		SELECT r.id, r.section, e.artist, e.title, e.spotify_url, e.synced
48
-		FROM public.round r
49
-		LEFT JOIN public.entry e ON r.id = e.round_id
50
-		LEFT JOIN public."user" u ON u.id = e.user_id AND lower(u.username) = lower($1)
51
-		ORDER BY r.start ASC`
52
-		rows, err := getDb().Query(query, session.username)
31
+		songs, err := s.db.FindAllEntries(session.username)
53
 		if err != nil {
32
 		if err != nil {
54
-			log.Println("Error while executing query", err)
33
+			log.Println("Error while reading entries from database", err)
55
 			http.Error(w, err.Error(), http.StatusInternalServerError)
34
 			http.Error(w, err.Error(), http.StatusInternalServerError)
56
 			return
35
 			return
57
 		}
36
 		}
58
-		defer rows.Close()
59
-		for i := 0; rows.Next(); i++ {
60
-			row := &struct {
61
-				RoundID   int
62
-				RoundName string
63
-				Title     string
64
-				Artist    string
65
-				URL       string
66
-				Sync      bool
67
-			}{}
68
-			data.Songs = append(data.Songs, row)
69
-			var (
70
-				artist, title, url *string
71
-				sync               *bool
72
-			)
73
-			err = rows.Scan(&data.Songs[i].RoundID, &data.Songs[i].RoundName, &artist, &title, &url, &sync)
74
-			if err != nil {
75
-				log.Println("Error while scanning cursor", err)
76
-				http.Error(w, err.Error(), http.StatusInternalServerError)
77
-				return
78
-			}
79
-			if artist != nil {
80
-				row.Artist = *artist
81
-			}
82
-			if title != nil {
83
-				row.Title = *title
84
-			}
85
-			if url != nil {
86
-				row.URL = *url
87
-			}
88
-			if sync != nil {
89
-				row.Sync = *sync
90
-			}
91
-		}
37
+		data.Songs = songs
92
 		data.Username = session.username
38
 		data.Username = session.username
93
 	}
39
 	}
94
 
40
 
95
 	var templates = cachedTemplates
41
 	var templates = cachedTemplates
96
-	if true {
42
+	if s.noCache {
97
 		templates = template.Must(template.ParseFiles("web/index.html"))
43
 		templates = template.Must(template.ParseFiles("web/index.html"))
98
 	}
44
 	}
99
 
45
 
115
 	return url
61
 	return url
116
 }
62
 }
117
 
63
 
118
-func updateHandler(w http.ResponseWriter, r *http.Request) {
64
+func (s *WebService) UpdateHandler(w http.ResponseWriter, r *http.Request) {
119
 	if r.URL.Path != "/update" {
65
 	if r.URL.Path != "/update" {
120
 		http.Error(w, "Forbidden", http.StatusForbidden)
66
 		http.Error(w, "Forbidden", http.StatusForbidden)
121
 		return
67
 		return
139
 	if artist == "" && title == "" && url != "" {
85
 	if artist == "" && title == "" && url != "" {
140
 		log.Println("Resolving Spotify URL")
86
 		log.Println("Resolving Spotify URL")
141
 		trackID := getTrackID(url)
87
 		trackID := getTrackID(url)
142
-		err := spotifyClient.Authenticate()
88
+		err := s.spotifyClient.Authenticate()
143
 		if err != nil {
89
 		if err != nil {
144
 			http.Error(w, err.Error(), http.StatusInternalServerError)
90
 			http.Error(w, err.Error(), http.StatusInternalServerError)
145
 			return
91
 			return
146
 		}
92
 		}
147
-		track, err := spotifyClient.GetTrack(trackID)
93
+		track, err := s.spotifyClient.GetTrack(trackID)
148
 
94
 
149
 		if err != nil {
95
 		if err != nil {
150
 			http.Error(w, err.Error(), http.StatusInternalServerError)
96
 			http.Error(w, err.Error(), http.StatusInternalServerError)
164
 		url = track.ExternalUrls.Spotify
110
 		url = track.ExternalUrls.Spotify
165
 	}
111
 	}
166
 
112
 
167
-	query := `
168
-	INSERT INTO public.entry
169
-	SELECT id, $2, $3, $4, $5, false
170
-	FROM public."user" u
171
-	WHERE lower(u.username) = lower($1)
172
-	ON CONFLICT (user_id, round_id) DO UPDATE SET artist = EXCLUDED.artist, title = EXCLUDED.title, spotify_url = EXCLUDED.spotify_url, synced = EXCLUDED.synced`
173
-	res, err := getDb().Exec(query, session.username, round, artist, title, url)
174
-
113
+	updated, err := s.db.UpdateEntry(session.username, round, artist, title, url)
175
 	if err != nil {
114
 	if err != nil {
176
 		http.Error(w, err.Error(), http.StatusInternalServerError)
115
 		http.Error(w, err.Error(), http.StatusInternalServerError)
177
 		return
116
 		return
178
 	}
117
 	}
179
-	affected, err := res.RowsAffected()
180
-	if err != nil {
181
-		http.Error(w, err.Error(), http.StatusInternalServerError)
182
-		return
183
-	}
184
-	if affected != 1 {
185
-		http.Error(w, err.Error(), http.StatusInternalServerError)
118
+	if !updated {
119
+		http.Error(w, "No round in DB", http.StatusNotFound)
186
 		return
120
 		return
187
 	}
121
 	}
188
 
122
 
192
 	http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
126
 	http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
193
 }
127
 }
194
 
128
 
195
-func loginHandler(w http.ResponseWriter, r *http.Request) {
129
+func (s *WebService) LoginHandler(w http.ResponseWriter, r *http.Request) {
196
 	err := r.ParseForm()
130
 	err := r.ParseForm()
197
 	if err != nil {
131
 	if err != nil {
198
 		http.Error(w, err.Error(), http.StatusBadRequest)
132
 		http.Error(w, err.Error(), http.StatusBadRequest)
203
 	remember := r.Form.Get("remember")
137
 	remember := r.Form.Get("remember")
204
 	longer := remember == "remember-me"
138
 	longer := remember == "remember-me"
205
 
139
 
206
-	cookie, err := tryLogin(username, password, longer)
140
+	cookie, err := tryLogin(s.db, username, password, longer)
207
 
141
 
208
 	if err != nil {
142
 	if err != nil {
209
 		log.Println("Error while trying to login", err.Error())
143
 		log.Println("Error while trying to login", err.Error())
215
 	http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
149
 	http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
216
 }
150
 }
217
 
151
 
218
-func webStart(listenAddr string, spot *spotify.SpotifyClient) {
219
-	spotifyClient = spot
152
+type WebService struct {
153
+	db            *DB
154
+	spotifyClient *spotify.SpotifyClient
155
+	noCache       bool
156
+}
157
+
158
+func webStart(listenAddr string, db *DB, spotifyClient *spotify.SpotifyClient) {
159
+	service := &WebService{db, spotifyClient, true}
220
 
160
 
221
 	mux := http.NewServeMux()
161
 	mux := http.NewServeMux()
222
 
162
 
225
 	mux.Handle("/fonts/", fs)
165
 	mux.Handle("/fonts/", fs)
226
 	mux.Handle("/js/", fs)
166
 	mux.Handle("/js/", fs)
227
 	mux.Handle("/favicon.ico", fs)
167
 	mux.Handle("/favicon.ico", fs)
228
-	mux.HandleFunc("/", indexHandler)
229
-	mux.HandleFunc("/update", updateHandler)
230
-	mux.HandleFunc("/login", loginHandler)
168
+	mux.HandleFunc("/", service.IndexHandler)
169
+	mux.HandleFunc("/update", service.UpdateHandler)
170
+	mux.HandleFunc("/login", service.LoginHandler)
231
 
171
 
232
 	http.ListenAndServe(listenAddr, mux)
172
 	http.ListenAndServe(listenAddr, mux)
233
 }
173
 }