5 Commits

Author SHA1 Message Date
ismailmasseran 757cd1ca9e DONE: increase limit size for activity (#17)
Build Docker Image / build-backend (push) Successful in 1m34s
Build Docker Image / build-frontend (push) Successful in 8s
Co-authored-by: ISMAIL MASSERAN <topaz@ISMAILs-Macbook.local>
Reviewed-on: #17
2026-08-11 15:01:12 +08:00
ismailmasseran 820f8e46d9 DONE: fix error where the verification uses localhost after user register, use malay name in membership application (#16)
Build Docker Image / build-backend (push) Successful in 45s
Build Docker Image / build-frontend (push) Successful in 7s
Co-authored-by: ISMAIL MASSERAN <topaz@ISMAILs-Macbook.local>
Reviewed-on: #16
2026-08-10 14:44:43 +08:00
ISMAIL MASSERAN db3ac37042 DONE: update prefix for BOARD_MEETING_REFERENCE_PREFIX, add placeholder for address
Build Docker Image / build-backend (push) Successful in 2m52s
Build Docker Image / build-frontend (push) Successful in 19s
2026-08-09 11:46:37 +08:00
ISMAIL MASSERAN 605ccf5a7b DONE: increase limit size of profile pic, fix swal error when uploading profile pic large than 2 mb on prod
Build Docker Image / build-backend (push) Successful in 48s
Build Docker Image / build-frontend (push) Successful in 10s
2026-07-19 12:58:47 +08:00
ismailmasseran 06b687f408 DONE: fix bypass password on production not working (#14)
Build Docker Image / build-backend (push) Successful in 4m51s
Build Docker Image / build-frontend (push) Successful in 15s
Co-authored-by: ISMAIL MASSERAN <topaz@ISMAILs-Macbook.local>
Reviewed-on: #14
2026-07-16 11:56:03 +08:00
17 changed files with 89 additions and 115 deletions
+43 -18
View File
@@ -1,9 +1,12 @@
APP_NAME="SUTERA 3.0 Dev" APP_NAME="MYKOPKB 1.0"
APP_ENV=local APP_ENV=local
APP_KEY=base64:0dM5HYEFRu9P6ni51QTUZm5mQ23znc/s/aebLTRUGxU= APP_KEY=base64:0dM5HYEFRu9P6ni51QTUZm5mQ23znc/s/aebLTRUGxU=
APP_DEBUG=true APP_DEBUG=true
APP_TIMEZONE=Asia/Kuala_Lumpur APP_TIMEZONE=Asia/Kuala_Lumpur
APP_URL=http://localhost APP_URL=http://localhost
FRONTEND_URL=http://localhost:5173
TELESCOPE_ENABLED=true
APP_LOCALE=en APP_LOCALE=en
APP_FALLBACK_LOCALE=en APP_FALLBACK_LOCALE=en
@@ -24,8 +27,8 @@ LOG_LEVEL=debug
DB_CONNECTION=pgsql DB_CONNECTION=pgsql
DB_HOST=pgsql DB_HOST=pgsql
DB_PORT=5432 DB_PORT=5432
DB_DATABASE=sutera DB_DATABASE=mykopkb
DB_USERNAME=suterauser DB_USERNAME=mykopkbuser
DB_PASSWORD=password DB_PASSWORD=password
SESSION_DRIVER=redis SESSION_DRIVER=redis
@@ -38,8 +41,11 @@ BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local FILESYSTEM_DISK=local
QUEUE_CONNECTION=database QUEUE_CONNECTION=database
HORIZON_WORKER_MEMORY=512
HORIZON_WORKER_TIMEOUT=3600
CACHE_STORE=redis CACHE_STORE=redis
CACHE_PREFIX=sutera_cache CACHE_PREFIX=mykopkb_cache
MEMCACHED_HOST=memcached MEMCACHED_HOST=memcached
@@ -54,8 +60,9 @@ MAIL_HOST=mailpit
MAIL_PORT=1025 MAIL_PORT=1025
MAIL_USERNAME=null MAIL_USERNAME=null
MAIL_PASSWORD=null MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.com" MAIL_FROM_ADDRESS="no-reply@army.mil.my"
MAIL_FROM_NAME="${APP_NAME}" MAIL_FROM_NAME="${APP_NAME}"
MAIL_LOGO_PATH="images/logo/logo-kopkb-letterhead.svg"
AWS_ACCESS_KEY_ID= AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY= AWS_SECRET_ACCESS_KEY=
@@ -73,18 +80,6 @@ VITE_APP_NAME="${APP_NAME}"
EXTERNAL_API_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsibWJzcC1yZXNvdXJjZUlkIl0sInRpbWVvdXRQZXJpb2QiOjkwMCwidXNlcl9uYW1lIjoic3BhLXVzZXIiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNzI4NTgzNjEyLCJqdGkiOiJjMmU2OTkxOS0yZDU3LTQ5YmYtYTVmZS1lZjVmMzIyNDZkZDIiLCJjbGllbnRfaWQiOiJzcGEtY2xpZW50In0.LE2byaRzsYkchzI4ox9Gz2u6jNmTqZQa-_ttR2PY8e8" EXTERNAL_API_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsibWJzcC1yZXNvdXJjZUlkIl0sInRpbWVvdXRQZXJpb2QiOjkwMCwidXNlcl9uYW1lIjoic3BhLXVzZXIiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNzI4NTgzNjEyLCJqdGkiOiJjMmU2OTkxOS0yZDU3LTQ5YmYtYTVmZS1lZjVmMzIyNDZkZDIiLCJjbGllbnRfaWQiOiJzcGEtY2xpZW50In0.LE2byaRzsYkchzI4ox9Gz2u6jNmTqZQa-_ttR2PY8e8"
EXTERNAL_API_BASE_URL=http://20.11.32.134/rest/td/gasset/asset EXTERNAL_API_BASE_URL=http://20.11.32.134/rest/td/gasset/asset
SSO_SECRET="Y6g#Rb@Km=Nz]o^X+fEC~<W/LP%qh;M}9T&v!["
SSO_VALIDATION_URL=http://host.docker.internal:8080/api/sso-validate
SSO_MAX_ATTEMPTS=5
SSO_TOKEN_EXPIRATION=300
SSO_AUTO_ACTIVATE_USERS=true
SSO_LOG_ACTIVITIES=true
SSO_CONNECTION_TIMEOUT=10
SSO_RESPONSE_TIMEOUT=30
SSO_PROXY_URL=http://20.9.91.1:3128
SSO_ENABLE_FALLBACK=true
SSO_PROXY_ENABLED=false
SANCTUM_STATEFUL_DOMAINS=http://localhost SANCTUM_STATEFUL_DOMAINS=http://localhost
BLOCK_API_TOOLS_IN_PRODUCTION=true BLOCK_API_TOOLS_IN_PRODUCTION=true
@@ -97,5 +92,35 @@ ENABLE_API_KEY_AUTH=true
API_VALID_KEYS=86f58825e5c1e0872d8786092d47e3bd,6461fc5390660c55dc47bdfcd85ed9ae API_VALID_KEYS=86f58825e5c1e0872d8786092d47e3bd,6461fc5390660c55dc47bdfcd85ed9ae
API_LOG_KEY_USAGE=true API_LOG_KEY_USAGE=true
# HttpOnly Sanctum token cookie (set on api.* domain)
AUTH_COOKIE_NAME=auth_token
AUTH_COOKIE_LIFETIME=360
AUTH_COOKIE_SECURE=true
AUTH_COOKIE_SAME_SITE=lax
AUTH_COOKIE_EXPOSE_TOKEN=false
# Browsershot (PDF generation via headless Chrome + Puppeteer in be/)
# Required in Docker/Sail. After first sail up: sail npm install && sail npx puppeteer browsers install chrome-headless-shell
BROWSERSHOT_NO_SANDBOX=true
# BROWSERSHOT_NODE_BINARY=
# BROWSERSHOT_NPM_BINARY=
# BROWSERSHOT_NODE_MODULE_PATH=
# BROWSERSHOT_CHROME_PATH=
# BROWSERSHOT_TIMEOUT=60
PUBLIC_PROFILE_TOKEN_TTL_DAYS=7 PUBLIC_PROFILE_TOKEN_TTL_DAYS=7
FRONTEND_URL=https://mykopkb.koppkb.com
EMAIL_VERIFICATION_EXPIRE_MINUTES=60
EMAIL_VERIFICATION_REDIRECT_PATH=/profile-overview-2
PHONE_VERIFICATION_OTP_EXPIRY_MINUTES=10
PHONE_VERIFICATION_TOKEN_EXPIRY_MINUTES=30
PHONE_VERIFICATION_OTP_MAX_ATTEMPTS=5
SMS_DRIVER=log
ONEWAYSMS_BASE_URL=http://gateway.onewaysms.com.my:10001/api.aspx
ONEWAYSMS_API_USERNAME=API7LO0ELJTAU
ONEWAYSMS_API_PASSWORD=API7LO0ELJTAU7LO0E
ONEWAYSMS_SENDER_ID=INFO
EXTERNAL_SYSTEM_SSO_ISSUER=mykopkb
-88
View File
@@ -1,88 +0,0 @@
APP_NAME="SUTERA 3.0"
APP_ENV=staging
APP_KEY=base64:9vnA0vvAweC8C+/WKyEDOziWp4QPsdKZ/blgDZXIbKQ=
APP_DEBUG=false
APP_TIMEZONE=Asia/Kuala_Lumpur
APP_URL=https://sutera.koppkb.com
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
PHP_CLI_SERVER_WORKERS=8
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=error
DB_CONNECTION=pgsql
DB_HOST=pgsql
DB_PORT=5432
DB_DATABASE=sutera
DB_USERNAME=suterauser
DB_PASSWORD=Aretus@2025
DB_ROOT_PASSWORD=Sutera_Staging@root_2025
SESSION_DRIVER=redis
SESSION_LIFETIME=60
SESSION_ENCRYPT=true
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=redis
CACHE_STORE=redis
CACHE_PREFIX=sutera_cache
CACHE_DRIVER=redis
MEMCACHED_HOST=memcached
REDIS_CLIENT=phpredis
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=sutera_redis@2025
REDIS_DB=0
MAIL_MAILER=smtp
MAIL_SCHEME=null
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"
# VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
# VITE_PUSHER_HOST="${PUSHER_HOST}"
# VITE_PUSHER_PORT="${PUSHER_PORT}"
# VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
# VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
EXTERNAL_API_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsibWJzcC1yZXNvdXJjZUlkIl0sInRpbWVvdXRQZXJpb2QiOjkwMCwidXNlcl9uYW1lIjoic3BhLXVzZXIiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNzI4NTgzNjEyLCJqdGkiOiJjMmU2OTkxOS0yZDU3LTQ5YmYtYTVmZS1lZjVmMzIyNDZkZDIiLCJjbGllbnRfaWQiOiJzcGEtY2xpZW50In0.LE2byaRzsYkchzI4ox9Gz2u6jNmTqZQa-_ttR2PY8e8"
EXTERNAL_API_BASE_URL=http://20.11.32.134/rest/td/gasset/asset
SSO_SECRET="Y6g#Rb@Km=Nz]o^X+fEC~<W/LP%qh;M}9T&v!["
SSO_VALIDATION_URL=http://host.docker.internal:8080/api/sso-validate
SSO_MAX_ATTEMPTS=5
SSO_TOKEN_EXPIRATION=300
SSO_AUTO_ACTIVATE_USERS=true
SSO_LOG_ACTIVITIES=true
SSO_CONNECTION_TIMEOUT=10
SSO_RESPONSE_TIMEOUT=30
SSO_PROXY_URL=http://20.9.91.1:3128
SSO_ENABLE_FALLBACK=true
@@ -38,6 +38,14 @@ class VerifyEmailNotification extends Notification
protected function verificationUrl(User $notifiable): string protected function verificationUrl(User $notifiable): string
{ {
$expireMinutes = (int) config('auth.verification.expire_minutes', 60); $expireMinutes = (int) config('auth.verification.expire_minutes', 60);
$root = rtrim((string) config('app.url'), '/');
// Prefer APP_URL over the current request host so queued/local proxy
// contexts do not embed http://localhost in production emails.
URL::forceRootUrl($root);
if ($scheme = parse_url($root, PHP_URL_SCHEME)) {
URL::forceScheme($scheme);
}
return URL::temporarySignedRoute( return URL::temporarySignedRoute(
'verification.verify', 'verification.verify',
@@ -2,7 +2,9 @@
namespace Modules\Auth\Providers; namespace Modules\Auth\Providers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
class EventServiceProvider extends ServiceProvider class EventServiceProvider extends ServiceProvider
{ {
@@ -24,4 +26,13 @@ class EventServiceProvider extends ServiceProvider
* Configure the proper event listeners for email verification. * Configure the proper event listeners for email verification.
*/ */
protected function configureEmailVerification(): void {} protected function configureEmailVerification(): void {}
public function boot(): void
{
// Framework auto-registers SendEmailVerificationNotification on Registered.
// Verification is user-initiated via the email verification banner instead.
$this->app->booted(function () {
Event::forget(Registered::class);
});
}
} }
@@ -4,12 +4,11 @@
@section('letter-reference', $application->application_number) @section('letter-reference', $application->application_number)
@section('letter-date', now()->translatedFormat('d F Y')) @section('letter-date', now()->locale('ms')->translatedFormat('d F Y'))
@section('letter-recipient') @section('letter-recipient')
<strong>{{ $applicant->name }}</strong><br> <strong>{{ $applicant->name }}</strong><br>
{!! nl2br(e($applicant->address)) !!}<br> {!! nl2br(e($applicant->address)) !!}
{{ $applicant->postcode }}
@endsection @endsection
@section('letter-honorific') @section('letter-honorific')
@@ -444,7 +444,7 @@ class MembershipApplicationService
'application' => $application, 'application' => $application,
'applicant' => $applicant, 'applicant' => $applicant,
'boardMeetingReference' => $boardMeetingReference, 'boardMeetingReference' => $boardMeetingReference,
'boardMeetingDate' => Carbon::parse($boardMeetingDate)->translatedFormat('d F Y'), 'boardMeetingDate' => Carbon::parse($boardMeetingDate)->locale('ms')->translatedFormat('d F Y'),
'isPassed' => $isPassed, 'isPassed' => $isPassed,
'letterSubject' => $isPassed 'letterSubject' => $isPassed
? 'KELULUSAN PERMOHONAN MENJADI ANGGOTA KOPERASI PERMODALAN KELANTAN BERHAD (KoPKB)' ? 'KELULUSAN PERMOHONAN MENJADI ANGGOTA KOPERASI PERMODALAN KELANTAN BERHAD (KoPKB)'
@@ -122,7 +122,7 @@ class UserController extends BaseCrudController
'ic_number' => 'sometimes|string|max:255|unique:users,ic_number,'.$user->id, 'ic_number' => 'sometimes|string|max:255|unique:users,ic_number,'.$user->id,
'position' => 'sometimes|string|max:255', 'position' => 'sometimes|string|max:255',
'phone_number' => 'sometimes|string|max:255', 'phone_number' => 'sometimes|string|max:255',
'image' => 'sometimes|image|mimes:jpeg,png,jpg,gif|max:2048', 'image' => 'sometimes|image|mimes:jpeg,png,jpg,gif|max:5120',
'image_url' => 'sometimes|string|max:255', 'image_url' => 'sometimes|string|max:255',
'gender' => 'sometimes|string|max:255', 'gender' => 'sometimes|string|max:255',
'marriage_status' => 'sometimes|string|max:255', 'marriage_status' => 'sometimes|string|max:255',
+1 -1
View File
@@ -106,7 +106,7 @@ class FortifyServiceProvider extends ServiceProvider
$failLogin(); $failLogin();
} }
$shouldBypassPassword = config('app.env', 'local'); $shouldBypassPassword = app()->environment('local');
if (! $shouldBypassPassword && ! Hash::check($request->password, $user->password)) { if (! $shouldBypassPassword && ! Hash::check($request->password, $user->password)) {
$failLogin(); $failLogin();
+3
View File
@@ -163,6 +163,9 @@ COPY --from=builder /usr/local/bin/docker-php-ext-* /usr/local/bin/
# Use the recommended production PHP configuration # Use the recommended production PHP configuration
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# Custom PHP overrides (upload limits, etc.) — official image loads conf.d/*.ini
COPY be/docker/common/unified/php/uploads.ini /usr/local/etc/php/conf.d/uploads.ini
# Enable PHP-FPM status page via separate config file (avoid modifying zz-docker.conf) # Enable PHP-FPM status page via separate config file (avoid modifying zz-docker.conf)
RUN echo '[www]' > /usr/local/etc/php-fpm.d/zzz-status.conf && \ RUN echo '[www]' > /usr/local/etc/php-fpm.d/zzz-status.conf && \
echo 'pm.status_path = /status' >> /usr/local/etc/php-fpm.d/zzz-status.conf echo 'pm.status_path = /status' >> /usr/local/etc/php-fpm.d/zzz-status.conf
@@ -9,6 +9,8 @@ http {
sendfile on; sendfile on;
keepalive_timeout 65; keepalive_timeout 65;
client_max_body_size 128M;
# Logging # Logging
access_log /var/log/nginx/access.log; access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log; error_log /var/log/nginx/error.log;
+3
View File
@@ -0,0 +1,3 @@
; Keep above Laravel validation (max:10240 = 10MB)
upload_max_filesize = 12M
post_max_size = 12M
+2
View File
@@ -8,6 +8,8 @@ http {
sendfile on; sendfile on;
keepalive_timeout 65; keepalive_timeout 65;
client_max_body_size 128M;
access_log /var/log/nginx/access.log; access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log; error_log /var/log/nginx/error.log;
+2
View File
@@ -9,6 +9,8 @@ http {
sendfile on; sendfile on;
keepalive_timeout 65; keepalive_timeout 65;
client_max_body_size 128M;
# Logging # Logging
access_log /var/log/nginx/access.log; access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log; error_log /var/log/nginx/error.log;
@@ -692,7 +692,12 @@ function stepLabelClass(stepId: number) {
<template v-else-if="currentStep === 2"> <template v-else-if="currentStep === 2">
<Field class="col-span-12"> <Field class="col-span-12">
<FieldLabel for="address">Alamat</FieldLabel> <FieldLabel for="address">Alamat</FieldLabel>
<Textarea id="address" v-model="form.applicant.address" rows="3" /> <Textarea
id="address"
v-model="form.applicant.address"
rows="3"
placeholder="Lot Pt 448, Tingkat 1, Jalan Kuala Krai, Batu 3, Wakaf Che Yeh, 15150 Kota Bharu, Kelantan."
/>
<FieldError v-if="fieldErrors['applicant.address']">{{ fieldErrors['applicant.address'] }}</FieldError> <FieldError v-if="fieldErrors['applicant.address']">{{ fieldErrors['applicant.address'] }}</FieldError>
</Field> </Field>
<Field class="col-span-12 sm:col-span-4"> <Field class="col-span-12 sm:col-span-4">
@@ -65,7 +65,7 @@ const DOCUMENT_TYPE_LABELS: Record<string, string> = {
[RESULT_LETTER_DOCUMENT_TYPE]: 'Surat Keputusan', [RESULT_LETTER_DOCUMENT_TYPE]: 'Surat Keputusan',
} }
const BOARD_MEETING_REFERENCE_PREFIX = 'Mesyuarat Lembaga Bil.' const BOARD_MEETING_REFERENCE_PREFIX = 'Mesyuarat Lembaga KoPKB Bil. '
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
@@ -190,6 +190,9 @@ onMounted(async () => {
No. {{ authStore.userMemberNumber }} No. {{ authStore.userMemberNumber }}
</Badge> </Badge>
</div> </div>
<p class="mt-2 text-xs opacity-70">
Gambar profil: maksimum 2 MB (JPEG, PNG, GIF).
</p>
</div> </div>
</div> </div>
-1
View File
@@ -318,7 +318,6 @@ async function confirmRestore() {
const headers: TableHeader[] = [ const headers: TableHeader[] = [
{ title: 'Bil.', key: '#', sortable: false }, { title: 'Bil.', key: '#', sortable: false },
{ title: 'Name', key: 'name', sortable: true }, { title: 'Name', key: 'name', sortable: true },
{ title: 'Jenis Anggota', key: 'member_type', sortable: true },
{ {
title: 'Unit', title: 'Unit',
key: 'company_name', key: 'company_name',