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-09-26 08:05:25 +00:00
"compress/gzip"
2018-05-14 08:56:56 +00:00
"errors"
2018-06-24 13:49:29 +00:00
"fmt"
2017-06-25 09:56:39 +00:00
"io/ioutil"
2018-09-26 08:05:25 +00:00
"mime"
2017-06-25 09:56:39 +00:00
"net/http"
2017-09-03 04:50:31 +00:00
"os"
"path/filepath"
2018-09-26 08:05:25 +00:00
"strconv"
"strings"
"sync"
2018-05-14 08:56:56 +00:00
2018-10-27 03:21:02 +00:00
"github.com/Azareal/Gosora/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-06-24 13:49:29 +00:00
fragMap [ "topics_topic" ] = tmpl . GetFrag ( "topics_topic" )
fragMap [ "topic_posts" ] = tmpl . GetFrag ( "topic_posts" )
fragMap [ "topic_alt_posts" ] = tmpl . GetFrag ( "topic_alt_posts" )
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
}
2018-06-24 13:49:29 +00:00
path = strings . TrimPrefix ( path , "tmpl_client/" )
tmplName := strings . TrimSuffix ( path , ".go" )
shortName := strings . TrimPrefix ( tmplName , "template_" )
2018-05-14 08:56:56 +00:00
var replace = func ( data [ ] byte , replaceThis string , withThis string ) [ ] byte {
return bytes . Replace ( data , [ ] byte ( replaceThis ) , [ ] byte ( withThis ) , - 1 )
}
2018-06-24 13:49:29 +00:00
startIndex , hasFunc := skipAllUntilCharsExist ( data , 0 , [ ] byte ( "func init() {" ) )
2018-05-14 08:56:56 +00:00
if ! hasFunc {
2018-06-24 13:49:29 +00:00
return errors . New ( "no init function found" )
2018-05-14 08:56:56 +00:00
}
2018-06-24 13:49:29 +00:00
data = data [ startIndex - len ( [ ] byte ( "func init() {" ) ) : ]
2018-05-14 08:56:56 +00:00
data = replace ( data , "func " , "function " )
2018-06-24 13:49:29 +00:00
data = replace ( data , "function init() {" , "tmplInits[\"" + tmplName + "\"] = " )
2018-05-14 08:56:56 +00:00
data = replace ( data , " error {\n" , " {\nlet out = \"\"\n" )
2018-06-24 13:49:29 +00:00
funcIndex , hasFunc := skipAllUntilCharsExist ( data , 0 , [ ] byte ( "function Template_" ) )
if ! hasFunc {
return errors . New ( "no template function found" )
}
spaceIndex , hasSpace := skipUntilIfExists ( data , funcIndex , ' ' )
2018-05-14 08:56:56 +00:00
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-06-24 13:49:29 +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-06-24 13:49:29 +00:00
fmt . Println ( "new endBrace: " , endBrace )
fmt . Println ( "data: " , string ( data ) )
2018-05-14 08:56:56 +00:00
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
2018-06-24 13:49:29 +00:00
if index < 0 {
panic ( "index under zero: " + strconv . Itoa ( index ) )
}
2018-05-14 08:56:56 +00:00
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
}
} )
2018-11-22 07:21:43 +00:00
each ( " = []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
}
} )
2018-12-15 04:39:50 +00:00
each ( "w.Write(StringToBytes(" , 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 ( " = StringToBytes(" , 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
}
} )
2018-05-14 08:56:56 +00:00
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
}
} )
2018-06-24 13:49:29 +00:00
each ( "for _, item := range " , func ( index int ) {
//fmt.Println("for index: ", index)
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 , "for _, item := range " , "for(item of " )
2018-05-14 08:56:56 +00:00
data = replace ( data , "w.Write([]byte(" , "out += " )
2018-12-15 04:39:50 +00:00
data = replace ( data , "w.Write(StringToBytes(" , "out += " )
2018-05-14 08:56:56 +00:00
data = replace ( data , "w.Write(" , "out += " )
data = replace ( data , "strconv.Itoa(" , "" )
2018-09-26 08:05:25 +00:00
data = replace ( data , "strconv.FormatInt(" , "" )
2018-06-24 13:49:29 +00:00
data = replace ( data , "common." , "" )
2018-11-01 06:51:04 +00:00
data = replace ( data , "phrases." , "" )
2018-09-26 08:05:25 +00:00
data = replace ( data , ", 10;" , "" )
2018-06-24 13:49:29 +00:00
data = replace ( data , shortName + "_tmpl_phrase_id = RegisterTmplPhraseNames([]string{" , "[" )
2018-11-01 06:51:04 +00:00
data = replace ( data , "var plist = GetTmplPhrasesBytes(" + shortName + "_tmpl_phrase_id)" , "let plist = tmplPhrases[\"" + tmplName + "\"];" )
2018-06-24 13:49:29 +00:00
//data = replace(data, "var phrases = GetTmplPhrasesBytes("+shortName+"_tmpl_phrase_id)", "let phrases = tmplPhrases[\""+tmplName+"\"];\nconsole.log('tmplName:','"+tmplName+"')\nconsole.log('phrases:', phrases);")
2018-11-22 07:21:43 +00:00
data = replace ( data , "var cached_var_" , "let cached_var_" )
data = replace ( data , " = []byte(" , " = " )
2018-12-15 04:39:50 +00:00
data = replace ( data , " = StringToBytes(" , " = " )
2018-05-14 08:56:56 +00:00
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 , "};" , "}" )
2018-06-24 13:49:29 +00:00
data = replace ( data , "[;" , "[" )
2018-05-14 08:56:56 +00:00
data = replace ( data , ";;" , ";" )
2018-06-24 13:49:29 +00:00
data = replace ( data , ",;" , "," )
data = replace ( data , "=;" , "=" )
data = replace ( data , ` ,
} ) ;
} ` , "\n\t];" )
data = replace ( data , ` =
} ` , "= []" )
2018-05-14 08:56:56 +00:00
2018-06-24 13:49:29 +00:00
fragset , ok := fragMap [ shortName ]
2018-05-14 08:56:56 +00:00
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" )
}
2018-06-24 13:49:29 +00:00
var sfrags = [ ] byte ( "let " + shortName + "_frags = [];\n" )
2018-05-14 08:56:56 +00:00
for _ , frags := range fragset {
2018-06-24 13:49:29 +00:00
sfrags = append ( sfrags , [ ] byte ( shortName + "_frags.push(`" + string ( frags ) + "`);\n" ) ... )
2018-05-14 08:56:56 +00:00
}
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-08-21 08:00:35 +00:00
gzipData , err := compressBytesGzip ( data )
if err != nil {
return err
}
2018-05-14 08:56:56 +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 ) } )
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 )
2018-08-22 01:32:07 +00:00
mimetype := mime . TypeByExtension ( ext )
// Avoid double-compressing images
var gzipData [ ] byte
if mimetype != "image/jpeg" && mimetype != "image/png" && mimetype != "image/gif" {
gzipData , err = compressBytesGzip ( data )
if err != nil {
return err
}
// Don't use Gzip if we get meagre gains from it as it takes longer to process the responses
if len ( gzipData ) >= ( len ( data ) + 100 ) {
gzipData = nil
} else {
diff := len ( data ) - len ( gzipData )
if diff <= len ( data ) / 100 {
gzipData = nil
}
}
2018-08-21 08:00:35 +00:00
}
2017-06-25 09:56:39 +00:00
2018-08-22 01:32:07 +00:00
list . Set ( "/static/" + path , SFile { data , gzipData , 0 , int64 ( len ( data ) ) , int64 ( len ( gzipData ) ) , mimetype , 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 )
2018-08-21 08:00:35 +00:00
gzipData , err := compressBytesGzip ( data )
if err != nil {
return err
}
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
}
2018-08-21 08:00:35 +00:00
func compressBytesGzip ( in [ ] byte ) ( [ ] byte , error ) {
2017-02-10 13:39:13 +00:00
var buff bytes . Buffer
2018-08-21 08:00:35 +00:00
gz , err := gzip . NewWriterLevel ( & buff , gzip . BestCompression )
if err != nil {
return nil , err
}
_ , err = gz . Write ( in )
if err != nil {
return nil , err
}
err = gz . Close ( )
if err != nil {
return nil , err
}
return buff . Bytes ( ) , nil
2017-02-10 13:39:13 +00:00
}