diff --git a/backend/kor/.gitignore b/backend/kor/.gitignore new file mode 100644 index 00000000..a902256f --- /dev/null +++ b/backend/kor/.gitignore @@ -0,0 +1,2 @@ +server.crt +server.key \ No newline at end of file diff --git a/backend/kor/docs/docs.go b/backend/kor/docs/docs.go new file mode 100644 index 00000000..97f262e3 --- /dev/null +++ b/backend/kor/docs/docs.go @@ -0,0 +1,134 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/api/v1/example-get": { + "get": { + "description": "An example GET API", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Example GET endpoint", + "parameters": [ + { + "type": "string", + "description": "Authorization token", + "name": "Authorization", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/main.response" + } + } + } + } + }, + "/api/v1/example-post": { + "post": { + "description": "An example POST API", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Example POST endpoint", + "parameters": [ + { + "description": "Post Request Data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/main.postRequest" + } + }, + { + "type": "string", + "description": "Authorization token", + "name": "Authorization", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/main.response" + } + } + } + } + }, + "/healthcheck": { + "get": { + "description": "Returns the status of the server", + "summary": "Health Check", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/main.response" + } + } + } + } + } + }, + "definitions": { + "main.postRequest": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + }, + "main.response": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "", + BasePath: "", + Schemes: []string{}, + Title: "KOR API Swagger", + Description: "KOR API Swagger", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/backend/kor/docs/swagger.json b/backend/kor/docs/swagger.json new file mode 100644 index 00000000..39e24c8c --- /dev/null +++ b/backend/kor/docs/swagger.json @@ -0,0 +1,108 @@ +{ + "swagger": "2.0", + "info": { + "description": "KOR API Swagger", + "title": "KOR API Swagger", + "contact": {}, + "version": "1.0" + }, + "paths": { + "/api/v1/example-get": { + "get": { + "description": "An example GET API", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Example GET endpoint", + "parameters": [ + { + "type": "string", + "description": "Authorization token", + "name": "Authorization", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/main.response" + } + } + } + } + }, + "/api/v1/example-post": { + "post": { + "description": "An example POST API", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Example POST endpoint", + "parameters": [ + { + "description": "Post Request Data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/main.postRequest" + } + }, + { + "type": "string", + "description": "Authorization token", + "name": "Authorization", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/main.response" + } + } + } + } + }, + "/healthcheck": { + "get": { + "description": "Returns the status of the server", + "summary": "Health Check", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/main.response" + } + } + } + } + } + }, + "definitions": { + "main.postRequest": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + }, + "main.response": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/backend/kor/docs/swagger.yaml b/backend/kor/docs/swagger.yaml new file mode 100644 index 00000000..0fbe4e80 --- /dev/null +++ b/backend/kor/docs/swagger.yaml @@ -0,0 +1,69 @@ +definitions: + main.postRequest: + properties: + data: + type: string + type: object + main.response: + properties: + message: + type: string + type: object +info: + contact: {} + description: KOR API Swagger + title: KOR API Swagger + version: "1.0" +paths: + /api/v1/example-get: + get: + consumes: + - application/json + description: An example GET API + parameters: + - description: Authorization token + in: header + name: Authorization + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/main.response' + summary: Example GET endpoint + /api/v1/example-post: + post: + consumes: + - application/json + description: An example POST API + parameters: + - description: Post Request Data + in: body + name: request + required: true + schema: + $ref: '#/definitions/main.postRequest' + - description: Authorization token + in: header + name: Authorization + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/main.response' + summary: Example POST endpoint + /healthcheck: + get: + description: Returns the status of the server + responses: + "200": + description: OK + schema: + $ref: '#/definitions/main.response' + summary: Health Check +swagger: "2.0" diff --git a/backend/kor/go.mod b/backend/kor/go.mod new file mode 100644 index 00000000..3896420e --- /dev/null +++ b/backend/kor/go.mod @@ -0,0 +1,25 @@ +module main + +go 1.22.2 + +require ( + github.com/golang-jwt/jwt/v5 v5.2.1 + github.com/gorilla/mux v1.8.1 + github.com/swaggo/http-swagger v1.3.4 + github.com/swaggo/swag v1.16.3 +) + +require ( + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/spec v0.20.6 // indirect + github.com/go-openapi/swag v0.19.15 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/tools v0.7.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/backend/kor/go.sum b/backend/kor/go.sum new file mode 100644 index 00000000..10bda6ca --- /dev/null +++ b/backend/kor/go.sum @@ -0,0 +1,71 @@ +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/spec v0.20.6 h1:ich1RQ3WDbfoeTqTAb+5EIxNmpKVJZWBNah9RAT0jIQ= +github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe h1:K8pHPVoTgxFJt1lXuIzzOX7zZhZFldJQK/CgKx9BFIc= +github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= +github.com/swaggo/http-swagger v1.3.4 h1:q7t/XLx0n15H1Q9/tk3Y9L4n210XzJF5WtnDX64a5ww= +github.com/swaggo/http-swagger v1.3.4/go.mod h1:9dAh0unqMBAlbp1uE2Uc2mQTxNMU/ha4UbucIg1MFkQ= +github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= +github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/backend/kor/main.go b/backend/kor/main.go new file mode 100644 index 00000000..1457f6d6 --- /dev/null +++ b/backend/kor/main.go @@ -0,0 +1,117 @@ +package main + +import ( + "crypto/tls" + "encoding/json" + "github.com/golang-jwt/jwt/v5" + "github.com/gorilla/mux" + "github.com/swaggo/http-swagger" + "log" + _ "main/docs" + "net/http" + "os" +) + +var jwtSecret = []byte(os.Getenv("KOR_API_SECRET")) + +type response struct { + Message string `json:"message"` +} + +type postRequest struct { + Data string `json:"data"` +} + +// Auth middleware that verifies the JWT token using golang-jwt/jwt +func authMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !(os.Getenv("NO_AUTH") == "true") { + tokenHeader := r.Header.Get("Authorization") + if tokenHeader == "" { + w.WriteHeader(http.StatusUnauthorized) + json.NewEncoder(w).Encode(response{Message: "Missing token"}) + return + } + + tokenString := tokenHeader[len("Bearer "):] + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + return jwtSecret, nil + }) + + if err != nil || !token.Valid { + w.WriteHeader(http.StatusUnauthorized) + json.NewEncoder(w).Encode(response{Message: "Invalid token"}) + return + } + + next.ServeHTTP(w, r) + } else { + next.ServeHTTP(w, r) + } + }) +} + +// @Summary Health Check +// @Description Returns the status of the server +// @Success 200 {object} response +// @Router /healthcheck [get] +func healthCheckHandler(w http.ResponseWriter, r *http.Request) { + json.NewEncoder(w).Encode(response{Message: "OK"}) +} + +// @Summary Example GET endpoint +// @Description An example GET API +// @Accept json +// @Produce json +// @Success 200 {object} response +// @Router /api/v1/example-get [get] +// @Param Authorization header string false "Authorization token" +func exampleGetHandler(w http.ResponseWriter, r *http.Request) { + json.NewEncoder(w).Encode(response{Message: "This is a GET response"}) +} + +// @Summary Example POST endpoint +// @Description An example POST API +// @Accept json +// @Produce json +// @Param request body postRequest true "Post Request Data" +// @Success 200 {object} response +// @Router /api/v1/example-post [post] +// @Param Authorization header string false "Authorization token" +func examplePostHandler(w http.ResponseWriter, r *http.Request) { + var req postRequest + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode(response{Message: "Invalid request"}) + return + } + json.NewEncoder(w).Encode(response{Message: "Received: " + req.Data}) +} + +// @title KOR API Swagger +// @version 1.0 +// @description KOR API Swagger +func main() { + router := mux.NewRouter() + + // Base path for the API is /api/v1 + router.HandleFunc("/healthcheck", healthCheckHandler).Methods("GET") + api := router.PathPrefix("/api/v1").Subrouter() + api.Handle("/example-get", authMiddleware(http.HandlerFunc(exampleGetHandler))).Methods("GET") + api.Handle("/example-post", authMiddleware(http.HandlerFunc(examplePostHandler))).Methods("POST") + // Swagger documentation route + router.PathPrefix("/swagger/").Handler(httpSwagger.WrapHandler) + + // Start HTTPS server + srv := &http.Server{ + Addr: "localhost:8080", + Handler: router, + TLSConfig: &tls.Config{ + MinVersion: tls.VersionTLS12, + }, + } + + log.Println("Server running on https://localhost:8080") + log.Fatal(srv.ListenAndServeTLS("server.crt", "server.key")) +} diff --git a/backend/tools/generate_token.go b/backend/tools/generate_token.go new file mode 100644 index 00000000..23e4d9aa --- /dev/null +++ b/backend/tools/generate_token.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + "github.com/golang-jwt/jwt/v5" +) + +var jwtSecret = []byte("XXXX") // Replace with your actual secret key + +// GenerateJWT generates a JWT token that doesn't expire +func GenerateJWT() (string, error) { + // Create a new token object with claims (additional data) + claims := jwt.MapClaims{ + "authorized": true, + "user": "exampleUser", // Replace with actual user info + // No "exp" field means the token will not expire + } + + // Create the token with signing method and claims + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + + // Sign the token with the secret key + tokenString, err := token.SignedString(jwtSecret) + if err != nil { + return "", err + } + + return tokenString, nil +} + +func main() { + // Generate the JWT token + token, err := GenerateJWT() + if err != nil { + fmt.Println("Error generating token:", err) + return + } + + // Print the generated token (Bearer token) + fmt.Println("Bearer token:", token) +} diff --git a/backend/tools/pre-setup.sh b/backend/tools/pre-setup.sh new file mode 100644 index 00000000..d5ac1b99 --- /dev/null +++ b/backend/tools/pre-setup.sh @@ -0,0 +1,3 @@ +#TODO: place static files in dockerfile +openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -days 365 -nodes +go run server.go \ No newline at end of file