From e378db58b81319c4d886d1ee49dde996c7ccbfec Mon Sep 17 00:00:00 2001 From: Ankit Patial Date: Tue, 19 Nov 2024 17:16:36 +0530 Subject: [PATCH] serve frontend + backend as a one binary --- {cmd => bin}/migrate-up/main.go | 0 {cmd => bin}/server/handler/request.go | 0 {cmd => bin}/server/main.go | 37 +++++++++++++++++++++++-- config/config.go | 12 ++++---- svelte.config.js | 19 ++++++------- taskfile.yml | 28 +++++++++++++++---- web/public/android-chrome-192x192.png | Bin 0 -> 5103 bytes web/public/android-chrome-512x512.png | Bin 0 -> 14989 bytes web/public/apple-touch-icon.png | Bin 0 -> 4580 bytes web/public/favicon-16x16.png | Bin 0 -> 262 bytes web/public/favicon-32x32.png | Bin 0 -> 548 bytes web/public/favicon.ico | Bin 0 -> 15406 bytes web/public/site.webmanifest | 11 ++++++++ web/web.go | 6 ++++ 14 files changed, 87 insertions(+), 26 deletions(-) rename {cmd => bin}/migrate-up/main.go (100%) rename {cmd => bin}/server/handler/request.go (100%) rename {cmd => bin}/server/main.go (52%) create mode 100644 web/public/android-chrome-192x192.png create mode 100644 web/public/android-chrome-512x512.png create mode 100644 web/public/apple-touch-icon.png create mode 100644 web/public/favicon-16x16.png create mode 100644 web/public/favicon-32x32.png create mode 100644 web/public/favicon.ico create mode 100644 web/public/site.webmanifest create mode 100644 web/web.go diff --git a/cmd/migrate-up/main.go b/bin/migrate-up/main.go similarity index 100% rename from cmd/migrate-up/main.go rename to bin/migrate-up/main.go diff --git a/cmd/server/handler/request.go b/bin/server/handler/request.go similarity index 100% rename from cmd/server/handler/request.go rename to bin/server/handler/request.go diff --git a/cmd/server/main.go b/bin/server/main.go similarity index 52% rename from cmd/server/main.go rename to bin/server/main.go index d91c128..f77bf27 100644 --- a/cmd/server/main.go +++ b/bin/server/main.go @@ -7,13 +7,15 @@ package main import ( "fmt" "net/http" + "strings" "gitserver.in/patialtech/mux" "gitserver.in/patialtech/mux/middleware" - "gitserver.in/patialtech/rano/cmd/server/handler" + "gitserver.in/patialtech/rano/bin/server/handler" "gitserver.in/patialtech/rano/config" "gitserver.in/patialtech/rano/graph" "gitserver.in/patialtech/rano/util/logger" + "gitserver.in/patialtech/rano/web" ) func main() { @@ -24,6 +26,7 @@ func main() { // CORS r.Use(middleware.CORS(middleware.CORSOption{ AllowedHeaders: []string{"Content-Type"}, + AllowedOrigins: []string{config.Read().WebURL}, MaxAge: 60, })) // Secure Headers @@ -40,7 +43,37 @@ func main() { // catch all r.GET("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("hello there")) + var ( + resourcePath string + path = r.URL.Path + ) + + if strings.HasPrefix(path, "/_app") { + resourcePath = "public/build" + path + } else if strings.HasSuffix(path, ".png") || + strings.HasSuffix(path, ".ico") || + strings.HasSuffix(path, ".svg") || + strings.HasSuffix(path, "robot.txt") || + strings.HasSuffix(path, "site.webmanifest") { + resourcePath = "public" + path + } else { + resourcePath = "public/build/fallback.html" + } + + if b, err := web.Public.ReadFile(resourcePath); err != nil { + _, _ = w.Write([]byte("hello there")) + } else { + if strings.HasSuffix(path, ".js") { + w.Header().Set("Content-Type", "text/javascript") + } else if strings.HasSuffix(path, ".css") { + w.Header().Set("Content-Type", "text/css") + } else { + w.Header().Set("Content-Type", http.DetectContentType(b)) + } + + w.WriteHeader(http.StatusOK) + _, _ = w.Write(b) + } }) r.Serve(func(srv *http.Server) error { diff --git a/config/config.go b/config/config.go index 2ffac1e..2da3474 100644 --- a/config/config.go +++ b/config/config.go @@ -18,17 +18,13 @@ import ( const ( // projDir need to be same as project code root dir name - projDir = "rano" - EnvDev = "development" - EnvProd = "production" - EnvStage = "staging" - + projDir = "rano" AuthUserCtxKey = "AuthUser" ) var ( conf *Config - AppEnv Env = EnvDev + AppEnv = "development" // production | staging ) type ( @@ -47,7 +43,7 @@ type ( func init() { var base string - if AppEnv == EnvDev { + if AppEnv == "development" { // AppEnv will be changed on build time using ldflags -X wd, _ := os.Getwd() idx := strings.Index(wd, projDir) if idx > -1 { @@ -57,6 +53,8 @@ func init() { base, _ = os.Executable() } + logger.Info("*** %s", AppEnv) + envVar, err := dotenv.Read(base, fmt.Sprintf(".env.%s", AppEnv)) if err != nil { panic(err) diff --git a/svelte.config.js b/svelte.config.js index 80b60b3..50cce16 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -1,7 +1,5 @@ -import path from 'node:path'; import adapter from '@sveltejs/adapter-static'; -const webDir = path.resolve('.', 'web'); /** @type {import('@sveltejs/kit').Config} */ const config = { @@ -9,16 +7,15 @@ const config = { // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. // If your environment is not supported, or you settled on a specific environment, switch out the adapter. // See https://kit.svelte.dev/docs/adapters for more information about adapters. - adapter: adapter(), files: { - appTemplate: path.resolve(webDir, 'app.html'), - routes: path.resolve(webDir, 'routes'), - lib: path.resolve(webDir, 'lib'), - assets: path.resolve(webDir, 'public') + appTemplate: "web/app.html" , + routes: "web/routes", + lib: "web/lib", + assets: "public/lib" }, alias: { - $image: path.resolve(webDir, 'assets', 'image'), - $svg: path.resolve(webDir, 'assets', 'svg') + $image: "web/assets/image", + $svg: "web/assets/svg" }, paths: { assets: process.env.ASSETS_HOST ?? '' @@ -28,8 +25,8 @@ const config = { pollInterval: 1000 * 60 * 1 // 5 minutes }, adapter: adapter({ - pages: path.resolve(webDir, 'public', 'build'), - assets: path.resolve(webDir, 'public', 'build'), + pages: "web/public/build", + assets: "web/public/build", fallback: 'fallback.html', strict: true }) diff --git a/taskfile.yml b/taskfile.yml index dfce2fc..9e0154a 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -1,9 +1,12 @@ version: '3' +dotenv: ['.env.{{.ENV}}'] + env: ENV: development -dotenv: ['.env.{{.ENV}}'] +vars: + MOD_NAME: gitserver.in/patialtech/rano tasks: install: @@ -14,7 +17,7 @@ tasks: start-graph: desc: run graph server cmds: - - cmd: go run ./cmd/server + - cmd: go run ./bin/server start-web: desc: run web in dev mode @@ -44,11 +47,11 @@ tasks: - cmd: deno task codegen ent-new: - desc: create new db Emtity - cmd: cd ./db && go run -mod=mod entgo.io/ent/cmd/ent new {{.name}} + desc: create new db Entity + cmd: cd ./db && go run -mod=mod entry.io/ent/cmd/ent new {{.name}} ent-gen: - desc: genertate from ent schema + desc: generate from ent schema cmds: - go generate ./db/ent @@ -61,4 +64,17 @@ tasks: desc: apply automatically migration using Ent schema cmds: - task: ent-gen - - go run ./cmd/migrate-up + - go run ./bin/migrate-up + + build-web-prod: + desc: apply automatically migration using Ent schema + cmds: + - deno task build + env: + ASSETS_HOST: + + build-prod: + desc: apply automatically migration using Ent schema + cmds: + - task: build-web-prod + - CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -ldflags "-X '{{.MOD_NAME}}/config.AppEnv=production'" -o ./server ./bin/server \ No newline at end of file diff --git a/web/public/android-chrome-192x192.png b/web/public/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..cc8a46185ba919165c3337fb08fb55722651cf2a GIT binary patch literal 5103 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 MwBAs#-t)-+1J&2_asU7T literal 0 HcmV?d00001 diff --git a/web/public/apple-touch-icon.png b/web/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f7c2d2202ebcb01b92d6ca06e2c0378b736b4ef9 GIT binary patch literal 4580 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 O0000!dJ}c6iyYiGoGjaI z{Q2DZI^j8UCovlJTv&ZJdd&yHA6e}!&w@U6l<#@;{O_dvzRU#i+^Na3dybs0bd~*X zdQ9*VgWbony#FBG}y`sF}Bw;);!kS z*tDwEp5M^R{I7Jj>N4(?s!xw*CbpTMd$fH@^n@*`S?PPr=k~KLw6yJESn?g{7Y0vP KKbLh*2~7Y$32O=f literal 0 HcmV?d00001 diff --git a/web/public/favicon-32x32.png b/web/public/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..ac578dc9c8584b6f8ab44e960f4b95d360b9eba3 GIT binary patch literal 548 zcmV+<0^9wGP)Px$-bqA3R9HvN*FTI+Q4q)R&xt5#NKoiBR9pCyQ0NpCDk`!SY6>JYG!!IOLF-SW zP^lNU8g};> zt%~P3L)?%>!V27oxy1Q_AjtqLDarOG;yLasqH<+X`W#!9DGS3zX$4|emgQ|jMDRl+ zWqB mPREsuG#SqyC%#Wo$bSLcxp#+gWV_G+0000x&Lbm9i{m@ z*8z^YzEDS-!rVu;NL|0*qK@T%6KB@NUx`A0UfsZDLDbyDpDSVk+^{-=!|MWSf|s+OFI4wZxxOmk-o;)~?ic-5$1C zL;X2*iS69>bNipXCAHt(9`lcHz@Jl>*v=h)h=Cd7udM;(im81+TYtx%JqTXm)7#>9suCCSonTZa?5+j4Gbfl)J6Ksn|S5@8@KNYuKBalHB9cRQJ zr|CsplP$i%C*nRa#QTd5blnq6_ZSopNw;nZ;$`taVz)hX`C?`RTe3M6#dFg4lXwV! zH}x4C*pkh@WPab4u4m$Xag*(#>am4QYt#x1Ac9lMuc(OhaG^E=-vbI)a0r34e$YR180B;S6@Z*^J!fC+4htyN_4 zJw=RM7IV2B*t7;MB3oBkKgu$z_6)|g_j_RZ%+3U`yD!129L&k`Cvp(4U-c5K%K0G6 zfyn_^dES@(305$B{!SiSZnNsoVD`O7m;60<|G>N<(|v;Iq#YJ_TSHE`s&Fg4}QiVm;JtW!9Mlxck1zz^T@vAm`k4ZYwGy} zIS{!}E+2Na3udsBgHAbrW)Hw#V50jM+kk@} zpW|m7E{P|4e?dR&{)j&0yvM*ccP}mPkKP-HZOQXJaQK3!C4HYwjx?XH%K{ z{**X#z6sUx^>(ksoigjgm}}gNnT>((uZumkdk)rba*!d`I&`4xo_MWjX}sT-FOl<+ zw~~8z**_rjqxhN_U5WmGqv)0S`TK_K`&p`=>G|1`pK