Merge pull request #11 from aldy505/client/docs

Updating docs on client web
This commit is contained in:
Reinaldy Rafli 2022-10-01 18:45:39 +07:00 committed by GitHub
commit 58605e6af3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 8098 additions and 2706 deletions

View File

@ -93,17 +93,17 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Installling dependencies - name: Installling dependencies
run: yarn install run: npm install
- name: Lint - name: Lint
run: yarn lint run: npm run lint
- name: Build - name: Build
run: yarn build run: npm run build
env: env:
VITE_SENTRY_DSN: https://examplePublicKey@o0.ingest.sentry.io/0 VITE_SENTRY_DSN: https://examplePublicKey@o0.ingest.sentry.io/0
VITE_NODE_ENV: development VITE_NODE_ENV: development
VITE_API_ENDPOINT: https://jokesbapak2.herokuapp.com/v1 VITE_API_ENDPOINT: https://jokesbapak2.reinaldyrafli.com/api/v1
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v1 uses: github/codeql-action/init@v1

View File

@ -19,7 +19,7 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Installling dependencies - name: Installling dependencies
run: yarn install run: npm install
- name: Lint - name: Lint
run: npx eslint --ext .svelte,.js,.ts --ignore-path .gitignore . run: npx eslint --ext .svelte,.js,.ts --ignore-path .gitignore .
@ -28,11 +28,11 @@ jobs:
run: npx prettier --check --ignore-path .gitignore --plugin-search-dir=. "./**/*.(ts|json|js|svelte)" run: npx prettier --check --ignore-path .gitignore --plugin-search-dir=. "./**/*.(ts|json|js|svelte)"
- name: Build - name: Build
run: yarn build run: npm run build
env: env:
VITE_SENTRY_DSN: https://examplePublicKey@o0.ingest.sentry.io/0 VITE_SENTRY_DSN: https://examplePublicKey@o0.ingest.sentry.io/0
VITE_NODE_ENV: development VITE_NODE_ENV: development
VITE_API_ENDPOINT: https://jokesbapak2.herokuapp.com/v1 VITE_API_ENDPOINT: https://jokesbapak2.reinaldyrafli.com/api/v1
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v1 uses: github/codeql-action/init@v1

12
.gitignore vendored
View File

@ -1,5 +1,5 @@
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,goland,webstorm,datagrip # Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,goland,webstorm
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,goland,webstorm,datagrip # Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,goland,webstorm
### GoLand ### ### GoLand ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
@ -214,6 +214,10 @@ fabric.properties
# Azure Toolkit for IntelliJ plugin # Azure Toolkit for IntelliJ plugin
# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,goland,webstorm,datagrip # End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,goland,webstorm
data docker-compose.override.yml
data/
.env.*
.env
!.env.example

View File

@ -7,9 +7,9 @@
<br> <br>
</h1> </h1>
👋 Hey there! Still a work in progress, if you'd like to contribute this while this repo is still growing, that would be so great! 👋 Hey there! Always a work in progress, if you'd like to contribute this while this repo is still growing, that would be so great!
It's on alpha phase now. You can access the front facing web on [jokesbapak2.pages.dev](http://jokesbapak2.pages.dev/). ou can access the front facing web on [jokesbapak2.reinaldyrafli.com](http://jokesbapak2.reinaldyrafli.com/).
## Brief explanation of what is this ## Brief explanation of what is this

View File

@ -1,7 +1,11 @@
ENV=development ENV=development
PORT=5000 PORT=5000
HOSTNAME=127.0.0.1
DATABASE_URL=postgres://postgres:password@localhost:5432/jokesbapak2
REDIS_URL=redis://@localhost:6379 REDIS_URL=redis://@localhost:6379
MINIO_HOST=
MINIO_ACCESS_ID=
MINIO_SECRET_KEY=
MINIO_TOKEN=
SENTRY_DSN= SENTRY_DSN=

View File

@ -10,6 +10,10 @@ FROM debian:bullseye AS runtime
WORKDIR /app WORKDIR /app
RUN apt-get update && apt-get upgrade --no-install-recommends -y \
&& apt-get install -y --no-install-recommends curl \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/main . COPY --from=builder /app/main .
ENV PORT=5000 ENV PORT=5000

View File

@ -16,11 +16,11 @@
}, },
"servers": [ "servers": [
{ {
"url": "https://jokesbapak2.herokuapp.com/v1", "url": "https://jokesbapak2.reinaldyrafli.com/api/v1",
"description": "Production" "description": "Production"
}, },
{ {
"url": "https://jokesbapak2.herokuapp.com", "url": "https://jokesbapak2.reinaldyrafli.com/api",
"description": "Production" "description": "Production"
}, },
{ {
@ -72,375 +72,281 @@
} }
}, },
"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": {
"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": { "400": {
"summary": "Update a Joke with certain image ID", "description": "Bad request",
"description": "Returns consistent image for every call.", "content": {
"tags": [ "application/json": {
"Jokes" "schema": {
], "$ref": "#/components/schemas/response.error"
"responses": { },
"200": { "example": {
"description": "Sucessfully updated an image item", "error": "URL provided is not a valid image"
"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": { "403": {
"summary": "Delete a Joke with certain image ID", "description": "Must be authenticated to submit a joke",
"description": "hi", "content": {
"tags": [ "application/json": {
"Jokes" "schema": {
], "$ref": "#/components/schemas/response.error"
"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", "/id/{id}": {
"description": "A joke a day makes more of a dad out of you.", "parameters": [
"tags": [ {
"Jokes" "in": "path",
], "name": "id",
"responses": { "schema": {
"200": { "type": "number"
"description": "Image data", },
"content": { "required": true,
"image/jpeg": {}, "description": "A number that represents image's ID"
"image/png": {}, }
"image/gif": {} ],
} "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": {
"/total": { "description": "Provided image ID was not found",
"get": { "content": {
"summary": "Get total amount of jokes in database", "text/plain": {
"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": { "schema": {
"type": "string" "type": "string"
} },
}, "example": "Requested ID was not found."
{
"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": { "patch": {
"application/json": { "summary": "Update a Joke with certain image ID",
"schema": { "description": "Returns consistent image for every call.",
"type": "object", "tags": [
"properties": { "Jokes"
"count": { ],
"type": "number" "responses": {
}, "200": {
"jokes": { "description": "Sucessfully updated an image item",
"type": "array", "content": {
"items": { "application/json": {
"$ref": "#/components/schemas/response.submission" "schema": {
} "allOf": [
} {
} "$ref": "#/components/schemas/response.confirmation"
},
{
"$ref": "#/components/schemas/request.joke"
} }
} ]
} }
} }
} }
}, },
"post": { "400": {
"summary": "Submit a joke", "description": "Link provided is not a valid image",
"description": "Must be in multipart/form-data format. Author must be in the format of \"Name &lt;email&gt;\".\n", "content": {
"tags": [ "application/json": {
"Submission" "schema": {
], "$ref": "#/components/schemas/response.error"
"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": { "403": {
"description": "Joke successfully submitted", "description": "Must be authenticated to submit a joke",
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"allOf": [ "$ref": "#/components/schemas/response.error"
{
"$ref": "#/components/schemas/response.confirmation"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/response.submission"
}
}
}
]
}
}
} }
}, }
"400": { }
"description": "Invalid data was sent", },
"content": { "406": {
"application/json": { "description": "If the Joke ID does not exists",
"schema": { "content": {
"$ref": "#/components/schemas/response.error" "application/json": {
} "schema": {
} "$ref": "#/components/schemas/response.error"
} }
} }
} }
} }
}, }
"/health": { },
"get": { "delete": {
"summary": "Health check", "summary": "Delete a Joke with certain image ID",
"description": "Ping the databases to make sure everything's alright", "description": "hi",
"tags": [ "tags": [
"Health" "Jokes"
], ],
"responses": { "responses": {
"200": { "200": {
"description": "Everything is okay" "description": "Sucessfully deleted an image item",
}, "content": {
"403": { "application/json": {
"description": "Something is not okay", "schema": {
"content": { "$ref": "#/components/schemas/response.confirmation"
"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"
}
}
}
}
}
}
},
"/today": {
"get": {
"summary": "Get the joke of the day",
"description": "A joke a day makes more of a dad out of you.",
"tags": [
"Jokes"
],
"responses": {
"200": {
"description": "Image data",
"content": {
"image/jpeg": {},
"image/png": {},
"image/gif": {}
}
}
}
}
},
"/total": {
"get": {
"summary": "Get total amount of jokes in database",
"tags": [
"Jokes"
],
"responses": {
"200": {
"description": "Total jokes",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/response.confirmation"
},
"example": {
"message": "154"
}
}
}
}
}
}
},
"/submit": {
"get": {
"summary": "Get submitted Jokes",
"tags": [
"Submission"
],
"parameters": [
{
"name": "author",
"in": "query",
"required": false,
"description": "Author to be queried",
"schema": {
"type": "string"
}
},
{
"name": "approved",
"in": "query",
"required": false,
"description": "Whether query just approved jokes or not",
"schema": {
"type": "boolean"
}
},
{
"name": "limit",
"in": "query",
"required": false,
"schema": {
"type": "number"
}
},
{
"name": "page",
"in": "query",
"required": false,
"schema": {
"type": "number"
}
}
],
"responses": {
"200": {
"description": "asd",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"count": {
"type": "number"
},
"jokes": {
"type": "array",
"items": {
"$ref": "#/components/schemas/response.submission"
}
} }
} }
} }
@ -448,6 +354,100 @@
} }
} }
} }
},
"post": {
"summary": "Submit a joke",
"description": "Must be in multipart/form-data format. Author must be in the format of \"Name &lt;email&gt;\".\n",
"tags": [
"Submission"
],
"requestBody": {
"content": {
"multipart/form-data": {
"schema": {
"properties": {
"link": {
"description": "Image link",
"type": "string"
},
"image": {
"description": "Image data",
"type": "string"
},
"author": {
"description": "Person who submitted this",
"type": "string"
}
},
"required": [
"author",
"image",
"link"
]
}
}
}
},
"responses": {
"201": {
"description": "Joke successfully submitted",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/response.confirmation"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/response.submission"
}
}
}
]
}
}
}
},
"400": {
"description": "Invalid data was sent",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/response.error"
}
}
}
}
}
}
},
"/health": {
"get": {
"summary": "Health check",
"description": "Ping the databases to make sure everything's alright",
"tags": [
"Health"
],
"responses": {
"200": {
"description": "Everything is okay"
},
"403": {
"description": "Something is not okay",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/response.error"
}
}
}
}
}
}
}
}, },
"components": { "components": {
"schemas": { "schemas": {

View File

@ -14,9 +14,9 @@ info:
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.herokuapp.com/v1" - url: "https://jokesbapak2.reinaldyrafli.com/api/v1"
description: Production description: Production
- url: "https://jokesbapak2.herokuapp.com" - url: "https://jokesbapak2.reinaldyrafli.com/api"
description: Production description: Production
- url: "http://localhost:5000" - url: "http://localhost:5000"
description: Development description: Development
@ -31,9 +31,9 @@ paths:
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
@ -46,15 +46,15 @@ paths:
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:
@ -62,7 +62,7 @@ paths:
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:
@ -70,7 +70,7 @@ paths:
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
@ -88,17 +88,16 @@ paths:
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: example: Requested ID was not found.
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.
@ -111,26 +110,26 @@ paths:
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
@ -142,19 +141,19 @@ paths:
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
@ -165,9 +164,9 @@ paths:
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
@ -179,7 +178,7 @@ paths:
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:
@ -223,7 +222,7 @@ paths:
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: >
@ -256,17 +255,17 @@ paths:
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
@ -281,7 +280,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/response.error' $ref: "#/components/schemas/response.error"
components: components:
schemas: schemas:

View File

@ -16,6 +16,7 @@ require (
require ( require (
github.com/go-chi/chi/v5 v5.0.7 github.com/go-chi/chi/v5 v5.0.7
github.com/minio/minio-go/v7 v7.0.35 github.com/minio/minio-go/v7 v7.0.35
github.com/rs/cors v1.8.2
) )
require ( require (

View File

@ -238,6 +238,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 h1:xoIK0ctDddBMnc74udxJYBqlo9Ylnsp1waqjLsnef20= github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 h1:xoIK0ctDddBMnc74udxJYBqlo9Ylnsp1waqjLsnef20=
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U=
github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=

View File

@ -3,6 +3,7 @@ package main
import ( import (
"errors" "errors"
"log" "log"
"net"
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
@ -20,6 +21,7 @@ import (
"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"
) )
func main() { func main() {
@ -69,7 +71,7 @@ func main() {
} }
// Setup In Memory // Setup In Memory
memory, err := bigcache.NewBigCache(bigcache.DefaultConfig(6 * time.Hour)) memory, err := bigcache.NewBigCache(bigcache.DefaultConfig(10 * time.Minute))
if err != nil { if err != nil {
log.Panicln(err) log.Panicln(err)
} }
@ -127,12 +129,19 @@ func main() {
router := chi.NewRouter() 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("/health", healthRouter)
router.Mount("/", jokeRouter) router.Mount("/", jokeRouter)
server := &http.Server{ server := &http.Server{
Handler: router, Handler: router,
Addr: 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,

View File

@ -14,9 +14,9 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "yarn run b:random && yarn run b:today && yarn run b:byid", "start": "yarn run b:random && yarn run b:today && yarn run b:byid",
"b:random": "autocannon --title \"GET /v1/ - Random Jokes\" --method GET --amount 100 --bailout 3 --headers user-agent=benchmark https://jokesbapak2.herokuapp.com/v1/", "b:random": "autocannon --title \"GET /v1/ - Random Jokes\" --method GET --amount 100 --bailout 3 --headers user-agent=benchmark https://jokesbapak2.reinaldyrafli.com/api/v1/",
"b:today": "autocannon --title \"GET /v1/today - Today's Jokes\" --method GET --amount 100 --bailout 3 --headers user-agent=benchmark https://jokesbapak2.herokuapp.com/v1/today", "b:today": "autocannon --title \"GET /v1/today - Today's Jokes\" --method GET --amount 100 --bailout 3 --headers user-agent=benchmark https://jokesbapak2.reinaldyrafli.com/api/v1/today",
"b:byid": "autocannon --title \"GET /v1/id/5 - Joke by ID\" --method GET --amount 100 --bailout 3 --headers user-agent=benchmark https://jokesbapak2.herokuapp.com/v1/id/5" "b:byid": "autocannon --title \"GET /v1/id/5 - Joke by ID\" --method GET --amount 100 --bailout 3 --headers user-agent=benchmark https://jokesbapak2.reinaldyrafli.com/api/v1/id/5"
}, },
"dependencies": { "dependencies": {
"autocannon": "^7.4.0" "autocannon": "^7.4.0"

View File

@ -1 +1 @@
v14.17.3 v16.13

View File

@ -1,14 +1,15 @@
FROM node:14.17.3-buster FROM node:16.17.0-bullseye
RUN apt-get update && apt-get upgrade --no-install-recommends -y \
&& apt-get install -y --no-install-recommends curl \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
WORKDIR /app WORKDIR /app
COPY . . COPY . .
RUN npm install -g yarn RUN npm ci && npm run build && rm -rf node_modules && npm ci --omit=dev
RUN yarn install
RUN yarn build
RUN yarn cache clean
EXPOSE ${PORT} EXPOSE ${PORT}
CMD ["yarn", "preview"] CMD ["node", "dist/index.js"]

View File

@ -1,35 +1,35 @@
# Jokes Bapak2 Client # Jokes Bapak2 Client
Still work in progress The frontend.
## Development ## Development
```bash ```bash
# Install modules # Install modules
$ yarn install $ npm install
# Run local server # Run local server
$ yarn dev $ npm run dev
# build everything # build everything
$ yarn build $ npm run build
``` ```
> You can preview the built app with `yarn preview`, regardless of whether you installed an adapter. This should _not_ be used to serve your app in production. > You can preview the built app with `npm run preview`, regardless of whether you installed an adapter. This should _not_ be used to serve your app in production.
## Used packages ## Used packages
| Name | Version | Type | | Name | Version | Type |
| --- | --- | --- | | -------------------------- | ---------------- | -------------------- |
| @sveltejs/kit | `1.0.0-next.129` | Framework | | @sveltejs/kit | `1.0.0-next.480` | Framework |
| svelte | `3.38.3` | Framework | | svelte | `3.50.1` | Framework |
| typescript | `4.3.5` | Static type language | | typescript | `4.8.3` | Static type language |
| svelte-i18n | `3.3.9` | i18n Library | | svelte-i18n | `3.4.0` | i18n Library |
| svelte-windicss-preprocess | `4.0.12` | CSS Library | | svelte-windicss-preprocess | `4.2.8` | CSS Library |
| @fontsource/fira-mono | `4.5.0` | Webfont | | @fontsource/fira-mono | `4.5.9` | Webfont |
| @fontsource/rubik | `4.5.0` | Webfont | | @fontsource/rubik | `4.5.11` | Webfont |
| dotenv | `10.0.0` | Utils | | dotenv | `16.0.2` | Utils |
| @sentry/browser | `6.9.0` | Logging | | @sentry/browser | `7.12.1` | Logging |
## Directory structure ## Directory structure
@ -48,8 +48,8 @@ $ yarn build
├── static - Static/public directory ├── static - Static/public directory
├── svelte.config.js - Svelte configuration file ├── svelte.config.js - Svelte configuration file
├── tsconfig.json - Typescript configuration file ├── tsconfig.json - Typescript configuration file
├── windi.config.js - WindiCSS configuration file ├── windi.config.ts - WindiCSS configuration file
└── yarn.lock - Packages lock file └── package-lock.json - Packages lock file
``` ```
## `.env` configuration ## `.env` configuration

7418
client/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -11,38 +11,39 @@
], ],
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "svelte-kit dev", "dev": "vite dev",
"build": "svelte-kit build", "build": "vite build",
"preview": "svelte-kit preview", "preview": "vite preview",
"check": "svelte-check --tsconfig ./tsconfig.json", "check": "svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch", "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "eslint --fix --ext .svelte,.js,.ts --ignore-path .gitignore .", "lint": "eslint --fix --ext .svelte,.js,.ts --ignore-path .gitignore .",
"format": "prettier --write --ignore-path .gitignore --plugin-search-dir=. \"./**/*.(ts|json|js|svelte)\"" "format": "prettier --write --ignore-path .gitignore --plugin-search-dir=. \"./**/*.(ts|json|js|svelte)\""
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-static": "1.0.0-next.13", "@sveltejs/adapter-node": "^1.0.0-next.89",
"@sveltejs/kit": "1.0.0-next.129", "@sveltejs/kit": "1.0.0-next.480",
"@typescript-eslint/eslint-plugin": "4.28.3", "@typescript-eslint/eslint-plugin": "5.36.2",
"@typescript-eslint/parser": "4.28.3", "@typescript-eslint/parser": "5.36.2",
"cssnano": "5.0.6", "cssnano": "5.1.13",
"eslint": "7.30.0", "eslint": "8.23.0",
"eslint-config-prettier": "8.3.0", "eslint-config-prettier": "8.5.0",
"eslint-plugin-svelte3": "3.2.0", "eslint-plugin-svelte3": "4.0.0",
"prettier": "2.3.2", "prettier": "2.7.1",
"prettier-plugin-svelte": "2.3.1", "prettier-plugin-svelte": "2.7.0",
"svelte": "3.38.3", "svelte": "3.50.1",
"svelte-check": "2.2.2", "svelte-check": "2.9.0",
"svelte-preprocess": "4.7.4", "svelte-preprocess": "4.10.7",
"svelte-windicss-preprocess": "4.0.12", "svelte-windicss-preprocess": "4.2.8",
"tslib": "2.3.0", "tslib": "2.4.0",
"typescript": "4.3.5" "typescript": "4.8.3",
"vite": "^3.1.0"
}, },
"dependencies": { "dependencies": {
"@fontsource/fira-mono": "4.5.0", "@fontsource/fira-mono": "4.5.9",
"@fontsource/rubik": "4.5.0", "@fontsource/rubik": "4.5.11",
"@sentry/browser": "6.9.0", "@sentry/browser": "7.12.1",
"dotenv": "10.0.0", "dotenv": "16.0.2",
"ohmyfetch": "^0.2.0", "ohmyfetch": "0.4.18",
"svelte-i18n": "3.3.9" "svelte-i18n": "3.4.0"
} }
} }

View File

@ -1,30 +1,29 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="%lang%">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="/favicon.png" /> <link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="keywords" content="jokesbapak2, jokes, bapak2, indonesian, dad, jokes, api, rest" /> <meta name="keywords" content="jokesbapak2, jokes, bapak2, indonesian, dad, jokes, api, rest" />
<meta name="author" content="Reinaldy Rafli" /> <meta name="author" content="Reinaldy Rafli" />
<meta name="theme-color" content="#f4a9f5" /> <meta name="theme-color" content="#f4a9f5" />
<meta name="publisher" content="https://jokesbapak2.pages.dev/" /> <meta name="publisher" content="https://jokesbapak2.reinaldyrafli.com/" />
<meta property="og:image" content="https://jokesbapak2.pages.dev/social.jpg" /> <meta property="og:image" content="%sveltekit.assets%/social.jpg" />
<meta property="og:image:alt" content="Jokesbapak2 - Largest collection of Indonesian dad jokes" /> <meta property="og:image:alt" content="Jokesbapak2 - Largest collection of Indonesian dad jokes" />
<meta property="og:image:secure_url" content="https://jokesbapak2.pages.dev/social.jpg" /> <meta property="og:image:secure_url" content="%sveltekit.assets%/social.jpg" />
<meta property="og:image:width" content="1280" /> <meta property="og:image:width" content="1280" />
<meta property="og:image:height" content="640" /> <meta property="og:image:height" content="640" />
<meta property="og:locale" content="en_US" /> <meta property="og:locale" content="en_US" />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image:src" content="https://jokesbapak2.pages.dev/social.jpg" /> <meta name="twitter:image:src" content="%sveltekit.assets%/social.jpg" />
<meta name="twitter:image" content="https://jokesbapak2.pages.dev/social.jpg" /> <meta name="twitter:image" content="%sveltekit.assets%/social.jpg" />
<meta name="twitter:creator" content="Reinaldy Rafli" /> <meta name="twitter:creator" content="Reinaldy Rafli" />
<link rel="icon" type="image/png" href="/favicon.png"> <link rel="icon" type="image/png" href="/favicon.png">
<link rel="icon" type="image/svg" href="/favicon.svg"> <link rel="icon" type="image/svg" href="/favicon.svg">
<script async defer data-website-id="82725fd7-2542-4828-b313-349f68d84894" src="https://jokesbapak2-analytics.herokuapp.com/umami.js" data-do-not-track="true" data-domains="jokesbapak2.pages.dev"></script> %sveltekit.head%
%svelte.head%
</head> </head>
<body> <body>
<div id="svelte">%svelte.body%</div> <div id="svelte">%sveltekit.body%</div>
</body> </body>
</html> </html>

View File

@ -0,0 +1,7 @@
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = ({ event, resolve }) => {
return resolve(event, {
transformPageChunk: ({ html }) => html.replace('%lang%', 'en'),
});
};

View File

@ -71,7 +71,7 @@
} }
}, },
"api": { "api": {
"limit": "We limit the request to be 30 request/minute.", "limit": "We limit the request to be 120 request/minute.",
"get": { "get": {
"title": "Get Jokes", "title": "Get Jokes",
"random": { "random": {
@ -105,7 +105,7 @@
"fetch": { "fetch": {
"1": "Using fetch API", "1": "Using fetch API",
"2": "You can use it, but I personally don't recommend it.", "2": "You can use it, but I personally don't recommend it.",
"3": "data is a base64 string" "3": "Do some error handling if the request fails"
} }
} }
} }

View File

@ -71,7 +71,7 @@
} }
}, },
"api": { "api": {
"limit": "Kami membatasi request menjadi 30 request/menit.", "limit": "Kami membatasi request menjadi 120 request/menit.",
"get": { "get": {
"title": "Dapatkan Jokes", "title": "Dapatkan Jokes",
"random": { "random": {
@ -105,7 +105,7 @@
"fetch": { "fetch": {
"1": "Menggunakan fetch API", "1": "Menggunakan fetch API",
"2": "Bapak bisa menggunakannya, tapi saya secara personal tidak merekomendasikannya.", "2": "Bapak bisa menggunakannya, tapi saya secara personal tidak merekomendasikannya.",
"3": "data adalah string dengan isi base64" "3": "Periksa dan jangan abaikan errornya jika request gagal"
} }
} }
} }

View File

@ -42,20 +42,14 @@
<p class="text-sm opacity-50 hover:opacity-90 transition duration-300 ease-in-out inline-block"> <p class="text-sm opacity-50 hover:opacity-90 transition duration-300 ease-in-out inline-block">
{$_('footer.language')} {$_('footer.language')}
<a href="?lang=en" target="_top" class="hover:underline">{$_('footer.english')}</a> <a href="?lang=en" target="_top" class="hover:underline">{$_('footer.english')}</a>
|
<a href="?lang=id" target="_top" class="hover:underline">{$_('footer.indonesian')}</a>. <a href="?lang=id" target="_top" class="hover:underline">{$_('footer.indonesian')}</a>.
</p> </p>
</div> </div>
<div class="flex-initial pr-3">
<p class="text-sm opacity-50 hover:opacity-90 transition duration-300 ease-in-out inline-block">
<a href="https://jokesbapak2-analytics.herokuapp.com/share/1xYuVSyl/Jokes%20Bapak2" class="hover:underline"
>Analytics</a
>.
</p>
</div>
</div> </div>
</footer> </footer>
</div> </div>
</div> </div>
<style windi:preflights:global windi:safelist:global> <style windi:global windi:preflights:global windi:safelist:global>
</style> </style>

View File

@ -0,0 +1,19 @@
import { $fetch } from 'ohmyfetch';
import env from '../lib/env';
interface TotalResponse {
message: number;
}
/** @type {import('./$types').PageServerLoad} */
export async function load() {
const response = await $fetch<TotalResponse>('total', {
method: 'GET',
baseURL: env.API_ENDPOINT,
parseResponse: JSON.parse,
});
return {
total: response.message,
};
}

View File

@ -1,25 +1,13 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte';
import { _ } from 'svelte-i18n'; import { _ } from 'svelte-i18n';
import { $fetch as omf } from 'ohmyfetch';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import env from '$lib/env'; import env from '$lib/env';
import Codeblock from '../components/codeblock.svelte'; import Codeblock from '../components/codeblock.svelte';
interface TotalResponse { /** @type {import('./$types').PageData} */
message: string; export let data;
}
let total; let total = data.total;
onMount(async () => {
const totalJokes = async (): Promise<string> => {
const response = await omf<TotalResponse>(`${env.API_ENDPOINT}/total`);
return response.message;
};
total = await totalJokes();
});
</script> </script>
<svelte:head> <svelte:head>
@ -27,7 +15,7 @@
<meta name="title" content={$_('meta.title') + '-' + $_('meta.tagline')} /> <meta name="title" content={$_('meta.title') + '-' + $_('meta.tagline')} />
<meta name="twitter:title" content={$_('meta.title') + '-' + $_('meta.tagline')} /> <meta name="twitter:title" content={$_('meta.title') + '-' + $_('meta.tagline')} />
<meta property="og:title" content={$_('meta.title') + '-' + $_('meta.tagline')} /> <meta property="og:title" content={$_('meta.title') + '-' + $_('meta.tagline')} />
<link rel="canonical" href="https://jokesbapak2.pages.dev/" /> <link rel="canonical" href="https://jokesbapak2.reinaldyrafli.com/" />
<meta name="description" content="Largest collection of Indonesian dad jokes as a consumable API" /> <meta name="description" content="Largest collection of Indonesian dad jokes as a consumable API" />
<meta name="twitter:description" content="Largest collection of Indonesian dad jokes as a consumable API" /> <meta name="twitter:description" content="Largest collection of Indonesian dad jokes as a consumable API" />
<meta property="og:description" content="Largest collection of Indonesian dad jokes as a consumable API" /> <meta property="og:description" content="Largest collection of Indonesian dad jokes as a consumable API" />
@ -43,17 +31,13 @@
<div class="max-w-xs mx-auto"> <div class="max-w-xs mx-auto">
<img src={env.API_ENDPOINT + `/today`} alt="Sample joke" class="py-6 shadow-2xl" /> <img src={env.API_ENDPOINT + `/today`} alt="Sample joke" class="py-6 shadow-2xl" />
</div> </div>
<Codeblock>$ curl -XGET 'https://jokesbapak2.herokuapp.com/v1/'</Codeblock> <Codeblock>$ curl -XGET 'https://jokesbapak2.reinaldyrafli.com/api/'</Codeblock>
<p class="text-sm text-center py-4 opacity-70 hover:opacity-100 transition duration-300 ease-in-out"> <p class="text-sm text-center py-4 opacity-70 hover:opacity-100 transition duration-300 ease-in-out">
{$_('home.more.1')} <span on:click={() => goto('/guide')}>{$_('navigation.guide')}</span> {$_('home.more.1')}
{$_('home.more.2')} <span on:click={() => goto('/api')}>{$_('navigation.api')}</span> <span class="hover:underline cursor-pointer" on:click={() => goto('/guide')}>{$_('navigation.guide')}</span>
{$_('home.more.2')}
<span class="hover:underline cursor-pointer" on:click={() => goto('/api')}>{$_('navigation.api')}</span>
</p> </p>
</div> </div>
</div> </div>
</section> </section>
<style>
span {
@apply 'hover:underline' cursor-pointer;
}
</style>

View File

@ -0,0 +1 @@
export const ssr = true;

View File

@ -4,8 +4,8 @@
import { _ } from 'svelte-i18n'; import { _ } from 'svelte-i18n';
import env from '$lib/env'; import env from '$lib/env';
import { $fetch as omf } from 'ohmyfetch'; import { $fetch as omf } from 'ohmyfetch';
import Codeblock from '../components/codeblock.svelte'; import Codeblock from '../../components/codeblock.svelte';
import Notice from '../components/notice.svelte'; import Notice from '../../components/notice.svelte';
interface TotalResponse { interface TotalResponse {
message: string; message: string;
@ -28,7 +28,7 @@
<meta name="title" content={$_('navigation.api') + '-' + $_('meta.title')} /> <meta name="title" content={$_('navigation.api') + '-' + $_('meta.title')} />
<meta name="twitter:title" content={$_('navigation.api') + '-' + $_('meta.title')} /> <meta name="twitter:title" content={$_('navigation.api') + '-' + $_('meta.title')} />
<meta property="og:title" content={$_('navigation.api') + '-' + $_('meta.title')} /> <meta property="og:title" content={$_('navigation.api') + '-' + $_('meta.title')} />
<link rel="canonical" href="https://jokesbapak2.pages.dev/api" /> <link rel="canonical" href="https://jokesbapak2.reinaldyrafli.com/api" />
<meta name="description" content="Largest collection of Indonesian dad jokes as a consumable API" /> <meta name="description" content="Largest collection of Indonesian dad jokes as a consumable API" />
<meta name="twitter:description" content="Largest collection of Indonesian dad jokes as a consumable API" /> <meta name="twitter:description" content="Largest collection of Indonesian dad jokes as a consumable API" />
<meta property="og:description" content="Largest collection of Indonesian dad jokes as a consumable API" /> <meta property="og:description" content="Largest collection of Indonesian dad jokes as a consumable API" />
@ -40,7 +40,7 @@
</Notice> </Notice>
</section> </section>
<section> <section class="api_page">
<h1>{$_('api.get.title')}</h1> <h1>{$_('api.get.title')}</h1>
<h2>{$_('api.get.random.title')}</h2> <h2>{$_('api.get.random.title')}</h2>
<p>{$_('api.get.random.body')}</p> <p>{$_('api.get.random.body')}</p>
@ -66,12 +66,19 @@
<style> <style>
h1 { h1 {
@apply text-4xl font-bold py-4; @apply text-4xl;
@apply font-bold;
@apply py-4;
} }
h2 { h2 {
@apply text-2xl font-bold pt-6 pb-1; @apply text-2xl;
@apply font-bold;
@apply pt-6;
@apply pb-1;
} }
p { p {
@apply text-base opacity-80 py-2; @apply text-base;
@apply opacity-80;
@apply py-2;
} }
</style> </style>

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import Codeblock from '../components/codeblock.svelte'; import Codeblock from '../../components/codeblock.svelte';
import { _ } from 'svelte-i18n'; import { _ } from 'svelte-i18n';
// This page is meant to guide people on how to use the API. // This page is meant to guide people on how to use the API.
</script> </script>
@ -9,13 +9,13 @@
<meta name="title" content={$_('navigation.guide') + '-' + $_('meta.title')} /> <meta name="title" content={$_('navigation.guide') + '-' + $_('meta.title')} />
<meta name="twitter:title" content={$_('navigation.guide') + '-' + $_('meta.title')} /> <meta name="twitter:title" content={$_('navigation.guide') + '-' + $_('meta.title')} />
<meta property="og:title" content={$_('navigation.guide') + '-' + $_('meta.title')} /> <meta property="og:title" content={$_('navigation.guide') + '-' + $_('meta.title')} />
<link rel="canonical" href="https://jokesbapak2.pages.dev/guide" /> <link rel="canonical" href="https://jokesbapak2.reinaldyrafli.com/guide" />
<meta name="description" content="Largest collection of Indonesian dad jokes as a consumable API" /> <meta name="description" content="Largest collection of Indonesian dad jokes as a consumable API" />
<meta name="twitter:description" content="Largest collection of Indonesian dad jokes as a consumable API" /> <meta name="twitter:description" content="Largest collection of Indonesian dad jokes as a consumable API" />
<meta property="og:description" content="Largest collection of Indonesian dad jokes as a consumable API" /> <meta property="og:description" content="Largest collection of Indonesian dad jokes as a consumable API" />
</svelte:head> </svelte:head>
<section> <section class="guide_page">
<h1>{$_('navigation.guide')}</h1> <h1>{$_('navigation.guide')}</h1>
<p> <p>
{$_('guide.introduction.1')} {$_('guide.introduction.1')}
@ -23,38 +23,52 @@
</p> </p>
</section> </section>
<section> <section class="guide_page">
<h2>{$_('guide.direct.1')} <code>&lt;img&gt;</code> {$_('guide.direct.2')}</h2> <h2>{$_('guide.direct.1')} <code>&lt;img&gt;</code> {$_('guide.direct.2')}</h2>
<Codeblock>&lt;img src="https://jokesbapak2.herokuapp.com/v1/" /&gt;</Codeblock> <Codeblock>&lt;img src="https://jokesbapak2.reinaldyrafli.com/api/" /&gt;</Codeblock>
</section> </section>
<section> <section class="guide_page">
<h2>{$_('guide.fetch.1')}</h2> <h2>{$_('guide.fetch.1')}</h2>
<p>{$_('guide.fetch.2')}</p> <p>{$_('guide.fetch.2')}</p>
<Codeblock> <Codeblock>
fetch("https://jokesbapak2.herokuapp.com/v1/")<br /> const response = await fetch(&quot;https://jokesbapak2.reinaldyrafli.com/api/&quot;);<br />
&nbsp;&nbsp;.then(response => response.text())<br />
&nbsp;&nbsp;.then(data => console.log(data))<br />
<br /> <br />
// {$_('guide.fetch.3')}<br /> if (!response.ok) &#123;<br />
&nbsp;&nbsp;// {$_('guide.fetch.3')}<br />
&#125;<br />
<br /> <br />
&lt;img src="data:image/jpeg;base64, &lcub; data &rcub;" /&gt; const blob = await response.blob();<br />
<br />
const objectURL = URL.createObjectURL(blob);<br />
<br />
&lt;img src=&quot;&#123; objectURL &#125;&quot; /&gt;
</Codeblock> </Codeblock>
</section> </section>
<style> <style>
p { p {
@apply text-base py-2 'lg:w-2/3'; @apply text-base;
@apply py-2;
@apply lg\:w-2\/3;
} }
h1 { h1 {
@apply text-4xl font-bold py-2; @apply text-4xl;
@apply font-bold;
@apply py-2;
} }
h2 { h2 {
@apply text-2xl font-bold py-2; @apply text-2xl;
@apply font-bold;
@apply py-2;
} }
a { a {
/* This would probably be an error if you installed WindiCSS extension on VSCode */ @apply hover\:underline;
@apply 'hover:underline' 'dark:text-dodger-200' text-dodger-700; @apply dark\:text-dodger-200;
@apply text-dodger-700;
} }
section { section {
@apply pt-6; @apply pt-6;

View File

@ -7,18 +7,18 @@
<meta name="title" content={$_('navigation.why') + '-' + $_('meta.title')} /> <meta name="title" content={$_('navigation.why') + '-' + $_('meta.title')} />
<meta name="twitter:title" content={$_('navigation.why') + '-' + $_('meta.title')} /> <meta name="twitter:title" content={$_('navigation.why') + '-' + $_('meta.title')} />
<meta property="og:title" content={$_('navigation.why') + '-' + $_('meta.title')} /> <meta property="og:title" content={$_('navigation.why') + '-' + $_('meta.title')} />
<link rel="canonical" href="https://jokesbapak2.pages.dev/why" /> <link rel="canonical" href="https://jokesbapak2.reinaldyrafli.com/why" />
<meta name="description" content="Largest collection of Indonesian dad jokes as a consumable API" /> <meta name="description" content="Largest collection of Indonesian dad jokes as a consumable API" />
<meta name="twitter:description" content="Largest collection of Indonesian dad jokes as a consumable API" /> <meta name="twitter:description" content="Largest collection of Indonesian dad jokes as a consumable API" />
<meta property="og:description" content="Largest collection of Indonesian dad jokes as a consumable API" /> <meta property="og:description" content="Largest collection of Indonesian dad jokes as a consumable API" />
</svelte:head> </svelte:head>
<section> <section class="why_page">
<h1 id="why-does-this-project-exists">{$_('why.exists.title')}</h1> <h1 id="why-does-this-project-exists">{$_('why.exists.title')}</h1>
<p>{$_('why.exists.body.1')} {$_('why.exists.body.2')} {$_('why.exists.body.3')} {$_('why.exists.body.4')}</p> <p>{$_('why.exists.body.1')} {$_('why.exists.body.2')} {$_('why.exists.body.3')} {$_('why.exists.body.4')}</p>
<p>{$_('why.exists.body.5')} {$_('why.exists.body.6')}</p> <p>{$_('why.exists.body.5')} {$_('why.exists.body.6')}</p>
</section> </section>
<section> <section class="why_page">
<h1 id="can-i-submit-my-dad-joke">{$_('why.submit.title')}</h1> <h1 id="can-i-submit-my-dad-joke">{$_('why.submit.title')}</h1>
<p> <p>
{$_('why.submit.body.1')} {$_('why.submit.body.1')}
@ -29,7 +29,7 @@
{$_('why.submit.body.6')} {$_('why.submit.body.6')}
</p> </p>
</section> </section>
<section> <section class="why_page">
<h1 id="can-i-contribute">{$_('why.contribute.title')}</h1> <h1 id="can-i-contribute">{$_('why.contribute.title')}</h1>
<p> <p>
{$_('why.contribute.body.1')} {$_('why.contribute.body.1')}
@ -38,7 +38,7 @@
{$_('why.contribute.body.4')} {$_('why.contribute.body.4')}
</p> </p>
</section> </section>
<section> <section class="why_page">
<h1 id="other-inquiries">{$_('why.inquiries.title')}</h1> <h1 id="other-inquiries">{$_('why.inquiries.title')}</h1>
<p> <p>
{$_('why.inquiries.body.1')} {$_('why.inquiries.body.1')}
@ -48,15 +48,23 @@
<style> <style>
p { p {
@apply text-base py-2 'lg:w-2/3'; @apply text-base;
@apply py-2;
@apply lg\:w-2\/3;
} }
h1 { h1 {
@apply text-3xl font-bold py-2; @apply text-3xl;
@apply font-bold;
@apply py-2;
} }
a { a {
/* This would probably be an error if you installed WindiCSS extension on VSCode */ @apply hover\:underline;
@apply 'hover:underline' 'dark:text-dodger-200' text-dodger-700; @apply dark\:text-dodger-200;
@apply text-dodger-700;
} }
section { section {
@apply pt-6; @apply pt-6;
} }

View File

@ -1,5 +1,5 @@
import preprocess from 'svelte-preprocess'; import preprocess from 'svelte-preprocess';
import adapter from '@sveltejs/adapter-static'; import adapter from '@sveltejs/adapter-node';
import { windi } from 'svelte-windicss-preprocess'; import { windi } from 'svelte-windicss-preprocess';
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */
@ -8,25 +8,26 @@ const config = {
// for more information about preprocessors // for more information about preprocessors
preprocess: [ preprocess: [
windi({ windi({
configPath: './windi.config.js', configPath: './windi.config.ts',
preflights: false,
}), }),
preprocess({ postcss: false }), preprocess({ postcss: false }),
], ],
kit: { kit: {
// hydrate the <div id="svelte"> element in src/app.html // hydrate the <div id="svelte"> element in src/app.html
target: '#svelte',
trailingSlash: 'never', trailingSlash: 'never',
files: { files: {
routes: './src/routes', routes: './src/routes',
assets: './static', assets: './static',
hooks: './src', hooks: {
server: './src',
client: './src',
},
lib: './src/lib', lib: './src/lib',
}, },
adapter: adapter({ adapter: adapter({
// default options are shown out: 'dist',
pages: 'dist',
assets: 'dist',
}), }),
}, },
}; };

View File

@ -1,4 +1,5 @@
{ {
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"moduleResolution": "node", "moduleResolution": "node",
"module": "es2020", "module": "es2020",
@ -23,6 +24,7 @@
"allowJs": true, "allowJs": true,
"checkJs": true, "checkJs": true,
"paths": { "paths": {
"$lib": ["src/lib"],
"$lib/*": ["src/lib/*"] "$lib/*": ["src/lib/*"]
} }
}, },

6
client/vite.config.ts Normal file
View File

@ -0,0 +1,6 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()],
});

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,35 @@
services: services:
api: api:
build: ./api/ build: ./api/
restart: always
env_file: ./api/.env env_file: ./api/.env
ports: ports:
- 5000:5000 - 5000:5000
healthcheck:
test: "curl -f http://api:5000/health/"
interval: 15s
timeout: 12s
retries: 10
start_period: 30s
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: on-failure
delay: 30s
max_attempts: 20
window: 60s
resources:
limits:
memory: 1GB
cpus: '1'
reservations:
memory: 50MB
cpus: '0.1'
depends_on: depends_on:
- db bucket:
- cache condition: service_healthy
# Uncomment these if you want to have it on cache:
# volumes: condition: service_healthy
# ./api:/app
client: client:
build: ./client/ build: ./client/
@ -18,38 +37,33 @@ services:
env_file: ./client/.env env_file: ./client/.env
ports: ports:
- 3000:3000 - 3000:3000
depends_on:
- api
# Uncomment these if you want to have it on
# volumes:
# ./client:/app
db:
build: ./database/postgres/
command: >
-c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key
restart: unless-stopped
ports:
- 5432:5432
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: jokesbapak2
PGDATA: /data/postgres
# I got this key from somewhere. It works when you run it locally.
POSTGRES_SSL_CA_CERT: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURjekNDQWx1Z0F3SUJBZ0lVR3lDaElvR3g0
healthcheck: healthcheck:
test: pg_isready -U postgres test: "curl -f http://client:3000/"
interval: 10s interval: 15s
timeout: 5s timeout: 12s
retries: 5 retries: 10
start_period: 30s start_period: 30s
volumes: deploy:
- ./database/postgres/data:/data/postgres mode: replicated
replicas: 1
restart_policy:
condition: on-failure
delay: 30s
max_attempts: 20
window: 60s
resources:
limits:
memory: 500MB
cpus: '1'
reservations:
memory: 50MB
cpus: '0.1'
depends_on:
api:
condition: service_healthy
cache: cache:
image: redis:6.2.4-alpine image: redis:6.2.7-alpine
restart: unless-stopped
ports: ports:
- 6379:6379 - 6379:6379
healthcheck: healthcheck:
@ -58,12 +72,27 @@ services:
timeout: 5s timeout: 5s
retries: 5 retries: 5
start_period: 30s start_period: 30s
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: on-failure
delay: 30s
max_attempts: 20
window: 60s
resources:
limits:
memory: 2GB
cpus: '1'
reservations:
memory: 100MB
cpus: '0.1'
volumes: volumes:
- ./database/redis/etc:/usr/local/etc/redis - ./data/redis/etc:/usr/local/etc/redis
- ./database/redis/data:/data - ./data/redis/data:/data
bucket: bucket:
image: quay.io/minio/minio:RELEASE.2022-02-05T04-40-59Z image: quay.io/minio/minio:RELEASE.2022-09-07T22-25-02Z
command: server /data --console-address ":9001" command: server /data --console-address ":9001"
restart: unless-stopped restart: unless-stopped
ports: ports:
@ -80,28 +109,20 @@ services:
timeout: 10s timeout: 10s
retries: 5 retries: 5
start_period: 60s start_period: 60s
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: on-failure
delay: 30s
max_attempts: 20
window: 60s
resources:
limits:
memory: 4GB
cpus: '2'
reservations:
memory: 50MB
cpus: '0.1'
volumes: volumes:
- ./data/minio:/data - ./data/minio:/data
cache-admin:
image: rediscommander/redis-commander:latest
restart: always
environment:
REDIS_PORT: 6379
REDIS_HOST: redis
ports:
- 2084:8081
depends_on:
- cache
db-admin:
image: sosedoff/pgweb:0.11.8
restart: always
ports:
- 2086:8081
links:
- postgres:postgres
environment:
DATABASE_URL: postgres://postgres:password@db/jokesbapak2
depends_on:
- db