fix: redis version on ci
This commit is contained in:
parent
e949b11ab9
commit
9aedbc6648
|
@ -3,17 +3,17 @@ root = true
|
||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
indent_size = 4
|
indent_size = 2
|
||||||
indent_style = space
|
indent_style = space
|
||||||
insert_final_newline = false
|
insert_final_newline = false
|
||||||
max_line_length = 120
|
max_line_length = 120
|
||||||
tab_width = 4
|
tab_width = 2
|
||||||
ij_continuation_indent_size = 8
|
ij_continuation_indent_size = 4
|
||||||
ij_formatter_off_tag = @formatter:off
|
ij_formatter_off_tag = @formatter:off
|
||||||
ij_formatter_on_tag = @formatter:on
|
ij_formatter_on_tag = @formatter:on
|
||||||
ij_formatter_tags_enabled = true
|
ij_formatter_tags_enabled = true
|
||||||
ij_smart_tabs = false
|
ij_smart_tabs = false
|
||||||
ij_visual_guides =
|
ij_visual_guides =
|
||||||
ij_wrap_on_typing = false
|
ij_wrap_on_typing = false
|
||||||
|
|
||||||
[*.css]
|
[*.css]
|
||||||
|
@ -30,7 +30,7 @@ ij_css_hex_color_upper_case = false
|
||||||
ij_css_keep_blank_lines_in_code = 2
|
ij_css_keep_blank_lines_in_code = 2
|
||||||
ij_css_keep_indents_on_empty_lines = false
|
ij_css_keep_indents_on_empty_lines = false
|
||||||
ij_css_keep_single_line_blocks = 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_after_colon = true
|
||||||
ij_css_space_before_opening_brace = true
|
ij_css_space_before_opening_brace = true
|
||||||
ij_css_use_double_quotes = 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_keep_single_line_blocks = false
|
||||||
ij_sass_line_comment_add_space = false
|
ij_sass_line_comment_add_space = false
|
||||||
ij_sass_line_comment_at_first_column = 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_after_colon = true
|
||||||
ij_sass_space_before_opening_brace = true
|
ij_sass_space_before_opening_brace = true
|
||||||
ij_sass_use_double_quotes = true
|
ij_sass_use_double_quotes = true
|
||||||
|
@ -114,7 +114,7 @@ ij_typescript_array_initializer_wrap = off
|
||||||
ij_typescript_assignment_wrap = off
|
ij_typescript_assignment_wrap = off
|
||||||
ij_typescript_binary_operation_sign_on_next_line = false
|
ij_typescript_binary_operation_sign_on_next_line = false
|
||||||
ij_typescript_binary_operation_wrap = off
|
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_after_imports = 1
|
||||||
ij_typescript_blank_lines_around_class = 1
|
ij_typescript_blank_lines_around_class = 1
|
||||||
ij_typescript_blank_lines_around_field = 0
|
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_function_returns = false
|
||||||
ij_typescript_prefer_explicit_types_vars_fields = false
|
ij_typescript_prefer_explicit_types_vars_fields = false
|
||||||
ij_typescript_prefer_parameters_wrap = false
|
ij_typescript_prefer_parameters_wrap = false
|
||||||
ij_typescript_property_prefix =
|
ij_typescript_property_prefix =
|
||||||
ij_typescript_reformat_c_style_comments = false
|
ij_typescript_reformat_c_style_comments = false
|
||||||
ij_typescript_space_after_colon = true
|
ij_typescript_space_after_colon = true
|
||||||
ij_typescript_space_after_comma = true
|
ij_typescript_space_after_comma = true
|
||||||
|
@ -297,7 +297,7 @@ ij_javascript_array_initializer_wrap = off
|
||||||
ij_javascript_assignment_wrap = off
|
ij_javascript_assignment_wrap = off
|
||||||
ij_javascript_binary_operation_sign_on_next_line = false
|
ij_javascript_binary_operation_sign_on_next_line = false
|
||||||
ij_javascript_binary_operation_wrap = off
|
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_after_imports = 1
|
||||||
ij_javascript_blank_lines_around_class = 1
|
ij_javascript_blank_lines_around_class = 1
|
||||||
ij_javascript_blank_lines_around_field = 0
|
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_function_returns = false
|
||||||
ij_javascript_prefer_explicit_types_vars_fields = false
|
ij_javascript_prefer_explicit_types_vars_fields = false
|
||||||
ij_javascript_prefer_parameters_wrap = false
|
ij_javascript_prefer_parameters_wrap = false
|
||||||
ij_javascript_property_prefix =
|
ij_javascript_property_prefix =
|
||||||
ij_javascript_reformat_c_style_comments = false
|
ij_javascript_reformat_c_style_comments = false
|
||||||
ij_javascript_space_after_colon = true
|
ij_javascript_space_after_colon = true
|
||||||
ij_javascript_space_after_comma = true
|
ij_javascript_space_after_comma = true
|
||||||
|
@ -460,7 +460,7 @@ ij_go_group_stdlib_imports = true
|
||||||
ij_go_import_sorting = gofmt
|
ij_go_import_sorting = gofmt
|
||||||
ij_go_keep_indents_on_empty_lines = false
|
ij_go_keep_indents_on_empty_lines = false
|
||||||
ij_go_local_group_mode = project
|
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_imports_in_one_declaration = true
|
||||||
ij_go_move_all_stdlib_imports_in_one_group = true
|
ij_go_move_all_stdlib_imports_in_one_group = true
|
||||||
ij_go_remove_redundant_import_aliases = false
|
ij_go_remove_redundant_import_aliases = false
|
||||||
|
|
|
@ -2,7 +2,7 @@ name: CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["master"]
|
branches: [ "master" ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
api-build:
|
api-build:
|
||||||
|
@ -29,7 +29,7 @@ jobs:
|
||||||
volumes:
|
volumes:
|
||||||
- minio-data:/data
|
- minio-data:/data
|
||||||
redis:
|
redis:
|
||||||
image: redis:7.0.2-bookworm
|
image: redis:7.0.12-bookworm
|
||||||
ports:
|
ports:
|
||||||
- 6379:6379
|
- 6379:6379
|
||||||
defaults:
|
defaults:
|
||||||
|
|
|
@ -2,7 +2,7 @@ name: PR
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: ["*"]
|
branches: [ "*" ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
client-build:
|
client-build:
|
||||||
|
@ -18,7 +18,7 @@ jobs:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Installling dependencies
|
- name: Install dependencies
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
|
@ -66,7 +66,7 @@ jobs:
|
||||||
volumes:
|
volumes:
|
||||||
- minio-data:/data
|
- minio-data:/data
|
||||||
redis:
|
redis:
|
||||||
image: redis:7.0.2-bookworm
|
image: redis:7.0.12-bookworm
|
||||||
ports:
|
ports:
|
||||||
- 6379:6379
|
- 6379:6379
|
||||||
defaults:
|
defaults:
|
||||||
|
|
160
CONTRIBUTING.md
160
CONTRIBUTING.md
|
@ -1,75 +1,87 @@
|
||||||
# Contributing Guide
|
# 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.
|
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.
|
|
||||||
|
This project is a monorepo, meaning the backend, frontend, and Github CI are all in one place (one repository). Before
|
||||||
## Project Prerequisites && Setup
|
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
|
||||||
### Front End (`./client`)
|
on developing this.
|
||||||
|
|
||||||
You'll have to install:
|
## Project Prerequisites && Setup
|
||||||
* Node.js LTS (preferably with [fnm](https://github.com/Schniz/fnm) or [nvm](https://github.com/nvm-sh/nvm))
|
|
||||||
* Yarn v1
|
### Front End (`./client`)
|
||||||
|
|
||||||
See the [README](./client/README.md) on client for detailed project setup.
|
You'll have to install:
|
||||||
|
|
||||||
### Back End (`./api`)
|
* Node.js LTS (preferably with [fnm](https://github.com/Schniz/fnm) or [nvm](https://github.com/nvm-sh/nvm))
|
||||||
|
* Yarn v1
|
||||||
You'll have to install:
|
|
||||||
* Go v1.16.x
|
See the [README](./client/README.md) on client for detailed project setup.
|
||||||
* (Optional) [Fiber CLI](https://github.com/gofiber/cli) for ease of development
|
|
||||||
|
### Back End (`./api`)
|
||||||
See the [README](./api/README.md) on client for detailed project setup.
|
|
||||||
|
You'll have to install:
|
||||||
### With Docker Compose
|
|
||||||
|
* Go v1.16.x
|
||||||
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.
|
* (Optional) [Fiber CLI](https://github.com/gofiber/cli) for ease of development
|
||||||
|
|
||||||
You'll have to install:
|
See the [README](./api/README.md) on client for detailed project setup.
|
||||||
* Docker (preferably with Docker Desktop if you're on Windows or Mac)
|
|
||||||
* Docker Compose
|
### With Docker Compose
|
||||||
|
|
||||||
```bash
|
If you're just developing the front end and too lazy installing Go and such (or the other way around), you can
|
||||||
# Create a docker container but don't start it yet.
|
use `docker-compose` file specified on the main page.
|
||||||
$ docker-compose up --no-start
|
|
||||||
|
You'll have to install:
|
||||||
# Or if you want to create the docker container and start it right away
|
|
||||||
$ docker-compose up
|
* Docker (preferably with Docker Desktop if you're on Windows or Mac)
|
||||||
|
* Docker Compose
|
||||||
# If you want to have it running in the background
|
|
||||||
$ docker-compose up --detach
|
```bash
|
||||||
|
# Create a docker container but don't start it yet.
|
||||||
# Start existing container
|
$ docker-compose up --no-start
|
||||||
$ docker-compose start
|
|
||||||
|
# Or if you want to create the docker container and start it right away
|
||||||
# Stop running container
|
$ docker-compose up
|
||||||
$ docker-compose stop
|
|
||||||
|
# If you want to have it running in the background
|
||||||
# Destroy current container
|
$ docker-compose up --detach
|
||||||
$ docker-compose down
|
|
||||||
```
|
# Start existing container
|
||||||
|
$ docker-compose start
|
||||||
## Before submitting PR
|
|
||||||
|
# Stop running container
|
||||||
### Front End (`./client`)
|
$ docker-compose stop
|
||||||
|
|
||||||
Please run these:
|
# Destroy current container
|
||||||
* `yarn lint`
|
$ docker-compose down
|
||||||
* `yarn format`
|
```
|
||||||
* `yarn build`
|
|
||||||
|
## Before submitting PR
|
||||||
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.
|
|
||||||
|
### Front End (`./client`)
|
||||||
### Back End (`./api`)
|
|
||||||
|
Please run these:
|
||||||
Please run these:
|
|
||||||
* `go fmt`
|
* `yarn lint`
|
||||||
* `go build main.go`
|
* `yarn format`
|
||||||
* `go test -v -race -coverprofile=coverage.out -covermode=atomic ./...`
|
* `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.
|
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..
|
|
||||||
|
### 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?? 😆
|
Oh my God, thank you so much!!! Working on an open source project is interesting right?? 😆
|
34
README.md
34
README.md
|
@ -7,15 +7,19 @@
|
||||||
<br>
|
<br>
|
||||||
</h1>
|
</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/).
|
ou can access the front facing web on [jokesbapak2.reinaldyrafli.com](http://jokesbapak2.reinaldyrafli.com/).
|
||||||
|
|
||||||
## Brief explanation of what is this
|
## 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
|
## 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:
|
You can consume this API via a website (linked in the front facing web) with a few endpoints:
|
||||||
|
|
||||||
* `/` - Random jokes bapak2
|
* `/` - Random jokes bapak2
|
||||||
* `/id/{number}` - Jokes bapak2 based on ID
|
* `/id/{number}` - Jokes bapak2 based on ID
|
||||||
* `/today` - Jokes bapak2 of the day
|
* `/today` - Jokes bapak2 of the day
|
||||||
* `/total` - Total available jokes bapak2
|
* `/total` - Total available jokes bapak2
|
||||||
|
|
||||||
Currently I'm (still) searching for an alternative for AWS S3 that I can use for free.
|
Currently I'm (still) searching for an alternative for AWS S3 that I can use for free.
|
||||||
|
|
||||||
## Tech stacks
|
## Tech stacks
|
||||||
|
|
||||||
* Go (for `api` / back end)
|
* Go (for `api` / back end)
|
||||||
* Node.js (for `client` / front end)
|
* Node.js (for `client` / front end)
|
||||||
* Postgres
|
* Postgres
|
||||||
* Redis
|
* Redis
|
||||||
|
|
||||||
That's it.
|
That's it.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
Two ways of doing this:
|
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
|
## Thanks to
|
||||||
|
|
||||||
|
|
155
api/README.md
155
api/README.md
|
@ -1,78 +1,79 @@
|
||||||
# Jokes Bapak2 API
|
# Jokes Bapak2 API
|
||||||
|
|
||||||
Still work in progress
|
Still work in progress
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install modules
|
# Install modules
|
||||||
$ go mod download
|
$ go mod download
|
||||||
# or
|
# or
|
||||||
$ go mod vendor
|
$ go mod vendor
|
||||||
|
|
||||||
# run the local server
|
# run the local server
|
||||||
$ go run main.go
|
$ go run main.go
|
||||||
|
|
||||||
# build everything
|
# build everything
|
||||||
$ go build main.go
|
$ 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!
|
There is a placeholder data ready for you to query it manually in `/platform/database/placeholder.sql`. Have a good time
|
||||||
|
developing!
|
||||||
## Used packages
|
|
||||||
|
## Used packages
|
||||||
| Name | Version | Type |
|
|
||||||
| --- | --- | --- |
|
| 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 |
|
| [gofiber/fiber](https://github.com/gofiber/fiber) | `v2.21.0` | Framework |
|
||||||
| [go-redis/redis](https://github.com/go-redis/redis) | `v8.11.4` | Cache |
|
| [jackc/pgx](https://github.com/jackc/pgx) | `v4.13.0` | Database |
|
||||||
| [allegro/bigcache](https://github.com/allegro/bigcache) | `v3.0.1` | Cache |
|
| [go-redis/redis](https://github.com/go-redis/redis) | `v8.11.4` | Cache |
|
||||||
| [joho/godotenv](https://github.com/joho/godotenv) | `v1.4.0` | Config |
|
| [allegro/bigcache](https://github.com/allegro/bigcache) | `v3.0.1` | Cache |
|
||||||
| [getsentry/sentry-go](https://github.com/getsentry/sentry-go) | `v0.11.0` | Logging |
|
| [joho/godotenv](https://github.com/joho/godotenv) | `v1.4.0` | Config |
|
||||||
| [aldy505/phc-crypto](https://github.com/aldy505/phc-crypto) | `v1.1.0` | Utils |
|
| [getsentry/sentry-go](https://github.com/getsentry/sentry-go) | `v0.11.0` | Logging |
|
||||||
| [Masterminds/squirrel](https://github.com/Masterminds/squirrel ) | `v1.5.1` | Utils |
|
| [aldy505/phc-crypto](https://github.com/aldy505/phc-crypto) | `v1.1.0` | Utils |
|
||||||
| [aldy505/bob](https://github.com/aldy505/bob) | `v0.0.4` | Utils |
|
| [Masterminds/squirrel](https://github.com/Masterminds/squirrel ) | `v1.5.1` | Utils |
|
||||||
| [gojek/heimdall](https://github.com/gojek/heimdall) | `v7.0.2` | Utils |
|
| [aldy505/bob](https://github.com/aldy505/bob) | `v0.0.4` | Utils |
|
||||||
| [georgysavva/scany](https://github.com/georgysavva/scany) | `v0.2.9` | Utils |
|
| [gojek/heimdall](https://github.com/gojek/heimdall) | `v7.0.2` | Utils |
|
||||||
| [pquerna/ffjson](https://github.com/pquerna/ffjson) | `v0.0.0-20190930134022-aa0246cd15f7` | 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
|
|
||||||
|
## Directory structure
|
||||||
```
|
|
||||||
.
|
```
|
||||||
├── core - Pure business logic
|
.
|
||||||
│ ├── administrator
|
├── core - Pure business logic
|
||||||
│ ├── joke
|
│ ├── administrator
|
||||||
│ ├── schema
|
│ ├── joke
|
||||||
│ ├── submit
|
│ ├── schema
|
||||||
│ └── validator
|
│ ├── submit
|
||||||
├── Dockerfile - Docker image for API
|
│ └── validator
|
||||||
├── documentation.json - Swagger documentation
|
├── Dockerfile - Docker image for API
|
||||||
├── documentation.yaml - Swagger documentation
|
├── documentation.json - Swagger documentation
|
||||||
├── favicon.png
|
├── documentation.yaml - Swagger documentation
|
||||||
├── go.mod - Module declaration
|
├── favicon.png
|
||||||
├── go.sum - Checksum for modules
|
├── go.mod - Module declaration
|
||||||
├── handler - Route handlers
|
├── go.sum - Checksum for modules
|
||||||
│ ├── health
|
├── handler - Route handlers
|
||||||
│ ├── joke
|
│ ├── health
|
||||||
│ └── submit
|
│ ├── joke
|
||||||
├── main.go - Application entry point
|
│ └── submit
|
||||||
├── middleware - Route middlewares
|
├── main.go - Application entry point
|
||||||
├── platform - Third party packages
|
├── middleware - Route middlewares
|
||||||
│ └── database
|
├── platform - Third party packages
|
||||||
├── README.md - You are here
|
│ └── database
|
||||||
├── routes - Route definitions
|
├── README.md - You are here
|
||||||
└── utils - Utility functions
|
├── routes - Route definitions
|
||||||
```
|
└── utils - Utility functions
|
||||||
|
```
|
||||||
## `.env` configuration
|
|
||||||
|
## `.env` configuration
|
||||||
```ini
|
|
||||||
ENV=development
|
```ini
|
||||||
PORT=5000
|
ENV=development
|
||||||
|
PORT=5000
|
||||||
DATABASE_URL=postgres://postgres:password@localhost:5432/jokesbapak2
|
|
||||||
REDIS_URL=redis://@localhost:6379
|
DATABASE_URL=postgres://postgres:password@localhost:5432/jokesbapak2
|
||||||
|
REDIS_URL=redis://@localhost:6379
|
||||||
SENTRY_DSN=
|
|
||||||
|
SENTRY_DSN=
|
||||||
```
|
```
|
|
@ -2,9 +2,10 @@ package joke_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"jokes-bapak2-api/core/joke"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"jokes-bapak2-api/core/joke"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetRandomJoke(t *testing.T) {
|
func TestGetRandomJoke(t *testing.T) {
|
||||||
|
|
|
@ -2,9 +2,10 @@ package joke_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"jokes-bapak2-api/core/joke"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"jokes-bapak2-api/core/joke"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestListJokeFromBucket(t *testing.T) {
|
func TestListJokeFromBucket(t *testing.T) {
|
||||||
|
|
|
@ -2,9 +2,10 @@ package joke_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"jokes-bapak2-api/core/joke"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"jokes-bapak2-api/core/joke"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetTodaysJoke(t *testing.T) {
|
func TestGetTodaysJoke(t *testing.T) {
|
||||||
|
|
|
@ -2,9 +2,10 @@ package joke_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"jokes-bapak2-api/core/joke"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"jokes-bapak2-api/core/joke"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetTotalJoke(t *testing.T) {
|
func TestGetTotalJoke(t *testing.T) {
|
||||||
|
|
|
@ -1,321 +1,321 @@
|
||||||
openapi: 3.0.0
|
openapi: 3.0.0
|
||||||
info:
|
info:
|
||||||
title: Jokesbapak2 Image API
|
title: Jokesbapak2 Image API
|
||||||
description: >
|
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,
|
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.
|
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?
|
But I thought, why not make it as an API?
|
||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
contact:
|
contact:
|
||||||
name: Reinaldy Rafli
|
name: Reinaldy Rafli
|
||||||
url: https://github.com/aldy505
|
url: https://github.com/aldy505
|
||||||
email: aldy505@tutanota.com
|
email: aldy505@tutanota.com
|
||||||
license:
|
license:
|
||||||
name: GNU General Public License v3.0
|
name: GNU General Public License v3.0
|
||||||
url: https://github.com/aldy505/jokes-bapak2/blob/master/LICENSE
|
url: https://github.com/aldy505/jokes-bapak2/blob/master/LICENSE
|
||||||
servers:
|
servers:
|
||||||
- url: "https://jokesbapak2.reinaldyrafli.com/api/v1"
|
- url: "https://jokesbapak2.reinaldyrafli.com/api/v1"
|
||||||
description: Production
|
description: Production
|
||||||
- url: "https://jokesbapak2.reinaldyrafli.com/api"
|
- url: "https://jokesbapak2.reinaldyrafli.com/api"
|
||||||
description: Production
|
description: Production
|
||||||
- url: "http://localhost:5000"
|
- url: "http://localhost:5000"
|
||||||
description: Development
|
description: Development
|
||||||
paths:
|
paths:
|
||||||
/:
|
/:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Jokes
|
- Jokes
|
||||||
summary: Get random Jokes Bapak2 image
|
summary: Get random Jokes Bapak2 image
|
||||||
description: Returns a different image (PNG, JPG, or GIF) for every call.
|
description: Returns a different image (PNG, JPG, or GIF) for every call.
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: Image data
|
description: Image data
|
||||||
content:
|
content:
|
||||||
"image/gif": {}
|
"image/gif": { }
|
||||||
"image/png": {}
|
"image/png": { }
|
||||||
"image/jpeg": {}
|
"image/jpeg": { }
|
||||||
put:
|
put:
|
||||||
summary: Add a new joke into database
|
summary: Add a new joke into database
|
||||||
description: asd
|
description: asd
|
||||||
tags:
|
tags:
|
||||||
- Jokes
|
- Jokes
|
||||||
requestBody:
|
requestBody:
|
||||||
description: asds
|
description: asds
|
||||||
required: true
|
required: true
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/request.auth"
|
- $ref: "#/components/schemas/request.auth"
|
||||||
- $ref: "#/components/schemas/request.joke"
|
- $ref: "#/components/schemas/request.joke"
|
||||||
responses:
|
responses:
|
||||||
201:
|
201:
|
||||||
description: Image has been added
|
description: Image has been added
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/request.joke"
|
$ref: "#/components/schemas/request.joke"
|
||||||
example:
|
example:
|
||||||
link: https://link.to/image.jpg
|
link: https://link.to/image.jpg
|
||||||
400:
|
400:
|
||||||
description: Bad request
|
description: Bad request
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/response.error"
|
$ref: "#/components/schemas/response.error"
|
||||||
example:
|
example:
|
||||||
error: URL provided is not a valid image
|
error: URL provided is not a valid image
|
||||||
403:
|
403:
|
||||||
description: Must be authenticated to submit a joke
|
description: Must be authenticated to submit a joke
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/response.error"
|
$ref: "#/components/schemas/response.error"
|
||||||
/id/{id}:
|
/id/{id}:
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
name: id
|
name: id
|
||||||
schema:
|
schema:
|
||||||
type: number
|
type: number
|
||||||
required: true
|
required: true
|
||||||
description: A number that represents image's ID
|
description: A number that represents image's ID
|
||||||
get:
|
get:
|
||||||
summary: Get random Jokes Bapak2 image by ID
|
summary: Get random Jokes Bapak2 image by ID
|
||||||
description: Returns consistent image for every call.
|
description: Returns consistent image for every call.
|
||||||
tags:
|
tags:
|
||||||
- Jokes
|
- Jokes
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: Image data
|
description: Image data
|
||||||
content:
|
content:
|
||||||
"image/jpeg": {}
|
"image/jpeg": { }
|
||||||
"image/png": {}
|
"image/png": { }
|
||||||
"image/gif": {}
|
"image/gif": { }
|
||||||
404:
|
404:
|
||||||
description: Provided image ID was not found
|
description: Provided image ID was not found
|
||||||
content:
|
content:
|
||||||
text/plain:
|
text/plain:
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
example: Requested ID was not found.
|
example: Requested ID was not found.
|
||||||
patch:
|
patch:
|
||||||
summary: Update a Joke with certain image ID
|
summary: Update a Joke with certain image ID
|
||||||
description: Returns consistent image for every call.
|
description: Returns consistent image for every call.
|
||||||
tags:
|
tags:
|
||||||
- Jokes
|
- Jokes
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: Sucessfully updated an image item
|
description: Sucessfully updated an image item
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/response.confirmation"
|
- $ref: "#/components/schemas/response.confirmation"
|
||||||
- $ref: "#/components/schemas/request.joke"
|
- $ref: "#/components/schemas/request.joke"
|
||||||
400:
|
400:
|
||||||
description: Link provided is not a valid image
|
description: Link provided is not a valid image
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/response.error"
|
$ref: "#/components/schemas/response.error"
|
||||||
403:
|
403:
|
||||||
description: Must be authenticated to submit a joke
|
description: Must be authenticated to submit a joke
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/response.error"
|
$ref: "#/components/schemas/response.error"
|
||||||
406:
|
406:
|
||||||
description: If the Joke ID does not exists
|
description: If the Joke ID does not exists
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/response.error"
|
$ref: "#/components/schemas/response.error"
|
||||||
delete:
|
delete:
|
||||||
summary: Delete a Joke with certain image ID
|
summary: Delete a Joke with certain image ID
|
||||||
description: hi
|
description: hi
|
||||||
tags:
|
tags:
|
||||||
- Jokes
|
- Jokes
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: Sucessfully deleted an image item
|
description: Sucessfully deleted an image item
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/response.confirmation"
|
$ref: "#/components/schemas/response.confirmation"
|
||||||
403:
|
403:
|
||||||
description: Must be authenticated to submit a joke
|
description: Must be authenticated to submit a joke
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/response.error"
|
$ref: "#/components/schemas/response.error"
|
||||||
406:
|
406:
|
||||||
description: If the Joke ID does not exists
|
description: If the Joke ID does not exists
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/response.error"
|
$ref: "#/components/schemas/response.error"
|
||||||
/today:
|
/today:
|
||||||
get:
|
get:
|
||||||
summary: Get the joke of the day
|
summary: Get the joke of the day
|
||||||
description: A joke a day makes more of a dad out of you.
|
description: A joke a day makes more of a dad out of you.
|
||||||
tags:
|
tags:
|
||||||
- Jokes
|
- Jokes
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: Image data
|
description: Image data
|
||||||
content:
|
content:
|
||||||
"image/jpeg": {}
|
"image/jpeg": { }
|
||||||
"image/png": {}
|
"image/png": { }
|
||||||
"image/gif": {}
|
"image/gif": { }
|
||||||
/total:
|
/total:
|
||||||
get:
|
get:
|
||||||
summary: Get total amount of jokes in database
|
summary: Get total amount of jokes in database
|
||||||
tags:
|
tags:
|
||||||
- Jokes
|
- Jokes
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: Total jokes
|
description: Total jokes
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/response.confirmation"
|
$ref: "#/components/schemas/response.confirmation"
|
||||||
example:
|
example:
|
||||||
message: "154"
|
message: "154"
|
||||||
/submit:
|
/submit:
|
||||||
get:
|
get:
|
||||||
summary: Get submitted Jokes
|
summary: Get submitted Jokes
|
||||||
tags:
|
tags:
|
||||||
- Submission
|
- Submission
|
||||||
parameters:
|
parameters:
|
||||||
- name: author
|
- name: author
|
||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
description: Author to be queried
|
description: Author to be queried
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
- name: approved
|
- name: approved
|
||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
description: Whether query just approved jokes or not
|
description: Whether query just approved jokes or not
|
||||||
schema:
|
schema:
|
||||||
type: boolean
|
type: boolean
|
||||||
- name: limit
|
- name: limit
|
||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
schema:
|
schema:
|
||||||
type: number
|
type: number
|
||||||
- name: page
|
- name: page
|
||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
schema:
|
schema:
|
||||||
type: number
|
type: number
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: asd
|
description: asd
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
count:
|
count:
|
||||||
type: number
|
type: number
|
||||||
jokes:
|
jokes:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/response.submission"
|
$ref: "#/components/schemas/response.submission"
|
||||||
post:
|
post:
|
||||||
summary: Submit a joke
|
summary: Submit a joke
|
||||||
description: >
|
description: >
|
||||||
Must be in multipart/form-data format.
|
Must be in multipart/form-data format.
|
||||||
Author must be in the format of "Name <email>".
|
Author must be in the format of "Name <email>".
|
||||||
tags:
|
tags:
|
||||||
- Submission
|
- Submission
|
||||||
requestBody:
|
requestBody:
|
||||||
content:
|
content:
|
||||||
multipart/form-data:
|
multipart/form-data:
|
||||||
schema:
|
schema:
|
||||||
properties:
|
properties:
|
||||||
link:
|
link:
|
||||||
description: Image link
|
description: Image link
|
||||||
type: string
|
type: string
|
||||||
image:
|
image:
|
||||||
description: Image data
|
description: Image data
|
||||||
type: string
|
type: string
|
||||||
author:
|
author:
|
||||||
description: Person who submitted this
|
description: Person who submitted this
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- author
|
- author
|
||||||
- image
|
- image
|
||||||
- link
|
- link
|
||||||
responses:
|
responses:
|
||||||
201:
|
201:
|
||||||
description: Joke successfully submitted
|
description: Joke successfully submitted
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/response.confirmation"
|
- $ref: "#/components/schemas/response.confirmation"
|
||||||
- type: object
|
- type: object
|
||||||
properties:
|
properties:
|
||||||
data:
|
data:
|
||||||
$ref: "#/components/schemas/response.submission"
|
$ref: "#/components/schemas/response.submission"
|
||||||
400:
|
400:
|
||||||
description: Invalid data was sent
|
description: Invalid data was sent
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/response.error"
|
$ref: "#/components/schemas/response.error"
|
||||||
/health:
|
/health:
|
||||||
get:
|
get:
|
||||||
summary: Health check
|
summary: Health check
|
||||||
description: Ping the databases to make sure everything's alright
|
description: Ping the databases to make sure everything's alright
|
||||||
tags:
|
tags:
|
||||||
- Health
|
- Health
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: Everything is okay
|
description: Everything is okay
|
||||||
403:
|
403:
|
||||||
description: Something is not okay
|
description: Something is not okay
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/response.error"
|
$ref: "#/components/schemas/response.error"
|
||||||
|
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
request.auth:
|
request.auth:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
key:
|
key:
|
||||||
type: string
|
type: string
|
||||||
token:
|
token:
|
||||||
type: string
|
type: string
|
||||||
request.joke:
|
request.joke:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
link:
|
link:
|
||||||
type: string
|
type: string
|
||||||
response.confirmation:
|
response.confirmation:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
message:
|
message:
|
||||||
type: string
|
type: string
|
||||||
response.error:
|
response.error:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
error:
|
error:
|
||||||
type: string
|
type: string
|
||||||
response.submission:
|
response.submission:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
type: number
|
type: number
|
||||||
link:
|
link:
|
||||||
type: string
|
type: string
|
||||||
created_at:
|
created_at:
|
||||||
type: string
|
type: string
|
||||||
author:
|
author:
|
||||||
type: string
|
type: string
|
||||||
status:
|
status:
|
||||||
type: number
|
type: number
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
package joke
|
package joke
|
||||||
|
|
||||||
import (
|
import (
|
||||||
core "jokes-bapak2-api/core/joke"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
core "jokes-bapak2-api/core/joke"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package joke
|
package joke
|
||||||
|
|
||||||
import (
|
import (
|
||||||
core "jokes-bapak2-api/core/joke"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
core "jokes-bapak2-api/core/joke"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TotalJokes provides a HTTP handler for acquiring total jokes
|
// TotalJokes provides a HTTP handler for acquiring total jokes
|
||||||
|
|
363
api/main.go
363
api/main.go
|
@ -1,182 +1,181 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"context"
|
||||||
"log"
|
"errors"
|
||||||
"net"
|
"log"
|
||||||
"net/http"
|
"net"
|
||||||
"os"
|
"net/http"
|
||||||
"os/signal"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"context"
|
"time"
|
||||||
"jokes-bapak2-api/core/joke"
|
|
||||||
"jokes-bapak2-api/routes"
|
"jokes-bapak2-api/core/joke"
|
||||||
|
"jokes-bapak2-api/routes"
|
||||||
"github.com/go-redis/redis/v8"
|
|
||||||
"github.com/minio/minio-go/v7"
|
"github.com/go-redis/redis/v8"
|
||||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
"github.com/minio/minio-go/v7"
|
||||||
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||||
"time"
|
|
||||||
|
"github.com/allegro/bigcache/v3"
|
||||||
"github.com/allegro/bigcache/v3"
|
"github.com/getsentry/sentry-go"
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/rs/cors"
|
||||||
"github.com/rs/cors"
|
)
|
||||||
)
|
|
||||||
|
func main() {
|
||||||
func main() {
|
redisURL, ok := os.LookupEnv("REDIS_URL")
|
||||||
redisURL, ok := os.LookupEnv("REDIS_URL")
|
if !ok {
|
||||||
if !ok {
|
redisURL = "redis://@localhost:6379"
|
||||||
redisURL = "redis://@localhost:6379"
|
}
|
||||||
}
|
|
||||||
|
minioHost, ok := os.LookupEnv("MINIO_HOST")
|
||||||
minioHost, ok := os.LookupEnv("MINIO_HOST")
|
if !ok {
|
||||||
if !ok {
|
minioHost = "localhost:9000"
|
||||||
minioHost = "localhost:9000"
|
}
|
||||||
}
|
|
||||||
|
minioRegion, ok := os.LookupEnv("MINIO_REGION")
|
||||||
minioRegion, ok := os.LookupEnv("MINIO_REGION")
|
if !ok {
|
||||||
if !ok {
|
minioRegion = ""
|
||||||
minioRegion = ""
|
}
|
||||||
}
|
|
||||||
|
minioSecure, ok := os.LookupEnv("MINIO_SECURE")
|
||||||
minioSecure, ok := os.LookupEnv("MINIO_SECURE")
|
if !ok {
|
||||||
if !ok {
|
minioSecure = "false"
|
||||||
minioSecure = "false"
|
}
|
||||||
}
|
|
||||||
|
minioID, ok := os.LookupEnv("MINIO_ACCESS_ID")
|
||||||
minioID, ok := os.LookupEnv("MINIO_ACCESS_ID")
|
if !ok {
|
||||||
if !ok {
|
minioID = "minio"
|
||||||
minioID = "minio"
|
}
|
||||||
}
|
|
||||||
|
minioSecret, ok := os.LookupEnv("MINIO_SECRET_KEY")
|
||||||
minioSecret, ok := os.LookupEnv("MINIO_SECRET_KEY")
|
if !ok {
|
||||||
if !ok {
|
minioSecret = "password"
|
||||||
minioSecret = "password"
|
}
|
||||||
}
|
|
||||||
|
minioToken, ok := os.LookupEnv("MINIO_TOKEN")
|
||||||
minioToken, ok := os.LookupEnv("MINIO_TOKEN")
|
if !ok {
|
||||||
if !ok {
|
minioToken = ""
|
||||||
minioToken = ""
|
}
|
||||||
}
|
|
||||||
|
sentryDsn, ok := os.LookupEnv("SENTRY_DSN")
|
||||||
sentryDsn, ok := os.LookupEnv("SENTRY_DSN")
|
if !ok {
|
||||||
if !ok {
|
sentryDsn = ""
|
||||||
sentryDsn = ""
|
}
|
||||||
}
|
|
||||||
|
port, ok := os.LookupEnv("PORT")
|
||||||
port, ok := os.LookupEnv("PORT")
|
if !ok {
|
||||||
if !ok {
|
port = "5000"
|
||||||
port = "5000"
|
}
|
||||||
}
|
|
||||||
|
hostname, ok := os.LookupEnv("HOSTNAME")
|
||||||
hostname, ok := os.LookupEnv("HOSTNAME")
|
if !ok {
|
||||||
if !ok {
|
hostname = "127.0.0.1"
|
||||||
hostname = "127.0.0.1"
|
}
|
||||||
}
|
|
||||||
|
environment, ok := os.LookupEnv("ENVIRONMENT")
|
||||||
environment, ok := os.LookupEnv("ENVIRONMENT")
|
if !ok {
|
||||||
if !ok {
|
environment = "development"
|
||||||
environment = "development"
|
}
|
||||||
}
|
|
||||||
|
// Setup In Memory
|
||||||
// Setup In Memory
|
memory, err := bigcache.NewBigCache(bigcache.DefaultConfig(10 * time.Minute))
|
||||||
memory, err := bigcache.NewBigCache(bigcache.DefaultConfig(10 * time.Minute))
|
if err != nil {
|
||||||
if err != nil {
|
log.Panicln(err)
|
||||||
log.Panicln(err)
|
}
|
||||||
}
|
defer memory.Close()
|
||||||
defer memory.Close()
|
|
||||||
|
// Setup MinIO
|
||||||
// Setup MinIO
|
minioClient, err := minio.New(minioHost, &minio.Options{
|
||||||
minioClient, err := minio.New(minioHost, &minio.Options{
|
Creds: credentials.NewStaticV4(minioID, minioSecret, minioToken),
|
||||||
Creds: credentials.NewStaticV4(minioID, minioSecret, minioToken),
|
Region: minioRegion,
|
||||||
Region: minioRegion,
|
Secure: minioSecure == "true",
|
||||||
Secure: minioSecure == "true",
|
})
|
||||||
})
|
if err != nil {
|
||||||
if err != nil {
|
log.Fatalf("setting up minio client: %s", err.Error())
|
||||||
log.Fatalf("setting up minio client: %s", err.Error())
|
return
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
parsedRedisURL, err := redis.ParseURL(redisURL)
|
||||||
parsedRedisURL, err := redis.ParseURL(redisURL)
|
if err != nil {
|
||||||
if err != nil {
|
log.Fatalf("parsing redis url: %s", err.Error())
|
||||||
log.Fatalf("parsing redis url: %s", err.Error())
|
return
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
redisClient := redis.NewClient(parsedRedisURL)
|
||||||
redisClient := redis.NewClient(parsedRedisURL)
|
defer func() {
|
||||||
defer func() {
|
err := redisClient.Close()
|
||||||
err := redisClient.Close()
|
if err != nil {
|
||||||
if err != nil {
|
log.Printf("closing redis client: %s", err.Error())
|
||||||
log.Printf("closing redis client: %s", err.Error())
|
}
|
||||||
}
|
}()
|
||||||
}()
|
|
||||||
|
// Setup Sentry
|
||||||
// Setup Sentry
|
err = sentry.Init(sentry.ClientOptions{
|
||||||
err = sentry.Init(sentry.ClientOptions{
|
Dsn: sentryDsn,
|
||||||
Dsn: sentryDsn,
|
Environment: environment,
|
||||||
Environment: environment,
|
AttachStacktrace: true,
|
||||||
AttachStacktrace: true,
|
// Enable printing of SDK debug messages.
|
||||||
// Enable printing of SDK debug messages.
|
// Useful when getting started or trying to figure something out.
|
||||||
// Useful when getting started or trying to figure something out.
|
Debug: environment != "production",
|
||||||
Debug: environment != "production",
|
})
|
||||||
})
|
if err != nil {
|
||||||
if err != nil {
|
log.Fatalf("setting up sentry: %s", err.Error())
|
||||||
log.Fatalf("setting up sentry: %s", err.Error())
|
return
|
||||||
return
|
}
|
||||||
}
|
defer sentry.Flush(2 * time.Second)
|
||||||
defer sentry.Flush(2 * time.Second)
|
|
||||||
|
setupCtx, setupCancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute*4))
|
||||||
setupCtx, setupCancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute*4))
|
defer setupCancel()
|
||||||
defer setupCancel()
|
|
||||||
|
_, _, err = joke.GetTodaysJoke(setupCtx, minioClient, redisClient, memory)
|
||||||
_, _, err = joke.GetTodaysJoke(setupCtx, minioClient, redisClient, memory)
|
if err != nil {
|
||||||
if err != nil {
|
log.Fatalf("getting initial joke data: %s", err.Error())
|
||||||
log.Fatalf("getting initial joke data: %s", err.Error())
|
return
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
healthRouter := routes.Health(minioClient, redisClient)
|
||||||
healthRouter := routes.Health(minioClient, redisClient)
|
jokeRouter := routes.Joke(minioClient, redisClient, memory)
|
||||||
jokeRouter := routes.Joke(minioClient, redisClient, memory)
|
|
||||||
|
router := chi.NewRouter()
|
||||||
router := chi.NewRouter()
|
|
||||||
|
router.Use(cors.New(cors.Options{
|
||||||
router.Use(cors.New(cors.Options{
|
AllowedMethods: []string{http.MethodGet},
|
||||||
AllowedMethods: []string{http.MethodGet},
|
AllowCredentials: false,
|
||||||
AllowCredentials: false,
|
MaxAge: int(60 * 60 * 24 * 365),
|
||||||
MaxAge: int(60 * 60 * 24 * 365),
|
Debug: false,
|
||||||
Debug: false,
|
}).Handler)
|
||||||
}).Handler)
|
|
||||||
|
router.Mount("/health", healthRouter)
|
||||||
router.Mount("/health", healthRouter)
|
router.Mount("/", jokeRouter)
|
||||||
router.Mount("/", jokeRouter)
|
|
||||||
|
server := &http.Server{
|
||||||
server := &http.Server{
|
Handler: router,
|
||||||
Handler: router,
|
Addr: net.JoinHostPort(hostname, port),
|
||||||
Addr: net.JoinHostPort(hostname, port),
|
ReadTimeout: time.Minute,
|
||||||
ReadTimeout: time.Minute,
|
WriteTimeout: time.Minute,
|
||||||
WriteTimeout: time.Minute,
|
IdleTimeout: time.Second * 30,
|
||||||
IdleTimeout: time.Second * 30,
|
ReadHeaderTimeout: time.Minute,
|
||||||
ReadHeaderTimeout: time.Minute,
|
}
|
||||||
}
|
|
||||||
|
exitSignal := make(chan os.Signal, 1)
|
||||||
exitSignal := make(chan os.Signal, 1)
|
signal.Notify(exitSignal, os.Interrupt)
|
||||||
signal.Notify(exitSignal, os.Interrupt)
|
|
||||||
|
go func() {
|
||||||
go func() {
|
err := server.ListenAndServe()
|
||||||
err := server.ListenAndServe()
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
log.Fatalf("listening http server: %v", err)
|
||||||
log.Fatalf("listening http server: %v", err)
|
}
|
||||||
}
|
}()
|
||||||
}()
|
|
||||||
|
<-exitSignal
|
||||||
<-exitSignal
|
|
||||||
|
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||||
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), time.Second*30)
|
defer shutdownCancel()
|
||||||
defer shutdownCancel()
|
|
||||||
|
err = server.Shutdown(shutdownCtx)
|
||||||
err = server.Shutdown(shutdownCtx)
|
if err != nil {
|
||||||
if err != nil {
|
log.Printf("shutting down http server: %v", err)
|
||||||
log.Printf("shutting down http server: %v", err)
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package utils_test
|
package utils_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"jokes-bapak2-api/utils"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"jokes-bapak2-api/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIsIn_True(t *testing.T) {
|
func TestIsIn_True(t *testing.T) {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package utils_test
|
package utils_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"jokes-bapak2-api/utils"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"jokes-bapak2-api/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIsToday_Today(t *testing.T) {
|
func TestIsToday_Today(t *testing.T) {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package utils_test
|
package utils_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"jokes-bapak2-api/utils"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"jokes-bapak2-api/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseToJSONBody(t *testing.T) {
|
func TestParseToJSONBody(t *testing.T) {
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package utils_test
|
package utils_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"jokes-bapak2-api/utils"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"jokes-bapak2-api/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRandomString_Valid(t *testing.T) {
|
func TestRandomString_Valid(t *testing.T) {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package utils_test
|
package utils_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"jokes-bapak2-api/utils"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"jokes-bapak2-api/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRequest_Get(t *testing.T) {
|
func TestRequest_Get(t *testing.T) {
|
||||||
|
|
|
@ -14,6 +14,6 @@ $ yarn start
|
||||||
|
|
||||||
## Used package
|
## Used package
|
||||||
|
|
||||||
| Name | Version | Type |
|
| Name | Version | Type |
|
||||||
| --- | --- | --- |
|
|---------------------------------------------------------------|----------|-------------------|
|
||||||
| [mcollina/autocannon](https://github.com/mcollina/autocannon) | `v7.4.0` | Benchmarking Tool |
|
| [mcollina/autocannon](https://github.com/mcollina/autocannon) | `v7.4.0` | Benchmarking Tool |
|
Loading…
Reference in New Issue