integration

This commit is contained in:
armiejean 2025-05-09 01:12:43 +08:00
parent 5d52948d50
commit 8a24e65251
11 changed files with 317 additions and 233 deletions

View File

@ -4,61 +4,133 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Validator;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Log;
class AuthController extends Controller
{
protected $apiBaseUrl = 'http://192.168.100.6:8081/api';
/**
* Show the login form
*/
public function showLoginForm()
{
return view('login');
}
public function login(Request $request): RedirectResponse
/**
* Handle login form submission by calling the API
*/
public function login(Request $request)
{
$validator = Validator::make($request->all(), [
'username' => 'required|string',
'password' => 'required|string',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
$url = "{$this->apiBaseUrl}/cms/login_password";
$csrfToken = $request->session()->token();
try {
$response = Http::timeout(30)->post(config('services.backend_api.url') . '/api/cms/login_password', [
'username' => $request->username,
'password' => $request->password,
$response = Http::withHeaders([
'X-CSRF-TOKEN' => $csrfToken,
'Accept' => 'application/json',
])->post($url, [
'username' => $request->input('username'),
'password' => $request->input('password'),
]);
$json = $response->json();
$data = $response->json();
if ($response->successful()) {
if ($json['code'] === 200) {
Session::put('user', $json['data']['user'] ?? null);
return redirect('my-profile');
// Log the full response for debugging
Log::info('Login API Response: ', [$data]);
if ($response->successful() && isset($data['code']) && $data['code'] === 200) {
if (isset($data['data']) && isset($data['data']['access_token'])) {
session(['token' => $data['data']['access_token']]);
return redirect()->intended(route('my-profile'));
} elseif (isset($data['data']['prompt_password'])) {
session(['admin_uuid' => $data['data']['admin_uuid']]);
return redirect()->route('password.change.form');
} else {
return redirect()->back()
->withErrors(['username' => $json['message'] ?? 'Login failed.'])
->withInput();
return redirect()->back()->withErrors(['login' => 'Invalid API response or no token received.']);
}
} else {
$message = $json['message'] ?? 'Login request failed. Please try again.';
return redirect()->back()
->withErrors(['username' => $message])
->withInput();
}
} catch (\Illuminate\Http\Client\ConnectionException $e) {
return redirect()->back()
->withErrors(['username' => 'Unable to connect to the server. Please try again later.'])
->withInput();
return redirect()->back()->withErrors(['login' => $data['message'] ?? 'Login failed.']);
} catch (\Exception $e) {
return redirect()->back()
->withErrors(['username' => 'An error occurred: ' . $e->getMessage()])
->withInput();
Log::error('Login Exception: ' . $e->getMessage());
return redirect()->back()->withErrors(['login' => 'Login request failed: ' . $e->getMessage()]);
}
}
/**
* Show the change password form
*/
public function showChangePasswordForm()
{
if (!session()->has('admin_uuid')) {
return redirect()->route('login')->withErrors(['error' => 'Unauthorized access']);
}
return view('change-password');
}
/**
* Handle change password form submission by calling the API
*/
public function changePassword(Request $request)
{
$url = "{$this->apiBaseUrl}/cms/login_changePassword";
$csrfToken = $request->session()->token();
try {
$response = Http::withHeaders([
'X-CSRF-TOKEN' => $csrfToken,
'Accept' => 'application/json',
'Authorization' => 'Bearer ' . session('token'),
])->post($url, [
'admin_uuid' => $request->input('admin_uuid'),
'password' => $request->input('password'),
]);
$data = $response->json();
Log::info('Change Password API Response: ', [$data]);
if ($response->successful() && isset($data['code']) && $data['code'] === 200) {
if (isset($data['data']['access_token'])) {
session(['token' => $data['data']['access_token']]);
} elseif (isset($data['data']['token'])) {
session(['token' => $data['data']['token']]);
}
session()->forget('admin_uuid');
return redirect()->route('my-profile')->with('success', $data['message'] ?? 'Password changed successfully');
}
return redirect()->back()->withErrors(['error' => $data['message'] ?? 'Failed to change password']);
} catch (\Exception $e) {
Log::error('Change Password Exception: ' . $e->getMessage());
return redirect()->back()->withErrors(['error' => 'Password change request failed: ' . $e->getMessage()]);
}
}
/**
* Handle logout by calling the API
*/
public function logout(Request $request)
{
$url = "{$this->apiBaseUrl}/logout_cms";
$csrfToken = $request->session()->token();
try {
Http::withHeaders([
'X-CSRF-TOKEN' => $csrfToken,
'Accept' => 'application/json',
'Authorization' => 'Bearer ' . session('token'),
])->post($url);
session()->flush();
return redirect()->route('login')->with('success', 'Logged out successfully');
} catch (\Exception $e) {
Log::error('Logout Exception: ' . $e->getMessage());
session()->flush();
return redirect()->route('login')->with('success', 'Logged out successfully');
}
}
}

View File

@ -0,0 +1,86 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Session;
class ChangePasswordController extends Controller
{
protected $apiBaseUrl;
public function __construct()
{
$this->apiBaseUrl = env('API_BASE_URL', 'http://your-backend-api-url');
}
public function showChangePasswordForm()
{
return view('change-password');
}
public function updatePassword(Request $request)
{
$request->validate([
'password' => 'required|min:8|confirmed',
]);
$adminId = Session::get('admin_id');
$apiToken = Session::get('api_token');
if (!$adminId || !$apiToken) {
return redirect()->route('login')->with('error', 'You must be logged in to change your password');
}
$response = Http::withToken($apiToken)
->get("{$this->apiBaseUrl}/api/admin/{$adminId}");
if ($response->failed()) {
return redirect()->back()->with('error', 'Unable to fetch admin data. Please try again later.');
}
$admin = $response->json();
if (!$admin) {
return redirect()->route('login')->with('error', 'Admin not found');
}
$updateResponse = Http::withToken($apiToken)
->put("{$this->apiBaseUrl}/api/admin/{$adminId}", [
'password' => bcrypt($request->password),
'is_passwordChanged' => 1,
]);
if ($updateResponse->failed()) {
return redirect()->back()->with('error', 'Failed to update password. Please try again.');
}
return redirect()->route('my-profile')->with('success', 'Password updated successfully');
}
// Method to fetch and display admin profile data
public function showProfile()
{
$adminId = Session::get('admin_id');
$apiToken = Session::get('api_token');
if (!$adminId || !$apiToken) {
return redirect()->route('login')->with('error', 'You must be logged in to view your profile');
}
$response = Http::withToken($apiToken)
->get("{$this->apiBaseUrl}/api/admin/{$adminId}");
if ($response->failed()) {
return redirect()->back()->with('error', 'Unable to fetch profile data. Please try again later.');
}
$admin = $response->json();
if (!$admin) {
return redirect()->route('login')->with('error', 'Admin not found');
}
return view('my-profile', ['admin' => $admin]);
}
}

View File

@ -3,6 +3,9 @@
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\View;
class AppServiceProvider extends ServiceProvider
{
@ -19,6 +22,10 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot(): void
{
//
// Share authenticated user with the 'layouts.app' view
View::composer('layouts.app', function ($view) {
$user = Auth::user();
$view->with('user', $user);
});
}
}

View File

@ -35,9 +35,9 @@ return [
],
],
'backend_api' => [
'url' => env('BACKEND_API_URL', 'http://192.168.56.1:80'),
],
'backend_api' => [
'url' => 'http://192.168.100.6:8081', // Use the backend container name and internal port
],
];

View File

@ -9,6 +9,9 @@ services:
- .:/var/www/html
- ./storage:/var/www/html/storage
- ./bootstrap/cache:/var/www/html/bootstrap/cache
depends_on:
db_mysql:
condition: service_healthy
command: [ "sh", "-c", "/var/www/html/docker/php/entrypoint.sh" ]
healthcheck:
test: [ "CMD", "pgrep", "php-fpm" ]
@ -16,8 +19,26 @@ services:
timeout: 10s
retries: 10
networks:
- frontend_network
- unioil-mobile-api_app_network
- app_network
db_mysql:
image: mysql:8.2
container_name: unioil-db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: newpassword
MYSQL_DATABASE: unioil-database
MYSQL_USER: rootuser
MYSQL_PASSWORD: password
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ]
interval: 30s
timeout: 10s
retries: 5
networks:
- app_network
nginx:
image: nginx:alpine
@ -32,14 +53,13 @@ services:
app:
condition: service_healthy
networks:
- frontend_network
- app_network
volumes:
mysql-data:
storage-volume:
driver: local
networks:
frontend_network:
app_network:
driver: bridge
unioil-mobile-api_app_network:
external: true

View File

@ -8,57 +8,50 @@
<div class="mb-3 text-center">
<h4 class="mb-1 fw-bold">Change Password</h4>
<span style="font-size: 14px;" class="text-muted">Enter your new password</span>
<span style="font-size: 14px;" class="text-muted">Enter a new password to continue</span>
</div>
<!-- Error/Success Messages -->
@if ($errors->any())
<div class="alert alert-danger">
@foreach ($errors->all() as $error)
{{ $error }}<br>
@endforeach
</div>
@endif
@if (session('success'))
<div class="alert alert-success" role="alert">
<div class="alert alert-success">
{{ session('success') }}
</div>
@endif
@if (session('error'))
<div class="alert alert-danger" role="alert">
{{ session('error') }}
</div>
@endif
@if ($errors->any())
<div class="alert alert-danger" role="alert">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form method="POST" action="{{ route('password.submit') }}">
<form method="POST" action="{{ route('password.change') }}">
@csrf
<input type="hidden" name="admin_uuid" value="{{ session('admin_uuid') }}">
<div class="mb-3">
<label class="form-label fw-semibold" style="font-size: 13px; color: #003366;">Username</label>
<input type="text" class="form-control @error('username') is-invalid @enderror" id="username" name="username" placeholder="Username" value="{{ old('username', session('username')) }}" required readonly>
@error('username')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
<label for="password" class="form-label fw-semibold" style="font-size: 13px; color: #003366;">New Password</label>
<input type="password" class="form-control" id="password" name="password" placeholder="••••••••" required>
</div>
<div class="mb-3">
<label class="form-label fw-semibold" style="font-size: 13px; color: #003366;">New Password</label>
<input type="password" class="form-control @error('new_password') is-invalid @enderror" id="new_password" name="new_password" placeholder="New Password" required>
@error('new_password')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
<label for="password_confirmation" class="form-label fw-semibold" style="font-size: 13px; color: #003366;">Confirm Password</label>
<input type="password" class="form-control" id="password_confirmation" name="password_confirmation" placeholder="••••••••" required>
</div>
<div class="row mt-4">
<div class="col-12 text-end">
<button type="submit" class="btn btn-primary w-100" style="background-color: #E74610; border-color: #E74610;">
Change Password
</button>
<div class="col-6">
<a href="{{ route('login') }}" class="text-decoration-none text-primary">Back to Login</a>
</div>
<div class="col-6 text-end">
<button type="submit" class="btn btn-primary w-100" style="background-color: #E74610; border-color: #E74610;">Change Password</button>
</div>
</div>
</form>
</div>
</div>
</div>
<!-- Include Bootstrap 5 JS for modal functionality -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
@endsection

View File

@ -230,7 +230,7 @@
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle d-flex align-items-center gap-2" href="#"
role="button" data-toggle="dropdown">
<span style="margin-right:5px">LBTek Systems</span>
<span style="margin-right:5px">{{ $user->username }}</span>
<i class="fa-solid fa-user-circle" style="padding-right:5px"></i>
</a>
<ul class="dropdown-menu dropdown-menu-end dropdown-menu-sm">
@ -242,12 +242,13 @@
</a>
</li>
<li>
<a class="dropdown-item d-flex align-items-center gap-2"
href="{{ route('login') }}">
<i class="fa-solid fa-right-from-bracket"
style="font-size:16px; color:gray;"></i>
<span style="margin-left:5px">Logout</span>
</a>
<form action="{{ route('logout') }}" method="POST">
@csrf
<button type="submit" class="dropdown-item d-flex align-items-center gap-2 logout-btn">
<i class="fa-solid fa-right-from-bracket" style="font-size:16px; color:gray;"></i>
<span style="margin-left:5px;color:black">Logout</span>
</button>
</form>
</li>
</ul>
</li>

View File

@ -12,23 +12,16 @@
</div>
<!-- Error/Success Messages -->
@if (session('success'))
<div class="alert alert-success" role="alert">
{{ session('success') }}
</div>
@endif
@if (session('error'))
<div class="alert alert-danger" role="alert">
{{ session('error') }}
</div>
@endif
@if ($errors->any())
<div class="alert alert-danger" role="alert">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
<div class="alert alert-danger">
@foreach ($errors->all() as $error)
{{ $error }}<br>
@endforeach
</div>
@endif
@if (session('success'))
<div class="alert alert-success">
{{ session('success') }}
</div>
@endif

View File

@ -1,67 +0,0 @@
@extends('layouts.app')
@section('content')
<div class="container py-5">
<h1>Change Password</h1>
<div id="alert" class="alert d-none" role="alert"></div>
<form id="changePasswordForm">
<div class="mb-3">
<label class="form-label">New Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="mb-3">
<label class="form-label">Confirm Password</label>
<input type="password" class="form-control" id="password_confirmation" name="password_confirmation" required>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
document.getElementById('changePasswordForm').addEventListener('submit', async function (e) {
e.preventDefault();
const password = document.getElementById('password').value.trim();
const passwordConfirmation = document.getElementById('password_confirmation').value.trim();
const adminUuid = localStorage.getItem('admin_uuid');
const alertBox = document.getElementById('alert');
if (password !== passwordConfirmation) {
alertBox.classList.remove('d-none', 'alert-success');
alertBox.classList.add('alert-danger');
alertBox.textContent = 'Passwords do not match';
return;
}
const apiBaseUrl = 'http://localhost:8080/api';
try {
const response = await axios.post(${apiBaseUrl}/cms/login_changePassword, {
admin_uuid: adminUuid,
password: password,
password_confirmation: passwordConfirmation,
});
if (!response.data.success) {
throw new Error(response.data.message || 'Password change failed');
}
alertBox.classList.remove('d-none', 'alert-danger');
alertBox.classList.add('alert-success');
alertBox.textContent = 'Password changed successfully! Redirecting...';
localStorage.setItem('authToken', response.data.data.token);
localStorage.removeItem('admin_uuid');
setTimeout(() => {
window.location.href = '{{ url("/my-profile") }}';
}, 1000);
} catch (error) {
alertBox.classList.remove('d-none', 'alert-success');
alertBox.classList.add('alert-danger');
alertBox.textContent = error.response?.data?.message || error.message || 'Password change failed.';
}
});
</script>
@endsection

View File

@ -3,38 +3,37 @@
@section('page_title', 'My Profile')
@section('content')
<div class="card w-100">
<!-- Banner -->
<div class="banner d-flex align-items-center p-3">
<div class="banner-icon me-3">
<i class="fas fa-user-circle" style="font-size: 40px; color: #6c757d;"></i>
</div>
<h4 class="fw-bold text-primary mb-0" style="margin-left:10px">LBTek Systems</h4>
<div class="card w-100">
<!-- Banner -->
<div class="banner d-flex align-items-center p-3">
<div class="banner-icon me-3">
<i class="fas fa-user-circle" style="font-size: 40px; color: #6c757d;"></i>
</div>
<h4 class="fw-bold text-primary mb-0" style="margin-left:10px">{{ $user->name ?? 'User' }}</h4>
</div>
<!-- Profile Section -->
<div class="card-body p-4">
<div class="row">
<!-- Profile Information -->
<div class="col-md-9">
<h3 class="fw-bold mb-3" style="font-size: 20px; font-weight:400">My Information</h3>
<div class="mb-2">
<span class="fw-bold text-dark">Username: </span>
<span>lbteksupport</span>
</div>
<div class="mb-2">
<span class="fw-bold text-dark">Email: </span>
<a href="mailto:support@lbteksystems.com" class="text-primary">support@lbteksystems.com</a>
</div>
<div>
<span class="fw-bold text-dark">Access Role: </span>
<span>System Admin</span>
</div>
<!-- Profile Section -->
<div class="card-body p-4">
<div class="row">
<!-- Profile Information -->
<div class="col-md-9">
<h3 class="fw-bold mb-3" style="font-size: 20px; font-weight:400">My Information</h3>
<div class="mb-2">
<span class="fw-bold text-dark">Username: </span>
<span>{{ $user->username }}</span>
</div>
<div class="mb-2">
<span class="fw-bold text-dark">Email: </span>
<a href="mailto:{{ $user->email }}" class="text-primary">{{ $user->email }}</a>
</div>
<div>
<span class="fw-bold text-dark">Access Role: </span>
<span>{{ $user->role ?? 'System Admin' }}</span>
</div>
</div>
</div>
</div>
</div>
<style>
.card {
@ -99,32 +98,4 @@
}
}
</style>
<script>
const profileImage = document.getElementById('profileImage');
const pictureInput = document.getElementById('profilePictureInput');
const changePictureBtn = document.getElementById('changePicture');
// Load saved profile picture
const savedPicture = sessionStorage.getItem('profilePicture');
if (savedPicture) {
profileImage.src = savedPicture;
}
changePictureBtn?.addEventListener('click', () => {
pictureInput?.click();
});
pictureInput?.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (event) => {
profileImage.src = event.target.result;
sessionStorage.setItem('profilePicture', event.target.result);
};
reader.readAsDataURL(file);
}
});
</script>
@endsection
@endsection

View File

@ -10,11 +10,26 @@ use App\Http\Controllers\AuthController;
Route::get('/', function () {
return redirect()->route('login');
});
Route::get('/login', [AuthController::class, 'showLoginForm'])->name('login.form');
// Show login form
Route::get('/login', [AuthController::class, 'showLoginForm'])->name('login');
// Handle login form submission
Route::post('/login', [AuthController::class, 'login'])->name('login');
Route::get('/change-password', [AuthController::class, 'showChangePasswordForm'])->name('password.change');
Route::post('/change-password', [AuthController::class, 'changePassword'])->name('password.submit');
// Show change password form
Route::get('/change-password', [AuthController::class, 'showChangePasswordForm'])->name('password.change.form');
// Handle change password form submission
Route::post('/change-password', [AuthController::class, 'changePassword'])->name('password.change');
// Redirect to my-profile (adjust as needed)
Route::get('/my-profile', function () {
return view('my-profile'); // Replace with your actual profile view or controller
})->name('my-profile')->middleware('auth');
// Handle logout
Route::post('/logout', [AuthController::class, 'logout'])->name('logout');
Route::get('/dashboard', function () {
@ -90,10 +105,6 @@ Route::get('/fuels', function () {
return view('pages.station locator.fuels');
})->name('fuels');
Route::get('/my-profile', function () {
return view('pages.my-profile');
})->name('my-profile');
Route::get('/add-user', function () {
return view('pages.user-management.add-user');
})->name('add-user');
@ -160,6 +171,3 @@ Route::get('/fuel-price-update-logs', function () {
return view('pages.fuel-price-update-logs');
})->name('fuel-price-update-logs');
Route::get('/change-password', function () {
return view('pages.change-password');
})->name('change-password');