| /cert/ | /cert/ | ||||
| /goweb | |||||
| /vendor | |||||
| /deploy/biukopweb-html/ |
| FROM alpine:3.16 | |||||
| # make sure it support go binary library | |||||
| # https://stackoverflow.com/questions/34729748/installed-go-binary-not-found-in-path-on-alpine-linux-docker | |||||
| RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2 | |||||
| # we don't compile golang at alpine, but we will compile it at somewhere else. | |||||
| ENV GO111MODULE=on | |||||
| ENV GOFLAGS=-mod=vendor | |||||
| #APP HOME | |||||
| ENV APP_HOME /biukop/web | |||||
| RUN mkdir -p "$APP_HOME" | |||||
| #update static html files | |||||
| RUN mkdir -p $APP_HOME/html | |||||
| COPY ./deploy/biukopweb-html $APP_HOME/html | |||||
| #copy production configuration file | |||||
| COPY ./deploy/config_production.json $APP_HOME/config.json | |||||
| COPY ./goweb $APP_HOME/goweb | |||||
| WORKDIR "$APP_HOME" | |||||
| EXPOSE 8080 | |||||
| ENV PATH "$APP_HOME:$PATH" | |||||
| CMD ["goweb", "-f", "config.json"] |
| "encoding/json" | "encoding/json" | ||||
| "io/ioutil" | "io/ioutil" | ||||
| "log" | "log" | ||||
| "os" | |||||
| "path/filepath" | |||||
| ) | ) | ||||
| type configStaticHtml struct { | type configStaticHtml struct { | ||||
| Dir string | Dir string | ||||
| StaticUrl string | StaticUrl string | ||||
| StripPrefix string | StripPrefix string | ||||
| Sync string | |||||
| } | } | ||||
| type configuration struct { | 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 | |||||
| Host string | |||||
| Port string | |||||
| DSN string | |||||
| TlsCert string | |||||
| TlsKey string | |||||
| RSyncKey string | |||||
| Static []configStaticHtml | |||||
| Debug bool | |||||
| TempDir string | |||||
| Session struct { //TODO: figure what is this intended for | |||||
| Guest bool | Guest bool | ||||
| Year int //how many years | Year int //how many years | ||||
| Month int //how many years | Month int //how many years | ||||
| } | } | ||||
| e = json.Unmarshal(body, m) | e = json.Unmarshal(body, m) | ||||
| // Check upload dir and defaults | |||||
| if !config.checkUploadDir() { | |||||
| log.Fatal("bad config file", configFile) | |||||
| return | |||||
| } | |||||
| if config.Debug { | if config.Debug { | ||||
| log.Println(config) | log.Println(config) | ||||
| } | } | ||||
| return | return | ||||
| } | } | ||||
| func (m *configuration) checkUploadDir() (valid bool) { | |||||
| valid = true | |||||
| for idx, node := range m.Static { | |||||
| if node.StaticUrl == "/" { | |||||
| if !fileExists(node.Dir) { | |||||
| valid = false | |||||
| log.Fatal(" html / not exist ", node) | |||||
| } else { | |||||
| // convert to absolute path : fileDir | |||||
| p, e := filepath.Abs(node.Dir) | |||||
| if e != nil { | |||||
| valid = false | |||||
| log.Fatal("bad html (webroot) dir ", node, e) | |||||
| } | |||||
| m.Static[idx].Dir = p + string(os.PathSeparator) //change it to absolute dir | |||||
| } | |||||
| } | |||||
| } | |||||
| // convert rsync key file to absolute dir | |||||
| p, e := filepath.Abs(config.RSyncKey) | |||||
| if e != nil { | |||||
| valid = false | |||||
| log.Fatal("bad html (webroot) dir ", config.RSyncKey, e) | |||||
| } | |||||
| m.RSyncKey = p //change it to absolute dir | |||||
| return | |||||
| } | |||||
| func fileExists(path string) bool { | |||||
| if _, err := os.Stat(path); os.IsNotExist(err) { | |||||
| // path/to/whatever does not exist | |||||
| return false | |||||
| } | |||||
| return true | |||||
| } |
| "TlsCert": "cert/fullchain.pem", | "TlsCert": "cert/fullchain.pem", | ||||
| "TlsKey": "cert/privkey.pem", | "TlsKey": "cert/privkey.pem", | ||||
| "Debug": true, | "Debug": true, | ||||
| "RSyncKey": "cert/rsync.key", | |||||
| "Static": [ | "Static": [ | ||||
| { | { | ||||
| "Dir": "/mnt/hgfs/workspace/2021-07-31-BiukopWeb", | |||||
| "Dir": "./deploy/biukopweb-html/", | |||||
| "StaticUrl": "/", | "StaticUrl": "/", | ||||
| "StripPrefix" : "/" | |||||
| "StripPrefix" : "/", | |||||
| "Sync": "a@c5015.biukop.com.au:/home/a/public_html/" | |||||
| }, | }, | ||||
| { | { | ||||
| "Dir": "./html/test/", | "Dir": "./html/test/", |
| "TlsCert": "cert/fullchain.pem", | "TlsCert": "cert/fullchain.pem", | ||||
| "TlsKey": "cert/privkey.pem", | "TlsKey": "cert/privkey.pem", | ||||
| "Debug": true, | "Debug": true, | ||||
| "RSyncKey": "cert/rsync.key", | |||||
| "Static": [ | "Static": [ | ||||
| { | { | ||||
| "Dir": "./html/", | "Dir": "./html/", | ||||
| "StaticUrl": "/", | "StaticUrl": "/", | ||||
| "StripPrefix" : "/" | |||||
| "StripPrefix" : "/", | |||||
| "Sync": "a@c5015.biukop.com.au:/home/a/public_html/" | |||||
| }, | }, | ||||
| { | { | ||||
| "Dir": "./html/test/", | "Dir": "./html/test/", |
| func (m *formInput) contactUs() { | func (m *formInput) contactUs() { | ||||
| ret := map[string]interface{}{} | ret := map[string]interface{}{} | ||||
| ret["success"] = true | ret["success"] = true | ||||
| m.r.ParseForm() | |||||
| // e := m.r.ParseForm() | |||||
| e := m.r.ParseMultipartForm(32 * 1024 * 1024) | |||||
| if e != nil { | |||||
| return | |||||
| } | |||||
| for key, values := range m.r.Form { // range over map | |||||
| for _, value := range values { // range over []string | |||||
| fmt.Println(key, value) | |||||
| } | |||||
| } | |||||
| name := m.r.FormValue("name") | name := m.r.FormValue("name") | ||||
| email := m.r.FormValue("email") | email := m.r.FormValue("email") | ||||
| message := m.r.FormValue("message") | message := m.r.FormValue("message") | ||||
| smtpHost := "smtp.gmail.com" | smtpHost := "smtp.gmail.com" | ||||
| smtpPort := "587" | smtpPort := "587" | ||||
| raw := `Subject: {name} Contact form on Biukop Web | |||||
| raw := `Subject: {name} [ Contact form on Biukop Web ] | |||||
| Content-Type: text/plain; charset="UTF-8" | Content-Type: text/plain; charset="UTF-8" | ||||
| Dear Manager, | Dear Manager, | ||||
| We receive a a form submission from biukop.com.au | |||||
| We received a a form submission from biukop.com.au | |||||
| name : {name} | name : {name} | ||||
| email : {email} | email : {email} | ||||
| message: | message: |
| module github.com/lawipac/biukopweb | |||||
| go 1.18 | |||||
| require ( | |||||
| github.com/gorilla/websocket v1.5.0 // indirect | |||||
| github.com/sirupsen/logrus v1.8.1 // indirect | |||||
| golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect | |||||
| ) |
| // attempt to open the file via the http.FileSystem | // attempt to open the file via the http.FileSystem | ||||
| f, err := root.Open(upath) | f, err := root.Open(upath) | ||||
| if err != nil { | if err != nil { | ||||
| if os.IsNotExist(err) { | if os.IsNotExist(err) { | ||||
| // call handler | |||||
| if handler404 != nil { | |||||
| doDefault := handler404(w, r) | |||||
| if !doDefault { | |||||
| return | |||||
| } | |||||
| if handledBy404Handler(handler404, w, r) { | |||||
| return | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| // close if successfully opened | |||||
| if err == nil { | |||||
| f.Close() | |||||
| // if successfully opened, check if it's dir | |||||
| defer func(f http.File) { | |||||
| err := f.Close() | |||||
| if err != nil { | |||||
| return //cannot close this file | |||||
| } | |||||
| }(f) | |||||
| //see if it's directory | |||||
| info, e := f.Stat() | |||||
| if e != nil || info.IsDir() { | |||||
| if handledBy404Handler(handler404, w, r) { | |||||
| return | |||||
| } | |||||
| } | } | ||||
| // default serve | // default serve | ||||
| http.Redirect(w, r, "/404.html", http.StatusSeeOther) | http.Redirect(w, r, "/404.html", http.StatusSeeOther) | ||||
| return false | return false | ||||
| } | } | ||||
| //check if h is null, if not call this as handler. | |||||
| func handledBy404Handler(h FSHandler404, w http.ResponseWriter, r *http.Request) bool { | |||||
| if h != nil { | |||||
| doDefault := h(w, r) | |||||
| if !doDefault { | |||||
| return true | |||||
| } | |||||
| } | |||||
| return false | |||||
| } |
| type vimeoPlayer struct { | type vimeoPlayer struct { | ||||
| VideoId string | VideoId string | ||||
| Token string | |||||
| playsinline int | playsinline int | ||||
| autoplay int | autoplay int | ||||
| fmt.Fprintf(w, output) | fmt.Fprintf(w, output) | ||||
| } | } | ||||
| /** | |||||
| http://v.io/v/1234567/token where token might be optional | |||||
| */ | |||||
| func getVimeoParams(r *http.Request) (ret vimeoPlayer) { | func getVimeoParams(r *http.Request) (ret vimeoPlayer) { | ||||
| prefix := videoPrefix + "v/" | prefix := videoPrefix + "v/" | ||||
| ret.VideoId = r.URL.Path[len(prefix):] | |||||
| var IdAndToken = strings.Split(r.URL.Path[len(prefix):], "/") | |||||
| if len(IdAndToken) == 1 { | |||||
| ret.VideoId = IdAndToken[0] | |||||
| ret.Token = "" | |||||
| } else if len(IdAndToken) == 2 { | |||||
| ret.VideoId = IdAndToken[0] | |||||
| ret.Token = IdAndToken[1] | |||||
| } else { // a default video will be given | |||||
| ret.VideoId = "719542916" | |||||
| ret.Token = "" | |||||
| } | |||||
| if strings.ToLower(r.Host) == ("v.io") { | if strings.ToLower(r.Host) == ("v.io") { | ||||
| ret.Title = "您有一条视频消息" | ret.Title = "您有一条视频消息" | ||||
| ret.ContentDescription = "私密视频" | ret.ContentDescription = "私密视频" | ||||
| func (m *vimeoPlayer) getUrl() (ret string) { | func (m *vimeoPlayer) getUrl() (ret string) { | ||||
| ret = fmt.Sprintf( | ret = fmt.Sprintf( | ||||
| "https://player.vimeo.com/video/%s?playsinline=%d&autoplay=%d&autopause=%d&loop=%d&background=%d&muted=%d", | |||||
| m.VideoId, m.playsinline, m.autoplay, m.autopause, m.loop, m.background, m.muted) | |||||
| "https://player.vimeo.com/video/%s?h=%s&playsinline=%d&autoplay=%d&autopause=%d&loop=%d&background=%d&muted=%d", | |||||
| m.VideoId, m.Token, m.playsinline, m.autoplay, m.autopause, m.loop, m.background, m.muted) | |||||
| return | return | ||||
| } | } |
| log.Printf("setting up static %d with %+v\n", idx, node) | log.Printf("setting up static %d with %+v\n", idx, node) | ||||
| fs := FileServerWith404(http.Dir(node.Dir), fileSystem404) | fs := FileServerWith404(http.Dir(node.Dir), fileSystem404) | ||||
| http.Handle(node.StaticUrl, http.StripPrefix(node.StripPrefix, fs)) | http.Handle(node.StaticUrl, http.StripPrefix(node.StripPrefix, fs)) | ||||
| if node.Sync != "" { | |||||
| // go pullStaticHtml(node.Dir, node.Sync, config.RSyncKey) | |||||
| } | |||||
| } | } | ||||
| } | } |
| package main | |||||
| import ( | |||||
| "fmt" | |||||
| log "github.com/sirupsen/logrus" | |||||
| "os/exec" | |||||
| "time" | |||||
| ) | |||||
| func pullStaticHtml(local string, remote string, key string) { | |||||
| ssh := fmt.Sprintf("/usr/bin/ssh -i %s ", key) | |||||
| // rsync -Pav -e "ssh -i $HOME/.ssh/someKey" username@hostname:/from/dir/ /to/dir/ | |||||
| for { | |||||
| cmd := exec.Command("rsync", "-Pavz", "--rsh", ssh, remote, local) | |||||
| // cmd := exec.Command("sh", "-c", "echo stdout; echo 1>&2 stderr") | |||||
| stdoutStderr, err := cmd.CombinedOutput() | |||||
| if err != nil { | |||||
| log.Error(err) | |||||
| break | |||||
| } | |||||
| log.Printf("%s\n", stdoutStderr) | |||||
| time.Sleep(5 * time.Second) | |||||
| } | |||||
| } |