| @@ -0,0 +1,25 @@ | |||
| # This file specifies files that are *not* uploaded to Google Cloud Platform | |||
| # using gcloud. It follows the same syntax as .gitignore, with the addition of | |||
| # "#!include" directives (which insert the entries of the given .gitignore-style | |||
| # file at that point). | |||
| # | |||
| # For more information, run: | |||
| # $ gcloud topic gcloudignore | |||
| # | |||
| .gcloudignore | |||
| # If you would like to upload your .git directory, .gitignore file or files | |||
| # from your .gitignore file, remove the corresponding line | |||
| # below: | |||
| .git | |||
| .gitignore | |||
| # Binaries for programs and plugins | |||
| *.exe | |||
| *.exe~ | |||
| *.dll | |||
| *.so | |||
| *.dylib | |||
| # Test binary, build with `go test -c` | |||
| *.test | |||
| # Output of the go coverage tool, specifically when used with LiteIDE | |||
| *.out | |||
| @@ -0,0 +1,8 @@ | |||
| # Default ignored files | |||
| /shelf/ | |||
| /workspace.xml | |||
| # Datasource local storage ignored files | |||
| /dataSources/ | |||
| /dataSources.local.xml | |||
| # Editor-based HTTP Client requests | |||
| /httpRequests/ | |||
| @@ -0,0 +1,9 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <module type="WEB_MODULE" version="4"> | |||
| <component name="Go" enabled="true" /> | |||
| <component name="NewModuleRootManager"> | |||
| <content url="file://$MODULE_DIR$" /> | |||
| <orderEntry type="inheritedJdk" /> | |||
| <orderEntry type="sourceFolder" forTests="false" /> | |||
| </component> | |||
| </module> | |||
| @@ -0,0 +1,8 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <project version="4"> | |||
| <component name="ProjectModuleManager"> | |||
| <modules> | |||
| <module fileurl="file://$PROJECT_DIR$/.idea/goweb.iml" filepath="$PROJECT_DIR$/.idea/goweb.iml" /> | |||
| </modules> | |||
| </component> | |||
| </project> | |||
| @@ -0,0 +1,6 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <project version="4"> | |||
| <component name="VcsDirectoryMappings"> | |||
| <mapping directory="$PROJECT_DIR$" vcs="Git" /> | |||
| </component> | |||
| </project> | |||
| @@ -0,0 +1,11 @@ | |||
| package main | |||
| import ( | |||
| "net/http" | |||
| ) | |||
| const apiV1Prefix = "/api1/" | |||
| func apiV1Main(http.ResponseWriter, *http.Request) { | |||
| } | |||
| @@ -0,0 +1,67 @@ | |||
| package main | |||
| import ( | |||
| "fmt" | |||
| "github.com/gorilla/websocket" | |||
| "log" | |||
| "net/http" | |||
| ) | |||
| const apiV1WebSocket = apiV1Prefix + "ws" | |||
| // We'll need to define an upgradeToWs | |||
| // this will require a Read and Write buffer size | |||
| var upgradeToWs = websocket.Upgrader{ | |||
| ReadBufferSize: 1024, | |||
| WriteBufferSize: 1024, | |||
| } | |||
| func apiV1WebSocketHandler(w http.ResponseWriter, r *http.Request) { | |||
| upgradeToWs.CheckOrigin = func(r *http.Request) bool { return true } | |||
| ws, err := upgradeToWs.Upgrade(w, r, nil) | |||
| if err != nil { | |||
| log.Println("cannot upgrade websocket", err) | |||
| return | |||
| } | |||
| // helpful log statement to show connections | |||
| log.Println("Websocket Api/V1: Client Connected", r.RemoteAddr) | |||
| wsReader(ws) | |||
| } | |||
| func wsReader(conn *websocket.Conn) { | |||
| for { | |||
| // read in a message | |||
| messageType, p, err := conn.ReadMessage() | |||
| if err != nil { | |||
| log.Println(err) | |||
| return | |||
| } | |||
| WsEchoIncomingMessage(conn, string(p), messageType) | |||
| //switch messageType { | |||
| //case websocket.TextMessage: | |||
| // WsProcessingTxtMessage(conn, string(p)) | |||
| // break | |||
| //case websocket.BinaryMessage: | |||
| // WsProcessingBinaryMessage(conn, p) | |||
| // break | |||
| //case websocket.PingMessage: | |||
| // break | |||
| //case websocket.PongMessage: | |||
| // break | |||
| //} | |||
| } | |||
| } | |||
| func WsEchoIncomingMessage(conn *websocket.Conn, msg string, messageType int) { | |||
| // print out that message for clarity | |||
| fmt.Println(msg) | |||
| // this is echo | |||
| if err := conn.WriteMessage(messageType, []byte(msg)); err != nil { | |||
| log.Println(err) | |||
| return | |||
| } | |||
| } | |||
| @@ -0,0 +1 @@ | |||
| runtime: go115 | |||
| @@ -0,0 +1,48 @@ | |||
| package main | |||
| import ( | |||
| "encoding/json" | |||
| "io/ioutil" | |||
| "log" | |||
| ) | |||
| type configStaticHtml struct { | |||
| Dir string | |||
| StaticUrl string | |||
| StripPrefix string | |||
| } | |||
| type configuration struct { | |||
| Host string | |||
| Port string | |||
| DSN string | |||
| TlsCert string | |||
| TlsKey string | |||
| Static []configStaticHtml | |||
| Debug bool | |||
| TempDir string | |||
| Session struct { //TODO: figure what is this intended for | |||
| Guest bool | |||
| Year int //how many years | |||
| Month int //how many years | |||
| Day int //how many years | |||
| } | |||
| } | |||
| var configFile = "config.json" | |||
| var config = configuration{} | |||
| func (m *configuration) readConfig() (e error) { | |||
| log.Printf("read Path config from %s", configFile) | |||
| body, e := ioutil.ReadFile(configFile) | |||
| if e != nil { | |||
| log.Fatal("Cannot read config from " + configFile) | |||
| return | |||
| } | |||
| e = json.Unmarshal(body, m) | |||
| if config.Debug { | |||
| log.Println(config) | |||
| } | |||
| return | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| { | |||
| "Host":"0.0.0.0", | |||
| "Port":"8080", | |||
| "DSN": "sp:sp@tcp(192.168.1.70:3306)/syd_credit?parseTime=true&loc=Australia%2FSydney&collation=utf8mb4_0900_ai_ci", | |||
| "TlsCert": "fullchain.pem", | |||
| "TlsKey": "privkey.pem", | |||
| "Debug": true, | |||
| "Static": [ | |||
| { | |||
| "Dir": "./html/", | |||
| "StaticUrl": "/", | |||
| "StripPrefix" : "/" | |||
| }, | |||
| { | |||
| "Dir": "./html/test/", | |||
| "StaticUrl": "/spa1/", | |||
| "StripPrefix" : "/spa1/" | |||
| }, | |||
| { | |||
| "Dir": "./html/test/", | |||
| "StaticUrl": "/spa2/", | |||
| "StripPrefix" : "/spa2/" | |||
| } | |||
| ], | |||
| "Session" : { | |||
| "Guest": true, | |||
| "Year": 10, | |||
| "Month": 1, | |||
| "Day": 1 | |||
| } | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| p { | |||
| background-color:yellow; | |||
| } | |||
| h1 { | |||
| text-align: center; | |||
| text-decoration: #1ABC9C; | |||
| } | |||
| div { | |||
| width: 300px; | |||
| margin: auto; | |||
| text-align: left; | |||
| } | |||
| hr { | |||
| width: 100px; | |||
| border: 10px solid green; | |||
| border-radius: 5px; | |||
| } | |||
| li{ | |||
| padding-bottom:15px; | |||
| } | |||
| @@ -0,0 +1,69 @@ | |||
| <head> | |||
| <title>Demo static index</title> | |||
| <link rel="stylesheet" type="text/css" href="css/index.css"> | |||
| </head> | |||
| <body> | |||
| <h1> Static Html Site </h1> | |||
| <hr> | |||
| <div class="center"> | |||
| <p > | |||
| This is a static website located on / | |||
| </p> | |||
| <ol> | |||
| <li> | |||
| <a href="css/index.css"> css/index.css </a> | |||
| </li> | |||
| <li> | |||
| static url <a href="spa1/"> /spa1 </a> → "./html/test/" | |||
| </li> | |||
| <li> | |||
| static url <a href="spa2/">/spa2 </a> → "./html/test/" | |||
| </li> | |||
| <li> | |||
| static url <a href="test/">/test </a> → "./html/test/" | |||
| </li> | |||
| <script> | |||
| let socket = new WebSocket("wss://svr2021.lawipac.com:8080/api/v1/ws"); | |||
| console.log("Attempting Connection..."); | |||
| socket.onopen = () => { | |||
| console.log("Successfully Connected"); | |||
| socket.send("Hi From the Client!"); | |||
| socket.send("send dummy string for 500 times"); //this is a special command server will respond; | |||
| }; | |||
| socket.onclose = event => { | |||
| console.log("Socket Closed Connection: ", event); | |||
| socket.send("Client Closed!") | |||
| }; | |||
| socket.onerror = error => { | |||
| console.log("Socket Error: ", error); | |||
| }; | |||
| socket.onmessage = e => { | |||
| if(typeof e.data != "string" ) { | |||
| console.log("invalid data received ", e) | |||
| }else{ | |||
| let server_message = e.data; | |||
| console.log("server said: ", server_message) | |||
| document.getElementById("socketOutPut").innerHTML= server_message; | |||
| } | |||
| } | |||
| </script> | |||
| </ol> | |||
| <p id="socketOutPut"></p> | |||
| </div> | |||
| <!--<script type="text/javascript">--> | |||
| <!-- setTimeout(function(){--> | |||
| <!-- location.reload();--> | |||
| <!-- },1000)--> | |||
| <!--</script>--> | |||
| </body> | |||
| @@ -0,0 +1,22 @@ | |||
| p { | |||
| background-color:lightblue; | |||
| padding: 20px 5px; | |||
| } | |||
| h1 { | |||
| text-align: center; | |||
| text-decoration: overline; | |||
| } | |||
| div { | |||
| width: 300px; | |||
| margin: auto; | |||
| text-align: left; | |||
| background-color: yellow; | |||
| } | |||
| hr { | |||
| width: 100px; | |||
| border: 6px solid lightcoral; | |||
| border-radius: 5px; | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| <head> | |||
| <title>Demo static "Test" </title> | |||
| <link rel="stylesheet" type="text/css" href="css/test.css"> | |||
| </head> | |||
| <h1> Test </h1> | |||
| <h1> ↓ </h1> | |||
| <hr> | |||
| <div class="center"> | |||
| <p > | |||
| This is a static website located on /test but virtually accessed by /spa1 and /spa2 | |||
| </p> | |||
| <hr> | |||
| <ul> | |||
| <li> | |||
| <a href="css/test.css"> css/test.css </a> | |||
| </li> | |||
| </ul> | |||
| <h1> ↓ </h1> | |||
| <ul> | |||
| <li> | |||
| <a href="/"> back to "/" → "./html/" </a> | |||
| </li> | |||
| </ul> | |||
| </div> | |||
| @@ -0,0 +1,24 @@ | |||
| package main | |||
| import ( | |||
| "log" | |||
| "net/http" | |||
| ) | |||
| type httpEntry func(http.ResponseWriter, *http.Request) | |||
| var httpEntryMap = map[string]httpEntry{ | |||
| apiV1Prefix: apiV1Main, | |||
| apiV1WebSocket: apiV1WebSocketHandler, | |||
| } | |||
| func setupHTTPHandler() { | |||
| for key, val := range httpEntryMap { | |||
| http.HandleFunc(key, val) | |||
| } | |||
| log.Printf("Server started at %s:%s\n", config.Host, config.Port) | |||
| log.Fatal(http.ListenAndServe(config.Host+":"+config.Port, nil)) | |||
| // log.Fatal(http.ListenAndServeTLS(config.Host+":"+config.Port, config.TlsCert, config.TlsKey, nil)) | |||
| } | |||
| @@ -0,0 +1,44 @@ | |||
| package main | |||
| // [START import] | |||
| import ( | |||
| "log" | |||
| "net/http" | |||
| ) | |||
| // [END import] | |||
| // [START main_func] | |||
| func main() { | |||
| err := config.readConfig() // | |||
| if err != nil { | |||
| log.Println(err) | |||
| log.Fatalf("unable to read %s, program quit\n", configFile) | |||
| return | |||
| } | |||
| // [START setting_port] | |||
| port := config.Port | |||
| if port == "" { | |||
| port = "8081" | |||
| log.Printf("Defaulting to port %s", port) | |||
| } | |||
| // [END setting_port] | |||
| setupRootFileServer() | |||
| //always last | |||
| setupHTTPHandler() | |||
| } | |||
| // [END main_func] | |||
| func setupRootFileServer() { | |||
| //root of doc | |||
| for idx, node := range config.Static { | |||
| log.Printf("setting up static %d with %+v\n", idx, node) | |||
| fs := http.FileServer(http.Dir(node.Dir)) | |||
| http.Handle(node.StaticUrl, http.StripPrefix(node.StripPrefix, fs)) | |||
| } | |||
| } | |||