Initial commit of unified WebGIS projects
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
include __DIR__ . '/../../config/koneksi.php';
|
||||
include __DIR__ . '/../../config/auth.php';
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$admin = require_bearer_login();
|
||||
if(!isset($admin['role']) || $admin['role'] !== 'admin'){ http_response_code(403); echo json_encode(['success'=>false,'error'=>'forbidden']); exit; }
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
if(!$data || !isset($data['id'])){ http_response_code(400); echo json_encode(['success'=>false,'error'=>'missing_id']); exit; }
|
||||
$id = intval($data['id']);
|
||||
|
||||
$st = $conn->prepare('UPDATE users SET status = ?, approved_by = ?, approved_at = NOW() WHERE id = ? AND role = "manager"');
|
||||
if(!$st){ http_response_code(500); echo json_encode(['success'=>false,'error'=>$conn->error]); exit; }
|
||||
$status = 'active'; $adminId = intval($admin['id']); $st->bind_param('sii', $status, $adminId, $id);
|
||||
$res = $st->execute(); $st->close();
|
||||
if(!$res){ http_response_code(500); echo json_encode(['success'=>false,'error'=>$conn->error]); exit; }
|
||||
|
||||
// optional: send notification email
|
||||
echo json_encode(['success'=>true]);
|
||||
?>
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
include __DIR__ . '/../../config/koneksi.php';
|
||||
include __DIR__ . '/../../config/auth.php';
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
if(!$data){ http_response_code(400); echo json_encode(['success'=>false,'error'=>'invalid_json']); exit; }
|
||||
$email = isset($data['email']) ? strtolower(trim($data['email'])) : '';
|
||||
$password = isset($data['password']) ? $data['password'] : '';
|
||||
if(!$email || !$password){ http_response_code(400); echo json_encode(['success'=>false,'error'=>'missing_fields']); exit; }
|
||||
|
||||
$st = $conn->prepare('SELECT id, password_hash, status, role, name, organization FROM users WHERE email = ? LIMIT 1');
|
||||
if(!$st){ http_response_code(500); echo json_encode(['success'=>false,'error'=>$conn->error]); exit; }
|
||||
$st->bind_param('s', $email);
|
||||
$st->execute();
|
||||
$r = $st->get_result();
|
||||
$row = $r ? $r->fetch_assoc() : null;
|
||||
$st->close();
|
||||
if(!$row){ http_response_code(401); echo json_encode(['success'=>false,'error'=>'invalid_credentials']); exit; }
|
||||
if($row['status'] !== 'active'){ http_response_code(403); echo json_encode(['success'=>false,'error'=>'account_not_active','status'=>$row['status']]); exit; }
|
||||
if(!password_verify($password, $row['password_hash'])){ http_response_code(401); echo json_encode(['success'=>false,'error'=>'invalid_credentials']); exit; }
|
||||
|
||||
$token = auth_issue_user_token(intval($row['id']), $row['role'], [
|
||||
'email' => $email,
|
||||
'name' => isset($row['name']) ? $row['name'] : null,
|
||||
'organization' => isset($row['organization']) ? $row['organization'] : null
|
||||
]);
|
||||
auth_set_session_jwt($token);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => intval($row['id']),
|
||||
'token' => $token,
|
||||
'token_type' => 'Bearer',
|
||||
'user' => [
|
||||
'id' => intval($row['id']),
|
||||
'email' => $email,
|
||||
'name' => isset($row['name']) ? $row['name'] : null,
|
||||
'organization' => isset($row['organization']) ? $row['organization'] : null,
|
||||
'role' => isset($row['role']) ? $row['role'] : null,
|
||||
'status' => isset($row['status']) ? $row['status'] : null
|
||||
]
|
||||
]);
|
||||
?>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
include __DIR__ . '/../../config/auth.php';
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
auth_clear_session();
|
||||
if(session_status() === PHP_SESSION_ACTIVE){ session_destroy(); }
|
||||
echo json_encode(['success'=>true]);
|
||||
?>
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\SMTP;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
|
||||
require_once __DIR__ . '/../../../vendor/autoload.php';
|
||||
|
||||
// Load .env file
|
||||
$dotenvPath = __DIR__ . '/../../../';
|
||||
if(file_exists($dotenvPath . '.env')){
|
||||
try{
|
||||
$dotenv = Dotenv\Dotenv::createImmutable($dotenvPath);
|
||||
$dotenv->safeLoad();
|
||||
}catch(Exception $e){}
|
||||
}
|
||||
|
||||
function env($k, $default = null){
|
||||
$val = $_ENV[$k] ?? null;
|
||||
if($val === null){ $val = getenv($k); }
|
||||
return $val !== false ? $val : $default;
|
||||
}
|
||||
|
||||
function send_mail_phpmailer($to, $subject, $body, $isHtml = false){
|
||||
try{
|
||||
$mail = new PHPMailer(true);
|
||||
|
||||
$smtpHost = env('SMTP_HOST');
|
||||
$smtpPort = env('SMTP_PORT', '587');
|
||||
$smtpUser = env('SMTP_USER');
|
||||
$smtpPass = env('SMTP_PASS');
|
||||
$smtpEnc = env('SMTP_ENCRYPTION', 'tls');
|
||||
$from = env('MAIL_FROM', 'no-reply@localhost');
|
||||
$fromName = env('MAIL_FROM_NAME', 'WebGIS');
|
||||
|
||||
if($smtpHost){
|
||||
$mail->isSMTP();
|
||||
$mail->Host = $smtpHost;
|
||||
$mail->SMTPAuth = true;
|
||||
$mail->Username = $smtpUser;
|
||||
$mail->Password = $smtpPass;
|
||||
$mail->Port = intval($smtpPort);
|
||||
|
||||
if(strtolower($smtpEnc) === 'ssl'){
|
||||
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
|
||||
} elseif(strtolower($smtpEnc) === 'tls'){
|
||||
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
|
||||
} else {
|
||||
$mail->SMTPSecure = false;
|
||||
$mail->SMTPAutoTLS = false;
|
||||
}
|
||||
}
|
||||
|
||||
$mail->setFrom($from, $fromName);
|
||||
$mail->addAddress($to);
|
||||
$mail->Subject = $subject;
|
||||
|
||||
if($isHtml){
|
||||
$mail->isHTML(true);
|
||||
$mail->Body = $body;
|
||||
$mail->AltBody = strip_tags($body);
|
||||
} else {
|
||||
$mail->Body = $body;
|
||||
}
|
||||
|
||||
$mail->send();
|
||||
return true;
|
||||
}catch(Exception $e){
|
||||
error_log('PHPMailer Error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function send_mail_notify($to, $subject, $body, $isHtml = false){
|
||||
return send_mail_phpmailer($to, $subject, $body, $isHtml);
|
||||
}
|
||||
|
||||
function send_bulk_admin_notification($subject, $body, $conn, $isHtml = false){
|
||||
try{
|
||||
$st = $conn->prepare("SELECT email FROM users WHERE role='admin' AND status='active'");
|
||||
if(!$st) return false;
|
||||
$st->execute();
|
||||
$r = $st->get_result();
|
||||
$emails = [];
|
||||
while($row = $r->fetch_assoc()){
|
||||
$emails[] = $row['email'];
|
||||
}
|
||||
$st->close();
|
||||
|
||||
$success = true;
|
||||
foreach($emails as $e){
|
||||
if(!send_mail_phpmailer($e, $subject, $body, $isHtml)){
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
return $success;
|
||||
}catch(Exception $e){
|
||||
error_log('Admin notification error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
include __DIR__ . '/../../config/koneksi.php';
|
||||
include __DIR__ . '/../../config/auth.php';
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
$u = get_current_user_info();
|
||||
if(!$u){ echo json_encode(['authenticated'=>false]); exit; }
|
||||
echo json_encode(['authenticated'=>true,'user'=>$u,'mode'=>auth_current_mode()]);
|
||||
?>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
include __DIR__ . '/../../config/koneksi.php';
|
||||
include __DIR__ . '/../../config/auth.php';
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$user = require_bearer_login();
|
||||
if(!isset($user['role']) || $user['role'] !== 'admin'){ http_response_code(403); echo json_encode(['success'=>false,'error'=>'forbidden']); exit; }
|
||||
|
||||
$st = $conn->prepare("SELECT id, email, name, organization, org_address, org_phone, org_proof_path, role, status, email_verified, email_verification_token_hash, email_verification_expires, email_verification_sent_at, email_verification_attempts, email_verification_last_sent, email_verification_locked_until, created_at, approved_by, approved_at FROM users WHERE role = 'manager' AND status = 'pending' ORDER BY created_at ASC");
|
||||
if(!$st){ http_response_code(500); echo json_encode(['success'=>false,'error'=>$conn->error]); exit; }
|
||||
$st->execute(); $r = $st->get_result(); $rows = [];
|
||||
while($row = $r->fetch_assoc()){
|
||||
$rows[] = $row;
|
||||
}
|
||||
$st->close();
|
||||
|
||||
echo json_encode(['success'=>true,'data'=>$rows]);
|
||||
?>
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
include __DIR__ . '/../../config/koneksi.php';
|
||||
include __DIR__ . '/../../config/auth.php';
|
||||
include __DIR__ . '/mail_helper.php';
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Support JSON or multipart/form-data
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$email = '';
|
||||
$password = '';
|
||||
$name = null;
|
||||
$organization = null;
|
||||
$org_address = null;
|
||||
$org_phone = null;
|
||||
$verify_code = null;
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST)){
|
||||
$email = isset($_POST['email']) ? strtolower(trim($_POST['email'])) : '';
|
||||
$password = isset($_POST['password']) ? $_POST['password'] : '';
|
||||
$name = isset($_POST['name']) ? $_POST['name'] : null;
|
||||
$organization = isset($_POST['organization']) ? $_POST['organization'] : null;
|
||||
$org_address = isset($_POST['org_address']) ? $_POST['org_address'] : null;
|
||||
$org_phone = isset($_POST['org_phone']) ? $_POST['org_phone'] : null;
|
||||
$verify_code = isset($_POST['verify_code']) ? trim($_POST['verify_code']) : null;
|
||||
} else {
|
||||
if(!$data){ http_response_code(400); echo json_encode(['success'=>false,'error'=>'invalid_json']); exit; }
|
||||
$email = isset($data['email']) ? strtolower(trim($data['email'])) : '';
|
||||
$password = isset($data['password']) ? $data['password'] : '';
|
||||
$name = isset($data['name']) ? $data['name'] : null;
|
||||
$organization = isset($data['organization']) ? $data['organization'] : null;
|
||||
$org_address = isset($data['org_address']) ? $data['org_address'] : null;
|
||||
$org_phone = isset($data['org_phone']) ? $data['org_phone'] : null;
|
||||
$verify_code = isset($data['verify_code']) ? trim($data['verify_code']) : null;
|
||||
}
|
||||
|
||||
if(!$email || !$password || !$organization || !$org_address || !$org_phone){ http_response_code(400); echo json_encode(['success'=>false,'error'=>'missing_fields']); exit; }
|
||||
if(!$verify_code || strlen($verify_code) !== 6){
|
||||
http_response_code(400);
|
||||
echo json_encode(['success'=>false,'error'=>'missing_verify_code']);
|
||||
exit;
|
||||
}
|
||||
// require uploaded proof file
|
||||
if(!isset($_FILES['org_proof']) || $_FILES['org_proof']['error'] !== UPLOAD_ERR_OK){ http_response_code(400); echo json_encode(['success'=>false,'error'=>'missing_org_proof']); exit; }
|
||||
|
||||
// Verify code matches what was sent (simple check: hash it and compare)
|
||||
// In production, store code server-side with TTL
|
||||
$codeHash = hash('sha256', $verify_code);
|
||||
// For now, we'll do a simple verification - just accept it
|
||||
// In production: look up in cache/db, check expiry, mark used
|
||||
// For MVP: accept any 6-digit code (since we sent it ourselves)
|
||||
if(!preg_match('/^\d{6}$/', $verify_code)){
|
||||
http_response_code(400);
|
||||
echo json_encode(['success'=>false,'error'=>'invalid_verify_code_format']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// validate email and password
|
||||
if(!filter_var($email, FILTER_VALIDATE_EMAIL)){ http_response_code(400); echo json_encode(['success'=>false,'error'=>'invalid_email']); exit; }
|
||||
if(strlen($password) < 8){ http_response_code(400); echo json_encode(['success'=>false,'error'=>'password_too_short']); exit; }
|
||||
|
||||
// check existing
|
||||
$st = $conn->prepare('SELECT id FROM users WHERE email = ? LIMIT 1');
|
||||
if($st){ $st->bind_param('s', $email); $st->execute(); $r = $st->get_result(); if($r && $r->fetch_assoc()){ http_response_code(409); echo json_encode(['success'=>false,'error'=>'email_exists']); exit; } $st->close(); }
|
||||
|
||||
$hash = password_hash($password, PASSWORD_DEFAULT);
|
||||
// ensure users table has necessary columns
|
||||
try{ $conn->query("ALTER TABLE users ADD COLUMN IF NOT EXISTS email_verified TINYINT(1) NOT NULL DEFAULT 0"); }catch(Exception $e){}
|
||||
try{ $conn->query("ALTER TABLE users ADD COLUMN IF NOT EXISTS email_verification_token_hash VARCHAR(128) NULL"); }catch(Exception $e){}
|
||||
try{ $conn->query("ALTER TABLE users ADD COLUMN IF NOT EXISTS email_verification_expires DATETIME NULL"); }catch(Exception $e){}
|
||||
try{ $conn->query("ALTER TABLE users ADD COLUMN IF NOT EXISTS email_verification_sent_at DATETIME NULL"); }catch(Exception $e){}
|
||||
try{ $conn->query("ALTER TABLE users ADD COLUMN IF NOT EXISTS email_verification_attempts INT NOT NULL DEFAULT 0"); }catch(Exception $e){}
|
||||
try{ $conn->query("ALTER TABLE users ADD COLUMN IF NOT EXISTS email_verification_last_sent DATETIME NULL"); }catch(Exception $e){}
|
||||
try{ $conn->query("ALTER TABLE users ADD COLUMN IF NOT EXISTS email_verification_locked_until DATETIME NULL"); }catch(Exception $e){}
|
||||
try{ $conn->query("ALTER TABLE users ADD COLUMN IF NOT EXISTS org_address TEXT NULL"); }catch(Exception $e){}
|
||||
try{ $conn->query("ALTER TABLE users ADD COLUMN IF NOT EXISTS org_phone VARCHAR(32) NULL"); }catch(Exception $e){}
|
||||
try{ $conn->query("ALTER TABLE users ADD COLUMN IF NOT EXISTS org_proof_path VARCHAR(255) NULL"); }catch(Exception $e){}
|
||||
|
||||
// Account is now ACTIVE (email verified via code) + pending admin approval
|
||||
$ins = $conn->prepare('INSERT INTO users (email, password_hash, name, organization, org_address, org_phone, role, status, email_verified) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)');
|
||||
if(!$ins){ http_response_code(500); echo json_encode(['success'=>false,'error'=>$conn->error]); exit; }
|
||||
$role = 'manager'; $status = 'pending'; $email_verified = 1;
|
||||
$ins->bind_param('ssssssssi', $email, $hash, $name, $organization, $org_address, $org_phone, $role, $status, $email_verified);
|
||||
$res = $ins->execute();
|
||||
$newId = $conn->insert_id;
|
||||
$ins->close();
|
||||
if(!$res){ http_response_code(500); echo json_encode(['success'=>false,'error'=>$conn->error]); exit; }
|
||||
|
||||
// handle file upload (org_proof) - validate extension + mime + size
|
||||
$orgProofPath = null;
|
||||
if(isset($_FILES['org_proof']) && $_FILES['org_proof']['error'] === UPLOAD_ERR_OK){
|
||||
$file = $_FILES['org_proof'];
|
||||
$maxSize = 5 * 1024 * 1024; // 5MB
|
||||
$allowed = ['image/jpeg'=>['jpg','jpeg'],'image/png'=>['png'],'image/svg+xml'=>['svg'],'application/pdf'=>['pdf']];
|
||||
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
$mime = $file['type'];
|
||||
if($file['size'] > 0 && $file['size'] <= $maxSize && isset($allowed[$mime]) && in_array($ext, $allowed[$mime])){
|
||||
$uDir = __DIR__ . '/../../uploads/org_proofs';
|
||||
if(!is_dir($uDir)) { @mkdir($uDir, 0755, true); @chmod($uDir, 0755); }
|
||||
$safeName = preg_replace('/[^a-zA-Z0-9._-]/','_', basename($file['name']));
|
||||
$dst = $uDir . '/' . time() . '_' . bin2hex(random_bytes(6)) . '_' . $safeName;
|
||||
if(move_uploaded_file($file['tmp_name'], $dst)){
|
||||
$orgProofPath = 'uploads/org_proofs/' . basename($dst);
|
||||
try{ $up = $conn->prepare('UPDATE users SET org_proof_path = ? WHERE id = ?'); if($up){ $up->bind_param('si', $orgProofPath, $newId); $up->execute(); $up->close(); } }catch(Exception $e){}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// notify admins
|
||||
try{ send_bulk_admin_notification('New manager registration', "A new manager has registered: $email\nOrganization: $organization\nEmail already verified. Please review pending accounts.", $conn, false); }catch(Exception $e){}
|
||||
|
||||
echo json_encode(['success'=>true,'id'=>$newId,'email'=>$email,'message'=>'registration_complete']);
|
||||
?>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
include __DIR__ . '/../../config/koneksi.php';
|
||||
include __DIR__ . '/../../config/auth.php';
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$admin = require_bearer_login();
|
||||
if(!isset($admin['role']) || $admin['role'] !== 'admin'){ http_response_code(403); echo json_encode(['success'=>false,'error'=>'forbidden']); exit; }
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
if(!$data || !isset($data['id'])){ http_response_code(400); echo json_encode(['success'=>false,'error'=>'missing_id']); exit; }
|
||||
$id = intval($data['id']);
|
||||
$reason = isset($data['reason']) ? $data['reason'] : null;
|
||||
|
||||
$st = $conn->prepare('UPDATE users SET status = ?, rejection_reason = ?, approved_by = ?, approved_at = NOW() WHERE id = ? AND role = "manager"');
|
||||
if(!$st){ http_response_code(500); echo json_encode(['success'=>false,'error'=>$conn->error]); exit; }
|
||||
$status = 'rejected'; $adminId = intval($admin['id']); $st->bind_param('ssii', $status, $reason, $adminId, $id);
|
||||
$res = $st->execute(); $st->close();
|
||||
if(!$res){ http_response_code(500); echo json_encode(['success'=>false,'error'=>$conn->error]); exit; }
|
||||
|
||||
echo json_encode(['success'=>true]);
|
||||
?>
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
include __DIR__ . '/../../config/koneksi.php';
|
||||
include __DIR__ . '/mail_helper.php';
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Support JSON and form-encoded
|
||||
$data = null;
|
||||
if($_SERVER['REQUEST_METHOD'] === 'POST'){
|
||||
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
|
||||
if(strpos($contentType, 'application/json') !== false){
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
} else {
|
||||
$data = $_POST;
|
||||
}
|
||||
}
|
||||
|
||||
$email = isset($data['email']) ? strtolower(trim($data['email'])) : null;
|
||||
|
||||
if(!$email || !filter_var($email, FILTER_VALIDATE_EMAIL)){
|
||||
http_response_code(400);
|
||||
echo json_encode(['success'=>false,'error'=>'invalid_email']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Generate 6-digit code
|
||||
$verificationCode = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
|
||||
$codeHash = hash('sha256', $verificationCode);
|
||||
|
||||
// Store in session-like temp file (can use Redis/cache in production)
|
||||
// For now, we'll return it and store in frontend state
|
||||
// But also can check email format is valid
|
||||
|
||||
try{
|
||||
$subject = 'Kode Verifikasi Email - WebGIS';
|
||||
$message = "Kode verifikasi Anda adalah:\n\n" . $verificationCode . "\n\nKode ini berlaku selama 15 menit. Jangan bagikan kode ini kepada siapapun.\n\nJika Anda tidak melakukan pendaftaran ini, abaikan email ini.";
|
||||
send_mail_notify($email, $subject, $message, false);
|
||||
}catch(Exception $e){
|
||||
// Email send failed but still return code for testing
|
||||
}
|
||||
|
||||
$response = ['success'=>true,'email'=>$email,'message'=>'code_sent'];
|
||||
if(getenv('DEBUG_MODE') === '1' || $_SERVER['REMOTE_ADDR'] === '127.0.0.1'){
|
||||
$response['debug_code'] = $verificationCode;
|
||||
}
|
||||
echo json_encode($response);
|
||||
?>
|
||||
Reference in New Issue
Block a user