├── secrets
└── .gitignore
├── license
├── public
│ ├── favicon.ico
│ ├── robots.txt
│ ├── index.php
│ └── .htaccess
├── storage
│ ├── logs
│ │ └── .gitignore
│ ├── app
│ │ ├── public
│ │ │ └── .gitignore
│ │ └── .gitignore
│ └── framework
│ │ ├── sessions
│ │ └── .gitignore
│ │ ├── testing
│ │ └── .gitignore
│ │ ├── views
│ │ └── .gitignore
│ │ ├── cache
│ │ ├── data
│ │ │ └── .gitignore
│ │ └── .gitignore
│ │ └── .gitignore
├── bootstrap
│ ├── cache
│ │ └── .gitignore
│ ├── providers.php
│ └── app.php
├── .gitattributes
├── docker
│ ├── php.ini
│ ├── start-container
│ └── supervisord.conf
├── .editorconfig
├── .gitignore
├── artisan
├── src
│ ├── Providers
│ │ ├── DatabaseServiceProvider.php
│ │ ├── AppServiceProvider.php
│ │ └── RouteServiceProvider.php
│ ├── Routes
│ │ └── ApiV1.php
│ ├── Actions
│ │ ├── MeLicense.php
│ │ ├── GetLicenseOfUser.php
│ │ └── LicenseUpdated.php
│ ├── Models
│ │ ├── License.php
│ │ └── LicenseType.php
│ ├── Migrations
│ │ ├── 2024_06_07_021508_create_failed_jobs_table.php
│ │ ├── 2024_06_07_021519_create_jobs_table.php
│ │ └── 2024_06_06_135026_create_licenses_table.php
│ ├── Resources
│ │ └── LicenseResource.php
│ ├── Middlewares
│ │ └── EnsureTokenIsValid.php
│ ├── Commands
│ │ └── ConsumeMessages.php
│ └── Handlers
│ │ └── KafkaMessageHandler.php
├── .env.example
├── composer.json
├── config
│ └── kafka.php
└── Dockerfile
├── security
├── public
│ ├── favicon.ico
│ ├── robots.txt
│ ├── index.php
│ └── .htaccess
├── database
│ ├── .gitignore
│ └── migrations
│ │ ├── 2024_06_04_130737_create_oauth_personal_access_clients_table.php
│ │ ├── 2024_06_04_130738_create_oauth_refresh_tokens_table.php
│ │ ├── 2024_06_04_130741_create_oauth_auth_codes_table.php
│ │ ├── 0001_01_01_000001_create_cache_table.php
│ │ ├── 2024_06_04_130740_create_oauth_access_tokens_table.php
│ │ ├── 2024_06_04_130739_create_oauth_clients_table.php
│ │ ├── 0001_01_01_000000_create_users_table.php
│ │ └── 0001_01_01_000002_create_jobs_table.php
├── bootstrap
│ ├── cache
│ │ └── .gitignore
│ ├── providers.php
│ └── app.php
├── storage
│ ├── logs
│ │ └── .gitignore
│ ├── app
│ │ ├── public
│ │ │ └── .gitignore
│ │ └── .gitignore
│ └── framework
│ │ ├── sessions
│ │ └── .gitignore
│ │ ├── testing
│ │ └── .gitignore
│ │ ├── views
│ │ └── .gitignore
│ │ ├── cache
│ │ ├── data
│ │ │ └── .gitignore
│ │ └── .gitignore
│ │ └── .gitignore
├── .gitattributes
├── docker
│ ├── php.ini
│ ├── start-container
│ └── supervisord.conf
├── .editorconfig
├── .gitignore
├── artisan
├── src
│ ├── Models
│ │ └── User.php
│ ├── Routes
│ │ └── ApiV1.php
│ ├── Providers
│ │ ├── AppServiceProvider.php
│ │ ├── AuthServiceProvider.php
│ │ └── RouteServiceProvider.php
│ └── Actions
│ │ ├── PublishUserCreatedMessage.php
│ │ └── CreateNewUser.php
├── .env.example
├── composer.json
├── config
│ └── kafka.php
└── Dockerfile
├── file-management
├── public
│ ├── favicon.ico
│ ├── robots.txt
│ ├── index.php
│ └── .htaccess
├── database
│ └── .gitignore
├── storage
│ ├── logs
│ │ └── .gitignore
│ ├── app
│ │ ├── public
│ │ │ └── .gitignore
│ │ └── .gitignore
│ └── framework
│ │ ├── views
│ │ └── .gitignore
│ │ ├── cache
│ │ ├── data
│ │ │ └── .gitignore
│ │ └── .gitignore
│ │ ├── sessions
│ │ └── .gitignore
│ │ ├── testing
│ │ └── .gitignore
│ │ └── .gitignore
├── bootstrap
│ ├── cache
│ │ └── .gitignore
│ ├── providers.php
│ └── app.php
├── .gitattributes
├── docker
│ ├── php.ini
│ ├── start-container
│ └── supervisord.conf
├── src
│ ├── Exceptions
│ │ ├── DailyLimitException.php
│ │ ├── QuotaReachedException.php
│ │ ├── EntityTooLargeException.php
│ │ └── LicenseExpiredException.php
│ ├── Providers
│ │ ├── AppServiceProvider.php
│ │ └── RouteServiceProvider.php
│ ├── Routes
│ │ └── ApiV1.php
│ ├── Migrations
│ │ ├── 2024_06_07_020313_create_failed_jobs_table.php
│ │ ├── 2024_06_07_020305_create_jobs_table.php
│ │ ├── 2024_06_07_064602_create_cache_table.php
│ │ └── 2024_06_07_054155_create_files_table.php
│ ├── Actions
│ │ ├── GetFiles.php
│ │ ├── DownloadFile.php
│ │ └── UploadFile.php
│ ├── Clients
│ │ ├── LicenseClient.php
│ │ └── S3Client.php
│ ├── Jobs
│ │ └── CreateNewBucket.php
│ ├── Middlewares
│ │ └── EnsureTokenIsValid.php
│ ├── Commands
│ │ └── ConsumeMessages.php
│ ├── Models
│ │ ├── Limits.php
│ │ └── File.php
│ ├── Resources
│ │ └── FileResource.php
│ └── Handlers
│ │ └── KafkaMessageHandler.php
├── .editorconfig
├── .gitignore
├── artisan
├── config
│ ├── services.php
│ └── kafka.php
├── composer.json
├── .env.example
└── Dockerfile
├── notification
├── database
│ └── .gitignore
├── docker
│ ├── php.ini
│ ├── start-container
│ └── supervisord.conf
├── storage
│ ├── logs
│ │ └── .gitignore
│ ├── app
│ │ ├── public
│ │ │ └── .gitignore
│ │ └── .gitignore
│ └── framework
│ │ ├── sessions
│ │ └── .gitignore
│ │ ├── testing
│ │ └── .gitignore
│ │ ├── views
│ │ └── .gitignore
│ │ ├── cache
│ │ ├── data
│ │ │ └── .gitignore
│ │ └── .gitignore
│ │ └── .gitignore
├── bootstrap
│ ├── cache
│ │ └── .gitignore
│ ├── providers.php
│ └── app.php
├── views
│ └── emails
│ │ ├── welcome.blade.php
│ │ └── welcome-plaintext.blade.php
├── .gitattributes
├── config
│ ├── mail.php
│ └── kafka.php
├── .editorconfig
├── .gitignore
├── artisan
├── src
│ ├── Providers
│ │ └── AppServiceProvider.php
│ ├── Handlers
│ │ └── KafkaMessageHandler.php
│ ├── Jobs
│ │ └── SendWelcomeEmail.php
│ ├── Migrations
│ │ ├── 2024_06_07_013016_create_failed_jobs_table.php
│ │ └── 2024_06_07_012217_create_jobs_table.php
│ ├── Commands
│ │ └── ConsumeMessages.php
│ └── Mail
│ │ └── WelcomeEmail.php
├── composer.json
├── .env.example
└── Dockerfile
├── .gitignore
├── .env.example
├── readme.md
└── docker-compose.yml
/secrets/.gitignore:
--------------------------------------------------------------------------------
1 | *.key
--------------------------------------------------------------------------------
/license/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/security/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/file-management/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/security/database/.gitignore:
--------------------------------------------------------------------------------
1 | *.sqlite*
2 |
--------------------------------------------------------------------------------
/notification/database/.gitignore:
--------------------------------------------------------------------------------
1 | *.sqlite*
2 |
--------------------------------------------------------------------------------
/file-management/database/.gitignore:
--------------------------------------------------------------------------------
1 | *.sqlite*
2 |
--------------------------------------------------------------------------------
/license/storage/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/notification/docker/php.ini:
--------------------------------------------------------------------------------
1 | extension=rdkafka.so
2 |
--------------------------------------------------------------------------------
/license/bootstrap/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/license/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/notification/storage/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/security/bootstrap/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/security/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/security/storage/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/file-management/storage/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/license/storage/app/public/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/notification/bootstrap/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/security/storage/app/public/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/file-management/bootstrap/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/file-management/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/file-management/storage/app/public/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/license/storage/app/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !public/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/license/storage/framework/sessions/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/license/storage/framework/testing/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/license/storage/framework/views/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/notification/storage/app/public/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/security/storage/app/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !public/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/security/storage/framework/sessions/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/security/storage/framework/testing/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/security/storage/framework/views/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/file-management/storage/app/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !public/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/file-management/storage/framework/views/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/license/storage/framework/cache/data/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/notification/storage/app/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !public/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/notification/storage/framework/sessions/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/notification/storage/framework/testing/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/notification/storage/framework/views/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/security/storage/framework/cache/data/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/file-management/storage/framework/cache/data/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/file-management/storage/framework/sessions/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/file-management/storage/framework/testing/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/license/storage/framework/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !data/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/notification/storage/framework/cache/data/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/security/storage/framework/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !data/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/file-management/storage/framework/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !data/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/notification/storage/framework/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !data/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/notification/views/emails/welcome.blade.php:
--------------------------------------------------------------------------------
1 |
Welcome to our application!
2 |
--------------------------------------------------------------------------------
/notification/views/emails/welcome-plaintext.blade.php:
--------------------------------------------------------------------------------
1 | Welcome to our application!
2 |
--------------------------------------------------------------------------------
/license/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
3 | *.md diff=markdown
4 | *.php diff=php
5 |
--------------------------------------------------------------------------------
/security/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
3 | *.md diff=markdown
4 | *.php diff=php
5 |
--------------------------------------------------------------------------------
/notification/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
3 | *.md diff=markdown
4 | *.php diff=php
5 |
--------------------------------------------------------------------------------
/file-management/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
3 | *.md diff=markdown
4 | *.php diff=php
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Project
2 | /.env
3 | /.env.backup
4 | /.env.production
5 |
6 | ### Intellij
7 | /.idea
8 |
--------------------------------------------------------------------------------
/license/bootstrap/providers.php:
--------------------------------------------------------------------------------
1 | [
5 | 'address' => env('MAIL_NO_REPLY_ADDRESS', 'no-reply@example.com'),
6 | 'name' => env('MAIL_NO_REPLY_NAME', 'No Reply'),
7 | ],
8 | ];
9 |
--------------------------------------------------------------------------------
/file-management/src/Exceptions/DailyLimitException.php:
--------------------------------------------------------------------------------
1 | handleCommand(new ArgvInput);
14 |
15 | exit($status);
16 |
--------------------------------------------------------------------------------
/license/src/Providers/DatabaseServiceProvider.php:
--------------------------------------------------------------------------------
1 | loadMigrationsFrom(join_paths(dirname(__DIR__), 'Migrations'));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/security/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | handleCommand(new ArgvInput);
14 |
15 | exit($status);
16 |
--------------------------------------------------------------------------------
/notification/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | handleCommand(new ArgvInput);
14 |
15 | exit($status);
16 |
--------------------------------------------------------------------------------
/file-management/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | handleCommand(new ArgvInput);
14 |
15 | exit($status);
16 |
--------------------------------------------------------------------------------
/notification/src/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | loadViewsFrom($this->app->basePath('views'), "notification");
12 | $this->loadMigrationsFrom($this->app->path('Migrations'));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/file-management/config/services.php:
--------------------------------------------------------------------------------
1 | [
5 | 'endpoint' => env('MINIO_ENDPOINT', 'http://127.0.0.1:9000'),
6 | 'access_key' => env('MINIO_ACCESS_KEY', 'mservice'),
7 | 'secret_key' => env('MINIO_SECRET_KEY', 'topsecret'),
8 | ],
9 |
10 | 'license' => [
11 | 'base_uri' => env('LICENSE_SERVICE_URL', 'http://license/api/v1/'),
12 | 'timeout' => env('LICENSE_SERVICE_TIMEOUT', 30),
13 | ]
14 | ];
15 |
--------------------------------------------------------------------------------
/security/src/Models/User.php:
--------------------------------------------------------------------------------
1 | make(Router::class);
10 | } catch (BindingResolutionException $e) {
11 | abort(500, $e->getMessage());
12 | }
13 |
14 | $route->group(['prefix' => 'users', 'as' => 'user.'], function (Router $route) {
15 | $route->post('/', CreateNewUser::class);
16 | });
17 |
--------------------------------------------------------------------------------
/license/public/index.php:
--------------------------------------------------------------------------------
1 | handleRequest(Request::capture());
18 |
--------------------------------------------------------------------------------
/security/public/index.php:
--------------------------------------------------------------------------------
1 | handleRequest(Request::capture());
18 |
--------------------------------------------------------------------------------
/file-management/public/index.php:
--------------------------------------------------------------------------------
1 | handleRequest(Request::capture());
18 |
--------------------------------------------------------------------------------
/notification/src/Handlers/KafkaMessageHandler.php:
--------------------------------------------------------------------------------
1 | info("New Message!");
14 | $email = $message->getBody()['email'];
15 |
16 | SendWelcomeEmail::dispatch($email);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/license/src/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | loadMigrationsFrom($this->app->path('Migrations'));
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/license/src/Routes/ApiV1.php:
--------------------------------------------------------------------------------
1 | make(Router::class);
12 | } catch (BindingResolutionException $e) {
13 | abort(500, $e->getMessage());
14 | }
15 |
16 | $route->get('me', MeLicense::class)->middleware(EnsureTokenIsValid::class);
17 | // TODO: check `admin.license` scope to internal RestAPI
18 | $route->get('users/{user}', GetLicenseOfUser::class);
19 |
--------------------------------------------------------------------------------
/license/src/Actions/MeLicense.php:
--------------------------------------------------------------------------------
1 | handler($request->input('user_id'))
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/license/docker/start-container:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [ "$SUPERVISOR_PHP_USER" != "root" ] && [ "$SUPERVISOR_PHP_USER" != "app" ]; then
4 | echo "You should set SUPERVISOR_PHP_USER to either 'app' or 'root'."
5 | exit 1
6 | fi
7 |
8 | if [ ! -z "$WWWUSER" ]; then
9 | usermod -u $WWWUSER app
10 | fi
11 |
12 | if [ ! -d /.composer ]; then
13 | mkdir /.composer
14 | fi
15 |
16 | chmod -R ugo+rw /.composer
17 |
18 | if [ $# -gt 0 ]; then
19 | if [ "$SUPERVISOR_PHP_USER" = "root" ]; then
20 | exec "$@"
21 | else
22 | exec gosu $WWWUSER "$@"
23 | fi
24 | else
25 | exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
26 | fi
27 |
--------------------------------------------------------------------------------
/notification/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | withCommands([ConsumeMessages::class])
11 | ->withMiddleware(function (Middleware $middleware) {
12 | //
13 | })
14 | ->withExceptions(function (Exceptions $exceptions) {
15 | //
16 | })->create();
17 |
18 | $app->useAppPath(join_paths(dirname(__DIR__), 'src'));
19 |
20 | return $app;
21 |
--------------------------------------------------------------------------------
/security/docker/start-container:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [ "$SUPERVISOR_PHP_USER" != "root" ] && [ "$SUPERVISOR_PHP_USER" != "app" ]; then
4 | echo "You should set SUPERVISOR_PHP_USER to either 'app' or 'root'."
5 | exit 1
6 | fi
7 |
8 | if [ ! -z "$WWWUSER" ]; then
9 | usermod -u $WWWUSER app
10 | fi
11 |
12 | if [ ! -d /.composer ]; then
13 | mkdir /.composer
14 | fi
15 |
16 | chmod -R ugo+rw /.composer
17 |
18 | if [ $# -gt 0 ]; then
19 | if [ "$SUPERVISOR_PHP_USER" = "root" ]; then
20 | exec "$@"
21 | else
22 | exec gosu $WWWUSER "$@"
23 | fi
24 | else
25 | exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
26 | fi
27 |
--------------------------------------------------------------------------------
/security/docker/supervisord.conf:
--------------------------------------------------------------------------------
1 | [supervisord]
2 | nodaemon=true
3 | user=root
4 | logfile=/var/log/supervisor/supervisord.log
5 | pidfile=/var/run/supervisord.pid
6 |
7 | [program:php]
8 | command=%(ENV_SUPERVISOR_PHP_COMMAND)s
9 | user=%(ENV_SUPERVISOR_PHP_USER)s
10 | environment=APP_IN_DOCKER="1"
11 | stdout_logfile=/dev/stdout
12 | stdout_logfile_maxbytes=0
13 | stderr_logfile=/dev/stderr
14 | stderr_logfile_maxbytes=0
15 |
16 | [program:worker]
17 | command=%(ENV_SUPERVISOR_WORKER_COMMAND)s
18 | user=%(ENV_SUPERVISOR_PHP_USER)s
19 | environment=APP_IN_DOCKER="1"
20 | stdout_logfile=/dev/stdout
21 | stdout_logfile_maxbytes=0
22 | stderr_logfile=/dev/stderr
23 | stderr_logfile_maxbytes=0
24 |
--------------------------------------------------------------------------------
/notification/docker/start-container:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [ "$SUPERVISOR_PHP_USER" != "root" ] && [ "$SUPERVISOR_PHP_USER" != "app" ]; then
4 | echo "You should set SUPERVISOR_PHP_USER to either 'app' or 'root'."
5 | exit 1
6 | fi
7 |
8 | if [ ! -z "$WWWUSER" ]; then
9 | usermod -u $WWWUSER app
10 | fi
11 |
12 | if [ ! -d /.composer ]; then
13 | mkdir /.composer
14 | fi
15 |
16 | chmod -R ugo+rw /.composer
17 |
18 | if [ $# -gt 0 ]; then
19 | if [ "$SUPERVISOR_PHP_USER" = "root" ]; then
20 | exec "$@"
21 | else
22 | exec gosu $WWWUSER "$@"
23 | fi
24 | else
25 | exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
26 | fi
27 |
--------------------------------------------------------------------------------
/notification/docker/supervisord.conf:
--------------------------------------------------------------------------------
1 | [supervisord]
2 | nodaemon=true
3 | user=root
4 | logfile=/var/log/supervisor/supervisord.log
5 | pidfile=/var/run/supervisord.pid
6 |
7 | [program:php]
8 | command=%(ENV_SUPERVISOR_PHP_COMMAND)s
9 | user=%(ENV_SUPERVISOR_PHP_USER)s
10 | environment=APP_IN_DOCKER="1"
11 | stdout_logfile=/dev/stdout
12 | stdout_logfile_maxbytes=0
13 | stderr_logfile=/dev/stderr
14 | stderr_logfile_maxbytes=0
15 |
16 | [program:worker]
17 | command=%(ENV_SUPERVISOR_WORKER_COMMAND)s
18 | user=%(ENV_SUPERVISOR_PHP_USER)s
19 | environment=APP_IN_DOCKER="1"
20 | stdout_logfile=/dev/stdout
21 | stdout_logfile_maxbytes=0
22 | stderr_logfile=/dev/stderr
23 | stderr_logfile_maxbytes=0
24 |
--------------------------------------------------------------------------------
/file-management/docker/start-container:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [ "$SUPERVISOR_PHP_USER" != "root" ] && [ "$SUPERVISOR_PHP_USER" != "app" ]; then
4 | echo "You should set SUPERVISOR_PHP_USER to either 'app' or 'root'."
5 | exit 1
6 | fi
7 |
8 | if [ ! -z "$WWWUSER" ]; then
9 | usermod -u $WWWUSER app
10 | fi
11 |
12 | if [ ! -d /.composer ]; then
13 | mkdir /.composer
14 | fi
15 |
16 | chmod -R ugo+rw /.composer
17 |
18 | if [ $# -gt 0 ]; then
19 | if [ "$SUPERVISOR_PHP_USER" = "root" ]; then
20 | exec "$@"
21 | else
22 | exec gosu $WWWUSER "$@"
23 | fi
24 | else
25 | exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
26 | fi
27 |
--------------------------------------------------------------------------------
/license/public/.htaccess:
--------------------------------------------------------------------------------
1 |
2 |
3 | Options -MultiViews -Indexes
4 |
5 |
6 | RewriteEngine On
7 |
8 | # Handle Authorization Header
9 | RewriteCond %{HTTP:Authorization} .
10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
11 |
12 | # Redirect Trailing Slashes If Not A Folder...
13 | RewriteCond %{REQUEST_FILENAME} !-d
14 | RewriteCond %{REQUEST_URI} (.+)/$
15 | RewriteRule ^ %1 [L,R=301]
16 |
17 | # Send Requests To Front Controller...
18 | RewriteCond %{REQUEST_FILENAME} !-d
19 | RewriteCond %{REQUEST_FILENAME} !-f
20 | RewriteRule ^ index.php [L]
21 |
22 |
--------------------------------------------------------------------------------
/security/public/.htaccess:
--------------------------------------------------------------------------------
1 |
2 |
3 | Options -MultiViews -Indexes
4 |
5 |
6 | RewriteEngine On
7 |
8 | # Handle Authorization Header
9 | RewriteCond %{HTTP:Authorization} .
10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
11 |
12 | # Redirect Trailing Slashes If Not A Folder...
13 | RewriteCond %{REQUEST_FILENAME} !-d
14 | RewriteCond %{REQUEST_URI} (.+)/$
15 | RewriteRule ^ %1 [L,R=301]
16 |
17 | # Send Requests To Front Controller...
18 | RewriteCond %{REQUEST_FILENAME} !-d
19 | RewriteCond %{REQUEST_FILENAME} !-f
20 | RewriteRule ^ index.php [L]
21 |
22 |
--------------------------------------------------------------------------------
/file-management/public/.htaccess:
--------------------------------------------------------------------------------
1 |
2 |
3 | Options -MultiViews -Indexes
4 |
5 |
6 | RewriteEngine On
7 |
8 | # Handle Authorization Header
9 | RewriteCond %{HTTP:Authorization} .
10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
11 |
12 | # Redirect Trailing Slashes If Not A Folder...
13 | RewriteCond %{REQUEST_FILENAME} !-d
14 | RewriteCond %{REQUEST_URI} (.+)/$
15 | RewriteRule ^ %1 [L,R=301]
16 |
17 | # Send Requests To Front Controller...
18 | RewriteCond %{REQUEST_FILENAME} !-d
19 | RewriteCond %{REQUEST_FILENAME} !-f
20 | RewriteRule ^ index.php [L]
21 |
22 |
--------------------------------------------------------------------------------
/license/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | withCommands([ConsumeMessages::class])
11 | ->withRouting(
12 | health: '/up',
13 | )
14 | ->withMiddleware(function (Middleware $middleware) {
15 | //
16 | })
17 | ->withExceptions(function (Exceptions $exceptions) {
18 | //
19 | })->create();
20 |
21 | $app->useAppPath(join_paths(dirname(__DIR__), 'src'));
22 |
23 | return $app;
24 |
--------------------------------------------------------------------------------
/file-management/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | withCommands([ConsumeMessages::class])
11 | ->withRouting(
12 | health: '/up',
13 | )
14 | ->withMiddleware(function (Middleware $middleware) {
15 | //
16 | })
17 | ->withExceptions(function (Exceptions $exceptions) {
18 | //
19 | })->create();
20 |
21 | $app->useAppPath(join_paths(dirname(__DIR__), 'src'));
22 |
23 | return $app;
24 |
--------------------------------------------------------------------------------
/file-management/src/Routes/ApiV1.php:
--------------------------------------------------------------------------------
1 | make(Router::class);
12 | } catch (BindingResolutionException $e) {
13 | abort(500, $e->getMessage());
14 | }
15 |
16 | $route->group(['prefix' => 'files', 'as' => 'file.'], function (Router $route) {
17 | $route->get('/', GetFiles::class)->name('index');
18 | $route->post('/', UploadFile::class)->name('store');
19 | $route->get('{file}/download', DownloadFile::class)->name('download');
20 | });
21 |
--------------------------------------------------------------------------------
/security/database/migrations/2024_06_04_130737_create_oauth_personal_access_clients_table.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
16 | $table->unsignedBigInteger('client_id');
17 | $table->timestamps();
18 | });
19 | }
20 |
21 | /**
22 | * Reverse the migrations.
23 | */
24 | public function down(): void
25 | {
26 | Schema::dropIfExists('oauth_personal_access_clients');
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/license/src/Actions/GetLicenseOfUser.php:
--------------------------------------------------------------------------------
1 | licenseModel->query()->forUser($userId)->notExpired()->first();
21 | }
22 |
23 | public function asController(ActionRequest $request, int $userId): LicenseResource
24 | {
25 | // TODO: check `admin.license` scope to internal RestAPI
26 | return new LicenseResource(
27 | $this->handler($userId)
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/notification/src/Jobs/SendWelcomeEmail.php:
--------------------------------------------------------------------------------
1 | email)->send(new WelcomeEmail());
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/security/database/migrations/2024_06_04_130738_create_oauth_refresh_tokens_table.php:
--------------------------------------------------------------------------------
1 | string('id', 100)->primary();
16 | $table->string('access_token_id', 100)->index();
17 | $table->boolean('revoked');
18 | $table->dateTime('expires_at')->nullable();
19 | });
20 | }
21 |
22 | /**
23 | * Reverse the migrations.
24 | */
25 | public function down(): void
26 | {
27 | Schema::dropIfExists('oauth_refresh_tokens');
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/security/src/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->make(Repository::class);
30 | $config->set('kafka.brokers', env('KAFKA_BROKERS'));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/license/src/Models/License.php:
--------------------------------------------------------------------------------
1 | LicenseType::class,
21 | ];
22 | }
23 |
24 | public function scopeForUser(Builder $query, string $user): Builder
25 | {
26 | return $query->where('user_id', $user);
27 | }
28 |
29 | public function scopeNotExpired(Builder $query): Builder
30 | {
31 | return $query->where('expired_at', '>=', now());
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/security/src/Actions/PublishUserCreatedMessage.php:
--------------------------------------------------------------------------------
1 | onTopic('user.created')
21 | ->withHeaders([
22 | 'user_id' => $userId,
23 | ])
24 | ->withBodyKey('user_id', $userId)
25 | ->withBodyKey('email', $email)
26 | ->send();
27 | }
28 |
29 | /**
30 | * @throws Exception
31 | */
32 | public function asJob(User $user): void
33 | {
34 | $this->handle($user->getKey(), $user->getAttribute('email'));
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/license/src/Migrations/2024_06_07_021508_create_failed_jobs_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->string('uuid')->unique();
17 | $table->text('connection');
18 | $table->text('queue');
19 | $table->longText('payload');
20 | $table->longText('exception');
21 | $table->timestamp('failed_at')->useCurrent();
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | */
28 | public function down(): void
29 | {
30 | Schema::dropIfExists('failed_jobs');
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/file-management/docker/supervisord.conf:
--------------------------------------------------------------------------------
1 | [supervisord]
2 | nodaemon=true
3 | user=root
4 | logfile=/var/log/supervisor/supervisord.log
5 | pidfile=/var/run/supervisord.pid
6 |
7 | [program:php]
8 | command=%(ENV_SUPERVISOR_PHP_COMMAND)s
9 | user=%(ENV_SUPERVISOR_PHP_USER)s
10 | environment=APP_IN_DOCKER="1"
11 | stdout_logfile=/dev/stdout
12 | stdout_logfile_maxbytes=0
13 | stderr_logfile=/dev/stderr
14 | stderr_logfile_maxbytes=0
15 |
16 | [program:worker]
17 | command=%(ENV_SUPERVISOR_WORKER_COMMAND)s
18 | user=%(ENV_SUPERVISOR_PHP_USER)s
19 | environment=APP_IN_DOCKER="1"
20 | stdout_logfile=/dev/stdout
21 | stdout_logfile_maxbytes=0
22 | stderr_logfile=/dev/stderr
23 | stderr_logfile_maxbytes=0
24 |
25 | [program:consumer]
26 | command=%(ENV_SUPERVISOR_CONSUMER_COMMAND)s
27 | user=%(ENV_SUPERVISOR_PHP_USER)s
28 | environment=APP_IN_DOCKER="1"
29 | stdout_logfile=/dev/stdout
30 | stdout_logfile_maxbytes=0
31 | stderr_logfile=/dev/stderr
32 | stderr_logfile_maxbytes=0
33 |
--------------------------------------------------------------------------------
/license/docker/supervisord.conf:
--------------------------------------------------------------------------------
1 | [supervisord]
2 | nodaemon=true
3 | user=root
4 | logfile=/var/log/supervisor/supervisord.log
5 | pidfile=/var/run/supervisord.pid
6 |
7 | [program:php]
8 | command=%(ENV_SUPERVISOR_PHP_COMMAND)s
9 | user=%(ENV_SUPERVISOR_PHP_USER)s
10 | environment=APP_IN_DOCKER="1"
11 | stdout_logfile=/dev/stdout
12 | stdout_logfile_maxbytes=0
13 | stderr_logfile=/dev/stderr
14 | stderr_logfile_maxbytes=0
15 |
16 | [program:worker]
17 | command=%(ENV_SUPERVISOR_WORKER_COMMAND)s
18 | user=%(ENV_SUPERVISOR_PHP_USER)s
19 | environment=APP_IN_DOCKER="1"
20 | stdout_logfile=/dev/stdout
21 | stdout_logfile_maxbytes=0
22 | stderr_logfile=/dev/stderr
23 | stderr_logfile_maxbytes=0
24 |
25 | [program:kafkaconsumer]
26 | command=%(ENV_SUPERVISOR_KAFKA_CONSUMER_COMMAND)s
27 | user=%(ENV_SUPERVISOR_PHP_USER)s
28 | environment=APP_IN_DOCKER="1"
29 | stdout_logfile=/dev/stdout
30 | stdout_logfile_maxbytes=0
31 | stderr_logfile=/dev/stderr
32 | stderr_logfile_maxbytes=0
33 |
--------------------------------------------------------------------------------
/notification/src/Migrations/2024_06_07_013016_create_failed_jobs_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->string('uuid')->unique();
17 | $table->text('connection');
18 | $table->text('queue');
19 | $table->longText('payload');
20 | $table->longText('exception');
21 | $table->timestamp('failed_at')->useCurrent();
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | */
28 | public function down(): void
29 | {
30 | Schema::dropIfExists('failed_jobs');
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/file-management/src/Migrations/2024_06_07_020313_create_failed_jobs_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->string('uuid')->unique();
17 | $table->text('connection');
18 | $table->text('queue');
19 | $table->longText('payload');
20 | $table->longText('exception');
21 | $table->timestamp('failed_at')->useCurrent();
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | */
28 | public function down(): void
29 | {
30 | Schema::dropIfExists('failed_jobs');
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/file-management/src/Actions/GetFiles.php:
--------------------------------------------------------------------------------
1 | fileModel->newQuery()->ofUser($userId)->latest()->paginate(20);
23 | }
24 |
25 | public function asController(ActionRequest $request): AnonymousResourceCollection
26 | {
27 | $files = $this->handle($request->input('user_id'));
28 |
29 | return FileResource::collection($files);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/file-management/src/Clients/LicenseClient.php:
--------------------------------------------------------------------------------
1 | client = new Client([
19 | 'base_uri' => config('services.license.base_uri'),
20 | 'timeout' => config('services.license.timeout', 30),
21 | ]);
22 | }
23 |
24 | /**
25 | * Pass dynamic methods onto the router instance.
26 | *
27 | * @param string $method
28 | * @param array $parameters
29 | * @return mixed
30 | */
31 | public function __call(string $method, array $parameters)
32 | {
33 | return $this->forwardCallTo(
34 | $this->client, $method, $parameters
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/security/database/migrations/2024_06_04_130741_create_oauth_auth_codes_table.php:
--------------------------------------------------------------------------------
1 | string('id', 100)->primary();
16 | $table->unsignedBigInteger('user_id')->index();
17 | $table->unsignedBigInteger('client_id');
18 | $table->text('scopes')->nullable();
19 | $table->boolean('revoked');
20 | $table->dateTime('expires_at')->nullable();
21 | });
22 | }
23 |
24 | /**
25 | * Reverse the migrations.
26 | */
27 | public function down(): void
28 | {
29 | Schema::dropIfExists('oauth_auth_codes');
30 | }
31 | };
32 |
--------------------------------------------------------------------------------
/license/src/Migrations/2024_06_07_021519_create_jobs_table.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
16 | $table->string('queue')->index();
17 | $table->longText('payload');
18 | $table->unsignedTinyInteger('attempts');
19 | $table->unsignedInteger('reserved_at')->nullable();
20 | $table->unsignedInteger('available_at');
21 | $table->unsignedInteger('created_at');
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | */
28 | public function down(): void
29 | {
30 | Schema::dropIfExists('jobs');
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/notification/src/Migrations/2024_06_07_012217_create_jobs_table.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
16 | $table->string('queue')->index();
17 | $table->longText('payload');
18 | $table->unsignedTinyInteger('attempts');
19 | $table->unsignedInteger('reserved_at')->nullable();
20 | $table->unsignedInteger('available_at');
21 | $table->unsignedInteger('created_at');
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | */
28 | public function down(): void
29 | {
30 | Schema::dropIfExists('jobs');
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/file-management/src/Migrations/2024_06_07_020305_create_jobs_table.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
16 | $table->string('queue')->index();
17 | $table->longText('payload');
18 | $table->unsignedTinyInteger('attempts');
19 | $table->unsignedInteger('reserved_at')->nullable();
20 | $table->unsignedInteger('available_at');
21 | $table->unsignedInteger('created_at');
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | */
28 | public function down(): void
29 | {
30 | Schema::dropIfExists('jobs');
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/license/src/Resources/LicenseResource.php:
--------------------------------------------------------------------------------
1 |
18 | */
19 | public function toArray(Request $request): array
20 | {
21 | return [
22 | 'type' => $this->resource->getAttribute('license_type'),
23 | 'started_at' => $this->resource->getAttribute('started_at'),
24 | 'expired_at' => $this->resource->getAttribute('expired_at'),
25 | 'max_file_size' => $this->resource->getAttribute('max_file_size'),
26 | 'daily_object_limit' => $this->resource->getAttribute('daily_object_limit'),
27 | 'quota' => $this->resource->getAttribute('quota'),
28 | ];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/file-management/src/Migrations/2024_06_07_064602_create_cache_table.php:
--------------------------------------------------------------------------------
1 | string('key')->primary();
16 | $table->mediumText('value');
17 | $table->integer('expiration');
18 | });
19 |
20 | Schema::create('cache_locks', function (Blueprint $table) {
21 | $table->string('key')->primary();
22 | $table->string('owner');
23 | $table->integer('expiration');
24 | });
25 | }
26 |
27 | /**
28 | * Reverse the migrations.
29 | */
30 | public function down(): void
31 | {
32 | Schema::dropIfExists('cache');
33 | Schema::dropIfExists('cache_locks');
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/security/database/migrations/0001_01_01_000001_create_cache_table.php:
--------------------------------------------------------------------------------
1 | string('key')->primary();
16 | $table->mediumText('value');
17 | $table->integer('expiration');
18 | });
19 |
20 | Schema::create('cache_locks', function (Blueprint $table) {
21 | $table->string('key')->primary();
22 | $table->string('owner');
23 | $table->integer('expiration');
24 | });
25 | }
26 |
27 | /**
28 | * Reverse the migrations.
29 | */
30 | public function down(): void
31 | {
32 | Schema::dropIfExists('cache');
33 | Schema::dropIfExists('cache_locks');
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/license/src/Middlewares/EnsureTokenIsValid.php:
--------------------------------------------------------------------------------
1 | bearerToken();
22 |
23 | if (is_null($jwt)) {
24 | return response(status: 403);
25 | }
26 |
27 | $pKey = file_get_contents("/tmp/secrets/oauth-public.key");
28 |
29 | try {
30 | $decoded = JWT::decode($jwt, new Key($pKey, 'RS256'));
31 | } catch (Exception) {
32 | return response(status: 403);
33 | }
34 |
35 | $request->merge(['user_id' => $decoded->sub]);
36 |
37 | return $next($request);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/file-management/src/Jobs/CreateNewBucket.php:
--------------------------------------------------------------------------------
1 | createBucket([
31 | 'Bucket' => $this->bucketName,
32 | ]);
33 |
34 | logger()->info("Bucket $this->bucketName for user $this->userId created.");
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/file-management/src/Middlewares/EnsureTokenIsValid.php:
--------------------------------------------------------------------------------
1 | bearerToken();
22 |
23 | if (is_null($jwt)) {
24 | return response(status: 403);
25 | }
26 |
27 | $pKey = file_get_contents("/tmp/secrets/oauth-public.key");
28 |
29 | try {
30 | $decoded = JWT::decode($jwt, new Key($pKey, 'RS256'));
31 | } catch (Exception) {
32 | return response(status: 403);
33 | }
34 |
35 | $request->merge(['user_id' => $decoded->sub]);
36 |
37 | return $next($request);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/license/src/Models/LicenseType.php:
--------------------------------------------------------------------------------
1 | 100 * 1024 * 1024,
15 | LicenseType::STARTER => 1024 * 1024 * 1024,
16 | LicenseType::ULTIMATE => 64 * 1024 * 1024 * 1024,
17 | };
18 | }
19 |
20 | public function getDailyObjectLimit(): int
21 | {
22 | return match($this) {
23 | LicenseType::DEMO => 5,
24 | LicenseType::STARTER => 50,
25 | LicenseType::ULTIMATE => 300,
26 | };
27 | }
28 |
29 | public function getQuota(): int
30 | {
31 | return match($this) {
32 | LicenseType::DEMO => 1024 * 1024 * 1024,
33 | LicenseType::STARTER => 30 * 1024 * 1024 * 1024,
34 | LicenseType::ULTIMATE => 1024 * 1024 * 1024 * 1024,
35 | };
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/security/database/migrations/2024_06_04_130740_create_oauth_access_tokens_table.php:
--------------------------------------------------------------------------------
1 | string('id', 100)->primary();
16 | $table->unsignedBigInteger('user_id')->nullable()->index();
17 | $table->unsignedBigInteger('client_id');
18 | $table->string('name')->nullable();
19 | $table->text('scopes')->nullable();
20 | $table->boolean('revoked');
21 | $table->timestamps();
22 | $table->dateTime('expires_at')->nullable();
23 | });
24 | }
25 |
26 | /**
27 | * Reverse the migrations.
28 | */
29 | public function down(): void
30 | {
31 | Schema::dropIfExists('oauth_access_tokens');
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/security/database/migrations/2024_06_04_130739_create_oauth_clients_table.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
16 | $table->unsignedBigInteger('user_id')->nullable()->index();
17 | $table->string('name');
18 | $table->string('secret', 100)->nullable();
19 | $table->string('provider')->nullable();
20 | $table->text('redirect');
21 | $table->boolean('personal_access_client');
22 | $table->boolean('password_client');
23 | $table->boolean('revoked');
24 | $table->timestamps();
25 | });
26 | }
27 |
28 | /**
29 | * Reverse the migrations.
30 | */
31 | public function down(): void
32 | {
33 | Schema::dropIfExists('oauth_clients');
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/license/src/Commands/ConsumeMessages.php:
--------------------------------------------------------------------------------
1 | subscribe('user.created')
39 | ->withHandler(new KafkaMessageHandler)
40 | ->build()
41 | ->consume();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/file-management/src/Commands/ConsumeMessages.php:
--------------------------------------------------------------------------------
1 | subscribe('license.updated')
39 | ->withHandler(new KafkaMessageHandler)
40 | ->build()
41 | ->consume();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/file-management/src/Clients/S3Client.php:
--------------------------------------------------------------------------------
1 | client = new Client([
21 | 'region' => 'us-east-1',
22 | 'endpoint' => config('services.minio.endpoint'),
23 | 'use_path_style_endpoint' => true,
24 | 'credentials' => [
25 | 'key' => config('services.minio.access_key'),
26 | 'secret' => config('services.minio.secret_key'),
27 | ],
28 | ]);
29 | }
30 |
31 | /**
32 | * Pass dynamic methods onto the router instance.
33 | *
34 | * @param string $method
35 | * @param array $parameters
36 | * @return mixed
37 | */
38 | public function __call(string $method, array $parameters)
39 | {
40 | return $this->forwardCallTo(
41 | $this->client, $method, $parameters
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/file-management/src/Models/Limits.php:
--------------------------------------------------------------------------------
1 | get("users/$userId");
26 |
27 | if ($response->getStatusCode() != 200) {
28 | throw new RuntimeException('User license information could not retrieve!');
29 | }
30 |
31 | $license = collect(json_decode($response->getBody())->data);
32 |
33 | return new Limits(
34 | $license->get('quota'),
35 | $license->get('max_file_size'),
36 | $license->get('daily_object_limit'),
37 | now()->gte($license->get('expired_at')),
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/notification/src/Commands/ConsumeMessages.php:
--------------------------------------------------------------------------------
1 | info("Listening kafka messages...");
36 |
37 | // TODO: add parallelism or use minimal worker
38 | Kafka::consumer()
39 | ->subscribe('user.created')
40 | ->withHandler(new KafkaMessageHandler)
41 | ->build()
42 | ->consume();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/license/src/Migrations/2024_06_06_135026_create_licenses_table.php:
--------------------------------------------------------------------------------
1 | id();
17 |
18 | $table->unsignedBigInteger('user_id');
19 | $table->enum('license_type', ['DEMO', 'STARTER', 'ULTIMATE']);
20 | $table->timestamp('started_at')->default(DB::raw('NOW()'));
21 | $table->timestamp('expired_at');
22 | $table->unsignedBigInteger('max_file_size');
23 | $table->unsignedBigInteger('daily_object_limit');
24 | $table->unsignedBigInteger('quota');
25 | $table->string('email')->nullable();
26 |
27 | $table->timestamps();
28 | });
29 | }
30 |
31 | /**
32 | * Reverse the migrations.
33 | */
34 | public function down(): void
35 | {
36 | Schema::dropIfExists('licenses');
37 | }
38 | };
39 |
--------------------------------------------------------------------------------
/file-management/src/Models/File.php:
--------------------------------------------------------------------------------
1 | 'boolean',
21 | ];
22 |
23 | public $incrementing = false;
24 |
25 | public $keyType = 'string';
26 |
27 | protected static function boot(): void
28 | {
29 | parent::boot();
30 |
31 | static::creating(function ($file) {
32 | if (empty($file->id)) {
33 | $file->id = Str::uuid()->toString();
34 | }
35 | });
36 | }
37 |
38 | public function scopeOfUser(Builder $query, int $userId): Builder
39 | {
40 | return $query->where('user_id', $userId);
41 | }
42 |
43 | public function scopeIn24h(Builder $query): Builder
44 | {
45 | return $query->where('created_at', '>=', now()->subDay());
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/file-management/src/Resources/FileResource.php:
--------------------------------------------------------------------------------
1 |
18 | */
19 | public function toArray(Request $request): array
20 | {
21 | return [
22 | 'id' => $this->resource->getKey(),
23 | 'filename' => $this->resource->getAttribute('filename'),
24 | 'description' => $this->resource->getAttribute('description'),
25 | 'is_public' => $this->resource->getAttribute('is_public'),
26 | 'checksum' => $this->resource->getAttribute('checksum'),
27 | 'mimetype' => $this->resource->getAttribute('mimetype'),
28 | 'size' => $this->resource->getAttribute('size'),
29 | 'metadata' => $this->resource->getAttribute('metadata'),
30 | 'expired_at' => $this->resource->getAttribute('expired_at'),
31 | 'created_at' => $this->resource->getAttribute('created_at'),
32 | ];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/security/src/Providers/AuthServiceProvider.php:
--------------------------------------------------------------------------------
1 | addHour());
22 | Passport::refreshTokensExpireIn(now()->addWeek());
23 | Passport::personalAccessTokensExpireIn(now()->addMonths(3));
24 |
25 | /** @var ConfigRepository $config */
26 | $config = $this->app->make(ConfigRepository::class);
27 | $config->set('auth.providers.users', [
28 | 'driver' => 'eloquent',
29 | 'model' => User::class,
30 | ]);
31 | $config->set('auth.guards.api', [
32 | 'driver' => 'passport',
33 | 'provider' => 'users',
34 | ]);
35 | }
36 |
37 | public function register(): void
38 | {
39 |
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/notification/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "erayaydin/mservices-notification",
3 | "type": "project",
4 | "description": "Notification Service",
5 | "keywords": ["notification"],
6 | "license": "MIT",
7 | "require": {
8 | "php": "^8.2",
9 | "laravel/framework": "^11.9",
10 | "laravel/tinker": "^2.9",
11 | "mateusjunges/laravel-kafka": "^2.1"
12 | },
13 | "require-dev": {
14 | "laravel/pint": "^1.13"
15 | },
16 | "autoload": {
17 | "psr-4": {
18 | "MService\\Notification\\": "src/"
19 | }
20 | },
21 | "scripts": {
22 | "post-autoload-dump": [
23 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
24 | "@php artisan package:discover --ansi"
25 | ],
26 | "post-root-package-install": [
27 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
28 | ],
29 | "post-create-project-cmd": [
30 | "@php artisan key:generate --ansi"
31 | ]
32 | },
33 | "extra": {
34 | "laravel": {
35 | "dont-discover": []
36 | }
37 | },
38 | "config": {
39 | "optimize-autoloader": true,
40 | "preferred-install": "dist",
41 | "sort-packages": true,
42 | "allow-plugins": {
43 | "pestphp/pest-plugin": true,
44 | "php-http/discovery": true
45 | }
46 | },
47 | "minimum-stability": "stable",
48 | "prefer-stable": true
49 | }
50 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | ZOOKEEPER_IMAGE=confluentinc/cp-zookeeper
2 | ZOOKEEPER_IMAGE_VERSION=7.6.1
3 |
4 | ZOONAVIGATOR_VERSION=1.1.2
5 |
6 | KAFKA_IMAGE=confluentinc/cp-kafka
7 | KAFKA_IMAGE_VERSION=7.6.1
8 |
9 | SCHEMA_REGISTRY_IMAGE=confluentinc/cp-schema-registry
10 | SCHEMA_REGISTRY_IMAGE_VERSION=7.6.1
11 |
12 | KAFKA_CONNECT_IMAGE=confluentinc/cp-kafka-connect
13 | KAFKA_CONNECT_IMAGE_VERSION=7.6.1
14 |
15 | MINIO_IMAGE_VERSION=RELEASE.2024-06-04T19-20-08Z.fips
16 | MINIO_USER=
17 | MINIO_PASSWORD=
18 |
19 | WWWUSER=1000
20 | WWWGROUP=1000
21 |
22 | APP_SECURITY_DB_IMAGE_VERSION=15
23 | APP_SECURITY_PORT=8082
24 | APP_SECURITY_XDEBUG_MODE=off
25 | APP_SECURITY_XDEBUG_CONFIG=-client_host=host.docker.internal
26 | APP_SECURITY_DB_PORT=5432
27 |
28 | APP_LICENSE_DB_IMAGE_VERSION=15
29 | APP_LICENSE_PORT=8083
30 | APP_LICENSE_XDEBUG_MODE=off
31 | APP_LICENSE_XDEBUG_CONFIG=-client_host=host.docker.internal
32 | APP_LICENSE_DB_PORT=5433
33 |
34 | APP_FILEMANAGEMENT_DB_IMAGE_VERSION=15
35 | APP_FILEMANAGEMENT_PORT=8084
36 | APP_FILEMANAGEMENT_XDEBUG_MODE=off
37 | APP_FILEMANAGEMENT_XDEBUG_CONFIG=-client_host=host.docker.internal
38 | APP_FILEMANAGEMENT_DB_PORT=5434
39 |
40 | APP_NOTIFICATION_DB_IMAGE_VERSION=15
41 | APP_NOTIFICATION_PORT=8085
42 | APP_NOTIFICATION_XDEBUG_MODE=off
43 | APP_NOTIFICATION_XDEBUG_CONFIG=-client_host=host.docker.internal
44 | APP_NOTIFICATION_DB_PORT=5435
45 |
--------------------------------------------------------------------------------
/notification/src/Mail/WelcomeEmail.php:
--------------------------------------------------------------------------------
1 |
45 | */
46 | public function attachments(): array
47 | {
48 | return [];
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/security/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME=Laravel
2 | APP_ENV=local
3 | APP_KEY=
4 | APP_DEBUG=true
5 | APP_TIMEZONE=UTC
6 | APP_URL=http://localhost
7 |
8 | APP_LOCALE=en
9 | APP_FALLBACK_LOCALE=en
10 | APP_FAKER_LOCALE=en_US
11 |
12 | APP_MAINTENANCE_DRIVER=file
13 | APP_MAINTENANCE_STORE=database
14 |
15 | BCRYPT_ROUNDS=12
16 |
17 | LOG_CHANNEL=stack
18 | LOG_STACK=single
19 | LOG_DEPRECATIONS_CHANNEL=null
20 | LOG_LEVEL=debug
21 |
22 | DB_CONNECTION=pgsql
23 | DB_HOST=127.0.0.1
24 | DB_PORT=5432
25 | DB_DATABASE=security
26 | DB_USERNAME=root
27 | DB_PASSWORD=
28 |
29 | SESSION_DRIVER=database
30 | SESSION_LIFETIME=120
31 | SESSION_ENCRYPT=false
32 | SESSION_PATH=/
33 | SESSION_DOMAIN=null
34 |
35 | KAFKA_BROKERS=
36 | BROADCAST_CONNECTION=log
37 | FILESYSTEM_DISK=local
38 | QUEUE_CONNECTION=database
39 |
40 | CACHE_STORE=database
41 | CACHE_PREFIX=
42 |
43 | MEMCACHED_HOST=127.0.0.1
44 |
45 | REDIS_CLIENT=phpredis
46 | REDIS_HOST=127.0.0.1
47 | REDIS_PASSWORD=null
48 | REDIS_PORT=6379
49 |
50 | MAIL_MAILER=log
51 | MAIL_HOST=127.0.0.1
52 | MAIL_PORT=2525
53 | MAIL_USERNAME=null
54 | MAIL_PASSWORD=null
55 | MAIL_ENCRYPTION=null
56 | MAIL_FROM_ADDRESS="hello@example.com"
57 | MAIL_FROM_NAME="${APP_NAME}"
58 |
59 | AWS_ACCESS_KEY_ID=
60 | AWS_SECRET_ACCESS_KEY=
61 | AWS_DEFAULT_REGION=us-east-1
62 | AWS_BUCKET=
63 | AWS_USE_PATH_STYLE_ENDPOINT=false
64 |
65 | VITE_APP_NAME="${APP_NAME}"
66 |
--------------------------------------------------------------------------------
/license/src/Handlers/KafkaMessageHandler.php:
--------------------------------------------------------------------------------
1 | getBody());
16 | $user = $body->get('user_id');
17 |
18 | if (License::query()->forUser($user)->notExpired()->count() > 0)
19 | return;
20 |
21 | $demoLicense = LicenseType::DEMO;
22 |
23 | $newLicense = License::query()->create([
24 | 'user_id' => $user,
25 | 'email' => $body->get('email'),
26 | 'started_at' => now(),
27 | 'expired_at' => now()->addDays(30),
28 | 'license_type' => $demoLicense,
29 | 'max_file_size' => $demoLicense->getMaxFileSize(),
30 | 'daily_object_limit' => $demoLicense->getDailyObjectLimit(),
31 | 'quota' => $demoLicense->getQuota(),
32 | ]);
33 |
34 | LicenseUpdated::dispatch($user, $newLicense);
35 |
36 | logger()->info(LicenseType::DEMO->name . " license created for user: " . $user);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/file-management/src/Actions/DownloadFile.php:
--------------------------------------------------------------------------------
1 | s3Client->getObject([
23 | 'Bucket' => $bucket,
24 | 'Key' => $fileKey,
25 | ]);
26 | }
27 |
28 | public function asController(ActionRequest $request, File $file): Response
29 | {
30 | $userId = $request->input('user_id');
31 | if ($file->getAttribute('user_id') != $userId)
32 | abort(403);
33 |
34 | $key = $file->getAttribute('key');
35 | $object = $this->handle("files-$userId", $key);
36 |
37 | return response($object['Body'], 200)
38 | ->header('Content-Type', $object['ContentType'])
39 | ->header(
40 | 'Content-Disposition',
41 | 'attachment; filename="' . ($file->getAttribute('filename') ?: $key) . '"'
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/license/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME=Laravel
2 | APP_ENV=local
3 | APP_KEY=
4 | APP_DEBUG=true
5 | APP_TIMEZONE=UTC
6 | APP_URL=http://localhost
7 |
8 | APP_LOCALE=en
9 | APP_FALLBACK_LOCALE=en
10 | APP_FAKER_LOCALE=en_US
11 |
12 | APP_MAINTENANCE_DRIVER=file
13 | APP_MAINTENANCE_STORE=database
14 |
15 | BCRYPT_ROUNDS=12
16 |
17 | LOG_CHANNEL=stack
18 | LOG_STACK=single
19 | LOG_DEPRECATIONS_CHANNEL=null
20 | LOG_LEVEL=debug
21 |
22 | DB_CONNECTION=pgsql
23 | DB_HOST=127.0.0.1
24 | DB_PORT=5432
25 | DB_DATABASE=license
26 | DB_USERNAME=root
27 | DB_PASSWORD=
28 |
29 | SESSION_DRIVER=database
30 | SESSION_LIFETIME=120
31 | SESSION_ENCRYPT=false
32 | SESSION_PATH=/
33 | SESSION_DOMAIN=null
34 |
35 | KAFKA_BROKERS=
36 | KAFKA_CONSUMER_GROUP_ID=license
37 | BROADCAST_CONNECTION=log
38 | FILESYSTEM_DISK=local
39 | QUEUE_CONNECTION=database
40 |
41 | CACHE_STORE=database
42 | CACHE_PREFIX=
43 |
44 | MEMCACHED_HOST=127.0.0.1
45 |
46 | REDIS_CLIENT=phpredis
47 | REDIS_HOST=127.0.0.1
48 | REDIS_PASSWORD=null
49 | REDIS_PORT=6379
50 |
51 | MAIL_MAILER=log
52 | MAIL_HOST=127.0.0.1
53 | MAIL_PORT=2525
54 | MAIL_USERNAME=null
55 | MAIL_PASSWORD=null
56 | MAIL_ENCRYPTION=null
57 | MAIL_FROM_ADDRESS="hello@example.com"
58 | MAIL_FROM_NAME="${APP_NAME}"
59 |
60 | AWS_ACCESS_KEY_ID=
61 | AWS_SECRET_ACCESS_KEY=
62 | AWS_DEFAULT_REGION=us-east-1
63 | AWS_BUCKET=
64 | AWS_USE_PATH_STYLE_ENDPOINT=false
65 |
66 | VITE_APP_NAME="${APP_NAME}"
67 |
--------------------------------------------------------------------------------
/license/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "erayaydin/mservices-license",
3 | "type": "project",
4 | "description": "License Service",
5 | "keywords": ["license"],
6 | "license": "MIT",
7 | "require": {
8 | "php": "^8.2",
9 | "firebase/php-jwt": "^6.10",
10 | "laravel/framework": "^11.9",
11 | "laravel/tinker": "^2.9",
12 | "lorisleiva/laravel-actions": "^2.8",
13 | "mateusjunges/laravel-kafka": "^2.1"
14 | },
15 | "require-dev": {
16 | "laravel/pint": "^1.13"
17 | },
18 | "autoload": {
19 | "psr-4": {
20 | "MService\\License\\": "src/"
21 | }
22 | },
23 | "scripts": {
24 | "post-autoload-dump": [
25 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
26 | "@php artisan package:discover --ansi"
27 | ],
28 | "post-root-package-install": [
29 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
30 | ],
31 | "post-create-project-cmd": [
32 | "@php artisan key:generate --ansi"
33 | ]
34 | },
35 | "extra": {
36 | "laravel": {
37 | "dont-discover": []
38 | }
39 | },
40 | "config": {
41 | "optimize-autoloader": true,
42 | "preferred-install": "dist",
43 | "sort-packages": true,
44 | "allow-plugins": {
45 | "pestphp/pest-plugin": true,
46 | "php-http/discovery": true
47 | }
48 | },
49 | "minimum-stability": "stable",
50 | "prefer-stable": true
51 | }
52 |
--------------------------------------------------------------------------------
/file-management/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "erayaydin/mservices-file-management",
3 | "type": "project",
4 | "description": "File Management Service",
5 | "keywords": ["filemanagement"],
6 | "license": "MIT",
7 | "require": {
8 | "php": "^8.2",
9 | "aws/aws-sdk-php": "^3.311",
10 | "firebase/php-jwt": "^6.10",
11 | "laravel/framework": "^11.9",
12 | "laravel/tinker": "^2.9",
13 | "lorisleiva/laravel-actions": "^2.8",
14 | "mateusjunges/laravel-kafka": "^2.1"
15 | },
16 | "require-dev": {
17 | "laravel/pint": "^1.13"
18 | },
19 | "autoload": {
20 | "psr-4": {
21 | "MService\\FileManagement\\": "src/"
22 | }
23 | },
24 | "scripts": {
25 | "post-autoload-dump": [
26 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
27 | "@php artisan package:discover --ansi"
28 | ],
29 | "post-root-package-install": [
30 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
31 | ],
32 | "post-create-project-cmd": [
33 | "@php artisan key:generate --ansi"
34 | ]
35 | },
36 | "extra": {
37 | "laravel": {
38 | "dont-discover": []
39 | }
40 | },
41 | "config": {
42 | "optimize-autoloader": true,
43 | "preferred-install": "dist",
44 | "sort-packages": true,
45 | "allow-plugins": {
46 | "pestphp/pest-plugin": true,
47 | "php-http/discovery": true
48 | }
49 | },
50 | "minimum-stability": "stable",
51 | "prefer-stable": true
52 | }
53 |
--------------------------------------------------------------------------------
/file-management/src/Migrations/2024_06_07_054155_create_files_table.php:
--------------------------------------------------------------------------------
1 | uuid('id')->primary();
16 |
17 | $table->unsignedBigInteger('user_id');
18 | $table->string('bucket');
19 | $table->string('key');
20 | $table->string('object_url');
21 | // It can be stream upload and doesn't have any filename. It's still downloadable with the key information.
22 | $table->string('filename')->nullable();
23 | $table->text('description')->nullable();
24 | $table->boolean('is_public')->default(false);
25 | $table->string('checksum')->nullable();
26 | $table->timestamp('expired_at')->nullable();
27 | $table->string('mimetype')->nullable();
28 | $table->unsignedBigInteger('size')->nullable();
29 | $table->json('metadata')->nullable();
30 |
31 | $table->timestamps();
32 | });
33 | }
34 |
35 | /**
36 | * Reverse the migrations.
37 | */
38 | public function down(): void
39 | {
40 | Schema::dropIfExists('files');
41 | }
42 | };
43 |
--------------------------------------------------------------------------------
/notification/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME=Laravel
2 | APP_ENV=local
3 | APP_KEY=
4 | APP_DEBUG=true
5 | APP_TIMEZONE=UTC
6 | APP_URL=http://localhost
7 |
8 | APP_LOCALE=en
9 | APP_FALLBACK_LOCALE=en
10 | APP_FAKER_LOCALE=en_US
11 |
12 | APP_MAINTENANCE_DRIVER=file
13 | APP_MAINTENANCE_STORE=database
14 |
15 | BCRYPT_ROUNDS=12
16 |
17 | LOG_CHANNEL=stack
18 | LOG_STACK=single
19 | LOG_DEPRECATIONS_CHANNEL=null
20 | LOG_LEVEL=debug
21 |
22 | DB_CONNECTION=pgsql
23 | DB_HOST=127.0.0.1
24 | DB_PORT=5432
25 | DB_DATABASE=notification
26 | DB_USERNAME=root
27 | DB_PASSWORD=
28 |
29 | SESSION_DRIVER=database
30 | SESSION_LIFETIME=120
31 | SESSION_ENCRYPT=false
32 | SESSION_PATH=/
33 | SESSION_DOMAIN=null
34 |
35 | KAFKA_BROKERS=
36 | KAFKA_CONSUMER_GROUP_ID=notification
37 | BROADCAST_CONNECTION=log
38 | FILESYSTEM_DISK=local
39 | QUEUE_CONNECTION=database
40 |
41 | CACHE_STORE=database
42 | CACHE_PREFIX=
43 |
44 | MEMCACHED_HOST=127.0.0.1
45 |
46 | REDIS_CLIENT=phpredis
47 | REDIS_HOST=127.0.0.1
48 | REDIS_PASSWORD=null
49 | REDIS_PORT=6379
50 |
51 | MAIL_MAILER=log
52 | MAIL_HOST=127.0.0.1
53 | MAIL_PORT=2525
54 | MAIL_USERNAME=null
55 | MAIL_PASSWORD=null
56 | MAIL_ENCRYPTION=null
57 | MAIL_FROM_ADDRESS="hello@example.com"
58 | MAIL_FROM_NAME="${APP_NAME}"
59 | MAIL_NO_REPLY_ADDRESS="no-reply@example.com"
60 | MAIL_NO_REPLY_NAME="${APP_NAME}"
61 |
62 | AWS_ACCESS_KEY_ID=
63 | AWS_SECRET_ACCESS_KEY=
64 | AWS_DEFAULT_REGION=us-east-1
65 | AWS_BUCKET=
66 | AWS_USE_PATH_STYLE_ENDPOINT=false
67 |
68 | VITE_APP_NAME="${APP_NAME}"
69 |
--------------------------------------------------------------------------------
/file-management/src/Handlers/KafkaMessageHandler.php:
--------------------------------------------------------------------------------
1 | getBody());
20 |
21 | $user = $body->get('user_id');
22 | $oldLicense = $body->get('old_license');
23 |
24 | $bucketName = "files-$user";
25 |
26 | if (is_null($oldLicense)) {
27 | Bus::dispatchChain([
28 | // TODO: generate bucket name with guid.
29 | new CreateNewBucket($user, $bucketName),
30 | // TODO: Add `SetBucketQuota` job to enforce quota.
31 | ]);
32 |
33 | return;
34 | }
35 |
36 | // TODO: implement job chain for "CheckQuotaApplicable" and "SetBucketQuota" with using old/new diff values
37 | cache()->set("{$user}_limits", new Limits(
38 | $body->get('new_quota'),
39 | $body->get('new_max_file_size'),
40 | $body->get('new_daily_object_limit'),
41 | false,
42 | ));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/file-management/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME=Laravel
2 | APP_ENV=local
3 | APP_KEY=
4 | APP_DEBUG=true
5 | APP_TIMEZONE=UTC
6 | APP_URL=http://localhost
7 |
8 | APP_LOCALE=en
9 | APP_FALLBACK_LOCALE=en
10 | APP_FAKER_LOCALE=en_US
11 |
12 | APP_MAINTENANCE_DRIVER=file
13 | APP_MAINTENANCE_STORE=database
14 |
15 | BCRYPT_ROUNDS=12
16 |
17 | LOG_CHANNEL=stack
18 | LOG_STACK=single
19 | LOG_DEPRECATIONS_CHANNEL=null
20 | LOG_LEVEL=debug
21 |
22 | DB_CONNECTION=pgsql
23 | DB_HOST=127.0.0.1
24 | DB_PORT=5432
25 | DB_DATABASE=filemanagement
26 | DB_USERNAME=root
27 | DB_PASSWORD=
28 |
29 | SESSION_DRIVER=database
30 | SESSION_LIFETIME=120
31 | SESSION_ENCRYPT=false
32 | SESSION_PATH=/
33 | SESSION_DOMAIN=null
34 |
35 | KAFKA_BROKERS=
36 | KAFKA_CONSUMER_GROUP_ID=file-management
37 | BROADCAST_CONNECTION=log
38 | FILESYSTEM_DISK=local
39 | QUEUE_CONNECTION=database
40 |
41 | CACHE_STORE=database
42 | CACHE_PREFIX=
43 |
44 | MEMCACHED_HOST=127.0.0.1
45 |
46 | REDIS_CLIENT=phpredis
47 | REDIS_HOST=127.0.0.1
48 | REDIS_PASSWORD=null
49 | REDIS_PORT=6379
50 |
51 | MAIL_MAILER=log
52 | MAIL_HOST=127.0.0.1
53 | MAIL_PORT=2525
54 | MAIL_USERNAME=null
55 | MAIL_PASSWORD=null
56 | MAIL_ENCRYPTION=null
57 | MAIL_FROM_ADDRESS="hello@example.com"
58 | MAIL_FROM_NAME="${APP_NAME}"
59 |
60 | AWS_ACCESS_KEY_ID=
61 | AWS_SECRET_ACCESS_KEY=
62 | AWS_DEFAULT_REGION=us-east-1
63 | AWS_BUCKET=
64 | AWS_USE_PATH_STYLE_ENDPOINT=false
65 |
66 | VITE_APP_NAME="${APP_NAME}"
67 |
68 | MINIO_ENDPOINT=http://127.0.0.1:9000/
69 | MINIO_ACCESS_KEY=mservice
70 | MINIO_SECRET_KEY=topsecret
71 |
--------------------------------------------------------------------------------
/security/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | singleton(
20 | HttpKernelContract::class,
21 | HttpKernel::class,
22 | );
23 | $app->singleton(
24 | ConsoleKernelContract::class,
25 | ConsoleKernel::class,
26 | );
27 |
28 | // Register essential providers
29 | $app->register(EventServiceProvider::class);
30 |
31 | // Register default bootstrap providers
32 | RegisterProviders::merge([], $app->getBootstrapProvidersPath());
33 | */
34 |
35 | $app = Application::configure(basePath: dirname(__DIR__))
36 | ->withRouting(
37 | health: '/up',
38 | )
39 | ->withMiddleware(function (Middleware $middleware) {
40 | //
41 | })
42 | ->withExceptions(function (Exceptions $exceptions) {
43 | //
44 | })->create();
45 |
46 | $app->useAppPath(join_paths(dirname(__DIR__), 'src'));
47 |
48 | return $app;
49 |
--------------------------------------------------------------------------------
/security/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "erayaydin/mservices-security",
3 | "type": "project",
4 | "description": "Security Service",
5 | "keywords": ["security"],
6 | "license": "MIT",
7 | "require": {
8 | "php": "^8.2",
9 | "laravel/framework": "^11.9",
10 | "laravel/passport": "^12.2",
11 | "laravel/tinker": "^2.9",
12 | "lorisleiva/laravel-actions": "^2.8",
13 | "mateusjunges/laravel-kafka": "^2.1"
14 | },
15 | "require-dev": {
16 | "laravel/pint": "^1.13"
17 | },
18 | "autoload": {
19 | "psr-4": {
20 | "MService\\Security\\": "src/"
21 | }
22 | },
23 | "scripts": {
24 | "post-autoload-dump": [
25 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
26 | "@php artisan package:discover --ansi"
27 | ],
28 | "post-root-package-install": [
29 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
30 | ],
31 | "post-create-project-cmd": [
32 | "@php artisan key:generate --ansi"
33 | ]
34 | },
35 | "extra": {
36 | "laravel": {
37 | "dont-discover": []
38 | }
39 | },
40 | "config": {
41 | "optimize-autoloader": true,
42 | "preferred-install": "dist",
43 | "sort-packages": true,
44 | "allow-plugins": {
45 | "pestphp/pest-plugin": true,
46 | "php-http/discovery": true
47 | }
48 | },
49 | "minimum-stability": "stable",
50 | "prefer-stable": true
51 | }
52 |
--------------------------------------------------------------------------------
/security/src/Actions/CreateNewUser.php:
--------------------------------------------------------------------------------
1 | userModel->query()->create([
25 | 'name' => $name,
26 | 'email' => $email,
27 | 'password' => $this->hasher->make($password),
28 | ]);
29 |
30 | PublishUserCreatedMessage::dispatch($user);
31 | }
32 |
33 | public function asController(ActionRequest $request): Response
34 | {
35 | $this->handle(
36 | $request->validated('name'),
37 | $request->validated('email'),
38 | $request->validated('password'),
39 | );
40 |
41 | return response(status: 201);
42 | }
43 |
44 | public function rules(): array
45 | {
46 | return [
47 | 'name' => ['required', 'string', 'min:3'],
48 | 'email' => ['required', 'string', 'email', 'unique:users'],
49 | 'password' => ['required', Password::default()->symbols()->numbers()->letters()->uncompromised()],
50 | ];
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/security/database/migrations/0001_01_01_000000_create_users_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->string('name');
17 | $table->string('email')->unique();
18 | $table->timestamp('email_verified_at')->nullable();
19 | $table->string('password');
20 | $table->rememberToken();
21 | $table->timestamps();
22 | });
23 |
24 | Schema::create('password_reset_tokens', function (Blueprint $table) {
25 | $table->string('email')->primary();
26 | $table->string('token');
27 | $table->timestamp('created_at')->nullable();
28 | });
29 |
30 | Schema::create('sessions', function (Blueprint $table) {
31 | $table->string('id')->primary();
32 | $table->foreignId('user_id')->nullable()->index();
33 | $table->string('ip_address', 45)->nullable();
34 | $table->text('user_agent')->nullable();
35 | $table->longText('payload');
36 | $table->integer('last_activity')->index();
37 | });
38 | }
39 |
40 | /**
41 | * Reverse the migrations.
42 | */
43 | public function down(): void
44 | {
45 | Schema::dropIfExists('users');
46 | Schema::dropIfExists('password_reset_tokens');
47 | Schema::dropIfExists('sessions');
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/license/src/Actions/LicenseUpdated.php:
--------------------------------------------------------------------------------
1 | onTopic('license.updated')
21 | ->withHeaders([
22 | 'user_id' => $userId,
23 | ])
24 | ->withBodyKey('user_id', $userId)
25 | ->withBodyKey('old_license', $oldLicense?->getAttribute('license_type')->value)
26 | ->withBodyKey('new_license', $newLicense->getAttribute('license_type')->value)
27 | ->withBodyKey('old_max_file_size', $oldLicense?->getAttribute('max_file_size'))
28 | ->withBodyKey('new_max_file_size', $newLicense->getAttribute('max_file_size'))
29 | ->withBodyKey('old_daily_object_limit', $oldLicense?->getAttribute('daily_object_limit'))
30 | ->withBodyKey('new_daily_object_limit', $newLicense->getAttribute('daily_object_limit'))
31 | ->withBodyKey('old_quota', $oldLicense?->getAttribute('quota'))
32 | ->withBodyKey('new_quota', $newLicense->getAttribute('quota'))
33 | ->send();
34 | }
35 |
36 | /**
37 | * @throws Exception
38 | */
39 | public function asJob(int $userId, License $newLicense, ?License $oldLicense = null): void
40 | {
41 | $this->handle(
42 | $userId,
43 | $newLicense,
44 | $oldLicense
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/license/src/Providers/RouteServiceProvider.php:
--------------------------------------------------------------------------------
1 | booted(function () {
28 | if (! is_null($this->namespace)) {
29 | $this->app[UrlGenerator::class]->setRootControllerNamespace($this->namespace);
30 | }
31 |
32 | if ($this->app->routesAreCached()) {
33 | $this->app->booted(function () {
34 | require $this->app->getCachedRoutesPath();
35 | });
36 | } else {
37 | $this->app->call([$this, 'routing']);
38 |
39 | $this->app->booted(function () {
40 | $this->app['router']->getRoutes()->refreshNameLookups();
41 | $this->app['router']->getRoutes()->refreshActionLookups();
42 | });
43 | }
44 | });
45 | }
46 |
47 | public function routing(): void
48 | {
49 | Route::middleware(['api'])
50 | ->prefix('api/v1')
51 | ->group($this->app->path('Routes/ApiV1.php'));
52 |
53 | Route::get('/health', function () {
54 | Event::dispatch(new DiagnosingHealth);
55 |
56 | return response(status: 200);
57 | });
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/security/src/Providers/RouteServiceProvider.php:
--------------------------------------------------------------------------------
1 | booted(function () {
28 | if (! is_null($this->namespace)) {
29 | $this->app[UrlGenerator::class]->setRootControllerNamespace($this->namespace);
30 | }
31 |
32 | if ($this->app->routesAreCached()) {
33 | $this->app->booted(function () {
34 | require $this->app->getCachedRoutesPath();
35 | });
36 | } else {
37 | $this->app->call([$this, 'routing']);
38 |
39 | $this->app->booted(function () {
40 | $this->app['router']->getRoutes()->refreshNameLookups();
41 | $this->app['router']->getRoutes()->refreshActionLookups();
42 | });
43 | }
44 | });
45 | }
46 |
47 | public function routing(): void
48 | {
49 | Route::middleware('api')
50 | ->prefix('api/v1')
51 | ->group($this->app->path('Routes/ApiV1.php'));
52 |
53 | Route::get('/health', function () {
54 | Event::dispatch(new DiagnosingHealth);
55 |
56 | return response(status: 200);
57 | });
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/file-management/src/Providers/RouteServiceProvider.php:
--------------------------------------------------------------------------------
1 | booted(function () {
29 | if (! is_null($this->namespace)) {
30 | $this->app[UrlGenerator::class]->setRootControllerNamespace($this->namespace);
31 | }
32 |
33 | if ($this->app->routesAreCached()) {
34 | $this->app->booted(function () {
35 | require $this->app->getCachedRoutesPath();
36 | });
37 | } else {
38 | $this->app->call([$this, 'routing']);
39 |
40 | $this->app->booted(function () {
41 | $this->app['router']->getRoutes()->refreshNameLookups();
42 | $this->app['router']->getRoutes()->refreshActionLookups();
43 | });
44 | }
45 | });
46 | }
47 |
48 | public function routing(): void
49 | {
50 | Route::middleware(['api', EnsureTokenIsValid::class])
51 | ->prefix('api/v1')
52 | ->group($this->app->path('Routes/ApiV1.php'));
53 |
54 | Route::get('/health', function () {
55 | Event::dispatch(new DiagnosingHealth);
56 |
57 | return response(status: 200);
58 | });
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/security/database/migrations/0001_01_01_000002_create_jobs_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->string('queue')->index();
17 | $table->longText('payload');
18 | $table->unsignedTinyInteger('attempts');
19 | $table->unsignedInteger('reserved_at')->nullable();
20 | $table->unsignedInteger('available_at');
21 | $table->unsignedInteger('created_at');
22 | });
23 |
24 | Schema::create('job_batches', function (Blueprint $table) {
25 | $table->string('id')->primary();
26 | $table->string('name');
27 | $table->integer('total_jobs');
28 | $table->integer('pending_jobs');
29 | $table->integer('failed_jobs');
30 | $table->longText('failed_job_ids');
31 | $table->mediumText('options')->nullable();
32 | $table->integer('cancelled_at')->nullable();
33 | $table->integer('created_at');
34 | $table->integer('finished_at')->nullable();
35 | });
36 |
37 | Schema::create('failed_jobs', function (Blueprint $table) {
38 | $table->id();
39 | $table->string('uuid')->unique();
40 | $table->text('connection');
41 | $table->text('queue');
42 | $table->longText('payload');
43 | $table->longText('exception');
44 | $table->timestamp('failed_at')->useCurrent();
45 | });
46 | }
47 |
48 | /**
49 | * Reverse the migrations.
50 | */
51 | public function down(): void
52 | {
53 | Schema::dropIfExists('jobs');
54 | Schema::dropIfExists('job_batches');
55 | Schema::dropIfExists('failed_jobs');
56 | }
57 | };
58 |
--------------------------------------------------------------------------------
/license/config/kafka.php:
--------------------------------------------------------------------------------
1 | env('KAFKA_BROKERS', 'localhost:9092'),
8 |
9 | /*
10 | | Default security protocol
11 | */
12 | 'securityProtocol' => env('KAFKA_SECURITY_PROTOCOL', 'PLAINTEXT'),
13 |
14 | /*
15 | | Default sasl configuration
16 | */
17 | 'sasl' => [
18 | 'mechanisms' => env('KAFKA_MECHANISMS', 'PLAINTEXT'),
19 | 'username' => env('KAFKA_USERNAME', null),
20 | 'password' => env('KAFKA_PASSWORD', null)
21 | ],
22 |
23 | /*
24 | | Kafka consumers belonging to the same consumer group share a group id.
25 | | The consumers in a group then divides the topic partitions as fairly amongst themselves as possible by
26 | | establishing that each partition is only consumed by a single consumer from the group.
27 | | This config defines the consumer group id you want to use for your project.
28 | */
29 | 'consumer_group_id' => env('KAFKA_CONSUMER_GROUP_ID', 'group'),
30 |
31 | 'consumer_timeout_ms' => env("KAFKA_CONSUMER_DEFAULT_TIMEOUT", 2000),
32 |
33 | /*
34 | | After the consumer receives its assignment from the coordinator,
35 | | it must determine the initial position for each assigned partition.
36 | | When the group is first created, before any messages have been consumed, the position is set according to a configurable
37 | | offset reset policy (auto.offset.reset). Typically, consumption starts either at the earliest offset or the latest offset.
38 | | You can choose between "latest", "earliest" or "none".
39 | */
40 | 'offset_reset' => env('KAFKA_OFFSET_RESET', 'latest'),
41 |
42 | /*
43 | | If you set enable.auto.commit (which is the default), then the consumer will automatically commit offsets periodically at the
44 | | interval set by auto.commit.interval.ms.
45 | */
46 | 'auto_commit' => env('KAFKA_AUTO_COMMIT', true),
47 |
48 | 'sleep_on_error' => env('KAFKA_ERROR_SLEEP', 5),
49 |
50 | 'partition' => env('KAFKA_PARTITION', 0),
51 |
52 | /*
53 | | Kafka supports 4 compression codecs: none , gzip , lz4 and snappy
54 | */
55 | 'compression' => env('KAFKA_COMPRESSION_TYPE', 'snappy'),
56 |
57 | /*
58 | | Choose if debug is enabled or not.
59 | */
60 | 'debug' => env('KAFKA_DEBUG', false),
61 |
62 | /*
63 | | Repository for batching messages together
64 | | Implement BatchRepositoryInterface to save batches in different storage
65 | */
66 | 'batch_repository' => env('KAFKA_BATCH_REPOSITORY', \Junges\Kafka\BatchRepositories\InMemoryBatchRepository::class),
67 |
68 | /*
69 | | The sleep time in milliseconds that will be used when retrying flush
70 | */
71 | 'flush_retry_sleep_in_ms' => 100,
72 |
73 | /*
74 | | The cache driver that will be used
75 | */
76 | 'cache_driver' => env('KAFKA_CACHE_DRIVER', env('CACHE_DRIVER', 'file')),
77 | ];
78 |
--------------------------------------------------------------------------------
/security/config/kafka.php:
--------------------------------------------------------------------------------
1 | env('KAFKA_BROKERS', 'localhost:9092'),
8 |
9 | /*
10 | | Default security protocol
11 | */
12 | 'securityProtocol' => env('KAFKA_SECURITY_PROTOCOL', 'PLAINTEXT'),
13 |
14 | /*
15 | | Default sasl configuration
16 | */
17 | 'sasl' => [
18 | 'mechanisms' => env('KAFKA_MECHANISMS', 'PLAINTEXT'),
19 | 'username' => env('KAFKA_USERNAME', null),
20 | 'password' => env('KAFKA_PASSWORD', null)
21 | ],
22 |
23 | /*
24 | | Kafka consumers belonging to the same consumer group share a group id.
25 | | The consumers in a group then divides the topic partitions as fairly amongst themselves as possible by
26 | | establishing that each partition is only consumed by a single consumer from the group.
27 | | This config defines the consumer group id you want to use for your project.
28 | */
29 | 'consumer_group_id' => env('KAFKA_CONSUMER_GROUP_ID', 'group'),
30 |
31 | 'consumer_timeout_ms' => env("KAFKA_CONSUMER_DEFAULT_TIMEOUT", 2000),
32 |
33 | /*
34 | | After the consumer receives its assignment from the coordinator,
35 | | it must determine the initial position for each assigned partition.
36 | | When the group is first created, before any messages have been consumed, the position is set according to a configurable
37 | | offset reset policy (auto.offset.reset). Typically, consumption starts either at the earliest offset or the latest offset.
38 | | You can choose between "latest", "earliest" or "none".
39 | */
40 | 'offset_reset' => env('KAFKA_OFFSET_RESET', 'latest'),
41 |
42 | /*
43 | | If you set enable.auto.commit (which is the default), then the consumer will automatically commit offsets periodically at the
44 | | interval set by auto.commit.interval.ms.
45 | */
46 | 'auto_commit' => env('KAFKA_AUTO_COMMIT', true),
47 |
48 | 'sleep_on_error' => env('KAFKA_ERROR_SLEEP', 5),
49 |
50 | 'partition' => env('KAFKA_PARTITION', 0),
51 |
52 | /*
53 | | Kafka supports 4 compression codecs: none , gzip , lz4 and snappy
54 | */
55 | 'compression' => env('KAFKA_COMPRESSION_TYPE', 'snappy'),
56 |
57 | /*
58 | | Choose if debug is enabled or not.
59 | */
60 | 'debug' => env('KAFKA_DEBUG', false),
61 |
62 | /*
63 | | Repository for batching messages together
64 | | Implement BatchRepositoryInterface to save batches in different storage
65 | */
66 | 'batch_repository' => env('KAFKA_BATCH_REPOSITORY', \Junges\Kafka\BatchRepositories\InMemoryBatchRepository::class),
67 |
68 | /*
69 | | The sleep time in milliseconds that will be used when retrying flush
70 | */
71 | 'flush_retry_sleep_in_ms' => 100,
72 |
73 | /*
74 | | The cache driver that will be used
75 | */
76 | 'cache_driver' => env('KAFKA_CACHE_DRIVER', env('CACHE_DRIVER', 'file')),
77 | ];
78 |
--------------------------------------------------------------------------------
/file-management/config/kafka.php:
--------------------------------------------------------------------------------
1 | env('KAFKA_BROKERS', 'localhost:9092'),
8 |
9 | /*
10 | | Default security protocol
11 | */
12 | 'securityProtocol' => env('KAFKA_SECURITY_PROTOCOL', 'PLAINTEXT'),
13 |
14 | /*
15 | | Default sasl configuration
16 | */
17 | 'sasl' => [
18 | 'mechanisms' => env('KAFKA_MECHANISMS', 'PLAINTEXT'),
19 | 'username' => env('KAFKA_USERNAME', null),
20 | 'password' => env('KAFKA_PASSWORD', null)
21 | ],
22 |
23 | /*
24 | | Kafka consumers belonging to the same consumer group share a group id.
25 | | The consumers in a group then divides the topic partitions as fairly amongst themselves as possible by
26 | | establishing that each partition is only consumed by a single consumer from the group.
27 | | This config defines the consumer group id you want to use for your project.
28 | */
29 | 'consumer_group_id' => env('KAFKA_CONSUMER_GROUP_ID', 'group'),
30 |
31 | 'consumer_timeout_ms' => env("KAFKA_CONSUMER_DEFAULT_TIMEOUT", 2000),
32 |
33 | /*
34 | | After the consumer receives its assignment from the coordinator,
35 | | it must determine the initial position for each assigned partition.
36 | | When the group is first created, before any messages have been consumed, the position is set according to a configurable
37 | | offset reset policy (auto.offset.reset). Typically, consumption starts either at the earliest offset or the latest offset.
38 | | You can choose between "latest", "earliest" or "none".
39 | */
40 | 'offset_reset' => env('KAFKA_OFFSET_RESET', 'latest'),
41 |
42 | /*
43 | | If you set enable.auto.commit (which is the default), then the consumer will automatically commit offsets periodically at the
44 | | interval set by auto.commit.interval.ms.
45 | */
46 | 'auto_commit' => env('KAFKA_AUTO_COMMIT', true),
47 |
48 | 'sleep_on_error' => env('KAFKA_ERROR_SLEEP', 5),
49 |
50 | 'partition' => env('KAFKA_PARTITION', 0),
51 |
52 | /*
53 | | Kafka supports 4 compression codecs: none , gzip , lz4 and snappy
54 | */
55 | 'compression' => env('KAFKA_COMPRESSION_TYPE', 'snappy'),
56 |
57 | /*
58 | | Choose if debug is enabled or not.
59 | */
60 | 'debug' => env('KAFKA_DEBUG', false),
61 |
62 | /*
63 | | Repository for batching messages together
64 | | Implement BatchRepositoryInterface to save batches in different storage
65 | */
66 | 'batch_repository' => env('KAFKA_BATCH_REPOSITORY', \Junges\Kafka\BatchRepositories\InMemoryBatchRepository::class),
67 |
68 | /*
69 | | The sleep time in milliseconds that will be used when retrying flush
70 | */
71 | 'flush_retry_sleep_in_ms' => 100,
72 |
73 | /*
74 | | The cache driver that will be used
75 | */
76 | 'cache_driver' => env('KAFKA_CACHE_DRIVER', env('CACHE_DRIVER', 'file')),
77 | ];
78 |
--------------------------------------------------------------------------------
/notification/config/kafka.php:
--------------------------------------------------------------------------------
1 | env('KAFKA_BROKERS', 'localhost:9092'),
8 |
9 | /*
10 | | Default security protocol
11 | */
12 | 'securityProtocol' => env('KAFKA_SECURITY_PROTOCOL', 'PLAINTEXT'),
13 |
14 | /*
15 | | Default sasl configuration
16 | */
17 | 'sasl' => [
18 | 'mechanisms' => env('KAFKA_MECHANISMS', 'PLAINTEXT'),
19 | 'username' => env('KAFKA_USERNAME', null),
20 | 'password' => env('KAFKA_PASSWORD', null)
21 | ],
22 |
23 | /*
24 | | Kafka consumers belonging to the same consumer group share a group id.
25 | | The consumers in a group then divides the topic partitions as fairly amongst themselves as possible by
26 | | establishing that each partition is only consumed by a single consumer from the group.
27 | | This config defines the consumer group id you want to use for your project.
28 | */
29 | 'consumer_group_id' => env('KAFKA_CONSUMER_GROUP_ID', 'group'),
30 |
31 | 'consumer_timeout_ms' => env("KAFKA_CONSUMER_DEFAULT_TIMEOUT", 2000),
32 |
33 | /*
34 | | After the consumer receives its assignment from the coordinator,
35 | | it must determine the initial position for each assigned partition.
36 | | When the group is first created, before any messages have been consumed, the position is set according to a configurable
37 | | offset reset policy (auto.offset.reset). Typically, consumption starts either at the earliest offset or the latest offset.
38 | | You can choose between "latest", "earliest" or "none".
39 | */
40 | 'offset_reset' => env('KAFKA_OFFSET_RESET', 'latest'),
41 |
42 | /*
43 | | If you set enable.auto.commit (which is the default), then the consumer will automatically commit offsets periodically at the
44 | | interval set by auto.commit.interval.ms.
45 | */
46 | 'auto_commit' => env('KAFKA_AUTO_COMMIT', true),
47 |
48 | 'sleep_on_error' => env('KAFKA_ERROR_SLEEP', 5),
49 |
50 | 'partition' => env('KAFKA_PARTITION', 0),
51 |
52 | /*
53 | | Kafka supports 4 compression codecs: none , gzip , lz4 and snappy
54 | */
55 | 'compression' => env('KAFKA_COMPRESSION_TYPE', 'snappy'),
56 |
57 | /*
58 | | Choose if debug is enabled or not.
59 | */
60 | 'debug' => env('KAFKA_DEBUG', false),
61 |
62 | /*
63 | | Repository for batching messages together
64 | | Implement BatchRepositoryInterface to save batches in different storage
65 | */
66 | 'batch_repository' => env('KAFKA_BATCH_REPOSITORY', \Junges\Kafka\BatchRepositories\InMemoryBatchRepository::class),
67 |
68 | /*
69 | | The sleep time in milliseconds that will be used when retrying flush
70 | */
71 | 'flush_retry_sleep_in_ms' => 100,
72 |
73 | /*
74 | | The cache driver that will be used
75 | */
76 | 'cache_driver' => env('KAFKA_CACHE_DRIVER', env('CACHE_DRIVER', 'file')),
77 | ];
78 |
--------------------------------------------------------------------------------
/notification/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:22.04
2 |
3 | ARG WWWGROUP=1000
4 | ARG NODE_VERSION=20
5 | ARG POSTGRES_VERSION=15
6 |
7 | WORKDIR /var/www/html
8 |
9 | ENV DEBIAN_FRONTEND noninteractive
10 | ENV TZ=UTC
11 | ENV SUPERVISOR_PHP_COMMAND="/usr/bin/php /var/www/html/artisan app:consume-messages"
12 | ENV SUPERVISOR_WORKER_COMMAND="/usr/bin/php /var/www/html/artisan queue:listen"
13 | ENV SUPERVISOR_PHP_USER="app"
14 |
15 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
16 |
17 | RUN apt-get update \
18 | && mkdir -p /etc/apt/keyrings \
19 | && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin fswatch ffmpeg nano \
20 | && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /etc/apt/keyrings/ppa_ondrej_php.gpg > /dev/null \
21 | && echo "deb [signed-by=/etc/apt/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \
22 | && apt-get update \
23 | && apt-get install -y librdkafka-dev php8.2-cli php8.2-dev \
24 | php8.2-pgsql php8.2-sqlite3 php8.2-gd php8.2-imagick \
25 | php8.2-curl \
26 | php8.2-imap php8.2-mysql php8.2-mbstring \
27 | php8.2-xml php8.2-zip php8.2-bcmath php8.2-soap \
28 | php8.2-intl php8.2-readline \
29 | php8.2-ldap \
30 | php8.2-msgpack php8.2-igbinary php8.2-redis php8.2-swoole \
31 | php8.2-memcached php8.2-pcov php8.2-xdebug \
32 | && pecl install rdkafka \
33 | && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \
34 | && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
35 | && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \
36 | && apt-get update \
37 | && apt-get install -y nodejs \
38 | && npm install -g npm \
39 | && npm install -g pnpm \
40 | && npm install -g bun \
41 | && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /etc/apt/keyrings/yarn.gpg >/dev/null \
42 | && echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
43 | && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/keyrings/pgdg.gpg >/dev/null \
44 | && echo "deb [signed-by=/etc/apt/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \
45 | && apt-get update \
46 | && apt-get install -y yarn \
47 | && apt-get install -y mysql-client \
48 | && apt-get install -y postgresql-client-$POSTGRES_VERSION \
49 | && apt-get -y autoremove \
50 | && apt-get clean \
51 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
52 |
53 | RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.2
54 |
55 | RUN groupadd --force -g $WWWGROUP app
56 | RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 app
57 |
58 | COPY docker/start-container /usr/local/bin/start-container
59 | COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
60 | COPY docker/php.ini /etc/php/8.2/cli/conf.d/99-app.ini
61 | RUN chmod +x /usr/local/bin/start-container
62 |
63 | EXPOSE 8000
64 |
65 | ENTRYPOINT ["start-container"]
66 |
--------------------------------------------------------------------------------
/security/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:22.04
2 |
3 | ARG WWWGROUP=1000
4 | ARG NODE_VERSION=20
5 | ARG POSTGRES_VERSION=15
6 |
7 | WORKDIR /var/www/html
8 |
9 | ENV DEBIAN_FRONTEND noninteractive
10 | ENV TZ=UTC
11 | ENV SUPERVISOR_PHP_COMMAND="/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80"
12 | ENV SUPERVISOR_WORKER_COMMAND="/usr/bin/php /var/www/html/artisan queue:listen"
13 | ENV SUPERVISOR_PHP_USER="app"
14 |
15 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
16 |
17 | RUN apt-get update \
18 | && mkdir -p /etc/apt/keyrings \
19 | && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin fswatch ffmpeg nano \
20 | && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /etc/apt/keyrings/ppa_ondrej_php.gpg > /dev/null \
21 | && echo "deb [signed-by=/etc/apt/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \
22 | && apt-get update \
23 | && apt-get install -y librdkafka-dev php8.2-cli php8.2-dev \
24 | php8.2-pgsql php8.2-sqlite3 php8.2-gd php8.2-imagick \
25 | php8.2-curl \
26 | php8.2-imap php8.2-mysql php8.2-mbstring \
27 | php8.2-xml php8.2-zip php8.2-bcmath php8.2-soap \
28 | php8.2-intl php8.2-readline \
29 | php8.2-ldap \
30 | php8.2-msgpack php8.2-igbinary php8.2-redis php8.2-swoole \
31 | php8.2-memcached php8.2-pcov php8.2-xdebug \
32 | && pecl install rdkafka \
33 | && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \
34 | && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
35 | && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \
36 | && apt-get update \
37 | && apt-get install -y nodejs \
38 | && npm install -g npm \
39 | && npm install -g pnpm \
40 | && npm install -g bun \
41 | && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /etc/apt/keyrings/yarn.gpg >/dev/null \
42 | && echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
43 | && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/keyrings/pgdg.gpg >/dev/null \
44 | && echo "deb [signed-by=/etc/apt/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \
45 | && apt-get update \
46 | && apt-get install -y yarn \
47 | && apt-get install -y mysql-client \
48 | && apt-get install -y postgresql-client-$POSTGRES_VERSION \
49 | && apt-get -y autoremove \
50 | && apt-get clean \
51 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
52 |
53 | RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.2
54 |
55 | RUN groupadd --force -g $WWWGROUP app
56 | RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 app
57 |
58 | COPY docker/start-container /usr/local/bin/start-container
59 | COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
60 | COPY docker/php.ini /etc/php/8.2/cli/conf.d/99-app.ini
61 | RUN chmod +x /usr/local/bin/start-container
62 |
63 | EXPOSE 8000
64 |
65 | ENTRYPOINT ["start-container"]
66 |
--------------------------------------------------------------------------------
/file-management/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:22.04
2 |
3 | ARG WWWGROUP=1000
4 | ARG NODE_VERSION=20
5 | ARG POSTGRES_VERSION=15
6 |
7 | WORKDIR /var/www/html
8 |
9 | ENV DEBIAN_FRONTEND noninteractive
10 | ENV TZ=UTC
11 | ENV SUPERVISOR_PHP_COMMAND="/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80"
12 | ENV SUPERVISOR_WORKER_COMMAND="/usr/bin/php /var/www/html/artisan queue:listen"
13 | ENV SUPERVISOR_CONSUMER_COMMAND="/usr/bin/php /var/www/html/artisan app:consume-messages"
14 | ENV SUPERVISOR_PHP_USER="app"
15 |
16 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
17 |
18 | RUN apt-get update \
19 | && mkdir -p /etc/apt/keyrings \
20 | && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin fswatch ffmpeg nano \
21 | && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /etc/apt/keyrings/ppa_ondrej_php.gpg > /dev/null \
22 | && echo "deb [signed-by=/etc/apt/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \
23 | && apt-get update \
24 | && apt-get install -y librdkafka-dev php8.2-cli php8.2-dev \
25 | php8.2-pgsql php8.2-sqlite3 php8.2-gd php8.2-imagick \
26 | php8.2-curl \
27 | php8.2-imap php8.2-mysql php8.2-mbstring \
28 | php8.2-xml php8.2-zip php8.2-bcmath php8.2-soap \
29 | php8.2-intl php8.2-readline \
30 | php8.2-ldap \
31 | php8.2-msgpack php8.2-igbinary php8.2-redis php8.2-swoole \
32 | php8.2-memcached php8.2-pcov php8.2-xdebug \
33 | && pecl install rdkafka \
34 | && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \
35 | && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
36 | && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \
37 | && apt-get update \
38 | && apt-get install -y nodejs \
39 | && npm install -g npm \
40 | && npm install -g pnpm \
41 | && npm install -g bun \
42 | && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /etc/apt/keyrings/yarn.gpg >/dev/null \
43 | && echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
44 | && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/keyrings/pgdg.gpg >/dev/null \
45 | && echo "deb [signed-by=/etc/apt/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \
46 | && apt-get update \
47 | && apt-get install -y yarn \
48 | && apt-get install -y mysql-client \
49 | && apt-get install -y postgresql-client-$POSTGRES_VERSION \
50 | && apt-get -y autoremove \
51 | && apt-get clean \
52 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
53 |
54 | RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.2
55 |
56 | RUN groupadd --force -g $WWWGROUP app
57 | RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 app
58 |
59 | COPY docker/start-container /usr/local/bin/start-container
60 | COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
61 | COPY docker/php.ini /etc/php/8.2/cli/conf.d/99-app.ini
62 | RUN chmod +x /usr/local/bin/start-container
63 |
64 | EXPOSE 8000
65 |
66 | ENTRYPOINT ["start-container"]
67 |
--------------------------------------------------------------------------------
/license/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:22.04
2 |
3 | ARG WWWGROUP=1000
4 | ARG NODE_VERSION=20
5 | ARG POSTGRES_VERSION=15
6 |
7 | WORKDIR /var/www/html
8 |
9 | ENV DEBIAN_FRONTEND noninteractive
10 | ENV TZ=UTC
11 | ENV SUPERVISOR_PHP_COMMAND="/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80"
12 | ENV SUPERVISOR_WORKER_COMMAND="/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan queue:listen"
13 | ENV SUPERVISOR_KAFKA_CONSUMER_COMMAND="/usr/bin/php /var/www/html/artisan app:consume-messages"
14 | ENV SUPERVISOR_PHP_USER="app"
15 |
16 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
17 |
18 | RUN apt-get update \
19 | && mkdir -p /etc/apt/keyrings \
20 | && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin fswatch ffmpeg nano \
21 | && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /etc/apt/keyrings/ppa_ondrej_php.gpg > /dev/null \
22 | && echo "deb [signed-by=/etc/apt/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \
23 | && apt-get update \
24 | && apt-get install -y librdkafka-dev php8.2-cli php8.2-dev \
25 | php8.2-pgsql php8.2-sqlite3 php8.2-gd php8.2-imagick \
26 | php8.2-curl \
27 | php8.2-imap php8.2-mysql php8.2-mbstring \
28 | php8.2-xml php8.2-zip php8.2-bcmath php8.2-soap \
29 | php8.2-intl php8.2-readline \
30 | php8.2-ldap \
31 | php8.2-msgpack php8.2-igbinary php8.2-redis php8.2-swoole \
32 | php8.2-memcached php8.2-pcov php8.2-xdebug \
33 | && pecl install rdkafka \
34 | && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \
35 | && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
36 | && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \
37 | && apt-get update \
38 | && apt-get install -y nodejs \
39 | && npm install -g npm \
40 | && npm install -g pnpm \
41 | && npm install -g bun \
42 | && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /etc/apt/keyrings/yarn.gpg >/dev/null \
43 | && echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
44 | && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/keyrings/pgdg.gpg >/dev/null \
45 | && echo "deb [signed-by=/etc/apt/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \
46 | && apt-get update \
47 | && apt-get install -y yarn \
48 | && apt-get install -y mysql-client \
49 | && apt-get install -y postgresql-client-$POSTGRES_VERSION \
50 | && apt-get -y autoremove \
51 | && apt-get clean \
52 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
53 |
54 | RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.2
55 |
56 | RUN groupadd --force -g $WWWGROUP app
57 | RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 app
58 |
59 | COPY docker/start-container /usr/local/bin/start-container
60 | COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
61 | COPY docker/php.ini /etc/php/8.2/cli/conf.d/99-app.ini
62 | RUN chmod +x /usr/local/bin/start-container
63 |
64 | EXPOSE 8000
65 |
66 | ENTRYPOINT ["start-container"]
67 |
--------------------------------------------------------------------------------
/file-management/src/Actions/UploadFile.php:
--------------------------------------------------------------------------------
1 | remember("{$userId}_limits", 15 * 60, fn() => Limits::forUser($userId));
49 |
50 | if ($limits->isExpired) {
51 | throw new LicenseExpiredException;
52 | }
53 |
54 | if ($file->getSize() > $limits->maxFileSize) {
55 | throw new EntityTooLargeException;
56 | }
57 |
58 | if ($this->fileModel->ofUser($userId)->in24h()->count() > $limits->dailyLimit) {
59 | throw new DailyLimitException;
60 | }
61 |
62 | /** @var int $balance */
63 | $balance = cache()->rememberForever(
64 | "{$userId}_balance",
65 | fn() => $this->fileModel->ofUser($userId)->sum('size')
66 | );
67 |
68 | if ($balance + $file->getSize() > $limits->quota) {
69 | throw new QuotaReachedException;
70 | }
71 |
72 | $key = Str::uuid() . "." . $file->extension();
73 |
74 | $uploadResult = $this->s3Client->putObject([
75 | 'Bucket' => $bucketName,
76 | 'Key' => $key,
77 | 'Body' => fopen($file->getPathname(), 'r'),
78 | 'ContentType' => $file->getMimeType(),
79 | ]);
80 |
81 | /** @var File $record */
82 | $record = $this->fileModel->query()->create([
83 | 'user_id' => $userId,
84 | 'bucket' => $bucketName,
85 | 'key' => $key,
86 | 'object_url' => $uploadResult['ObjectURL'],
87 | 'filename' => $file->getClientOriginalName(),
88 | 'description' => $description,
89 | 'is_public' => $isPublic,
90 | 'checksum' => hash('md5', $file->getContent()),
91 | 'mimetype' => $file->getMimeType(),
92 | 'size' => $file->getSize(),
93 | ]);
94 |
95 | cache()->set("{$userId}_balance", $balance + $file->getSize());
96 |
97 | return $record;
98 | }
99 |
100 | /**
101 | * @param ActionRequest $request
102 | * @return JsonResponse|FileResource
103 | * @throws DailyLimitException
104 | * @throws EntityTooLargeException
105 | * @throws GuzzleException
106 | * @throws QuotaReachedException
107 | * @throws LicenseExpiredException|InvalidArgumentException
108 | */
109 | public function asController(ActionRequest $request): JsonResponse|FileResource
110 | {
111 | $userId = $request->input('user_id');
112 |
113 | $file = $this->handle(
114 | $userId,
115 | "files-$userId",
116 | $request->file('file'),
117 | $request->validated('description'),
118 | $request->validated('is_public') ?: false,
119 | );
120 |
121 | return new FileResource($file);
122 | }
123 |
124 | public function rules(): array
125 | {
126 | return [
127 | 'file' => ['required', 'file'],
128 | 'description' => ['string', 'max:1000'],
129 | 'is_public' => ['bool'],
130 | ];
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Microservice Architecture for Laravel
2 |
3 | ## Table of Contents
4 |
5 | - [Overview](#overview)
6 | - [Technologies](#technologies)
7 | - [Getting Started](#getting-started)
8 | - [Installation](#installation)
9 | - [Configuration](#configuration)
10 | - [Generate RSA Keys](#generate-rsa-keys)
11 | - [Running the Services](#running-the-services)
12 | - [Microservices](#microservices)
13 | - [Security](#security-service)
14 | - [License](#license-service)
15 | - [File Management](#file-management-service)
16 | - [Need to Implement](#need-to-implement)
17 | - [Contributing](#contributing)
18 | - [License](#license)
19 |
20 | ## Overview
21 |
22 | This project is a microservice-based architecture designed to provide scalable and maintainable solutions.
23 | It includes various technologies for managing service discovery, messaging, and data integration.
24 |
25 | ## Technologies
26 |
27 | - **Zookeeper**: Used for service discovery and coordination.
28 | - **ZooNavigator**: A web-based UI for managing Zookeeper instances.
29 | - **Kafka**: A distributed streaming platform used for building real-time data pipelines and streaming applications.
30 | - **Schema Registry**: Manages and enforces schemas for Kafka topics.
31 | - **Kafka Connect**: A tool for scalable and reliably streaming data between Apache Kafka and other systems.
32 | - **Kafka UI**: A web-based UI for managing and monitoring Kafka clusters.
33 | - **PostgreSQL**: A powerful, open source object-relational database system.
34 |
35 | ## Getting Started
36 |
37 | Ensure you have the following software installed:
38 | - Docker
39 | - Docker Compose
40 |
41 | To get a local copy of this project up and running, follow these simple steps.
42 |
43 | ## Installation
44 |
45 | 1. Clone the repository:
46 | ```shell
47 | git clone https://github.com/erayaydin/microservice-laravel.git
48 | cd microservice-laravel
49 | ```
50 | 2. Copy the sample `.env.example` to `.env`:
51 | ```shell
52 | cp .env.example .env
53 | ```
54 | 3. Edit necessary parts in `.env` file as needed.
55 |
56 | ## Configuration
57 |
58 | The configuration settings are stored in the `.env` file. Customize the necessary parts as needed.
59 |
60 | - **ZOOKEEPER_IMAGE**: Docker image name to use for zookeeper instances. Default: `confluentinc/cp-zookeeper`
61 | - **ZOOKEEPER_IMAGE_VERSION**: Docker image version for _ZOOKEEPER_IMAGE_ image. Default: `7.6.1`
62 | - **ZOONAVIGATOR_VERSION**: ZooNavigator UI image version to install with services. Default: `1.1.2`
63 | - **KAFKA_IMAGE**: Docker image name to use for kafka instances. Default: `confluentinc/cp-kafka`
64 | - **KAFKA_IMAGE_VERSION**: Docker image version for _KAFKA_IMAGE_ image. Default: `7.6.1`
65 | - **SCHEMA_REGISTRY_IMAGE**: Docker image name to use for SchemaRegistry instances. Default: `confluentinc/cp-schema-registry`
66 | - **SCHEMA_REGISTRY_IMAGE_VERSION**: Docker image version for _SCHEMA_REGISTRY_IMAGE_ image. Default: `7.6.1`
67 | - **KAFKA_CONNECT_IMAGE**: Docker image name to use for kafka connect instances. Default: `confluentinc/cp-kafka-connect`
68 | - **KAFKA_CONNECT_IMAGE_VERSION**: Docker image version for _KAFKA_CONNECT_IMAGE_ image. Default: `7.6.1`
69 | - **WWWUSER**: User id for microservice instances. Default: `1000`
70 | - **WWWGROUP**: Group id for microservice instances. Default: `1000`
71 | - **APP_[SERVICE]_DB_IMAGE_VERSION**: PostgreSQL instance version for the `[SERVICE]` microservice. Default: `15`
72 | - **APP_[SERVICE]_PORT**: External port of the `[SERVICE]` microservice. It will be increased 1 by 1 starting from
73 | `8082`.
74 | - **APP_[SERVICE]_XDEBUG_MODE**: Enable xdebug extension for the `[SERVICE]` microservice. Default: `off`
75 | - **APP_[SERVICE]_XDEBUG_CONFIG**: If xdebug enabled, xdebug integration config. Edit for development environment needs.
76 | Default: `-client_host=host.docker.internal`
77 | - **APP_[SERVICE]_DB_PORT**: External port of the `[SERVICE]` microservice's database. It will be increased 1 by 1
78 | starting from `5432`.
79 |
80 | ## Generate RSA Keys
81 |
82 | You need to generate a pair of 4096-bit RSA private and public keys for inter-service authentication
83 | and authorization. These keys will be shared between services.
84 |
85 | ```shell
86 | openssl genrsa -out secrets/oauth-private.key 4096
87 | openssl rsa -in secrets/oauth-private.key -pubout -out secrets/oauth-public.key
88 | ```
89 |
90 | ## Running the Services
91 |
92 | To start all the services, run:
93 |
94 | ```shell
95 | docker compose up -d
96 | ```
97 |
98 | This command will start all the containers defined in the docker-compose.yml file.
99 |
100 | Remember to run migrations and necessary adjustments before testing services. Like:
101 |
102 | ```shell
103 | docker compose exec -u app security php artisan migrate
104 | ```
105 |
106 | ## Microservices
107 |
108 | ### Security Service
109 |
110 | The Security Service is responsible for handling user authentication and authorization.
111 | Authentication and authorization will be handled with OAuth2.
112 |
113 | #### Endpoints
114 |
115 | - `GET /health`: Health check endpoint. It'll respond with **200** status code.
116 | - `POST /users`: Create new user. It'll respond with **201** status code if success.
117 | - `GET /oauth/authorize`: Show authorization to the end user.
118 | - `POST /oauth/authorize`: Approve authorization.
119 | - `DELETE /oauth/authorize`: Deny authorization.
120 | - `GET /oauth/clients`: Get oauth clients for the user.
121 | - `POST /oauth/clients`: Create new oauth client.
122 | - `PUT /oauth/clients/{client_id}`: Update an oauth client.
123 | - `DELETE /oauth/clients/{client_id}`: Delete an oauth client.
124 | - `GET /oauth/personal-access-tokens`: Get personal access token oauth clients for the user.
125 | - `POST /oauth/personal-access-tokens`: Create new personal access token oauth client.
126 | - `DELETE /oauth/personal-access-tokens/{token_id}`: Delete a personal access token oauth client.
127 | - `GET /oauth/scopes`: Get all registered scopes.
128 | - `POST /oauth/token`: Issue new token with specified strategy.
129 | - `POST /oauth/token/refresh`: Refresh access token with refresh token.
130 | - `GET /oauth/tokens`: Get authorized access token for the user.
131 | - `DELETE /oauth/tokens/{token_id}`: Delete an access token.
132 |
133 | #### Data Store
134 |
135 | - **PostgreSQL**: The Security Service uses PostgreSQL to store user credentials and authorization data.
136 |
137 | ### License Service
138 |
139 | The Security Service is responsible for handling user licenses.
140 | Auth verification will be handled with JWT key decoding.
141 |
142 | #### Endpoints
143 |
144 | - `GET /health`: Health check endpoint. It'll respond with 200 status code.
145 | - `GET /me`: Get current user license information.
146 | - `GET /users/{user}`: Get user's license information. (need `admin.licenses` scope).
147 |
148 | #### Data Store
149 |
150 | - **PostgreSQL**: The License Service uses PostgreSQL to store license information.
151 |
152 | ### File Management Service
153 |
154 | The File Management Service is responsible for handling file operations.
155 | End user can upload and download files.
156 |
157 | #### Endpoints
158 |
159 | - `GET /health`: Health check endpoint. It'll respond with 200 status code.
160 | - `GET /files`: List of current user's uploaded files.
161 | - `POST /files`: Upload new file to user's bucket.
162 | - `GET /files/{file}/download`: Downloads the given file in attachment mode.
163 |
164 | #### Data Store
165 |
166 | - **PostgreSQL**: The File Management Service uses PostgreSQL to store file metadata.
167 |
168 | ## Need to Implement
169 |
170 | - Use [`Kong`](https://github.com/Kong/kong) api-gateway to single entrypoint.
171 | - Implement permission and scope system to license `/users/{user}` endpoint.
172 | - Use `docker secret` to share oauth private and public keys.
173 | - Implement `ObjectStorage` service to manage buckets.
174 | - Use RDKafka data processor instead of the current one.
175 | - Define schemas for `user.created` and `license.updated` kafka messages.
176 | - Remove config files for `services` and `kafka`. Use provider to bind instances with values.
177 | - Use kafka-connect1 for connect services to kafka cluster.
178 | - Fix style issues and apply on CI.
179 | - Add ELK stack and/or Grafana metrics.
180 | - Add unit, integration and e2e tests.
181 | - Add k8s yaml files.
182 | - Add OpenAPI documentation for the api-gateway and services.
183 | - Use kong+security to validate and decode OAuth2
184 |
185 | ## Contributing
186 |
187 | Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any
188 | contributions you make are **greatly appreciated.**
189 |
190 | 1. Fork the Project
191 | 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
192 | 3. Commit your changes (`git commit`)
193 | 4. Push to the branch (`git push origin feature/amazing-feature`)
194 | 5. Open a pull request with detailed description.
195 |
196 | ## License
197 |
198 | Distributed under the **MIT License**.
199 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | security:
3 | build:
4 | context: ./security
5 | dockerfile: Dockerfile
6 | args:
7 | WWWGROUP: '${WWWGROUP}'
8 | extra_hosts:
9 | - 'host.docker.internal:host-gateway'
10 | ports:
11 | - '${APP_SECURITY_PORT:-8082}:80'
12 | env_file:
13 | - ./security/.env
14 | environment:
15 | WWWUSER: '${WWWUSER}'
16 | APP_IN_DOCKER: 1
17 | XDEBUG_MODE: '${APP_SECURITY_XDEBUG_MODE:-off}'
18 | XDEBUG_CONFIG: '${APP_SECURITY_XDEBUG_CONFIG:-client_host=host.docker.internal}'
19 | IGNITION_LOCAL_SITES_PATH: '${PWD}'
20 | volumes:
21 | - './security:/var/www/html'
22 | - './secrets:/tmp/secrets'
23 | networks:
24 | - security
25 | - backend
26 | - vpc
27 | depends_on:
28 | - security-db
29 | security-db:
30 | image: postgres:${APP_SECURITY_DB_IMAGE_VERSION}
31 | ports:
32 | - '${APP_SECURITY_DB_PORT:-5432}:5432'
33 | env_file:
34 | - ./security/.env
35 | environment:
36 | PGPASSWD: '${DB_PASSWORD:-secret}'
37 | POSTGRES_DB: '${DB_DATABASE:-security}'
38 | POSTGRES_USER: '${DB_USERNAME:-security}'
39 | POSTGRES_PASSWORD: '${DB_PASSWORD:-secret}'
40 | volumes:
41 | - security-db-data:/var/lib/postgresql/data
42 | networks:
43 | - security
44 | healthcheck:
45 | test: ["CMD", "pg_isready", "-q", "-d", "${DB_DATABASE:-security}", "-U", "${DB_USERNAME:-security}"]
46 | retries: 3
47 | timeout: 5s
48 | license:
49 | build:
50 | context: ./license
51 | dockerfile: Dockerfile
52 | args:
53 | WWWGROUP: '${WWWGROUP}'
54 | extra_hosts:
55 | - 'host.docker.internal:host-gateway'
56 | ports:
57 | - '${APP_LICENSE_PORT:-8083}:80'
58 | env_file:
59 | - ./license/.env
60 | environment:
61 | WWWUSER: '${WWWUSER}'
62 | APP_IN_DOCKER: 1
63 | XDEBUG_MODE: '${APP_LICENSE_XDEBUG_MODE:-off}'
64 | XDEBUG_CONFIG: '${APP_LICENSE_XDEBUG_CONFIG:-client_host=host.docker.internal}'
65 | IGNITION_LOCAL_SITES_PATH: '${PWD}'
66 | volumes:
67 | - './license:/var/www/html'
68 | - './secrets:/tmp/secrets'
69 | networks:
70 | - license
71 | - file-management
72 | - backend
73 | - vpc
74 | depends_on:
75 | - license-db
76 | license-db:
77 | image: postgres:${APP_LICENSE_DB_IMAGE_VERSION}
78 | ports:
79 | - '${APP_LICENSE_DB_PORT:-5433}:5432'
80 | env_file:
81 | - ./license/.env
82 | environment:
83 | PGPASSWD: '${DB_PASSWORD:-secret}'
84 | POSTGRES_DB: '${DB_DATABASE:-license}'
85 | POSTGRES_USER: '${DB_USERNAME:-license}'
86 | POSTGRES_PASSWORD: '${DB_PASSWORD:-secret}'
87 | volumes:
88 | - license-db-data:/var/lib/postgresql/data
89 | networks:
90 | - license
91 | healthcheck:
92 | test: ["CMD", "pg_isready", "-q", "-d", "${DB_DATABASE:-license}", "-U", "${DB_USERNAME:-license}"]
93 | retries: 3
94 | timeout: 5s
95 | file-management:
96 | build:
97 | context: ./file-management
98 | dockerfile: Dockerfile
99 | args:
100 | WWWGROUP: '${WWWGROUP}'
101 | extra_hosts:
102 | - 'host.docker.internal:host-gateway'
103 | ports:
104 | - '${APP_FILEMANAGEMENT_PORT:-8084}:80'
105 | env_file:
106 | - ./file-management/.env
107 | environment:
108 | WWWUSER: '${WWWUSER}'
109 | APP_IN_DOCKER: 1
110 | XDEBUG_MODE: '${APP_FILEMANAGEMENT_XDEBUG_MODE:-off}'
111 | XDEBUG_CONFIG: '${APP_FILEMANAGEMENT_XDEBUG_CONFIG:-client_host=host.docker.internal}'
112 | IGNITION_LOCAL_SITES_PATH: '${PWD}'
113 | volumes:
114 | - './file-management:/var/www/html'
115 | - './secrets:/tmp/secrets'
116 | networks:
117 | - file-management
118 | - license
119 | - backend
120 | - vpc
121 | depends_on:
122 | - file-management-db
123 | file-management-db:
124 | image: postgres:${APP_FILEMANAGEMENT_DB_IMAGE_VERSION}
125 | ports:
126 | - '${APP_FILEMANAGEMENT_DB_PORT:-5434}:5432'
127 | env_file:
128 | - ./file-management/.env
129 | environment:
130 | PGPASSWD: '${DB_PASSWORD:-secret}'
131 | POSTGRES_DB: '${DB_DATABASE:-filemanagement}'
132 | POSTGRES_USER: '${DB_USERNAME:-filemanagement}'
133 | POSTGRES_PASSWORD: '${DB_PASSWORD:-secret}'
134 | volumes:
135 | - file-management-db-data:/var/lib/postgresql/data
136 | networks:
137 | - file-management
138 | healthcheck:
139 | test: ["CMD", "pg_isready", "-q", "-d", "${DB_DATABASE:-filemanagement}", "-U", "${DB_USERNAME:-filemanagement}"]
140 | retries: 3
141 | timeout: 5s
142 | notification:
143 | build:
144 | context: ./notification
145 | dockerfile: Dockerfile
146 | args:
147 | WWWGROUP: '${WWWGROUP}'
148 | extra_hosts:
149 | - 'host.docker.internal:host-gateway'
150 | ports:
151 | - '${APP_NOTIFICATION_PORT:-8085}:80'
152 | env_file:
153 | - ./notification/.env
154 | environment:
155 | WWWUSER: '${WWWUSER}'
156 | APP_IN_DOCKER: 1
157 | XDEBUG_MODE: '${APP_NOTIFICATION_XDEBUG_MODE:-off}'
158 | XDEBUG_CONFIG: '${APP_NOTIFICATION_XDEBUG_CONFIG:-client_host=host.docker.internal}'
159 | IGNITION_LOCAL_SITES_PATH: '${PWD}'
160 | volumes:
161 | - './notification:/var/www/html'
162 | - './secrets:/tmp/secrets'
163 | networks:
164 | - notification
165 | - backend
166 | - vpc
167 | depends_on:
168 | - notification-db
169 | notification-db:
170 | image: postgres:${APP_NOTIFICATION_DB_IMAGE_VERSION}
171 | ports:
172 | - '${APP_NOTIFICATION_DB_PORT:-5435}:5432'
173 | env_file:
174 | - ./notification/.env
175 | environment:
176 | PGPASSWD: '${DB_PASSWORD:-secret}'
177 | POSTGRES_DB: '${DB_DATABASE:-notification}'
178 | POSTGRES_USER: '${DB_USERNAME:-notification}'
179 | POSTGRES_PASSWORD: '${DB_PASSWORD:-secret}'
180 | volumes:
181 | - notification-db-data:/var/lib/postgresql/data
182 | networks:
183 | - notification
184 | healthcheck:
185 | test: ["CMD", "pg_isready", "-q", "-d", "${DB_DATABASE:-notification}", "-U", "${DB_USERNAME:-notification}"]
186 | retries: 3
187 | timeout: 5s
188 | zoo1:
189 | image: ${ZOOKEEPER_IMAGE}:${ZOOKEEPER_IMAGE_VERSION}
190 | restart: unless-stopped
191 | ports:
192 | - "2181:2181"
193 | environment:
194 | ZOOKEEPER_SERVER_ID: 1
195 | ZOOKEEPER_CLIENT_PORT: 2181
196 | ZOOKEEPER_TICK_TIME: 2000
197 | ZOOKEEPER_INIT_LIMIT: 10
198 | ZOOKEEPER_SYNC_LIMIT: 5
199 | ZOOKEEPER_PEER_PORT: 2888
200 | ZOOKEEPER_LEADER_PORT: 3888
201 | ZOOKEEPER_SERVERS: zoo1:2888:3888;zoo2:2888:3888;zoo3:2888:3888
202 | volumes:
203 | - zookeeper1-data:/var/lib/zookeeper/data
204 | - zookeeper1-logs:/var/lib/zookeeper/log
205 | networks:
206 | - backend
207 | zoo2:
208 | image: ${ZOOKEEPER_IMAGE}:${ZOOKEEPER_IMAGE_VERSION}
209 | restart: unless-stopped
210 | ports:
211 | - "2182:2182"
212 | environment:
213 | ZOOKEEPER_SERVER_ID: 2
214 | ZOOKEEPER_CLIENT_PORT: 2182
215 | ZOOKEEPER_TICK_TIME: 2000
216 | ZOOKEEPER_INIT_LIMIT: 10
217 | ZOOKEEPER_SYNC_LIMIT: 5
218 | ZOOKEEPER_PEER_PORT: 2888
219 | ZOOKEEPER_LEADER_PORT: 3888
220 | ZOOKEEPER_SERVERS: zoo1:2888:3888;zoo2:2888:3888;zoo3:2888:3888
221 | volumes:
222 | - zookeeper2-data:/var/lib/zookeeper/data
223 | - zookeeper2-logs:/var/lib/zookeeper/log
224 | networks:
225 | - backend
226 | zoo3:
227 | image: ${ZOOKEEPER_IMAGE}:${ZOOKEEPER_IMAGE_VERSION}
228 | restart: unless-stopped
229 | ports:
230 | - "2183:2183"
231 | environment:
232 | ZOOKEEPER_SERVER_ID: 3
233 | ZOOKEEPER_CLIENT_PORT: 2183
234 | ZOOKEEPER_TICK_TIME: 2000
235 | ZOOKEEPER_INIT_LIMIT: 10
236 | ZOOKEEPER_SYNC_LIMIT: 5
237 | ZOOKEEPER_PEER_PORT: 2888
238 | ZOOKEEPER_LEADER_PORT: 3888
239 | ZOOKEEPER_SERVERS: zoo1:2888:3888;zoo2:2888:3888;zoo3:2888:3888
240 | volumes:
241 | - zookeeper3-data:/var/lib/zookeeper/data
242 | - zookeeper3-logs:/var/lib/zookeeper/log
243 | networks:
244 | - backend
245 | zoonavigator:
246 | image: elkozmon/zoonavigator:${ZOONAVIGATOR_VERSION}
247 | restart: unless-stopped
248 | ports:
249 | - "19000:9000"
250 | environment:
251 | - CONNECTION_ZK1_NAME=zoo1
252 | - CONNECTION_ZK1_CONN=zoo1:2181
253 | - CONNECTION_ZK2_NAME=zoo2
254 | - CONNECTION_ZK2_CONN=zoo2:2182
255 | - CONNECTION_ZK3_NAME=zoo3
256 | - CONNECTION_ZK3_CONN=zoo3:2183
257 | - AUTO_CONNECT_CONNECTION_ID=ZK1
258 | depends_on:
259 | - zoo1
260 | - zoo2
261 | - zoo3
262 | networks:
263 | - backend
264 | - vpc
265 | kafka1:
266 | image: ${KAFKA_IMAGE}:${KAFKA_IMAGE_VERSION}
267 | ports:
268 | - "9092:9092"
269 | - "29092:29092"
270 | - "9997:9997"
271 | environment:
272 | KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka1:19092,EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9092,DOCKER://host.docker.internal:29092
273 | KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,DOCKER:PLAINTEXT
274 | KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
275 | KAFKA_ZOOKEEPER_CONNECT: "zoo1:2181,zoo2:2182,zoo3:2183"
276 | KAFKA_BROKER_ID: 1
277 | KAFKA_JMX_PORT: 9997
278 | KAFKA_JMX_OPTS: -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=kafka1 -Dcom.sun.management.jmxremote.rmi.port=9997
279 | KAFKA_LOG4J_LOGGERS: "kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO"
280 | KAFKA_AUTHORIZER_CLASS_NAME: kafka.security.authorizer.AclAuthorizer
281 | KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: "true"
282 | depends_on:
283 | - zoo1
284 | - zoo2
285 | - zoo3
286 | networks:
287 | - backend
288 | volumes:
289 | - kafka1-data:/var/lib/kafka/data
290 | kafka2:
291 | image: ${KAFKA_IMAGE}:${KAFKA_IMAGE_VERSION}
292 | ports:
293 | - "9093:9093"
294 | - "29093:29093"
295 | - "9998:9998"
296 | environment:
297 | KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka2:19093,EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9093,DOCKER://host.docker.internal:29093
298 | KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,DOCKER:PLAINTEXT
299 | KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
300 | KAFKA_ZOOKEEPER_CONNECT: "zoo1:2181,zoo2:2182,zoo3:2183"
301 | KAFKA_BROKER_ID: 2
302 | KAFKA_JMX_PORT: 9998
303 | KAFKA_JMX_OPTS: -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=kafka1 -Dcom.sun.management.jmxremote.rmi.port=9998
304 | KAFKA_LOG4J_LOGGERS: "kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO"
305 | KAFKA_AUTHORIZER_CLASS_NAME: kafka.security.authorizer.AclAuthorizer
306 | KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: "true"
307 | depends_on:
308 | - zoo1
309 | - zoo2
310 | - zoo3
311 | networks:
312 | - backend
313 | volumes:
314 | - kafka2-data:/var/lib/kafka/data
315 | kafka3:
316 | image: ${KAFKA_IMAGE}:${KAFKA_IMAGE_VERSION}
317 | ports:
318 | - "9094:9094"
319 | - "29094:29094"
320 | - "9999:9999"
321 | environment:
322 | KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka3:19094,EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9094,DOCKER://host.docker.internal:29094
323 | KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,DOCKER:PLAINTEXT
324 | KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
325 | KAFKA_ZOOKEEPER_CONNECT: "zoo1:2181,zoo2:2182,zoo3:2183"
326 | KAFKA_BROKER_ID: 3
327 | KAFKA_JMX_PORT: 9999
328 | KAFKA_JMX_OPTS: -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=kafka2 -Dcom.sun.management.jmxremote.rmi.port=9999
329 | KAFKA_LOG4J_LOGGERS: "kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO"
330 | KAFKA_AUTHORIZER_CLASS_NAME: kafka.security.authorizer.AclAuthorizer
331 | KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: "true"
332 | depends_on:
333 | - zoo1
334 | - zoo2
335 | - zoo3
336 | networks:
337 | - backend
338 | volumes:
339 | - kafka3-data:/var/lib/kafka/data
340 | schemaregistry1:
341 | image: ${SCHEMA_REGISTRY_IMAGE}:${SCHEMA_REGISTRY_IMAGE_VERSION}
342 | ports:
343 | - 8085:8085
344 | environment:
345 | SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS: PLAINTEXT://kafka1:19092
346 | SCHEMA_REGISTRY_KAFKASTORE_SECURITY_PROTOCOL: PLAINTEXT
347 | SCHEMA_REGISTRY_HOST_NAME: schemaregistry1
348 | SCHEMA_REGISTRY_LISTENERS: http://schemaregistry1:8085
349 |
350 | SCHEMA_REGISTRY_SCHEMA_REGISTRY_INTER_INSTANCE_PROTOCOL: "http"
351 | SCHEMA_REGISTRY_LOG4J_ROOT_LOGLEVEL: INFO
352 | SCHEMA_REGISTRY_KAFKASTORE_TOPIC: _schemas
353 | depends_on:
354 | - kafka1
355 | networks:
356 | - backend
357 | schemaregistry2:
358 | image: ${SCHEMA_REGISTRY_IMAGE}:${SCHEMA_REGISTRY_IMAGE_VERSION}
359 | ports:
360 | - 18085:8085
361 | environment:
362 | SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS: PLAINTEXT://kafka2:19093
363 | SCHEMA_REGISTRY_KAFKASTORE_SECURITY_PROTOCOL: PLAINTEXT
364 | SCHEMA_REGISTRY_HOST_NAME: schemaregistry2
365 | SCHEMA_REGISTRY_LISTENERS: http://schemaregistry2:8085
366 |
367 | SCHEMA_REGISTRY_SCHEMA_REGISTRY_INTER_INSTANCE_PROTOCOL: "http"
368 | SCHEMA_REGISTRY_LOG4J_ROOT_LOGLEVEL: INFO
369 | SCHEMA_REGISTRY_KAFKASTORE_TOPIC: _schemas
370 | depends_on:
371 | - kafka1
372 | networks:
373 | - backend
374 | kafka-connect1:
375 | image: ${KAFKA_CONNECT_IMAGE}:${KAFKA_CONNECT_IMAGE_VERSION}
376 | ports:
377 | - 8083:8083
378 | environment:
379 | CONNECT_BOOTSTRAP_SERVERS: kafka1:19092
380 | CONNECT_GROUP_ID: compose-connect-group
381 | CONNECT_CONFIG_STORAGE_TOPIC: _connect_configs
382 | CONNECT_CONFIG_STORAGE_REPLICATION_FACTOR: 1
383 | CONNECT_OFFSET_STORAGE_TOPIC: _connect_offset
384 | CONNECT_OFFSET_STORAGE_REPLICATION_FACTOR: 1
385 | CONNECT_STATUS_STORAGE_TOPIC: _connect_status
386 | CONNECT_STATUS_STORAGE_REPLICATION_FACTOR: 1
387 | CONNECT_KEY_CONVERTER: org.apache.kafka.connect.storage.StringConverter
388 | CONNECT_KEY_CONVERTER_SCHEMA_REGISTRY_URL: http://schemaregistry1:8085
389 | CONNECT_VALUE_CONVERTER: org.apache.kafka.connect.storage.StringConverter
390 | CONNECT_VALUE_CONVERTER_SCHEMA_REGISTRY_URL: http://schemaregistry1:8085
391 | CONNECT_INTERNAL_KEY_CONVERTER: org.apache.kafka.connect.json.JsonConverter
392 | CONNECT_INTERNAL_VALUE_CONVERTER: org.apache.kafka.connect.json.JsonConverter
393 | CONNECT_REST_ADVERTISED_HOST_NAME: kafka-connect1
394 | CONNECT_PLUGIN_PATH: "/usr/share/java,/usr/share/confluent-hub-components"
395 | depends_on:
396 | - kafka1
397 | - schemaregistry1
398 | networks:
399 | - backend
400 | kafkaui:
401 | image: provectuslabs/kafka-ui:latest
402 | ports:
403 | - 8081:8080
404 | environment:
405 | KAFKA_CLUSTERS_0_NAME: local
406 | KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka1:19092
407 | KAFKA_CLUSTERS_0_METRICS_PORT: 9997
408 | KAFKA_CLUSTERS_0_SCHEMAREGISTRY: http://schemaregistry1:8085
409 | KAFKA_CLUSTERS_0_KAFKACONNECT_0_NAME: first
410 | KAFKA_CLUSTERS_0_KAFKACONNECT_0_ADDRESS: http://kafka-connect1:8083
411 | KAFKA_CLUSTERS_1_NAME: secondLocal
412 | KAFKA_CLUSTERS_1_BOOTSTRAPSERVERS: kafka2:19093
413 | KAFKA_CLUSTERS_1_METRICS_PORT: 9998
414 | KAFKA_CLUSTERS_1_SCHEMAREGISTRY: http://schemaregistry2:8085
415 | KAFKA_CLUSTERS_2_NAME: thirdLocal
416 | KAFKA_CLUSTERS_2_BOOTSTRAPSERVERS: kafka3:19094
417 | KAFKA_CLUSTERS_2_METRICS_PORT: 9999
418 | KAFKA_CLUSTERS_2_SCHEMAREGISTRY: http://schemaregistry2:8085
419 | DYNAMIC_CONFIG_ENABLED: 'true'
420 | networks:
421 | - backend
422 | - vpc
423 | depends_on:
424 | - kafka1
425 | - kafka2
426 | - kafka3
427 | - schemaregistry1
428 | - schemaregistry2
429 | - kafka-connect1
430 | minio:
431 | image: quay.io/minio/minio:${MINIO_IMAGE_VERSION}
432 | ports:
433 | - "9000:9000"
434 | - "9001:9001"
435 | command: server /data --console-address ":9001"
436 | networks:
437 | - file-management
438 | - vpc
439 | volumes:
440 | - minio-data:/data
441 | environment:
442 | - MINIO_ROOT_USER=${MINIO_USER}
443 | - MINIO_ROOT_PASSWORD=${MINIO_PASSWORD}
444 | networks:
445 | backend:
446 | driver: bridge
447 | internal: true
448 | security:
449 | driver: bridge
450 | internal: true
451 | license:
452 | driver: bridge
453 | internal: true
454 | file-management:
455 | driver: bridge
456 | internal: true
457 | notification:
458 | driver: bridge
459 | internal: true
460 | vpc:
461 | driver: bridge
462 | volumes:
463 | zookeeper1-data:
464 | zookeeper1-logs:
465 | zookeeper2-data:
466 | zookeeper2-logs:
467 | zookeeper3-data:
468 | zookeeper3-logs:
469 | kafka1-data:
470 | kafka2-data:
471 | kafka3-data:
472 | security-db-data:
473 | license-db-data:
474 | file-management-db-data:
475 | notification-db-data:
476 | minio-data:
477 |
--------------------------------------------------------------------------------