2023-04-24 15:36:21 +01:00
package router
import (
"database/sql"
_ "embed"
2023-06-20 16:48:04 +01:00
"github.com/MrMelon54/rescheduler"
2023-06-03 19:33:06 +01:00
"github.com/MrMelon54/violet/proxy"
2023-04-24 15:36:21 +01:00
"github.com/MrMelon54/violet/target"
"log"
"net/http"
"sync"
)
// Manager is a database and mutex wrap around router allowing it to be
// dynamically regenerated after updating the database of routes.
type Manager struct {
db * sql . DB
s * sync . RWMutex
r * Router
2023-06-03 19:33:06 +01:00
p * proxy . HybridTransport
2023-06-20 16:48:04 +01:00
z * rescheduler . Rescheduler
2023-04-24 15:36:21 +01:00
}
var (
2023-07-12 16:55:09 +01:00
//go:embed create-tables.sql
createTables string
2023-04-24 15:36:21 +01:00
)
// NewManager create a new manager, initialises the routes and redirects tables
// in the database and runs a first time compile.
2023-06-03 19:33:06 +01:00
func NewManager ( db * sql . DB , proxy * proxy . HybridTransport ) * Manager {
2023-04-24 15:36:21 +01:00
m := & Manager {
db : db ,
s : & sync . RWMutex { } ,
2023-06-19 16:27:36 +01:00
r : New ( proxy ) ,
2023-04-24 15:36:21 +01:00
p : proxy ,
}
2023-06-20 16:48:04 +01:00
m . z = rescheduler . NewRescheduler ( m . threadCompile )
2023-04-24 15:36:21 +01:00
// init routes table
2023-07-12 16:55:09 +01:00
_ , err := m . db . Exec ( createTables )
2023-04-24 15:36:21 +01:00
if err != nil {
2023-07-12 16:55:09 +01:00
log . Printf ( "[WARN] Failed to generate tables\n" )
2023-04-24 15:36:21 +01:00
return nil
}
return m
}
func ( m * Manager ) ServeHTTP ( rw http . ResponseWriter , req * http . Request ) {
m . s . RLock ( )
m . r . ServeHTTP ( rw , req )
m . s . RUnlock ( )
}
func ( m * Manager ) Compile ( ) {
2023-06-20 16:48:04 +01:00
m . z . Run ( )
}
func ( m * Manager ) threadCompile ( ) {
// new router
router := New ( m . p )
2023-04-24 15:36:21 +01:00
2023-06-20 16:48:04 +01:00
// compile router and check errors
err := m . internalCompile ( router )
if err != nil {
log . Printf ( "[Manager] Compile failed: %s\n" , err )
return
}
2023-04-24 15:36:21 +01:00
2023-06-20 16:48:04 +01:00
// lock while replacing router
m . s . Lock ( )
m . r = router
m . s . Unlock ( )
2023-04-24 15:36:21 +01:00
}
// internalCompile is a hidden internal method for querying the database during
// the Compile() method.
func ( m * Manager ) internalCompile ( router * Router ) error {
log . Println ( "[Manager] Updating routes from database" )
// sql or something?
2023-07-12 16:55:09 +01:00
rows , err := m . db . Query ( ` SELECT source, destination, flags FROM routes WHERE active = 1 ` )
2023-04-24 15:36:21 +01:00
if err != nil {
return err
}
defer rows . Close ( )
// loop through rows and scan the options
for rows . Next ( ) {
var (
2023-07-12 16:55:09 +01:00
src , dst string
flags target . Flags
2023-04-24 15:36:21 +01:00
)
2023-07-12 16:55:09 +01:00
err := rows . Scan ( & src , & dst , & flags )
2023-04-24 15:36:21 +01:00
if err != nil {
return err
}
2023-07-12 16:55:09 +01:00
router . AddRoute ( target . Route {
Src : src ,
Dst : dst ,
Flags : flags . NormaliseRouteFlags ( ) ,
2023-04-24 15:36:21 +01:00
} )
}
// check for errors
if err := rows . Err ( ) ; err != nil {
return err
}
// sql or something?
2023-07-12 16:55:09 +01:00
rows , err = m . db . Query ( ` SELECT source,destination,flags,code FROM redirects WHERE active = 1 ` )
2023-04-24 15:36:21 +01:00
if err != nil {
return err
}
defer rows . Close ( )
// loop through rows and scan the options
for rows . Next ( ) {
var (
src , dst string
2023-07-12 16:55:09 +01:00
flags target . Flags
code int
2023-04-24 15:36:21 +01:00
)
2023-07-12 16:55:09 +01:00
err := rows . Scan ( & src , & dst , & flags , & code )
2023-04-24 15:36:21 +01:00
if err != nil {
return err
}
2023-07-12 16:55:09 +01:00
router . AddRedirect ( target . Redirect {
Src : src ,
Dst : dst ,
Flags : flags . NormaliseRedirectFlags ( ) ,
Code : code ,
2023-04-24 15:36:21 +01:00
} )
}
// check for errors
return rows . Err ( )
}
2023-07-13 00:15:00 +01:00
func ( m * Manager ) GetAllRoutes ( ) ( [ ] target . RouteWithActive , error ) {
s := make ( [ ] target . RouteWithActive , 0 )
2023-07-12 18:38:26 +01:00
query , err := m . db . Query ( ` SELECT source, destination, flags, active FROM routes ` )
if err != nil {
2023-07-13 00:15:00 +01:00
return nil , err
2023-07-12 18:38:26 +01:00
}
for query . Next ( ) {
2023-07-13 00:15:00 +01:00
var a target . RouteWithActive
if query . Scan ( & a . Src , & a . Dst , & a . Flags , & a . Active ) != nil {
return nil , err
2023-07-12 18:38:26 +01:00
}
2023-07-13 00:15:00 +01:00
s = append ( s , a )
2023-07-12 18:38:26 +01:00
}
2023-07-13 00:15:00 +01:00
return s , nil
2023-07-12 18:38:26 +01:00
}
2023-07-12 16:55:09 +01:00
func ( m * Manager ) InsertRoute ( route target . Route ) error {
_ , err := m . db . Exec ( ` INSERT INTO routes (source, destination, flags) VALUES (?, ?, ?) ON CONFLICT(source) DO UPDATE SET destination = excluded.destination, flags = excluded.flags, active = 1 ` , route . Src , route . Dst , route . Flags )
return err
2023-06-21 11:20:20 +01:00
}
2023-07-12 16:55:09 +01:00
func ( m * Manager ) DeleteRoute ( source string ) error {
_ , err := m . db . Exec ( ` UPDATE routes SET active = 0 WHERE source = ? ` , source )
return err
2023-04-24 15:36:21 +01:00
}
2023-07-13 00:15:00 +01:00
func ( m * Manager ) GetAllRedirects ( ) ( [ ] target . RedirectWithActive , error ) {
s := make ( [ ] target . RedirectWithActive , 0 )
2023-07-12 18:38:26 +01:00
query , err := m . db . Query ( ` SELECT source, destination, flags, code, active FROM redirects ` )
if err != nil {
2023-07-13 00:15:00 +01:00
return nil , err
2023-07-12 18:38:26 +01:00
}
for query . Next ( ) {
2023-07-13 00:15:00 +01:00
var a target . RedirectWithActive
if query . Scan ( & a . Src , & a . Dst , & a . Flags , & a . Code , & a . Active ) != nil {
return nil , err
2023-07-12 18:38:26 +01:00
}
2023-07-13 00:15:00 +01:00
s = append ( s , a )
2023-07-12 18:38:26 +01:00
}
2023-07-13 00:15:00 +01:00
return s , nil
2023-07-12 18:38:26 +01:00
}
2023-07-12 16:55:09 +01:00
func ( m * Manager ) InsertRedirect ( redirect target . Redirect ) error {
_ , err := m . db . Exec ( ` INSERT INTO redirects (source, destination, flags, code) VALUES (?, ?, ?, ?) ON CONFLICT(source) DO UPDATE SET destination = excluded.destination, flags = excluded.flags, code = excluded.code, active = 1 ` , redirect . Src , redirect . Dst , redirect . Flags , redirect . Code )
return err
2023-04-24 15:36:21 +01:00
}
2023-07-12 16:55:09 +01:00
func ( m * Manager ) DeleteRedirect ( source string ) error {
_ , err := m . db . Exec ( ` UPDATE redirects SET active = 0 WHERE source = ? ` , source )
return err
2023-04-24 15:36:21 +01:00
}