2017-11-11 04:06:16 +00:00
package common
2017-02-10 13:39:13 +00:00
2017-06-25 09:56:39 +00:00
import (
"bytes"
2018-05-14 08:56:56 +00:00
"errors"
2017-06-25 09:56:39 +00:00
"mime"
2017-09-03 04:50:31 +00:00
"strings"
2017-12-01 02:04:29 +00:00
"sync"
2017-06-25 09:56:39 +00:00
//"errors"
2017-09-03 04:50:31 +00:00
"compress/gzip"
2017-06-25 09:56:39 +00:00
"io/ioutil"
"net/http"
2017-09-03 04:50:31 +00:00
"os"
"path/filepath"
2018-05-14 08:56:56 +00:00
2018-05-14 10:21:18 +00:00
"../tmpl_client"
2017-06-25 09:56:39 +00:00
)
2016-12-05 07:21:17 +00:00
2017-11-11 04:06:16 +00:00
type SFileList map [ string ] SFile
var StaticFiles SFileList = make ( map [ string ] SFile )
2017-12-01 02:04:29 +00:00
var staticFileMutex sync . RWMutex
2017-11-11 04:06:16 +00:00
2017-09-03 04:50:31 +00:00
type SFile struct {
Data [ ] byte
GzipData [ ] byte
Pos int64
Length int64
GzipLength int64
Mimetype string
Info os . FileInfo
2016-12-05 07:21:17 +00:00
FormattedModTime string
}
2017-09-18 17:03:52 +00:00
type CSSData struct {
2018-03-11 09:33:49 +00:00
Phrases map [ string ] string
2016-12-05 07:21:17 +00:00
}
2018-05-14 08:56:56 +00:00
func ( list SFileList ) JSTmplInit ( ) error {
2018-05-15 05:59:52 +00:00
DebugLog ( "Initialising the client side templates" )
2018-05-14 08:56:56 +00:00
var fragMap = make ( map [ string ] [ ] [ ] byte )
2018-05-14 10:59:18 +00:00
fragMap [ "alert" ] = tmpl . GetFrag ( "alert" )
2018-05-15 05:59:52 +00:00
DebugLog ( "fragMap: " , fragMap )
2018-05-14 10:21:18 +00:00
return filepath . Walk ( "./tmpl_client" , func ( path string , f os . FileInfo , err error ) error {
2018-05-14 08:56:56 +00:00
if f . IsDir ( ) {
return nil
}
2018-05-14 10:59:18 +00:00
if strings . HasSuffix ( path , "template_list.go" ) || strings . HasSuffix ( path , "stub.go" ) {
2018-05-14 08:56:56 +00:00
return nil
}
path = strings . Replace ( path , "\\" , "/" , - 1 )
DebugLog ( "Processing client template " + path )
data , err := ioutil . ReadFile ( path )
if err != nil {
return err
}
var replace = func ( data [ ] byte , replaceThis string , withThis string ) [ ] byte {
return bytes . Replace ( data , [ ] byte ( replaceThis ) , [ ] byte ( withThis ) , - 1 )
}
startIndex , hasFunc := skipAllUntilCharsExist ( data , 0 , [ ] byte ( "func Template" ) )
if ! hasFunc {
return errors . New ( "no template function found" )
}
data = data [ startIndex - len ( [ ] byte ( "func Template" ) ) : ]
data = replace ( data , "func " , "function " )
data = replace ( data , " error {\n" , " {\nlet out = \"\"\n" )
spaceIndex , hasSpace := skipUntilIfExists ( data , 10 , ' ' )
if ! hasSpace {
return errors . New ( "no spaces found after the template function name" )
}
endBrace , hasBrace := skipUntilIfExists ( data , spaceIndex , ')' )
if ! hasBrace {
return errors . New ( "no right brace found after the template function name" )
}
2018-05-15 05:59:52 +00:00
//fmt.Println("spaceIndex: ", spaceIndex)
//fmt.Println("endBrace: ", endBrace)
//fmt.Println("string(data[spaceIndex:endBrace]): ", string(data[spaceIndex:endBrace]))
2018-05-14 08:56:56 +00:00
preLen := len ( data )
data = replace ( data , string ( data [ spaceIndex : endBrace ] ) , "" )
data = replace ( data , "))\n" , "\n" )
endBrace -= preLen - len ( data ) // Offset it as we've deleted portions
2018-05-15 05:59:52 +00:00
/ * var showPos = func ( data [ ] byte , index int ) ( out string ) {
2018-05-14 08:56:56 +00:00
out = "["
for j , char := range data {
if index == j {
out += "[" + string ( char ) + "] "
} else {
out += string ( char ) + " "
}
}
return out + "]"
2018-05-15 05:59:52 +00:00
} * /
2018-05-14 08:56:56 +00:00
// ? Can we just use a regex? I'm thinking of going more efficient, or just outright rolling wasm, this is a temp hack in a place where performance doesn't particularly matter
var each = func ( phrase string , handle func ( index int ) ) {
2018-05-15 05:59:52 +00:00
//fmt.Println("find each '" + phrase + "'")
2018-05-14 08:56:56 +00:00
var index = endBrace
var foundIt bool
for {
2018-05-15 05:59:52 +00:00
//fmt.Println("in index: ", index)
//fmt.Println("pos: ", showPos(data, index))
2018-05-14 08:56:56 +00:00
index , foundIt = skipAllUntilCharsExist ( data , index , [ ] byte ( phrase ) )
if ! foundIt {
break
}
handle ( index )
}
}
each ( "strconv.Itoa(" , func ( index int ) {
braceAt , hasEndBrace := skipUntilIfExists ( data , index , ')' )
// TODO: Make sure we don't go onto the next line in case someone misplaced a brace
if hasEndBrace {
data [ braceAt ] = ' ' // Blank it
}
} )
each ( "w.Write([]byte(" , func ( index int ) {
braceAt , hasEndBrace := skipUntilIfExists ( data , index , ')' )
// TODO: Make sure we don't go onto the next line in case someone misplaced a brace
if hasEndBrace {
data [ braceAt ] = ' ' // Blank it
}
braceAt , hasEndBrace = skipUntilIfExists ( data , braceAt , ')' )
if hasEndBrace {
data [ braceAt ] = ' ' // Blank this one too
}
} )
each ( "w.Write(" , func ( index int ) {
braceAt , hasEndBrace := skipUntilIfExists ( data , index , ')' )
// TODO: Make sure we don't go onto the next line in case someone misplaced a brace
if hasEndBrace {
data [ braceAt ] = ' ' // Blank it
}
} )
each ( "if " , func ( index int ) {
2018-05-15 05:59:52 +00:00
//fmt.Println("if index: ", index)
2018-05-14 08:56:56 +00:00
braceAt , hasBrace := skipUntilIfExists ( data , index , '{' )
if hasBrace {
if data [ braceAt - 1 ] != ' ' {
panic ( "couldn't find space before brace, found ' " + string ( data [ braceAt - 1 ] ) + "' instead" )
}
data [ braceAt - 1 ] = ')' // Drop a brace here to satisfy JS
}
} )
data = replace ( data , "w.Write([]byte(" , "out += " )
data = replace ( data , "w.Write(" , "out += " )
data = replace ( data , "strconv.Itoa(" , "" )
data = replace ( data , "if " , "if(" )
data = replace ( data , "return nil" , "return out" )
data = replace ( data , " )" , ")" )
data = replace ( data , " \n" , "\n" )
data = replace ( data , "\n" , ";\n" )
data = replace ( data , "{;" , "{" )
data = replace ( data , "};" , "}" )
data = replace ( data , ";;" , ";" )
2018-05-14 10:21:18 +00:00
path = strings . TrimPrefix ( path , "tmpl_client/" )
2018-05-14 08:56:56 +00:00
tmplName := strings . TrimSuffix ( path , ".go" )
fragset , ok := fragMap [ strings . TrimPrefix ( tmplName , "template_" ) ]
if ! ok {
2018-05-15 05:59:52 +00:00
DebugLog ( "tmplName: " , tmplName )
2018-05-14 08:56:56 +00:00
return errors . New ( "couldn't find template in fragmap" )
}
var sfrags = [ ] byte ( "let alert_frags = [];\n" )
for _ , frags := range fragset {
sfrags = append ( sfrags , [ ] byte ( "alert_frags.push(`" + string ( frags ) + "`);\n" ) ... )
}
data = append ( sfrags , data ... )
data = replace ( data , "\n;" , "\n" )
path = tmplName + ".js"
DebugLog ( "js path: " , path )
2018-05-14 10:21:18 +00:00
var ext = filepath . Ext ( "/tmpl_client/" + path )
2018-05-14 08:56:56 +00:00
gzipData := compressBytesGzip ( data )
list . Set ( "/static/" + path , SFile { data , gzipData , 0 , int64 ( len ( data ) ) , int64 ( len ( gzipData ) ) , mime . TypeByExtension ( ext ) , f , f . ModTime ( ) . UTC ( ) . Format ( http . TimeFormat ) } )
DebugLogf ( "Added the '%s' static file." , path )
return nil
} )
}
2017-11-11 04:06:16 +00:00
func ( list SFileList ) Init ( ) error {
2017-09-03 04:50:31 +00:00
return filepath . Walk ( "./public" , func ( path string , f os . FileInfo , err error ) error {
2017-06-25 09:56:39 +00:00
if f . IsDir ( ) {
return nil
}
2017-09-03 04:50:31 +00:00
path = strings . Replace ( path , "\\" , "/" , - 1 )
2017-06-25 09:56:39 +00:00
data , err := ioutil . ReadFile ( path )
if err != nil {
return err
}
2017-09-03 04:50:31 +00:00
path = strings . TrimPrefix ( path , "public/" )
var ext = filepath . Ext ( "/public/" + path )
gzipData := compressBytesGzip ( data )
2017-06-25 09:56:39 +00:00
2017-12-01 02:04:29 +00:00
list . Set ( "/static/" + path , SFile { data , gzipData , 0 , int64 ( len ( data ) ) , int64 ( len ( gzipData ) ) , mime . TypeByExtension ( ext ) , f , f . ModTime ( ) . UTC ( ) . Format ( http . TimeFormat ) } )
2017-06-25 09:56:39 +00:00
2018-02-19 04:26:01 +00:00
DebugLogf ( "Added the '%s' static file." , path )
2017-06-25 09:56:39 +00:00
return nil
} )
}
2017-01-07 06:31:04 +00:00
2017-11-11 04:06:16 +00:00
func ( list SFileList ) Add ( path string , prefix string ) error {
2017-01-07 06:31:04 +00:00
data , err := ioutil . ReadFile ( path )
if err != nil {
return err
}
fi , err := os . Open ( path )
if err != nil {
return err
}
f , err := fi . Stat ( )
if err != nil {
return err
}
2017-06-25 09:56:39 +00:00
2017-09-03 04:50:31 +00:00
var ext = filepath . Ext ( path )
2017-01-07 06:31:04 +00:00
path = strings . TrimPrefix ( path , prefix )
2017-09-03 04:50:31 +00:00
gzipData := compressBytesGzip ( data )
2017-06-25 09:56:39 +00:00
2017-12-01 02:04:29 +00:00
list . Set ( "/static" + path , SFile { data , gzipData , 0 , int64 ( len ( data ) ) , int64 ( len ( gzipData ) ) , mime . TypeByExtension ( ext ) , f , f . ModTime ( ) . UTC ( ) . Format ( http . TimeFormat ) } )
2017-06-25 09:56:39 +00:00
2018-02-19 04:26:01 +00:00
DebugLogf ( "Added the '%s' static file" , path )
2017-01-07 06:31:04 +00:00
return nil
2017-02-10 13:39:13 +00:00
}
2017-12-01 02:04:29 +00:00
func ( list SFileList ) Get ( name string ) ( file SFile , exists bool ) {
staticFileMutex . RLock ( )
defer staticFileMutex . RUnlock ( )
file , exists = list [ name ]
return file , exists
}
func ( list SFileList ) Set ( name string , data SFile ) {
staticFileMutex . Lock ( )
defer staticFileMutex . Unlock ( )
list [ name ] = data
}
2017-09-03 04:50:31 +00:00
func compressBytesGzip ( in [ ] byte ) [ ] byte {
2017-02-10 13:39:13 +00:00
var buff bytes . Buffer
2017-06-25 09:56:39 +00:00
gz := gzip . NewWriter ( & buff )
2017-09-10 16:57:22 +00:00
_ , _ = gz . Write ( in ) // TODO: What if this errors? What circumstances could it error under? Should we add a second return value?
2017-09-03 04:50:31 +00:00
_ = gz . Close ( )
2017-02-10 13:39:13 +00:00
return buff . Bytes ( )
}