Merge branch 'master' into feature/new-reviews

This commit is contained in:
Irfan 2022-10-26 14:38:17 +05:00 committed by GitHub
commit 2336b7c610
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1357 additions and 24 deletions

21
.dockerignore Normal file
View File

@ -0,0 +1,21 @@
.dockerignore
docker/*
Makefile
docker*.yml
Dockerfile
bootstrap/cache/*
storage/logs/*
storage/framework/cache/data/*
storage/framework/sessions/*
storage/framework/testing/*
storage/framework/views/*
storage/*.cache
vendor
node_modules
*.log
.gitignore
.editorconfig
.idea
.vscode
.github
.git

View File

@ -11,3 +11,8 @@ updates:
interval: "daily"
allow:
- dependency-name: "jikan*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"

View File

@ -0,0 +1,58 @@
name: Container Base Image Release
concurrency: production
on:
workflow_dispatch:
jobs:
release-base-image:
runs-on: ubuntu-latest
name: Release base container image
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up qemu
uses: docker/setup-qemu-action@v2
- name: Set up docker buildx
uses: docker/setup-buildx-action@v2
with:
platforms: linux/amd64,linux/arm64
- name: Read metadata
id: meta
uses: docker/metadata-action@v4
with:
images: |
ghcr.io/jikan-me/jikan-rest-php
jikanme/jikan-rest-php
tags: |
type=raw,value=latest
type=sha
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
with:
push: true
context: ./docker/base_image/php-8.0
# let's use github action cache storage
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
GITHUB_PERSONAL_TOKEN=${{ secrets.GITHUB_TOKEN }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View File

@ -0,0 +1,71 @@
name: Container Image Release
concurrency: production
on:
release:
types: [published]
jobs:
release-app-image:
runs-on: ubuntu-latest
name: Release App container image
steps:
- name: Check if base container image exists
id: baseImageExists
run: |
GHCR_TOKEN=$(echo ${{ secrets.GITHUB_TOKEN }} | base64)
curl --fail -H "Authorization: Bearer ${GHCR_TOKEN}" https://ghcr.io/v2/jikan-me/jikan-rest-php/tags/list | grep -q latest
- name: Base image existance check failed
if: ${{ always() && steps.baseImageExists.outcome == 'failure' }}
run: echo "Base image doesn't exist yet. Please run the base image creation workflow first."
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up qemu
uses: docker/setup-qemu-action@v2
- name: Set up docker buildx
uses: docker/setup-buildx-action@v2
with:
platforms: linux/amd64,linux/arm64
- name: Read metadata
id: meta
uses: docker/metadata-action@v4
with:
images: |
ghcr.io/jikan-me/jikan-rest
jikanme/jikan-rest
tags: |
type=raw,value=${{ github.ref_name }}
type=sha
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
with:
push: true
context: .
# let's use github action cache storage
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
GITHUB_PERSONAL_TOKEN=${{ secrets.GITHUB_TOKEN }}
BASE_IMAGE_VERSION=latest
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

220
.rr.local.unix.yaml Normal file
View File

@ -0,0 +1,220 @@
# ---------------------------------------------------------------------------------------------------
# WARNING! This file should be used ONLY for local application development. NOT for production usage!
# ---------------------------------------------------------------------------------------------------
# Hint: RR will replace any config options using reference to environment variables,
# eg.: `option_key: ${ENVIRONMENT_VARIABLE_NAME}`.
# RR configuration version
version: "2.7"
# Remote Procedures Calling (docs: https://roadrunner.dev/docs/beep-beep-rpc)
# Is used for connecting to RoadRunner server from your PHP workers.
rpc:
# TCP address:port for listening.
#
# Default: "tcp://127.0.0.1:6001"
listen: tcp://127.0.0.1:6001
# Application server settings (docs: https://roadrunner.dev/docs/php-worker)
server:
# Worker starting command, with any required arguments.
#
# This option is required.
command: "php -dxdebug.start_with_request=trigger -dxdebug.mode=debug -dxdebug.client_port=9000 -dxdebug.client_host=127.0.0.1 -didekey=PHPSTORM ./vendor/bin/rr-worker start --relay-dsn unix:///var/run/rr/rr-relay.sock"
env:
- XDEBUG_SESSION: "1"
## Environment variables for the worker processes.
##
## Default: <empty map>
#env:
# - SOME_KEY: "SOME_VALUE"
# - SOME_KEY2: "SOME_VALUE2"
# Worker relay can be: "pipes", TCP (eg.: tcp://127.0.0.1:6001), or socket (eg.: unix:///var/run/rr-relay.sock).
#
# Default: "pipes"
relay: "unix:///var/run/rr/rr-relay.sock"
# Timeout for relay connection establishing (only for socket and TCP port relay).
#
# Default: 60s
relay_timeout: 60s
# HTTP plugin settings.
http:
# Host and port to listen on (eg.: `127.0.0.1:8080`).
#
# This option is required.
address: 0.0.0.0:8080
# HTTP access logs
#
# Default: false
access_logs: true
# Maximal incoming request size in megabytes. Zero means no limit.
#
# Default: 0
max_request_size: 256
# Middlewares for the http plugin, order is important. Allowed values is: "headers", "gzip".
#
# Default value: []
middleware: ["static", "headers", "gzip"]
# File uploading settings.
uploads:
# Directory for file uploads. Empty value means to use $TEMP based on your OS.
#
# Default: ""
dir: "/tmp"
# Deny files with the following extensions to upload.
#
# Default: [".php", ".exe", ".bat"]
forbid: [".php", ".exe", ".bat", ".sh"]
# Settings for "headers" middleware (docs: https://roadrunner.dev/docs/http-headers).
headers:
# Automatically add headers to every response.
#
# Default: <empty map>
response:
X-Powered-By: "RoadRunner"
# Settings for serving static content (docs: https://roadrunner.dev/docs/http-static).
static:
# Path to the directory with static assets.
#
# This option is required.
dir: "./public"
# File extensions to forbid.
#
# Default: []
forbid: [".htaccess", ".php"]
# Automatically add headers to every response.
#
# Default: <empty map>
response:
X-Powered-By: "RoadRunner"
# Workers pool settings.
pool:
debug: true
# How many worker processes will be started. Zero (or nothing) means the number of logical CPUs.
#
# Default: 0
num_workers: 4
# Maximal count of worker executions. Zero (or nothing) means no limit.
#
# Default: 0
max_jobs: 64
# Timeout for worker allocation. Zero means no limit.
#
# Default: 60s
allocate_timeout: 10s
# Timeout for worker destroying before process killing. Zero means no limit.
#
# Default: 60s
destroy_timeout: 10s
# Supervisor is used to control http workers (previous name was "limit", docs:
# https://roadrunner.dev/docs/php-limit). "Soft" limits will not interrupt current request processing. "Hard"
# limit on the contrary - interrupts the execution of the request.
supervisor:
# Maximal worker memory usage in megabytes (soft limit). Zero means no limit.
#
# Default: 0
max_worker_memory: 128
# Maximal job lifetime (hard limit). Zero means no limit.
#
# Default: 0s
exec_ttl: 60s
# HTTP/2 settings.
http2:
# HTTP/2 over non-encrypted TCP connection using H2C.
#
# Default: false
h2c: false
# Maximal concurrent streams count.
#
# Default: 128
max_concurrent_streams: 128
## Application metrics in Prometheus format (docs: https://roadrunner.dev/docs/beep-beep-metrics). Drop this section
## for this feature disabling.
#metrics:
# # Prometheus client address (path /metrics added automatically).
# #
# # Default: "127.0.0.1:2112"
# address: 127.0.0.1:8081
# Health check endpoint (docs: https://roadrunner.dev/docs/beep-beep-health). If response code is 200 - it means at
# least one worker ready to serve requests. 500 - there are no workers ready to service requests.
# Drop this section for this feature disabling.
status:
# Host and port to listen on (eg.: `127.0.0.1:2114`). Use the following URL: http://127.0.0.1:2114/health?plugin=http
# Multiple plugins must be separated using "&" - http://127.0.0.1:2114/health?plugin=http&plugin=rpc where "http" and
# "rpc" are active (connected) plugins.
#
# This option is required.
address: 127.0.0.1:2114
# Response status code if a requested plugin not ready to handle requests
# Valid for both /health and /ready endpoints
#
# Default: 503
unavailable_status_code: 503
# Automatically detect PHP file changes and reload connected services (docs:
# https://roadrunner.dev/docs/beep-beep-reload). Drop this section for this feature disabling.
reload:
# Sync interval.
#
# Default: "1s"
interval: 1s
# Global patterns to sync.
#
# Default: [".php"]
patterns: [".php"]
# List of included for sync services (this is a map, where key name is a plugin name).
#
# Default: <empty map>
services:
http:
# Directories to sync. If recursive is set to true, recursive sync will be applied only to the directories in
# "dirs" section. Dot (.) means "current working directory".
#
# Default: []
dirs: ["."]
# Recursive search for file patterns to add.
#
# Default: false
recursive: true
# Ignored folders.
#
# Default: []
ignore: ["vendor"]
# RoadRunner internal container configuration (docs: https://github.com/spiral/endure).
endure:
# Logging level. Possible values: "debug", "info", "warning", "error", "panic", "fatal".
#
# Default: "error"
log_level: error
app:
debug: true

220
.rr.local.yaml Normal file
View File

@ -0,0 +1,220 @@
# ---------------------------------------------------------------------------------------------------
# WARNING! This file should be used ONLY for local application development. NOT for production usage!
# ---------------------------------------------------------------------------------------------------
# Hint: RR will replace any config options using reference to environment variables,
# eg.: `option_key: ${ENVIRONMENT_VARIABLE_NAME}`.
# RR configuration version
version: "2.7"
# Remote Procedures Calling (docs: https://roadrunner.dev/docs/beep-beep-rpc)
# Is used for connecting to RoadRunner server from your PHP workers.
rpc:
# TCP address:port for listening.
#
# Default: "tcp://127.0.0.1:6001"
listen: tcp://127.0.0.1:6001
# Application server settings (docs: https://roadrunner.dev/docs/php-worker)
server:
# Worker starting command, with any required arguments.
#
# This option is required.
command: "php -dxdebug.start_with_request=trigger -dxdebug.mode=debug -dxdebug.client_port=9000 -dxdebug.client_host=127.0.0.1 -didekey=PHPSTORM ./vendor/bin/rr-worker start"
env:
- XDEBUG_SESSION: "1"
## Environment variables for the worker processes.
##
## Default: <empty map>
#env:
# - SOME_KEY: "SOME_VALUE"
# - SOME_KEY2: "SOME_VALUE2"
# Worker relay can be: "pipes", TCP (eg.: tcp://127.0.0.1:6001), or socket (eg.: unix:///var/run/rr-relay.sock).
#
# Default: "pipes"
relay: "pipes"
# Timeout for relay connection establishing (only for socket and TCP port relay).
#
# Default: 60s
relay_timeout: 60s
# HTTP plugin settings.
http:
# Host and port to listen on (eg.: `127.0.0.1:8080`).
#
# This option is required.
address: 0.0.0.0:8080
# HTTP access logs
#
# Default: false
access_logs: true
# Maximal incoming request size in megabytes. Zero means no limit.
#
# Default: 0
max_request_size: 256
# Middlewares for the http plugin, order is important. Allowed values is: "headers", "gzip".
#
# Default value: []
middleware: ["static", "headers", "gzip"]
# File uploading settings.
uploads:
# Directory for file uploads. Empty value means to use $TEMP based on your OS.
#
# Default: ""
dir: "/tmp"
# Deny files with the following extensions to upload.
#
# Default: [".php", ".exe", ".bat"]
forbid: [".php", ".exe", ".bat", ".sh"]
# Settings for "headers" middleware (docs: https://roadrunner.dev/docs/http-headers).
headers:
# Automatically add headers to every response.
#
# Default: <empty map>
response:
X-Powered-By: "RoadRunner"
# Settings for serving static content (docs: https://roadrunner.dev/docs/http-static).
static:
# Path to the directory with static assets.
#
# This option is required.
dir: "./public"
# File extensions to forbid.
#
# Default: []
forbid: [".htaccess", ".php"]
# Automatically add headers to every response.
#
# Default: <empty map>
response:
X-Powered-By: "RoadRunner"
# Workers pool settings.
pool:
debug: true
# How many worker processes will be started. Zero (or nothing) means the number of logical CPUs.
#
# Default: 0
num_workers: 4
# Maximal count of worker executions. Zero (or nothing) means no limit.
#
# Default: 0
max_jobs: 64
# Timeout for worker allocation. Zero means no limit.
#
# Default: 60s
allocate_timeout: 10s
# Timeout for worker destroying before process killing. Zero means no limit.
#
# Default: 60s
destroy_timeout: 10s
# Supervisor is used to control http workers (previous name was "limit", docs:
# https://roadrunner.dev/docs/php-limit). "Soft" limits will not interrupt current request processing. "Hard"
# limit on the contrary - interrupts the execution of the request.
supervisor:
# Maximal worker memory usage in megabytes (soft limit). Zero means no limit.
#
# Default: 0
max_worker_memory: 128
# Maximal job lifetime (hard limit). Zero means no limit.
#
# Default: 0s
exec_ttl: 60s
# HTTP/2 settings.
http2:
# HTTP/2 over non-encrypted TCP connection using H2C.
#
# Default: false
h2c: false
# Maximal concurrent streams count.
#
# Default: 128
max_concurrent_streams: 128
## Application metrics in Prometheus format (docs: https://roadrunner.dev/docs/beep-beep-metrics). Drop this section
## for this feature disabling.
#metrics:
# # Prometheus client address (path /metrics added automatically).
# #
# # Default: "127.0.0.1:2112"
# address: 127.0.0.1:8081
# Health check endpoint (docs: https://roadrunner.dev/docs/beep-beep-health). If response code is 200 - it means at
# least one worker ready to serve requests. 500 - there are no workers ready to service requests.
# Drop this section for this feature disabling.
status:
# Host and port to listen on (eg.: `127.0.0.1:2114`). Use the following URL: http://127.0.0.1:2114/health?plugin=http
# Multiple plugins must be separated using "&" - http://127.0.0.1:2114/health?plugin=http&plugin=rpc where "http" and
# "rpc" are active (connected) plugins.
#
# This option is required.
address: 127.0.0.1:2114
# Response status code if a requested plugin not ready to handle requests
# Valid for both /health and /ready endpoints
#
# Default: 503
unavailable_status_code: 503
# Automatically detect PHP file changes and reload connected services (docs:
# https://roadrunner.dev/docs/beep-beep-reload). Drop this section for this feature disabling.
reload:
# Sync interval.
#
# Default: "1s"
interval: 1s
# Global patterns to sync.
#
# Default: [".php"]
patterns: [".php"]
# List of included for sync services (this is a map, where key name is a plugin name).
#
# Default: <empty map>
services:
http:
# Directories to sync. If recursive is set to true, recursive sync will be applied only to the directories in
# "dirs" section. Dot (.) means "current working directory".
#
# Default: []
dirs: ["."]
# Recursive search for file patterns to add.
#
# Default: false
recursive: true
# Ignored folders.
#
# Default: []
ignore: ["vendor"]
# RoadRunner internal container configuration (docs: https://github.com/spiral/endure).
endure:
# Logging level. Possible values: "debug", "info", "warning", "error", "panic", "fatal".
#
# Default: "error"
log_level: error
app:
debug: true

214
.rr.yaml Normal file
View File

@ -0,0 +1,214 @@
# Production usage guide: https://roadrunner.dev/docs/beep-beep-production
# Hint: RR will replace any config options using reference to environment variables,
# eg.: `option_key: ${ENVIRONMENT_VARIABLE_NAME}`.
# Please note that this config is used within the docker container.
# RR configuration version
version: "2.7"
# Remote Procedures Calling (docs: https://roadrunner.dev/docs/beep-beep-rpc)
# Is used for connecting to RoadRunner server from your PHP workers.
rpc:
# TCP address:port for listening.
#
# Default: "tcp://127.0.0.1:6001"
listen: tcp://127.0.0.1:6001
# Application server settings (docs: https://roadrunner.dev/docs/php-worker)
server:
# Worker starting command, with any required arguments.
#
# This option is required.
command: "php ./vendor/bin/rr-worker start --relay-dsn unix:///var/run/rr/rr-relay.sock"
## Environment variables for the worker processes.
##
## Default: <empty map>
#env:
# - SOME_KEY: "SOME_VALUE"
# - SOME_KEY2: "SOME_VALUE2"
# Worker relay can be: "pipes", TCP (eg.: tcp://127.0.0.1:6001), or socket (eg.: unix:///var/run/rr-relay.sock).
#
# Default: "pipes"
relay: "unix:///var/run/rr/rr-relay.sock"
# Timeout for relay connection establishing (only for socket and TCP port relay).
#
# Default: 60s
relay_timeout: 60s
# Logging settings (docs: https://roadrunner.dev/docs/beep-beep-logging)
logs:
# Logging mode can be "development" or "production". Do not forget to change this value for production environment.
#
# Development mode (which makes DPanicLevel logs panic), uses a console encoder, writes to standard error, and
# disables sampling. Stacktraces are automatically included on logs of WarnLevel and above.
#
# Default: "development"
mode: production
# Logging level can be "panic", "error", "warning", "info", "debug".
#
# Default: "debug"
level: info
# Encoding format can be "console" or "json" (last is preferred for production usage).
#
# Default: "console"
encoding: json
# we want to use docker's log drivers, so push logs to stdout
output: stdout
# we to use docker's log drivers, so push error logs to stdout
# this way it is possible for example to pipe logs to journald or to AWS Cloudwatch
err_output: stdout
# HTTP plugin settings.
http:
# Host and port to listen on (eg.: `127.0.0.1:8080`).
#
# This option is required.
address: 0.0.0.0:8080
# HTTP access logs
#
# Default: false
access_logs: true
# Maximal incoming request size in megabytes. Zero means no limit.
#
# Default: 0
max_request_size: 256
# Middlewares for the http plugin, order is important. Allowed values is: "headers", "gzip".
#
# Default value: []
middleware: ["static", "headers", "gzip"]
# File uploading settings.
uploads:
# Directory for file uploads. Empty value means to use $TEMP based on your OS.
#
# Default: ""
dir: "/tmp"
# Deny files with the following extensions to upload.
#
# Default: [".php", ".exe", ".bat"]
forbid: [".php", ".exe", ".bat", ".sh"]
# Settings for "headers" middleware (docs: https://roadrunner.dev/docs/http-headers).
headers:
# Automatically add headers to every response.
#
# Default: <empty map>
response:
X-Powered-By: "RoadRunner"
# Settings for serving static content (docs: https://roadrunner.dev/docs/http-static).
static:
# Path to the directory with static assets.
#
# This option is required.
dir: "./public"
# File extensions to forbid.
#
# Default: []
forbid: [".htaccess", ".php"]
# Automatically add headers to every response.
#
# Default: <empty map>
response:
X-Powered-By: "RoadRunner"
# Workers pool settings.
pool:
# How many worker processes will be started. Zero (or nothing) means the number of logical CPUs.
#
# Default: 0
num_workers: 0
# Maximal count of worker executions. Zero (or nothing) means no limit.
#
# Default: 0
max_jobs: 64
# Timeout for worker allocation. Zero means no limit.
#
# Default: 60s
allocate_timeout: 10s
# Timeout for worker destroying before process killing. Zero means no limit.
#
# Default: 60s
destroy_timeout: 10s
# Supervisor is used to control http workers (previous name was "limit", docs:
# https://roadrunner.dev/docs/php-limit). "Soft" limits will not interrupt current request processing. "Hard"
# limit on the contrary - interrupts the execution of the request.
supervisor:
# Maximal worker memory usage in megabytes (soft limit). Zero means no limit.
#
# Default: 0
max_worker_memory: 128
# Maximal job lifetime (hard limit). Zero means no limit.
#
# Default: 0s
exec_ttl: 60s
# HTTP/2 settings.
http2:
# HTTP/2 over non-encrypted TCP connection using H2C.
#
# Default: false
h2c: false
# Maximal concurrent streams count.
#
# Default: 128
max_concurrent_streams: 128
## Application metrics in Prometheus format (docs: https://roadrunner.dev/docs/beep-beep-metrics). Drop this section
## for this feature disabling.
#metrics:
# # Prometheus client address (path /metrics added automatically).
# #
# # Default: "127.0.0.1:2112"
# address: 127.0.0.1:8081
# Health check endpoint (docs: https://roadrunner.dev/docs/beep-beep-health). If response code is 200 - it means at
# least one worker ready to serve requests. 500 - there are no workers ready to service requests.
# Drop this section for this feature disabling.
status:
# Host and port to listen on (eg.: `127.0.0.1:2114`). Use the following URL: http://127.0.0.1:2114/health?plugin=http
# Multiple plugins must be separated using "&" - http://127.0.0.1:2114/health?plugin=http&plugin=rpc where "http" and
# "rpc" are active (connected) plugins.
#
# This option is required.
address: 127.0.0.1:2114
# Response status code if a requested plugin not ready to handle requests
# Valid for both /health and /ready endpoints
#
# Default: 503
unavailable_status_code: 503
service:
laravel_queue_worker_1:
command: "php /app/artisan queue:work --queue=high,default"
process_num: 1
restart_sec: 5
supercronic:
command: "supercronic /etc/supercronic/laravel"
# RoadRunner internal container configuration (docs: https://github.com/spiral/endure).
endure:
# Logging level. Possible values: "debug", "info", "warning", "error", "panic", "fatal".
#
# Default: "error"
log_level: error

63
Dockerfile Normal file
View File

@ -0,0 +1,63 @@
ARG BASE_IMAGE_VERSION="latest"
FROM jikanme/jikan-rest-php:${BASE_IMAGE_VERSION}
ARG GITHUB_PERSONAL_TOKEN
LABEL org.opencontainers.image.source=https://github.com/jikan-me/jikan-rest
RUN set -ex \
&& apt-get update && apt-get install -y --no-install-recommends \
openssl \
git \
dos2unix \
unzip \
wget \
# install supercronic (for laravel task scheduling), project page: <https://github.com/aptible/supercronic>
&& wget -q "https://github.com/aptible/supercronic/releases/download/v0.1.12/supercronic-linux-amd64" \
-O /usr/bin/supercronic \
&& chmod +x /usr/bin/supercronic \
&& mkdir /etc/supercronic \
&& echo '*/1 * * * * php /app/artisan schedule:run' > /etc/supercronic/laravel \
&& rm -rf /var/lib/apt/lists/* \
# enable opcache for CLI and JIT, docs: <https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.jit>
&& echo -e "\nopcache.enable=1\nopcache.enable_cli=1\nopcache.jit_buffer_size=32M\nopcache.jit=1235\n" >> \
${PHP_INI_DIR}/conf.d/docker-php-ext-opcache.ini \
# show installed modules
&& php -m \
# create unpriviliged user
&& adduser --disabled-password --shell "/sbin/nologin" --home "/nonexistent" --no-create-home --uid "10001" --gecos "" "jikanapi" \
&& mkdir /app /var/run/rr \
&& chown -R jikanapi:jikanapi /app /var/run/rr /etc/supercronic/laravel \
&& chmod -R 777 /var/run/rr
USER jikanapi:jikanapi
WORKDIR /app
# copy composer (json|lock) files for dependencies layer caching
COPY --chown=jikanapi:jikanapi ./composer.* /app/
# check if GITHUB_PERSONAL_TOKEN is set and configure it for composer
# it is recommended to set this for the build, otherwise the build might fail because of github's rate limits
RUN if [ -z "$GITHUB_PERSONAL_TOKEN" ]; then echo "** GITHUB_PERSONAL_TOKEN is not set. This build may fail due to github rate limits."; \
else composer config github-oauth.github.com "$GITHUB_PERSONAL_TOKEN"; fi
# install composer dependencies (autoloader MUST be generated later!)
RUN composer install -n --no-dev --no-cache --no-ansi --no-autoloader --no-scripts --prefer-dist
# copy application sources into image (completely)
COPY --chown=jikanapi:jikanapi . /app/
RUN set -ex \
&& composer update jikan-me/jikan \
&& composer dump-autoload -n --optimize \
&& chmod -R 777 ${COMPOSER_HOME}/cache \
&& chmod -R a+w storage/ \
&& chown -R jikanapi:jikanapi /app \
&& chmod +x docker-entrypoint.php \
&& chmod +x docker-entrypoint.sh
EXPOSE 8080
EXPOSE 2114
HEALTHCHECK CMD curl --fail http://localhost:2114/health?plugin=http || exit 1
# unset default image entrypoint.
ENTRYPOINT ["/app/docker-entrypoint.sh"]

View File

@ -25,7 +25,12 @@ Please read the [manual installation guide](https://github.com/jikan-me/jikan-re
For any additional help, join our [Discord server](http://discord.jikan.moe/).
### 🐳 Docker Installation
If you don't want to install it manually, you can use the [docker image](https://github.com/jikan-me/jikan-docker)
We distribute the app as a container image so you can just run it:
```bash
docker run -d --name=jikan-rest -p 8080:8080 -v ./.env:/app/.env jikanme/jikan-rest:latest
```
For more information please refer to our [container usage guide](container_usage.md).
## Public REST API
If you don't want to host your instance, there's a public API available.

View File

@ -89,24 +89,26 @@ class Anime extends JikanApiSearchableModel
public function getBroadcastAttribute()
{
$broadcastStr = $this->attributes['broadcast'];
if (array_key_exists("broadcast", $this->attributes)) {
$broadcastStr = $this->attributes['broadcast'];
if (!preg_match('~(.*) at (.*) \(~', $broadcastStr, $matches)) {
return [
'day' => null,
'time' => null,
'timezone' => null,
'string' => $broadcastStr
];
}
if (!preg_match('~(.*) at (.*) \(~', $broadcastStr, $matches)) {
return [
'day' => null,
'time' => null,
'timezone' => null,
'string' => $broadcastStr
];
}
if (preg_match('~(.*) at (.*) \(~', $broadcastStr, $matches)) {
return [
'day' => $matches[1],
'time' => $matches[2],
'timezone' => 'Asia/Tokyo',
'string' => $broadcastStr
];
if (preg_match('~(.*) at (.*) \(~', $broadcastStr, $matches)) {
return [
'day' => $matches[1],
'time' => $matches[2],
'timezone' => 'Asia/Tokyo',
'string' => $broadcastStr
];
}
}
return [

View File

@ -52,7 +52,7 @@ class GithubReport
/**
* @var string|bool
*/
private string|bool $redisRunning;
private string|bool $redisRunning = false;
/**
* @var string
@ -94,7 +94,12 @@ class GithubReport
$report->instanceType = 'UNKNOWN';
if (env('APP_ENV') !== 'testing') {
$report->instanceType = $_SERVER['SERVER_NAME'] === 'api.jikan.moe' ? 'OFFICIAL' : 'HOSTED';
if (array_key_exists('SERVER_NAME', $_SERVER)) {
$report->instanceType = $_SERVER['SERVER_NAME'] === 'api.jikan.moe' ? 'OFFICIAL' : 'HOSTED';
}
else {
$report->instanceType = 'HOSTED-RR';
}
}
return $report;

View File

@ -45,7 +45,7 @@ class Insights
->insert([
'timestamp' => time(),
'url' => $request->getRequestUri(),
'type' =>
'type' => ""
]);
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Listeners;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use pushrbx\LumenRoadRunner\Events\LoopErrorOccurredEvent;
class PsrWorkerErrorListener
{
private \App\Exceptions\Handler $exceptionHandler;
private Logger $logger;
public function __construct()
{
$this->exceptionHandler = new \App\Exceptions\Handler;
$this->logger = new Logger('psr-worker');
$this->logger->pushHandler(new StreamHandler(storage_path().'/logs/psr-worker.log'), env('APP_DEBUG') ? Logger::DEBUG : Logger::WARNING);
}
public function handle(LoopErrorOccurredEvent $event): void
{
try {
$this->exceptionHandler->report($event->exception());
} catch (\Exception $e) {
$this->logger->error($e->getMessage());
}
}
}

View File

@ -23,6 +23,7 @@ use App\Services\TypeSenseScoutSearchService;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Collection;
use Laravel\Scout\Builder as ScoutBuilder;
use Typesense\LaravelTypesense\Typesense;
class AppServiceProvider extends ServiceProvider
{
@ -161,4 +162,28 @@ class AppServiceProvider extends ServiceProvider
{
return $app["config"]->get("scout.driver");
}
public static function servicesToWarm(): array
{
$services = [
ScoutSearchService::class,
AnimeSearchQueryBuilder::class,
MangaSearchQueryBuilder::class,
ClubSearchQueryBuilder::class,
CharacterSearchQueryBuilder::class,
PeopleSearchQueryBuilder::class,
TopAnimeQueryBuilder::class,
TopMangaQueryBuilder::class
];
if (env("SCOUT_DRIVER") === "typesense") {
$services[] = Typesense::class;
}
if (env("SCOUT_DRIVER") === "Matchish\ScoutElasticSearch\Engines\ElasticSearchEngine") {
$services[] = \Elastic\Elasticsearch\Client::class;
}
return $services;
}
}

View File

@ -3,6 +3,7 @@
namespace App\Providers;
use Laravel\Lumen\Providers\EventServiceProvider as ServiceProvider;
use pushrbx\LumenRoadRunner\Events\LoopErrorOccurredEvent;
class EventServiceProvider extends ServiceProvider
{
@ -12,8 +13,8 @@ class EventServiceProvider extends ServiceProvider
* @var array
*/
protected $listen = [
'App\Events\SomeEvent' => [
'App\Listeners\EventListener',
],
LoopErrorOccurredEvent::class => [
\App\Listeners\PsrWorkerErrorListener::class
]
];
}

View File

@ -112,10 +112,13 @@ $app->configure('database');
$app->configure('queue');
$app->configure('controller-to-table-mapping');
$app->configure('controller');
$app->configure('roadrunner');
$app->register(\pushrbx\LumenRoadRunner\ServiceProvider::class);
$app->register(\SwaggerLume\ServiceProvider::class);
$app->register(Flipbox\LumenGenerator\LumenGeneratorServiceProvider::class);
$app->register(\App\Providers\SourceHeartbeatProvider::class);
$app->register(\App\Providers\EventServiceProvider::class);
$app->register(Illuminate\Database\Eloquent\LegacyFactoryServiceProvider::class);
$app->register(\App\Providers\AppServiceProvider::class);
@ -153,7 +156,7 @@ $app->register(Laravel\Scout\ScoutServiceProvider::class);
// we support TypeSense and ElasticSearch as search indexes.
if (env("SCOUT_DRIVER") === "typesense") {
// in this case the TYPESENSE_HOST env var should be set too
$app->register(Typesense\LaravelTypesense\TypesenseServiceProvider::class);
$app->register(\Typesense\LaravelTypesense\TypesenseServiceProvider::class);
}
if (env("SCOUT_DRIVER") === "Matchish\ScoutElasticSearch\Engines\ElasticSearchEngine") {
// in this case the ELASTICSEARCH_HOST env var should be set too

View File

@ -4,6 +4,12 @@
"keywords": ["framework", "laravel", "lumen"],
"license": "MIT",
"type": "project",
"repositories": [
{
"type": "vcs",
"url": "https://github.com/pushrbx/lumen-roadrunner"
}
],
"require": {
"php": "^8.0",
"ext-json": "*",
@ -22,8 +28,10 @@
"laravel/scout": "^9.4",
"league/flysystem": "^3.0",
"matchish/laravel-scout-elasticsearch": "^6.0",
"mirazmac/dotenvwriter": "^0.1.0",
"ocramius/package-versions": "^2.5",
"predis/predis": "^1.1",
"pushrbx/lumen-roadrunner": "dev-master",
"sentry/sentry-laravel": "^2.8",
"symfony/yaml": "^4.1",
"typesense/laravel-scout-typesense-driver": "^5.0",

124
config/roadrunner.php Normal file
View File

@ -0,0 +1,124 @@
<?php
use pushrbx\LumenRoadRunner\Events;
use pushrbx\LumenRoadRunner\Defaults;
use pushrbx\LumenRoadRunner\Listeners;
use Spiral\RoadRunner\Environment\Mode;
return [
/*
|--------------------------------------------------------------------------
| Force HTTPS Schema Usage
|--------------------------------------------------------------------------
|
| Set this value to `true` if your application uses HTTPS (required for
| correct links generation, for example).
|
*/
'force_https' => (bool)env('APP_FORCE_HTTPS', false),
/*
|--------------------------------------------------------------------------
| Event Listeners
|--------------------------------------------------------------------------
|
| Worker provided by this package allows to interacts with request
| processing loop using application events.
|
| Feel free to add your own event listeners.
|
*/
'listeners' => [
Events\BeforeLoopStartedEvent::class => [
...Defaults::beforeLoopStarted(),
],
Events\BeforeLoopIterationEvent::class => [
...Defaults::beforeLoopIteration(),
],
Events\BeforeRequestHandlingEvent::class => [
...Defaults::beforeRequestHandling(),
Listeners\InjectStatsIntoRequestListener::class
],
Events\AfterRequestHandlingEvent::class => [
...Defaults::afterRequestHandling(),
],
Events\AfterLoopIterationEvent::class => [
...Defaults::afterLoopIteration(),
Listeners\RunGarbageCollectorListener::class, // keep the memory usage low
// Listeners\CleanupUploadedFilesListener::class, // remove temporary files
],
Events\AfterLoopStoppedEvent::class => [
...Defaults::afterLoopStopped(),
],
Events\LoopErrorOccurredEvent::class => [
...Defaults::loopErrorOccurred(),
Listeners\SendExceptionToStderrListener::class,
Listeners\StopWorkerListener::class,
\App\Listeners\PsrWorkerErrorListener::class,
],
],
/*
|--------------------------------------------------------------------------
| Containers Pre Resolving / Clearing
|--------------------------------------------------------------------------
|
| The bindings listed below will be resolved before the events loop
| starting. Clearing a binding will force the container to resolve that
| binding again when asked.
|
| Feel free to add your own bindings here.
|
*/
'warm' => [
...Defaults::servicesToWarm(),
...\App\Providers\AppServiceProvider::servicesToWarm()
],
'clear' => [
...Defaults::servicesToClear(),
'auth', // is not required for Laravel >= v8.35
],
/*
|--------------------------------------------------------------------------
| Reset Providers
|--------------------------------------------------------------------------
|
| Providers that will be registered on every request.
|
| Feel free to add your service-providers here.
|
*/
'reset_providers' => [
...Defaults::providersToReset(),
Illuminate\Auth\AuthServiceProvider::class, // is not required for Laravel >= v8.35
Illuminate\Pagination\PaginationServiceProvider::class, // is not required for Laravel >= v8.35
],
/*
|--------------------------------------------------------------------------
| Worker Classes
|--------------------------------------------------------------------------
|
| Here you can override the worker class for processing different kinds of
| jobs, that received from the RoadRunner daemon. The key is a worker mode.
|
*/
'workers' => [
Mode::MODE_HTTP => \pushrbx\LumenRoadRunner\Worker::class,
// Mode::MODE_JOBS => ...,
// Mode::MODE_TEMPORAL => ...,
],
];

71
container_usage.md Normal file
View File

@ -0,0 +1,71 @@
# 🐳 Running Jikan API in a container
```bash
docker run -d --name=jikan-rest -p 8080:8080 -v ./.env:/app/.env jikanme/jikan-rest:latest
```
- Container listens on port `8080` for http requests
- By mounting your .env file on the container via `-v ./.env:/app/.env` command line option for `docker run` you can configure Jikan API.
> **Important**: You need to either mount a `.env` file on the container or specify the configuration through environment variables to make Jikan API work in the container. Jikan API needs a MongoDB and optionally a search engine. In high load environments additionally a `redis` server is required too. The configuration should point to the correct address of these services.
> **Tip**: If you run the container on a non-default network, you can use the container names in the configuration to specify the address of services like MongoDB and TypeSense/ElasticSearch. However this is not a concern if you use `docker-compose`.
There is also a `Dockerfile` in the repo which you can use to build the container image and startup the app in a container:
```bash
docker build -t jikan-rest:nightly .
docker run -d --name=jikan-rest -p 8080:8080 -v ./.env:/app/.env jikan-rest:nightly
```
### Docker compose usage
```
docker-compose up
```
Docker compose will use the `.env` file from the folder where you execute it from to load configurations for the services. If you don't have a `.env` file yet in the folder, copy the `.env.dist` file, and set the passwords.
> **Please note**: The syntax rules of docker compose for `.env` applies here: https://docs.docker.com/compose/env-file/#syntax-rules
#### Note for Podman
If you build the container image yourself with podman, the resulting image format will be OCI by default.
To make the health checks work in that situation you need to run the container the following way:
```bash
podman run -d --name=jikan-rest -p 8080:8080 -v ./.env:/app/.env --health-start-period=5s --health-cmd="curl --fail http://localhost:2114/health?plugin=http || exit 1" jikan-rest:nightly
```
#### Configuration of the container
You can change the settings of Jikan through setting environment variables via the `-e` command line argument option for the `docker run` command.
These environment variables are the same as the options found in the `.env` file. We also provide a sample file called `.env.dist`.
Additionally, you can use the `--env-file` option of `docker run` to specify configuration for Jikan, in which case you put all the configuration in the env file.
```bash
docker run -d --name=jikan-rest -p 8080:8080 --env-file ./env.list jikanme/jikan-rest:latest
```
The env-file should contain env var value pairs line by line.
```
VAR1=value1
VAR2=value2
```
There are additional configuration options:
| Name | Description |
|--------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| RR_MAX_WORKER_MEMORY | (Number) Configures the available memory in megabytes for the php scripts |
| RR_MAX_REQUEST_SIZE_MB | (Number) Configures the max allowed request body size in megabytes |
| JIKAN_QUEUE_WORKER_PROCESS_NUM | (Number) Configures the number of running queue worker processes. (You want to increase this if you experience huge load) |
| JIKAN_ENABLE_PERIODICAL_FULL_INDEXER | (Bool) Configures whether to run the anime/manga indexer every week, which would crawl all anime/manga at first then it would just grab the latest anime/manga entries from MAL. Defaults to false. |
You can read more about additional configuration options on the [Configuration Wiki page](https://github.com/jikan-me/jikan-rest/wiki/Configuration).
## Some facts about the container image
- Jikan uses RoadRunner as an application server within the container.
- Both `wget` and `curl` exists in the container image.
- The script in `docker-entrypoint.php` sets safe defaults. Because of this by default the app won't behave the same way as the publicly available version of the app at [https://api.jikan.moe/v4](https://api.jikan.moe/v4). The default settings:
- No redis caching
- No search index usage (inaccurate search results)
- Via Roadrunner multiple processes are running in the container, and their logs are aggregated and forwarded to `stdout`.
- These processes are:
- the php processes ingesting the http requests
- [Supercronic](https://github.com/aptible/supercronic), which runs cron jobs.
- Queue workers for populating the search index and other background jobs.

93
docker-compose.yml Normal file
View File

@ -0,0 +1,93 @@
# For now this is just for local development. This is not production ready.
version: '3.8'
volumes:
mongo-data: {}
redis-data: {}
tmp-data: {}
typesense-data: {}
services:
jikan_rest: &jikan_rest
build:
context: .
dockerfile: Dockerfile
user: "${APP_UID:-10001}:${APP_GID:-10001}"
environment:
PS1: '\[\033[1;32m\]\[\033[1;36m\][\u@\h] \[\033[1;34m\]\w\[\033[0;35m\] \[\033[1;36m\]# \[\033[0m\]'
HOME: /tmp
APP_DEBUG: 'true'
APP_ENV: local
REDIS_HOST: redis
REDIS_PASSWORD: "${REDIS_PASSWORD:-null}"
DB_CONNECTION: mongodb
DB_HOST: mongodb
DB_DATABASE: jikan
DB_PORT: 27017
DB_ADMIN: jikan
DB_USERNAME: "${DB_USERNAME}"
DB_PASSWORD: "${DB_PASSWORD}"
volumes:
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro
- tmp-data:/tmp:rw
- .:/app:rw
depends_on:
mongodb: {condition: service_healthy}
redis: {condition: service_healthy}
typesense: {condition: service_healthy}
web:
<<: *jikan_rest
ports:
- '8080:8080/tcp'
healthcheck:
test: [ 'CMD-SHELL', 'wget --spider -q "http://127.0.0.1:2114/health?plugin=http"' ]
interval: 2s
timeout: 2s
cron:
<<: *jikan_rest
command: supercronic /etc/supercronic/laravel # it runs artisan schedule:run
mongodb:
image: mongo:focal
volumes:
- mongo-data:/data/db
ports:
- '27017/tcp'
command: --wiredTigerCacheSizeGB 1
environment:
MONGO_INITDB_ROOT_USERNAME: "${DB_USERNAME:-root}"
MONGO_INITDB_ROOT_PASSWORD: "${DB_PASSWORD}"
healthcheck:
test: echo 'db.runCommand("ping").ok' | mongo mongodb://localhost:27017 --quiet
interval: 30s
timeout: 10s
retries: 5
redis:
image: redis:7-alpine
command:
- /bin/sh
- -c
- redis-server --requirepass "$${REDIS_PASSWORD:?REDIS_PASSWORD variable is not set}"
volumes:
- redis-data:/data:rw
ports:
- '6379/tcp'
healthcheck:
test: [ 'CMD', 'redis-cli', 'ping' ]
interval: 500ms
timeout: 1s
typesense:
image: typesense/typesense:0.23.1
entrypoint: /bin/sh
command:
- -c
- /opt/typesense-server --data-dir /data --api-key "$${TYPESENSE_API_KEY:?TYPESENSE_API_KEY variable is not set}"
restart: no
volumes:
- typesense-data:/data
ports:
- "8108/tcp"

56
docker-entrypoint.php Executable file
View File

@ -0,0 +1,56 @@
#!/usr/bin/env php
<?php
use Dotenv\Dotenv;
require_once __DIR__.'/vendor/autoload.php';
$safe_defaults = [
// mongodb regex search by default
"SCOUT_DRIVER" => "null",
"SCOUT_QUEUE" => false,
"THROTTLE" => false,
"QUEUE_CONNECTION" => "database",
"DB_CACHING" => true,
"DB_HOST" => "localhost",
"DB_PORT" => 27017,
"DB_DATABASE" => "jikan",
"DB_USERNAME" => "",
"DB_PASSWORD" => ""
];
if (!file_exists(".env")) {
copy(".env.dist", ".env");
$writer = new \MirazMac\DotEnv\Writer(__DIR__ . '/' . '.env');
foreach ($safe_defaults as $env_var_name => $env_var_default) {
$writer->set("SCOUT_DRIVER", env($env_var_name, $env_var_default));
}
$writer->write();
}
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();
$current_env = $_ENV;
if ($current_env["SCOUT_DRIVER"] === "typesense" && empty($current_env["TYPESENSE_API_KEY"])) {
echo "Please set the TYPESENSE_API_KEY environment variable when setting SCOUT_DRIVER to typesense.";
exit(1);
}
$rrConfig = \Symfony\Component\Yaml\Yaml::parse(file_get_contents(".rr.yaml"));
$rrConfig["http"]["pool"]["supervisor"]["max_worker_memory"] = (int) env("RR_MAX_WORKER_MEMORY", 128);
$rrConfig["http"]["max_request_size"] = (int) env("RR_MAX_REQUEST_SIZE_MB", 256);
$rrConfig["service"]["laravel_queue_worker_1"]["process_num"] = (int) env("JIKAN_QUEUE_WORKER_PROCESS_NUM", 1);
$periodical_full_indexer_key = "JIKAN_ENABLE_PERIODICAL_FULL_INDEXER";
if (array_key_exists($periodical_full_indexer_key, $current_env) && in_array($current_env[$periodical_full_indexer_key], [1, '1', 'true', 'True', 'TRUE'])) {
$supercronic_schedule = file_get_contents("/etc/supercronic/laravel");
$supercronic_schedule .= PHP_EOL;
$supercronic_schedule .= "0 1 * * 1 php /app/artisan indexer:anime --fail && php /app/artisan indexer:anime --resume && php /app/artisan indexer:manga --fail && php /app/artisan indexer:manga --resume";
$supercronic_schedule .= PHP_EOL;
file_put_contents("/etc/supercronic/laravel", $supercronic_schedule);
$current_time = time();
echo json_encode(["level" => "info", "ts" => "$current_time.0", "logger" => "container_entrypoint", "msg" => "Full anime/manga indexer is enabled. They will run every Monday at 1am."]) . PHP_EOL;
}
file_put_contents(".rr.yaml", \Symfony\Component\Yaml\Yaml::dump($rrConfig, 8));

15
docker-entrypoint.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/bash
set -eo pipefail
status=0
if [[ $# -eq 0 ]] ; then
php /app/docker-entrypoint.php
status=$?
else
php /app/docker-entrypoint.php "$@"
status=$?
fi
[[ $status -ne 0 ]] && exit $status
exec rr serve -c .rr.yaml

View File

@ -0,0 +1,12 @@
FROM spiralscout/roadrunner:2.10.6 as roadrunner
FROM composer:2.3.9 as composer
FROM mlocati/php-extension-installer:1.5.29 as php-ext-installer
FROM php:8.0-bullseye as runtime
ARG GITHUB_PERSONAL_TOKEN
COPY --from=composer /usr/bin/composer /usr/bin/composer
COPY --from=php-ext-installer /usr/bin/install-php-extensions /usr/local/bin/
ENV COMPOSER_HOME="/tmp/composer"
RUN install-php-extensions gd exif intl bz2 gettext mongodb-stable redis opcache sockets pcntl
# install roadrunner
COPY --from=roadrunner /usr/bin/rr /usr/bin/rr

View File

@ -0,0 +1,12 @@
FROM spiralscout/roadrunner:2.10.6 as roadrunner
FROM composer:2.3.9 as composer
FROM mlocati/php-extension-installer:1.5.29 as php-ext-installer
FROM php:8.1-bullseye as runtime
ARG GITHUB_PERSONAL_TOKEN
COPY --from=composer /usr/bin/composer /usr/bin/composer
COPY --from=php-ext-installer /usr/bin/install-php-extensions /usr/local/bin/
ENV COMPOSER_HOME="/tmp/composer"
RUN install-php-extensions gd exif intl bz2 gettext mongodb-stable redis opcache sockets pcntl
# install roadrunner
COPY --from=roadrunner /usr/bin/rr /usr/bin/rr