fix: redis version on ci

This commit is contained in:
Reinaldy Rafli 2023-08-12 12:36:08 +07:00
parent e949b11ab9
commit 9aedbc6648
Signed by: aldy505
GPG Key ID: A3F8A7E23DA2AD94
20 changed files with 726 additions and 697 deletions

View File

@ -3,17 +3,17 @@ root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_size = 2
indent_style = space
insert_final_newline = false
max_line_length = 120
tab_width = 4
ij_continuation_indent_size = 8
tab_width = 2
ij_continuation_indent_size = 4
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = true
ij_smart_tabs = false
ij_visual_guides =
ij_visual_guides =
ij_wrap_on_typing = false
[*.css]
@ -30,7 +30,7 @@ ij_css_hex_color_upper_case = false
ij_css_keep_blank_lines_in_code = 2
ij_css_keep_indents_on_empty_lines = false
ij_css_keep_single_line_blocks = false
ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
ij_css_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow
ij_css_space_after_colon = true
ij_css_space_before_opening_brace = true
ij_css_use_double_quotes = true
@ -60,7 +60,7 @@ ij_sass_keep_indents_on_empty_lines = false
ij_sass_keep_single_line_blocks = false
ij_sass_line_comment_add_space = false
ij_sass_line_comment_at_first_column = false
ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
ij_sass_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow
ij_sass_space_after_colon = true
ij_sass_space_before_opening_brace = true
ij_sass_use_double_quotes = true
@ -114,7 +114,7 @@ ij_typescript_array_initializer_wrap = off
ij_typescript_assignment_wrap = off
ij_typescript_binary_operation_sign_on_next_line = false
ij_typescript_binary_operation_wrap = off
ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
ij_typescript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/**
ij_typescript_blank_lines_after_imports = 1
ij_typescript_blank_lines_around_class = 1
ij_typescript_blank_lines_around_field = 0
@ -183,7 +183,7 @@ ij_typescript_prefer_explicit_types_function_expression_returns = false
ij_typescript_prefer_explicit_types_function_returns = false
ij_typescript_prefer_explicit_types_vars_fields = false
ij_typescript_prefer_parameters_wrap = false
ij_typescript_property_prefix =
ij_typescript_property_prefix =
ij_typescript_reformat_c_style_comments = false
ij_typescript_space_after_colon = true
ij_typescript_space_after_comma = true
@ -297,7 +297,7 @@ ij_javascript_array_initializer_wrap = off
ij_javascript_assignment_wrap = off
ij_javascript_binary_operation_sign_on_next_line = false
ij_javascript_binary_operation_wrap = off
ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
ij_javascript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/**
ij_javascript_blank_lines_after_imports = 1
ij_javascript_blank_lines_around_class = 1
ij_javascript_blank_lines_around_field = 0
@ -362,7 +362,7 @@ ij_javascript_prefer_explicit_types_function_expression_returns = false
ij_javascript_prefer_explicit_types_function_returns = false
ij_javascript_prefer_explicit_types_vars_fields = false
ij_javascript_prefer_parameters_wrap = false
ij_javascript_property_prefix =
ij_javascript_property_prefix =
ij_javascript_reformat_c_style_comments = false
ij_javascript_space_after_colon = true
ij_javascript_space_after_comma = true
@ -460,7 +460,7 @@ ij_go_group_stdlib_imports = true
ij_go_import_sorting = gofmt
ij_go_keep_indents_on_empty_lines = false
ij_go_local_group_mode = project
ij_go_local_package_prefixes =
ij_go_local_package_prefixes =
ij_go_move_all_imports_in_one_declaration = true
ij_go_move_all_stdlib_imports_in_one_group = true
ij_go_remove_redundant_import_aliases = false

View File

@ -2,7 +2,7 @@ name: CI
on:
push:
branches: ["master"]
branches: [ "master" ]
jobs:
api-build:
@ -29,7 +29,7 @@ jobs:
volumes:
- minio-data:/data
redis:
image: redis:7.0.2-bookworm
image: redis:7.0.12-bookworm
ports:
- 6379:6379
defaults:

View File

@ -2,7 +2,7 @@ name: PR
on:
pull_request:
branches: ["*"]
branches: [ "*" ]
jobs:
client-build:
@ -18,7 +18,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v2
- name: Installling dependencies
- name: Install dependencies
run: npm install
- name: Lint
@ -66,7 +66,7 @@ jobs:
volumes:
- minio-data:/data
redis:
image: redis:7.0.2-bookworm
image: redis:7.0.12-bookworm
ports:
- 6379:6379
defaults:

View File

@ -1,75 +1,87 @@
# Contributing Guide
First of all. Thank you for considering to contribute on Jokes Bapak2 API project. I hope this project will get better and we will become more bapak2 than ever.
This project is a monorepo, meaning the backend, frontend, and Github CI are all in one place (one repository). Before you do anything, if you're going to do some breaking change or you'll write (or remove) large numbers of LOC (line of codes), please open an issue first and let us know about it. So that our work won't bother you and you'll have a breeze on developing this.
## Project Prerequisites && Setup
### Front End (`./client`)
You'll have to install:
* Node.js LTS (preferably with [fnm](https://github.com/Schniz/fnm) or [nvm](https://github.com/nvm-sh/nvm))
* Yarn v1
See the [README](./client/README.md) on client for detailed project setup.
### Back End (`./api`)
You'll have to install:
* Go v1.16.x
* (Optional) [Fiber CLI](https://github.com/gofiber/cli) for ease of development
See the [README](./api/README.md) on client for detailed project setup.
### With Docker Compose
If you're just developing the front end and too lazy installing Go and such (or the other way around), you can use `docker-compose` file specified on the main page.
You'll have to install:
* Docker (preferably with Docker Desktop if you're on Windows or Mac)
* Docker Compose
```bash
# Create a docker container but don't start it yet.
$ docker-compose up --no-start
# Or if you want to create the docker container and start it right away
$ docker-compose up
# If you want to have it running in the background
$ docker-compose up --detach
# Start existing container
$ docker-compose start
# Stop running container
$ docker-compose stop
# Destroy current container
$ docker-compose down
```
## Before submitting PR
### Front End (`./client`)
Please run these:
* `yarn lint`
* `yarn format`
* `yarn build`
If those command didn't pass, please fix the problem first. Please recheck your changes, make sure NOT to leave any secret token/keys behind.
### Back End (`./api`)
Please run these:
* `go fmt`
* `go build main.go`
* `go test -v -race -coverprofile=coverage.out -covermode=atomic ./...`
If those command didn't pass, please fix the problem first. Please recheck your changes, make sure NOT to leave any secret token/keys behind.
## One more thing..
# Contributing Guide
First of all. Thank you for considering to contribute on Jokes Bapak2 API project. I hope this project will get better
and we will become more bapak2 than ever.
This project is a monorepo, meaning the backend, frontend, and Github CI are all in one place (one repository). Before
you do anything, if you're going to do some breaking change or you'll write (or remove) large numbers of LOC (line of
codes), please open an issue first and let us know about it. So that our work won't bother you and you'll have a breeze
on developing this.
## Project Prerequisites && Setup
### Front End (`./client`)
You'll have to install:
* Node.js LTS (preferably with [fnm](https://github.com/Schniz/fnm) or [nvm](https://github.com/nvm-sh/nvm))
* Yarn v1
See the [README](./client/README.md) on client for detailed project setup.
### Back End (`./api`)
You'll have to install:
* Go v1.16.x
* (Optional) [Fiber CLI](https://github.com/gofiber/cli) for ease of development
See the [README](./api/README.md) on client for detailed project setup.
### With Docker Compose
If you're just developing the front end and too lazy installing Go and such (or the other way around), you can
use `docker-compose` file specified on the main page.
You'll have to install:
* Docker (preferably with Docker Desktop if you're on Windows or Mac)
* Docker Compose
```bash
# Create a docker container but don't start it yet.
$ docker-compose up --no-start
# Or if you want to create the docker container and start it right away
$ docker-compose up
# If you want to have it running in the background
$ docker-compose up --detach
# Start existing container
$ docker-compose start
# Stop running container
$ docker-compose stop
# Destroy current container
$ docker-compose down
```
## Before submitting PR
### Front End (`./client`)
Please run these:
* `yarn lint`
* `yarn format`
* `yarn build`
If those command didn't pass, please fix the problem first. Please recheck your changes, make sure NOT to leave any
secret token/keys behind.
### Back End (`./api`)
Please run these:
* `go fmt`
* `go build main.go`
* `go test -v -race -coverprofile=coverage.out -covermode=atomic ./...`
If those command didn't pass, please fix the problem first. Please recheck your changes, make sure NOT to leave any
secret token/keys behind.
## One more thing..
Oh my God, thank you so much!!! Working on an open source project is interesting right?? 😆

View File

@ -7,15 +7,19 @@
<br>
</h1>
👋 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!
👋 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!
ou can access the front facing web on [jokesbapak2.reinaldyrafli.com](http://jokesbapak2.reinaldyrafli.com/).
## Brief explanation of what is this
Jokes Bapak2 is an image API that you can use for free! I've been seeing lots and lots of Indonesian dad jokes on Twitter, Facebook and Instagram on early 2020. In a month, I made a Discord bot that provides the jokes. But I thought, why not make it as an API?
Jokes Bapak2 is an image API that you can use for free! I've been seeing lots and lots of Indonesian dad jokes on
Twitter, Facebook and Instagram on early 2020. In a month, I made a Discord bot that provides the jokes. But I thought,
why not make it as an API?
This is some kind of [icanhazdadjokes](https://icanhazdadjoke.com/) but it's Indonesian and it's not text, it's images. Dad jokes in Indonesia is somewhat a bit different than in US/UK because, I guess, here, it's a lot dumber.
This is some kind of [icanhazdadjokes](https://icanhazdadjoke.com/) but it's Indonesian and it's not text, it's images.
Dad jokes in Indonesia is somewhat a bit different than in US/UK because, I guess, here, it's a lot dumber.
## Project Directories
@ -24,29 +28,31 @@ This is some kind of [icanhazdadjokes](https://icanhazdadjoke.com/) but it's Ind
You can consume this API via a website (linked in the front facing web) with a few endpoints:
* `/` - Random jokes bapak2
* `/id/{number}` - Jokes bapak2 based on ID
* `/today` - Jokes bapak2 of the day
* `/total` - Total available jokes bapak2
* `/` - Random jokes bapak2
* `/id/{number}` - Jokes bapak2 based on ID
* `/today` - Jokes bapak2 of the day
* `/total` - Total available jokes bapak2
Currently I'm (still) searching for an alternative for AWS S3 that I can use for free.
## Tech stacks
* Go (for `api` / back end)
* Node.js (for `client` / front end)
* Postgres
* Redis
* Go (for `api` / back end)
* Node.js (for `client` / front end)
* Postgres
* Redis
That's it.
## Development
Two ways of doing this:
1. Install all the tech stack on your local machine
2. Using docker-compose
See [CONTRIBUTING](./CONTRIBUTING.md) or README files on each project directory for further instruction on how to run the development environment.
1. Install all the tech stack on your local machine
2. Using docker-compose
See [CONTRIBUTING](./CONTRIBUTING.md) or README files on each project directory for further instruction on how to run
the development environment.
## Thanks to

View File

@ -1,78 +1,79 @@
# Jokes Bapak2 API
Still work in progress
## Development
```bash
# Install modules
$ go mod download
# or
$ go mod vendor
# run the local server
$ go run main.go
# build everything
$ go build main.go
```
There is a placeholder data ready for you to query it manually in `/platform/database/placeholder.sql`. Have a good time developing!
## Used packages
| Name | Version | Type |
| --- | --- | --- |
| [gofiber/fiber](https://github.com/gofiber/fiber) | `v2.21.0` | Framework |
| [jackc/pgx](https://github.com/jackc/pgx) | `v4.13.0` | Database |
| [go-redis/redis](https://github.com/go-redis/redis) | `v8.11.4` | Cache |
| [allegro/bigcache](https://github.com/allegro/bigcache) | `v3.0.1` | Cache |
| [joho/godotenv](https://github.com/joho/godotenv) | `v1.4.0` | Config |
| [getsentry/sentry-go](https://github.com/getsentry/sentry-go) | `v0.11.0` | Logging |
| [aldy505/phc-crypto](https://github.com/aldy505/phc-crypto) | `v1.1.0` | Utils |
| [Masterminds/squirrel](https://github.com/Masterminds/squirrel ) | `v1.5.1` | Utils |
| [aldy505/bob](https://github.com/aldy505/bob) | `v0.0.4` | Utils |
| [gojek/heimdall](https://github.com/gojek/heimdall) | `v7.0.2` | Utils |
| [georgysavva/scany](https://github.com/georgysavva/scany) | `v0.2.9` | Utils |
| [pquerna/ffjson](https://github.com/pquerna/ffjson) | `v0.0.0-20190930134022-aa0246cd15f7` | Utils |
## Directory structure
```
.
├── core - Pure business logic
│ ├── administrator
│ ├── joke
│ ├── schema
│ ├── submit
│ └── validator
├── Dockerfile - Docker image for API
├── documentation.json - Swagger documentation
├── documentation.yaml - Swagger documentation
├── favicon.png
├── go.mod - Module declaration
├── go.sum - Checksum for modules
├── handler - Route handlers
│ ├── health
│ ├── joke
│ └── submit
├── main.go - Application entry point
├── middleware - Route middlewares
├── platform - Third party packages
│ └── database
├── README.md - You are here
├── routes - Route definitions
└── utils - Utility functions
```
## `.env` configuration
```ini
ENV=development
PORT=5000
DATABASE_URL=postgres://postgres:password@localhost:5432/jokesbapak2
REDIS_URL=redis://@localhost:6379
SENTRY_DSN=
# Jokes Bapak2 API
Still work in progress
## Development
```bash
# Install modules
$ go mod download
# or
$ go mod vendor
# run the local server
$ go run main.go
# build everything
$ go build main.go
```
There is a placeholder data ready for you to query it manually in `/platform/database/placeholder.sql`. Have a good time
developing!
## Used packages
| Name | Version | Type |
|------------------------------------------------------------------|--------------------------------------|-----------|
| [gofiber/fiber](https://github.com/gofiber/fiber) | `v2.21.0` | Framework |
| [jackc/pgx](https://github.com/jackc/pgx) | `v4.13.0` | Database |
| [go-redis/redis](https://github.com/go-redis/redis) | `v8.11.4` | Cache |
| [allegro/bigcache](https://github.com/allegro/bigcache) | `v3.0.1` | Cache |
| [joho/godotenv](https://github.com/joho/godotenv) | `v1.4.0` | Config |
| [getsentry/sentry-go](https://github.com/getsentry/sentry-go) | `v0.11.0` | Logging |
| [aldy505/phc-crypto](https://github.com/aldy505/phc-crypto) | `v1.1.0` | Utils |
| [Masterminds/squirrel](https://github.com/Masterminds/squirrel ) | `v1.5.1` | Utils |
| [aldy505/bob](https://github.com/aldy505/bob) | `v0.0.4` | Utils |
| [gojek/heimdall](https://github.com/gojek/heimdall) | `v7.0.2` | Utils |
| [georgysavva/scany](https://github.com/georgysavva/scany) | `v0.2.9` | Utils |
| [pquerna/ffjson](https://github.com/pquerna/ffjson) | `v0.0.0-20190930134022-aa0246cd15f7` | Utils |
## Directory structure
```
.
├── core - Pure business logic
│ ├── administrator
│ ├── joke
│ ├── schema
│ ├── submit
│ └── validator
├── Dockerfile - Docker image for API
├── documentation.json - Swagger documentation
├── documentation.yaml - Swagger documentation
├── favicon.png
├── go.mod - Module declaration
├── go.sum - Checksum for modules
├── handler - Route handlers
│ ├── health
│ ├── joke
│ └── submit
├── main.go - Application entry point
├── middleware - Route middlewares
├── platform - Third party packages
│ └── database
├── README.md - You are here
├── routes - Route definitions
└── utils - Utility functions
```
## `.env` configuration
```ini
ENV=development
PORT=5000
DATABASE_URL=postgres://postgres:password@localhost:5432/jokesbapak2
REDIS_URL=redis://@localhost:6379
SENTRY_DSN=
```

View File

@ -2,9 +2,10 @@ package joke_test
import (
"context"
"jokes-bapak2-api/core/joke"
"testing"
"time"
"jokes-bapak2-api/core/joke"
)
func TestGetRandomJoke(t *testing.T) {

View File

@ -2,9 +2,10 @@ package joke_test
import (
"context"
"jokes-bapak2-api/core/joke"
"testing"
"time"
"jokes-bapak2-api/core/joke"
)
func TestListJokeFromBucket(t *testing.T) {

View File

@ -2,9 +2,10 @@ package joke_test
import (
"context"
"jokes-bapak2-api/core/joke"
"testing"
"time"
"jokes-bapak2-api/core/joke"
)
func TestGetTodaysJoke(t *testing.T) {

View File

@ -2,9 +2,10 @@ package joke_test
import (
"context"
"jokes-bapak2-api/core/joke"
"testing"
"time"
"jokes-bapak2-api/core/joke"
)
func TestGetTotalJoke(t *testing.T) {

View File

@ -1,321 +1,321 @@
openapi: 3.0.0
info:
title: Jokesbapak2 Image API
description: >
Jokes Bapak2 is an image API that you can use for free! I've been seeing lots and lots of Indonesian dad jokes on Twitter,
Facebook and Instagram on early 2020. In a month, I made a Discord bot that provides the jokes.
But I thought, why not make it as an API?
version: 0.0.1
contact:
name: Reinaldy Rafli
url: https://github.com/aldy505
email: aldy505@tutanota.com
license:
name: GNU General Public License v3.0
url: https://github.com/aldy505/jokes-bapak2/blob/master/LICENSE
servers:
- url: "https://jokesbapak2.reinaldyrafli.com/api/v1"
description: Production
- url: "https://jokesbapak2.reinaldyrafli.com/api"
description: Production
- url: "http://localhost:5000"
description: Development
paths:
/:
get:
tags:
- Jokes
summary: Get random Jokes Bapak2 image
description: Returns a different image (PNG, JPG, or GIF) for every call.
responses:
200:
description: Image data
content:
"image/gif": {}
"image/png": {}
"image/jpeg": {}
put:
summary: Add a new joke into database
description: asd
tags:
- Jokes
requestBody:
description: asds
required: true
content:
application/json:
schema:
allOf:
- $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"
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.
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"
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"
post:
summary: Submit a joke
description: >
Must be in multipart/form-data format.
Author must be in the format of "Name &lt;email&gt;".
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:
request.auth:
type: object
properties:
key:
type: string
token:
type: string
request.joke:
type: object
properties:
link:
type: string
response.confirmation:
type: object
properties:
message:
type: string
response.error:
type: object
properties:
error:
type: string
response.submission:
type: object
properties:
id:
type: number
link:
type: string
created_at:
type: string
author:
type: string
status:
type: number
openapi: 3.0.0
info:
title: Jokesbapak2 Image API
description: >
Jokes Bapak2 is an image API that you can use for free! I've been seeing lots and lots of Indonesian dad jokes on Twitter,
Facebook and Instagram on early 2020. In a month, I made a Discord bot that provides the jokes.
But I thought, why not make it as an API?
version: 0.0.1
contact:
name: Reinaldy Rafli
url: https://github.com/aldy505
email: aldy505@tutanota.com
license:
name: GNU General Public License v3.0
url: https://github.com/aldy505/jokes-bapak2/blob/master/LICENSE
servers:
- url: "https://jokesbapak2.reinaldyrafli.com/api/v1"
description: Production
- url: "https://jokesbapak2.reinaldyrafli.com/api"
description: Production
- url: "http://localhost:5000"
description: Development
paths:
/:
get:
tags:
- Jokes
summary: Get random Jokes Bapak2 image
description: Returns a different image (PNG, JPG, or GIF) for every call.
responses:
200:
description: Image data
content:
"image/gif": { }
"image/png": { }
"image/jpeg": { }
put:
summary: Add a new joke into database
description: asd
tags:
- Jokes
requestBody:
description: asds
required: true
content:
application/json:
schema:
allOf:
- $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"
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.
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"
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"
post:
summary: Submit a joke
description: >
Must be in multipart/form-data format.
Author must be in the format of "Name &lt;email&gt;".
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:
request.auth:
type: object
properties:
key:
type: string
token:
type: string
request.joke:
type: object
properties:
link:
type: string
response.confirmation:
type: object
properties:
message:
type: string
response.error:
type: object
properties:
error:
type: string
response.submission:
type: object
properties:
id:
type: number
link:
type: string
created_at:
type: string
author:
type: string
status:
type: number

View File

@ -1,10 +1,11 @@
package joke
import (
core "jokes-bapak2-api/core/joke"
"net/http"
"strconv"
core "jokes-bapak2-api/core/joke"
"github.com/go-chi/chi/v5"
)

View File

@ -1,9 +1,10 @@
package joke
import (
core "jokes-bapak2-api/core/joke"
"net/http"
"strconv"
core "jokes-bapak2-api/core/joke"
)
// TotalJokes provides a HTTP handler for acquiring total jokes

View File

@ -1,182 +1,181 @@
package main
import (
"errors"
"log"
"net"
"net/http"
"os"
"os/signal"
"context"
"jokes-bapak2-api/core/joke"
"jokes-bapak2-api/routes"
"github.com/go-redis/redis/v8"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"time"
"github.com/allegro/bigcache/v3"
"github.com/getsentry/sentry-go"
"github.com/go-chi/chi/v5"
"github.com/rs/cors"
)
func main() {
redisURL, ok := os.LookupEnv("REDIS_URL")
if !ok {
redisURL = "redis://@localhost:6379"
}
minioHost, ok := os.LookupEnv("MINIO_HOST")
if !ok {
minioHost = "localhost:9000"
}
minioRegion, ok := os.LookupEnv("MINIO_REGION")
if !ok {
minioRegion = ""
}
minioSecure, ok := os.LookupEnv("MINIO_SECURE")
if !ok {
minioSecure = "false"
}
minioID, ok := os.LookupEnv("MINIO_ACCESS_ID")
if !ok {
minioID = "minio"
}
minioSecret, ok := os.LookupEnv("MINIO_SECRET_KEY")
if !ok {
minioSecret = "password"
}
minioToken, ok := os.LookupEnv("MINIO_TOKEN")
if !ok {
minioToken = ""
}
sentryDsn, ok := os.LookupEnv("SENTRY_DSN")
if !ok {
sentryDsn = ""
}
port, ok := os.LookupEnv("PORT")
if !ok {
port = "5000"
}
hostname, ok := os.LookupEnv("HOSTNAME")
if !ok {
hostname = "127.0.0.1"
}
environment, ok := os.LookupEnv("ENVIRONMENT")
if !ok {
environment = "development"
}
// Setup In Memory
memory, err := bigcache.NewBigCache(bigcache.DefaultConfig(10 * time.Minute))
if err != nil {
log.Panicln(err)
}
defer memory.Close()
// Setup MinIO
minioClient, err := minio.New(minioHost, &minio.Options{
Creds: credentials.NewStaticV4(minioID, minioSecret, minioToken),
Region: minioRegion,
Secure: minioSecure == "true",
})
if err != nil {
log.Fatalf("setting up minio client: %s", err.Error())
return
}
parsedRedisURL, err := redis.ParseURL(redisURL)
if err != nil {
log.Fatalf("parsing redis url: %s", err.Error())
return
}
redisClient := redis.NewClient(parsedRedisURL)
defer func() {
err := redisClient.Close()
if err != nil {
log.Printf("closing redis client: %s", err.Error())
}
}()
// Setup Sentry
err = sentry.Init(sentry.ClientOptions{
Dsn: sentryDsn,
Environment: environment,
AttachStacktrace: true,
// Enable printing of SDK debug messages.
// Useful when getting started or trying to figure something out.
Debug: environment != "production",
})
if err != nil {
log.Fatalf("setting up sentry: %s", err.Error())
return
}
defer sentry.Flush(2 * time.Second)
setupCtx, setupCancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute*4))
defer setupCancel()
_, _, err = joke.GetTodaysJoke(setupCtx, minioClient, redisClient, memory)
if err != nil {
log.Fatalf("getting initial joke data: %s", err.Error())
return
}
healthRouter := routes.Health(minioClient, redisClient)
jokeRouter := routes.Joke(minioClient, redisClient, memory)
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: net.JoinHostPort(hostname, port),
ReadTimeout: time.Minute,
WriteTimeout: time.Minute,
IdleTimeout: time.Second * 30,
ReadHeaderTimeout: time.Minute,
}
exitSignal := make(chan os.Signal, 1)
signal.Notify(exitSignal, os.Interrupt)
go func() {
err := server.ListenAndServe()
if err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("listening http server: %v", err)
}
}()
<-exitSignal
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), time.Second*30)
defer shutdownCancel()
err = server.Shutdown(shutdownCtx)
if err != nil {
log.Printf("shutting down http server: %v", err)
}
}
package main
import (
"context"
"errors"
"log"
"net"
"net/http"
"os"
"os/signal"
"time"
"jokes-bapak2-api/core/joke"
"jokes-bapak2-api/routes"
"github.com/go-redis/redis/v8"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/allegro/bigcache/v3"
"github.com/getsentry/sentry-go"
"github.com/go-chi/chi/v5"
"github.com/rs/cors"
)
func main() {
redisURL, ok := os.LookupEnv("REDIS_URL")
if !ok {
redisURL = "redis://@localhost:6379"
}
minioHost, ok := os.LookupEnv("MINIO_HOST")
if !ok {
minioHost = "localhost:9000"
}
minioRegion, ok := os.LookupEnv("MINIO_REGION")
if !ok {
minioRegion = ""
}
minioSecure, ok := os.LookupEnv("MINIO_SECURE")
if !ok {
minioSecure = "false"
}
minioID, ok := os.LookupEnv("MINIO_ACCESS_ID")
if !ok {
minioID = "minio"
}
minioSecret, ok := os.LookupEnv("MINIO_SECRET_KEY")
if !ok {
minioSecret = "password"
}
minioToken, ok := os.LookupEnv("MINIO_TOKEN")
if !ok {
minioToken = ""
}
sentryDsn, ok := os.LookupEnv("SENTRY_DSN")
if !ok {
sentryDsn = ""
}
port, ok := os.LookupEnv("PORT")
if !ok {
port = "5000"
}
hostname, ok := os.LookupEnv("HOSTNAME")
if !ok {
hostname = "127.0.0.1"
}
environment, ok := os.LookupEnv("ENVIRONMENT")
if !ok {
environment = "development"
}
// Setup In Memory
memory, err := bigcache.NewBigCache(bigcache.DefaultConfig(10 * time.Minute))
if err != nil {
log.Panicln(err)
}
defer memory.Close()
// Setup MinIO
minioClient, err := minio.New(minioHost, &minio.Options{
Creds: credentials.NewStaticV4(minioID, minioSecret, minioToken),
Region: minioRegion,
Secure: minioSecure == "true",
})
if err != nil {
log.Fatalf("setting up minio client: %s", err.Error())
return
}
parsedRedisURL, err := redis.ParseURL(redisURL)
if err != nil {
log.Fatalf("parsing redis url: %s", err.Error())
return
}
redisClient := redis.NewClient(parsedRedisURL)
defer func() {
err := redisClient.Close()
if err != nil {
log.Printf("closing redis client: %s", err.Error())
}
}()
// Setup Sentry
err = sentry.Init(sentry.ClientOptions{
Dsn: sentryDsn,
Environment: environment,
AttachStacktrace: true,
// Enable printing of SDK debug messages.
// Useful when getting started or trying to figure something out.
Debug: environment != "production",
})
if err != nil {
log.Fatalf("setting up sentry: %s", err.Error())
return
}
defer sentry.Flush(2 * time.Second)
setupCtx, setupCancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute*4))
defer setupCancel()
_, _, err = joke.GetTodaysJoke(setupCtx, minioClient, redisClient, memory)
if err != nil {
log.Fatalf("getting initial joke data: %s", err.Error())
return
}
healthRouter := routes.Health(minioClient, redisClient)
jokeRouter := routes.Joke(minioClient, redisClient, memory)
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: net.JoinHostPort(hostname, port),
ReadTimeout: time.Minute,
WriteTimeout: time.Minute,
IdleTimeout: time.Second * 30,
ReadHeaderTimeout: time.Minute,
}
exitSignal := make(chan os.Signal, 1)
signal.Notify(exitSignal, os.Interrupt)
go func() {
err := server.ListenAndServe()
if err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("listening http server: %v", err)
}
}()
<-exitSignal
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), time.Second*30)
defer shutdownCancel()
err = server.Shutdown(shutdownCtx)
if err != nil {
log.Printf("shutting down http server: %v", err)
}
}

View File

@ -1,8 +1,9 @@
package utils_test
import (
"jokes-bapak2-api/utils"
"testing"
"jokes-bapak2-api/utils"
)
func TestIsIn_True(t *testing.T) {

View File

@ -1,9 +1,10 @@
package utils_test
import (
"jokes-bapak2-api/utils"
"testing"
"time"
"jokes-bapak2-api/utils"
)
func TestIsToday_Today(t *testing.T) {

View File

@ -1,9 +1,10 @@
package utils_test
import (
"jokes-bapak2-api/utils"
"strings"
"testing"
"jokes-bapak2-api/utils"
)
func TestParseToJSONBody(t *testing.T) {

View File

@ -1,8 +1,9 @@
package utils_test
import (
"jokes-bapak2-api/utils"
"testing"
"jokes-bapak2-api/utils"
)
func TestRandomString_Valid(t *testing.T) {

View File

@ -1,9 +1,10 @@
package utils_test
import (
"jokes-bapak2-api/utils"
"net/http"
"testing"
"jokes-bapak2-api/utils"
)
func TestRequest_Get(t *testing.T) {

View File

@ -14,6 +14,6 @@ $ yarn start
## Used package
| Name | Version | Type |
| --- | --- | --- |
| Name | Version | Type |
|---------------------------------------------------------------|----------|-------------------|
| [mcollina/autocannon](https://github.com/mcollina/autocannon) | `v7.4.0` | Benchmarking Tool |