diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a91dc2..80aec12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,7 +103,7 @@ jobs: env: VITE_SENTRY_DSN: https://examplePublicKey@o0.ingest.sentry.io/0 VITE_NODE_ENV: development - VITE_API_ENDPOINT: https://jokesbapak2.herokuapp.com/v1 + VITE_API_ENDPOINT: https://jokesbapak2.reinaldyrafli.com/api/v1 - name: Initialize CodeQL uses: github/codeql-action/init@v1 diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 6ad70fc..cdb8dec 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -32,7 +32,7 @@ jobs: env: VITE_SENTRY_DSN: https://examplePublicKey@o0.ingest.sentry.io/0 VITE_NODE_ENV: development - VITE_API_ENDPOINT: https://jokesbapak2.herokuapp.com/v1 + VITE_API_ENDPOINT: https://jokesbapak2.reinaldyrafli.com/api/v1 - name: Initialize CodeQL uses: github/codeql-action/init@v1 diff --git a/.gitignore b/.gitignore index 65da6d3..a125d2e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,goland,webstorm,datagrip -# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,goland,webstorm,datagrip +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,goland,webstorm +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,goland,webstorm ### GoLand ### # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider @@ -214,6 +214,10 @@ fabric.properties # Azure Toolkit for IntelliJ plugin # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij -# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,goland,webstorm,datagrip +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,goland,webstorm -data \ No newline at end of file +docker-compose.override.yml +data/ +.env.* +.env +!.env.example \ No newline at end of file diff --git a/README.md b/README.md index 04783e9..f44ddbb 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@
-👋 Hey there! Still a work in progress, if you'd like to contribute this while this repo is still growing, that would be so great! +👋 Hey there! Always a work in progress, if you'd like to contribute this while this repo is still growing, that would be so great! -It's on alpha phase now. You can access the front facing web on [jokesbapak2.pages.dev](http://jokesbapak2.pages.dev/). +ou can access the front facing web on [jokesbapak2.reinaldyrafli.com](http://jokesbapak2.reinaldyrafli.com/). ## Brief explanation of what is this diff --git a/api/.env.example b/api/.env.example index 4c6d416..3ee59a7 100644 --- a/api/.env.example +++ b/api/.env.example @@ -1,7 +1,11 @@ ENV=development PORT=5000 +HOSTNAME=127.0.0.1 -DATABASE_URL=postgres://postgres:password@localhost:5432/jokesbapak2 REDIS_URL=redis://@localhost:6379 +MINIO_HOST= +MINIO_ACCESS_ID= +MINIO_SECRET_KEY= +MINIO_TOKEN= SENTRY_DSN= \ No newline at end of file diff --git a/api/Dockerfile b/api/Dockerfile index e5cf156..9e203f3 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -10,6 +10,8 @@ FROM debian:bullseye AS runtime WORKDIR /app +RUN apt-get update && apt-get upgrade -y && apt-get install -y curl + COPY --from=builder /app/main . ENV PORT=5000 diff --git a/api/documentation.json b/api/documentation.json index 958db5c..0c21cad 100644 --- a/api/documentation.json +++ b/api/documentation.json @@ -16,11 +16,11 @@ }, "servers": [ { - "url": "https://jokesbapak2.herokuapp.com/v1", + "url": "https://jokesbapak2.reinaldyrafli.com/api/v1", "description": "Production" }, { - "url": "https://jokesbapak2.herokuapp.com", + "url": "https://jokesbapak2.reinaldyrafli.com/api", "description": "Production" }, { @@ -72,375 +72,281 @@ } }, "responses": { - "201": { - "description": "Image has been added", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/request.joke" - }, - "example": { - "link": "https://link.to/image.jpg" - } - } - } - }, - "400": { - "description": "Bad request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/response.error" - }, - "example": { - "error": "URL provided is not a valid image" - } - } - } - }, - "403": { - "description": "Must be authenticated to submit a joke", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/response.error" - } - } - } - } - } - } - }, - "/id/{id}": { - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "number" - }, - "required": true, - "description": "A number that represents image's ID" - } - ], - "get": { - "summary": "Get random Jokes Bapak2 image by ID", - "description": "Returns consistent image for every call.", - "tags": [ - "Jokes" - ], - "responses": { - "200": { - "description": "Image data", - "content": { - "image/jpeg": {}, - "image/png": {}, - "image/gif": {} - } - }, - "404": { - "description": "Provided image ID was not found", - "content": { - "text/plain": { - "schema": { - "type": "string" - }, - "example": "Requested ID was not found." - } + "201": { + "description": "Image has been added", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/request.joke" + }, + "example": { + "link": "https://link.to/image.jpg" } } } }, - "patch": { - "summary": "Update a Joke with certain image ID", - "description": "Returns consistent image for every call.", - "tags": [ - "Jokes" - ], - "responses": { - "200": { - "description": "Sucessfully updated an image item", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/response.confirmation" - }, - { - "$ref": "#/components/schemas/request.joke" - } - ] - } - } - } - }, - "400": { - "description": "Link provided is not a valid image", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/response.error" - } - } - } - }, - "403": { - "description": "Must be authenticated to submit a joke", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/response.error" - } - } - } - }, - "406": { - "description": "If the Joke ID does not exists", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/response.error" - } - } + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/response.error" + }, + "example": { + "error": "URL provided is not a valid image" } } } }, - "delete": { - "summary": "Delete a Joke with certain image ID", - "description": "hi", - "tags": [ - "Jokes" - ], - "responses": { - "200": { - "description": "Sucessfully deleted an image item", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/response.confirmation" - } - } - } - }, - "403": { - "description": "Must be authenticated to submit a joke", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/response.error" - } - } - } - }, - "406": { - "description": "If the Joke ID does not exists", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/response.error" - } - } + "403": { + "description": "Must be authenticated to submit a joke", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/response.error" } } } } - }, - "/today": { - "get": { - "summary": "Get the joke of the day", - "description": "A joke a day makes more of a dad out of you.", - "tags": [ - "Jokes" - ], - "responses": { - "200": { - "description": "Image data", - "content": { - "image/jpeg": {}, - "image/png": {}, - "image/gif": {} - } - } + } + } + }, + "/id/{id}": { + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "number" + }, + "required": true, + "description": "A number that represents image's ID" + } + ], + "get": { + "summary": "Get random Jokes Bapak2 image by ID", + "description": "Returns consistent image for every call.", + "tags": [ + "Jokes" + ], + "responses": { + "200": { + "description": "Image data", + "content": { + "image/jpeg": {}, + "image/png": {}, + "image/gif": {} } - } - }, - "/total": { - "get": { - "summary": "Get total amount of jokes in database", - "tags": [ - "Jokes" - ], - "responses": { - "200": { - "description": "Total jokes", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/response.confirmation" - }, - "example": { - "message": "154" - } - } - } - } - } - } - }, - "/submit": { - "get": { - "summary": "Get submitted Jokes", - "tags": [ - "Submission" - ], - "parameters": [ - { - "name": "author", - "in": "query", - "required": false, - "description": "Author to be queried", + }, + "404": { + "description": "Provided image ID was not found", + "content": { + "text/plain": { "schema": { "type": "string" - } - }, - { - "name": "approved", - "in": "query", - "required": false, - "description": "Whether query just approved jokes or not", - "schema": { - "type": "boolean" - } - }, - { - "name": "limit", - "in": "query", - "required": false, - "schema": { - "type": "number" - } - }, - { - "name": "page", - "in": "query", - "required": false, - "schema": { - "type": "number" - } + }, + "example": "Requested ID was not found." } - ], - "responses": { - "200": { - "description": "asd", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "count": { - "type": "number" - }, - "jokes": { - "type": "array", - "items": { - "$ref": "#/components/schemas/response.submission" - } - } - } + } + } + } + }, + "patch": { + "summary": "Update a Joke with certain image ID", + "description": "Returns consistent image for every call.", + "tags": [ + "Jokes" + ], + "responses": { + "200": { + "description": "Sucessfully updated an image item", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/response.confirmation" + }, + { + "$ref": "#/components/schemas/request.joke" } - } + ] } } } }, - "post": { - "summary": "Submit a joke", - "description": "Must be in multipart/form-data format. Author must be in the format of \"Name <email>\".\n", - "tags": [ - "Submission" - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "properties": { - "link": { - "description": "Image link", - "type": "string" - }, - "image": { - "description": "Image data", - "type": "string" - }, - "author": { - "description": "Person who submitted this", - "type": "string" - } - }, - "required": [ - "author", - "image", - "link" - ] - } + "400": { + "description": "Link provided is not a valid image", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/response.error" } } - }, - "responses": { - "201": { - "description": "Joke successfully submitted", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/response.confirmation" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/response.submission" - } - } - } - ] - } - } + } + }, + "403": { + "description": "Must be authenticated to submit a joke", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/response.error" } - }, - "400": { - "description": "Invalid data was sent", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/response.error" - } - } + } + } + }, + "406": { + "description": "If the Joke ID does not exists", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/response.error" } } } } - }, - "/health": { - "get": { - "summary": "Health check", - "description": "Ping the databases to make sure everything's alright", - "tags": [ - "Health" - ], - "responses": { - "200": { - "description": "Everything is okay" - }, - "403": { - "description": "Something is not okay", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/response.error" + } + }, + "delete": { + "summary": "Delete a Joke with certain image ID", + "description": "hi", + "tags": [ + "Jokes" + ], + "responses": { + "200": { + "description": "Sucessfully deleted an image item", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/response.confirmation" + } + } + } + }, + "403": { + "description": "Must be authenticated to submit a joke", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/response.error" + } + } + } + }, + "406": { + "description": "If the Joke ID does not exists", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/response.error" + } + } + } + } + } + } + }, + "/today": { + "get": { + "summary": "Get the joke of the day", + "description": "A joke a day makes more of a dad out of you.", + "tags": [ + "Jokes" + ], + "responses": { + "200": { + "description": "Image data", + "content": { + "image/jpeg": {}, + "image/png": {}, + "image/gif": {} + } + } + } + } + }, + "/total": { + "get": { + "summary": "Get total amount of jokes in database", + "tags": [ + "Jokes" + ], + "responses": { + "200": { + "description": "Total jokes", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/response.confirmation" + }, + "example": { + "message": "154" + } + } + } + } + } + } + }, + "/submit": { + "get": { + "summary": "Get submitted Jokes", + "tags": [ + "Submission" + ], + "parameters": [ + { + "name": "author", + "in": "query", + "required": false, + "description": "Author to be queried", + "schema": { + "type": "string" + } + }, + { + "name": "approved", + "in": "query", + "required": false, + "description": "Whether query just approved jokes or not", + "schema": { + "type": "boolean" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "number" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "asd", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "number" + }, + "jokes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/response.submission" + } } } } @@ -448,6 +354,100 @@ } } } + }, + "post": { + "summary": "Submit a joke", + "description": "Must be in multipart/form-data format. Author must be in the format of \"Name <email>\".\n", + "tags": [ + "Submission" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "properties": { + "link": { + "description": "Image link", + "type": "string" + }, + "image": { + "description": "Image data", + "type": "string" + }, + "author": { + "description": "Person who submitted this", + "type": "string" + } + }, + "required": [ + "author", + "image", + "link" + ] + } + } + } + }, + "responses": { + "201": { + "description": "Joke successfully submitted", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/response.confirmation" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/response.submission" + } + } + } + ] + } + } + } + }, + "400": { + "description": "Invalid data was sent", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/response.error" + } + } + } + } + } + } + }, + "/health": { + "get": { + "summary": "Health check", + "description": "Ping the databases to make sure everything's alright", + "tags": [ + "Health" + ], + "responses": { + "200": { + "description": "Everything is okay" + }, + "403": { + "description": "Something is not okay", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/response.error" + } + } + } + } + } + } + } }, "components": { "schemas": { diff --git a/api/documentation.yaml b/api/documentation.yaml index 4d67392..f577d7e 100644 --- a/api/documentation.yaml +++ b/api/documentation.yaml @@ -14,9 +14,9 @@ info: name: GNU General Public License v3.0 url: https://github.com/aldy505/jokes-bapak2/blob/master/LICENSE servers: - - url: "https://jokesbapak2.herokuapp.com/v1" + - url: "https://jokesbapak2.reinaldyrafli.com/api/v1" description: Production - - url: "https://jokesbapak2.herokuapp.com" + - url: "https://jokesbapak2.reinaldyrafli.com/api" description: Production - url: "http://localhost:5000" description: Development @@ -31,9 +31,9 @@ paths: 200: description: Image data content: - 'image/gif': {} - 'image/png': {} - 'image/jpeg': {} + "image/gif": {} + "image/png": {} + "image/jpeg": {} put: summary: Add a new joke into database description: asd @@ -46,15 +46,15 @@ paths: application/json: schema: allOf: - - $ref: '#/components/schemas/request.auth' - - $ref: '#/components/schemas/request.joke' + - $ref: "#/components/schemas/request.auth" + - $ref: "#/components/schemas/request.joke" responses: 201: description: Image has been added content: application/json: schema: - $ref: '#/components/schemas/request.joke' + $ref: "#/components/schemas/request.joke" example: link: https://link.to/image.jpg 400: @@ -62,7 +62,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/response.error' + $ref: "#/components/schemas/response.error" example: error: URL provided is not a valid image 403: @@ -70,7 +70,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/response.error' + $ref: "#/components/schemas/response.error" /id/{id}: parameters: - in: path @@ -88,17 +88,16 @@ paths: 200: description: Image data content: - 'image/jpeg': {} - 'image/png': {} - 'image/gif': {} + "image/jpeg": {} + "image/png": {} + "image/gif": {} 404: description: Provided image ID was not found content: text/plain: schema: type: string - example: - Requested ID was not found. + example: Requested ID was not found. patch: summary: Update a Joke with certain image ID description: Returns consistent image for every call. @@ -111,26 +110,26 @@ paths: application/json: schema: allOf: - - $ref: '#/components/schemas/response.confirmation' - - $ref: '#/components/schemas/request.joke' + - $ref: "#/components/schemas/response.confirmation" + - $ref: "#/components/schemas/request.joke" 400: description: Link provided is not a valid image content: application/json: schema: - $ref: '#/components/schemas/response.error' + $ref: "#/components/schemas/response.error" 403: description: Must be authenticated to submit a joke content: application/json: schema: - $ref: '#/components/schemas/response.error' + $ref: "#/components/schemas/response.error" 406: description: If the Joke ID does not exists content: application/json: schema: - $ref: '#/components/schemas/response.error' + $ref: "#/components/schemas/response.error" delete: summary: Delete a Joke with certain image ID description: hi @@ -142,19 +141,19 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/response.confirmation' + $ref: "#/components/schemas/response.confirmation" 403: description: Must be authenticated to submit a joke content: application/json: schema: - $ref: '#/components/schemas/response.error' + $ref: "#/components/schemas/response.error" 406: description: If the Joke ID does not exists content: application/json: schema: - $ref: '#/components/schemas/response.error' + $ref: "#/components/schemas/response.error" /today: get: summary: Get the joke of the day @@ -165,9 +164,9 @@ paths: 200: description: Image data content: - 'image/jpeg': {} - 'image/png': {} - 'image/gif': {} + "image/jpeg": {} + "image/png": {} + "image/gif": {} /total: get: summary: Get total amount of jokes in database @@ -179,7 +178,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/response.confirmation' + $ref: "#/components/schemas/response.confirmation" example: message: "154" /submit: @@ -223,7 +222,7 @@ paths: jokes: type: array items: - $ref: '#/components/schemas/response.submission' + $ref: "#/components/schemas/response.submission" post: summary: Submit a joke description: > @@ -256,17 +255,17 @@ paths: application/json: schema: allOf: - - $ref: '#/components/schemas/response.confirmation' + - $ref: "#/components/schemas/response.confirmation" - type: object properties: data: - $ref: '#/components/schemas/response.submission' + $ref: "#/components/schemas/response.submission" 400: description: Invalid data was sent content: application/json: schema: - $ref: '#/components/schemas/response.error' + $ref: "#/components/schemas/response.error" /health: get: summary: Health check @@ -281,7 +280,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/response.error' + $ref: "#/components/schemas/response.error" components: schemas: @@ -319,4 +318,4 @@ components: author: type: string status: - type: number \ No newline at end of file + type: number diff --git a/api/go.mod b/api/go.mod index f2819d7..37ce534 100644 --- a/api/go.mod +++ b/api/go.mod @@ -16,6 +16,7 @@ require ( require ( github.com/go-chi/chi/v5 v5.0.7 github.com/minio/minio-go/v7 v7.0.35 + github.com/rs/cors v1.8.2 ) require ( diff --git a/api/go.sum b/api/go.sum index fc9d91c..a3764ab 100644 --- a/api/go.sum +++ b/api/go.sum @@ -238,6 +238,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 h1:xoIK0ctDddBMnc74udxJYBqlo9Ylnsp1waqjLsnef20= github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= +github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= diff --git a/api/main.go b/api/main.go index 059769d..e87181a 100644 --- a/api/main.go +++ b/api/main.go @@ -3,6 +3,7 @@ package main import ( "errors" "log" + "net" "net/http" "os" "os/signal" @@ -20,6 +21,7 @@ import ( "github.com/allegro/bigcache/v3" "github.com/getsentry/sentry-go" "github.com/go-chi/chi/v5" + "github.com/rs/cors" ) func main() { @@ -69,7 +71,7 @@ func main() { } // Setup In Memory - memory, err := bigcache.NewBigCache(bigcache.DefaultConfig(6 * time.Hour)) + memory, err := bigcache.NewBigCache(bigcache.DefaultConfig(10 * time.Minute)) if err != nil { log.Panicln(err) } @@ -127,12 +129,19 @@ func main() { router := chi.NewRouter() + router.Use(cors.New(cors.Options{ + AllowedMethods: []string{http.MethodGet}, + AllowCredentials: false, + MaxAge: int(60 * 60 * 24 * 365), + Debug: false, + }).Handler) + router.Mount("/health", healthRouter) router.Mount("/", jokeRouter) server := &http.Server{ Handler: router, - Addr: hostname + ":" + port, + Addr: net.JoinHostPort(hostname, port), ReadTimeout: time.Minute, WriteTimeout: time.Minute, IdleTimeout: time.Second * 30, diff --git a/benchmark/package.json b/benchmark/package.json index 47f587b..558bcd7 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -14,11 +14,11 @@ "type": "module", "scripts": { "start": "yarn run b:random && yarn run b:today && yarn run b:byid", - "b:random": "autocannon --title \"GET /v1/ - Random Jokes\" --method GET --amount 100 --bailout 3 --headers user-agent=benchmark https://jokesbapak2.herokuapp.com/v1/", - "b:today": "autocannon --title \"GET /v1/today - Today's Jokes\" --method GET --amount 100 --bailout 3 --headers user-agent=benchmark https://jokesbapak2.herokuapp.com/v1/today", - "b:byid": "autocannon --title \"GET /v1/id/5 - Joke by ID\" --method GET --amount 100 --bailout 3 --headers user-agent=benchmark https://jokesbapak2.herokuapp.com/v1/id/5" + "b:random": "autocannon --title \"GET /v1/ - Random Jokes\" --method GET --amount 100 --bailout 3 --headers user-agent=benchmark https://jokesbapak2.reinaldyrafli.com/api/v1/", + "b:today": "autocannon --title \"GET /v1/today - Today's Jokes\" --method GET --amount 100 --bailout 3 --headers user-agent=benchmark https://jokesbapak2.reinaldyrafli.com/api/v1/today", + "b:byid": "autocannon --title \"GET /v1/id/5 - Joke by ID\" --method GET --amount 100 --bailout 3 --headers user-agent=benchmark https://jokesbapak2.reinaldyrafli.com/api/v1/id/5" }, "dependencies": { "autocannon": "^7.4.0" } -} +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index f6abde7..59b9b3f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,16 +1,35 @@ services: api: build: ./api/ - restart: always env_file: ./api/.env ports: - 5000:5000 + healthcheck: + test: "curl -f http://api:5000/health/" + interval: 15s + timeout: 12s + retries: 10 + start_period: 30s + deploy: + mode: replicated + replicas: 1 + restart_policy: + condition: on-failure + delay: 30s + max_attempts: 20 + window: 60s + resources: + limits: + memory: 1GB + cpus: '1' + reservations: + memory: 50MB + cpus: '0.1' depends_on: - - db - - cache - # Uncomment these if you want to have it on - # volumes: - # ./api:/app + bucket: + condition: service_healthy + cache: + condition: service_healthy client: build: ./client/ @@ -18,38 +37,33 @@ services: env_file: ./client/.env ports: - 3000:3000 - depends_on: - - api - # Uncomment these if you want to have it on - # volumes: - # ./client:/app - - db: - build: ./database/postgres/ - command: > - -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key - restart: unless-stopped - ports: - - 5432:5432 - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: password - POSTGRES_DB: jokesbapak2 - PGDATA: /data/postgres - # I got this key from somewhere. It works when you run it locally. - POSTGRES_SSL_CA_CERT: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURjekNDQWx1Z0F3SUJBZ0lVR3lDaElvR3g0 healthcheck: - test: pg_isready -U postgres - interval: 10s - timeout: 5s - retries: 5 + test: "curl -f http://client:3000/" + interval: 15s + timeout: 12s + retries: 10 start_period: 30s - volumes: - - ./database/postgres/data:/data/postgres + deploy: + mode: replicated + replicas: 1 + restart_policy: + condition: on-failure + delay: 30s + max_attempts: 20 + window: 60s + resources: + limits: + memory: 500MB + cpus: '1' + reservations: + memory: 50MB + cpus: '0.1' + depends_on: + api: + condition: service_healthy cache: - image: redis:6.2.4-alpine - restart: unless-stopped + image: redis:6.2.7-alpine ports: - 6379:6379 healthcheck: @@ -58,12 +72,27 @@ services: timeout: 5s retries: 5 start_period: 30s + deploy: + mode: replicated + replicas: 1 + restart_policy: + condition: on-failure + delay: 30s + max_attempts: 20 + window: 60s + resources: + limits: + memory: 2GB + cpus: '1' + reservations: + memory: 100MB + cpus: '0.1' volumes: - - ./database/redis/etc:/usr/local/etc/redis - - ./database/redis/data:/data + - ./data/redis/etc:/usr/local/etc/redis + - ./data/redis/data:/data bucket: - image: quay.io/minio/minio:RELEASE.2022-02-05T04-40-59Z + image: quay.io/minio/minio:RELEASE.2022-09-07T22-25-02Z command: server /data --console-address ":9001" restart: unless-stopped ports: @@ -80,28 +109,20 @@ services: timeout: 10s retries: 5 start_period: 60s + deploy: + mode: replicated + replicas: 1 + restart_policy: + condition: on-failure + delay: 30s + max_attempts: 20 + window: 60s + resources: + limits: + memory: 4GB + cpus: '2' + reservations: + memory: 50MB + cpus: '0.1' volumes: - ./data/minio:/data - - cache-admin: - image: rediscommander/redis-commander:latest - restart: always - environment: - REDIS_PORT: 6379 - REDIS_HOST: redis - ports: - - 2084:8081 - depends_on: - - cache - - db-admin: - image: sosedoff/pgweb:0.11.8 - restart: always - ports: - - 2086:8081 - links: - - postgres:postgres - environment: - DATABASE_URL: postgres://postgres:password@db/jokesbapak2 - depends_on: - - db