fix: update final code deployment
This commit is contained in:
@@ -2,6 +2,10 @@ FROM php:8.2-apache
|
|||||||
|
|
||||||
RUN a2enmod rewrite
|
RUN a2enmod rewrite
|
||||||
|
|
||||||
|
# The sub-projects connect via mysqli, which isn't bundled in the base image
|
||||||
|
RUN docker-php-ext-install mysqli pdo_mysql \
|
||||||
|
&& echo "date.timezone = Asia/Jakarta" > /usr/local/etc/php/conf.d/timezone.ini
|
||||||
|
|
||||||
COPY . /var/www/html/
|
COPY . /var/www/html/
|
||||||
|
|
||||||
RUN chown -R www-data:www-data /var/www/html \
|
RUN chown -R www-data:www-data /var/www/html \
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
FROM mysql:8.0
|
||||||
|
|
||||||
|
# Bake all three sub-project schemas + seed data into one database.
|
||||||
|
# No table-name collisions across them, and config.php reads the DB name
|
||||||
|
# from the DB_NAME env var, so a single shared database is fine.
|
||||||
|
# Files in initdb.d run alphabetically against $MYSQL_DATABASE on first boot.
|
||||||
|
COPY spbu/webgis.sql /docker-entrypoint-initdb.d/01_spbu.sql
|
||||||
|
COPY parsiljalan/webgis2.sql /docker-entrypoint-initdb.d/02_jalantanah.sql
|
||||||
|
COPY povertymap/gis_penduduk_miskin.sql /docker-entrypoint-initdb.d/03_miskin.sql
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
name: muthia-webgis
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
image: muthia-webgis-app
|
||||||
|
container_name: muthia-webgis-app
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8089:80"
|
||||||
|
environment:
|
||||||
|
DB_HOST: db
|
||||||
|
DB_USER: webgis_user
|
||||||
|
DB_PASS: webgis_password
|
||||||
|
DB_NAME: webgis_muthia
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
db:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.db
|
||||||
|
image: muthia-webgis-db
|
||||||
|
container_name: muthia-webgis-db
|
||||||
|
environment:
|
||||||
|
MYSQL_DATABASE: webgis_muthia
|
||||||
|
MYSQL_USER: webgis_user
|
||||||
|
MYSQL_PASSWORD: webgis_password
|
||||||
|
MYSQL_ROOT_PASSWORD: root_password
|
||||||
|
expose:
|
||||||
|
- "3306"
|
||||||
|
volumes:
|
||||||
|
- db_data:/var/lib/mysql
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p$$MYSQL_ROOT_PASSWORD --silent"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
phpmyadmin:
|
||||||
|
image: phpmyadmin:latest
|
||||||
|
container_name: muthia-webgis-phpmyadmin
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8090:80"
|
||||||
|
environment:
|
||||||
|
PMA_HOST: db
|
||||||
|
PMA_PORT: 3306
|
||||||
|
PMA_ABSOLUTE_URI: https://muthia.webgis-untan.xyz/db/
|
||||||
|
UPLOAD_LIMIT: 64M
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db_data:
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
// Semua project di-deploy dalam satu folder — gunakan relative path
|
// Semua project di-deploy dalam satu folder — gunakan relative path
|
||||||
$url_spbu = 'SIG_spbu/';
|
$url_spbu = 'spbu/';
|
||||||
$url_miskin = 'SIG_miskin/gis/';
|
$url_miskin = 'povertymap/';
|
||||||
$url_jalantanah = 'SIG2_jalantanah/';
|
$url_jalantanah = 'parsiljalan/';
|
||||||
$url_choropleth = 'Choropleth/ptk.html';
|
$url_choropleth = 'choropleth/';
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="id">
|
<html lang="id">
|
||||||
|
|||||||
@@ -15,12 +15,58 @@ function toGeoJsonPoint($lat, $lng) {
|
|||||||
return json_encode(['type' => 'Point', 'coordinates' => [(float)$lng, (float)$lat]]);
|
return json_encode(['type' => 'Point', 'coordinates' => [(float)$lng, (float)$lat]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deteksi POST yang melebihi post_max_size: PHP mengosongkan $_POST/$_FILES
|
||||||
|
// padahal CONTENT_LENGTH > 0. Tanpa ini respons jadi kosong -> "Koneksi gagal!".
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST'
|
||||||
|
&& empty($_POST) && empty($_FILES)
|
||||||
|
&& (int)($_SERVER['CONTENT_LENGTH'] ?? 0) > 0) {
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
http_response_code(413);
|
||||||
|
echo json_encode([
|
||||||
|
'ok' => false,
|
||||||
|
'msg' => 'Data terlalu besar (melebihi post_max_size=' . ini_get('post_max_size')
|
||||||
|
. '). Kecilkan ukuran foto atau naikkan post_max_size.'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
$action = $_GET['action'] ?? $_POST['action'] ?? null;
|
$action = $_GET['action'] ?? $_POST['action'] ?? null;
|
||||||
if (!$action) return;
|
if (!$action) return;
|
||||||
|
|
||||||
header('Content-Type: application/json; charset=utf-8');
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
$a = $action;
|
$a = $action;
|
||||||
|
|
||||||
|
if ($a === 'reverse') {
|
||||||
|
$lat = (float)($_GET['lat'] ?? 0);
|
||||||
|
$lon = (float)($_GET['lon'] ?? $_GET['lng'] ?? 0);
|
||||||
|
if ($lat == 0 && $lon == 0) { echo json_encode(['error' => 'koordinat kosong']); exit; }
|
||||||
|
$url = 'https://nominatim.openstreetmap.org/reverse?format=jsonv2&zoom=17&accept-language=id'
|
||||||
|
. '&lat=' . urlencode($lat) . '&lon=' . urlencode($lon);
|
||||||
|
// Nominatim mewajibkan User-Agent yang valid; browser tak bisa set ini, jadi diproxy dari server
|
||||||
|
$ua = 'SIG2-JalanTanah/1.0 (kontak: webmaster@webgis-untan.xyz)';
|
||||||
|
$body = false;
|
||||||
|
if (function_exists('curl_init')) {
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt_array($ch, [
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_USERAGENT => $ua,
|
||||||
|
CURLOPT_TIMEOUT => 8,
|
||||||
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
|
]);
|
||||||
|
$body = curl_exec($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
}
|
||||||
|
if ($body === false) {
|
||||||
|
$ctx = stream_context_create(['http' => [
|
||||||
|
'header' => "User-Agent: $ua\r\n",
|
||||||
|
'timeout' => 8,
|
||||||
|
]]);
|
||||||
|
$body = @file_get_contents($url, false, $ctx);
|
||||||
|
}
|
||||||
|
if ($body === false) { echo json_encode(['error' => 'gagal hubungi nominatim']); exit; }
|
||||||
|
echo $body; exit;
|
||||||
|
}
|
||||||
|
|
||||||
if ($a === 'search') {
|
if ($a === 'search') {
|
||||||
$q = '%' . $conn->real_escape_string($_GET['q'] ?? '') . '%';
|
$q = '%' . $conn->real_escape_string($_GET['q'] ?? '') . '%';
|
||||||
$tipe = $_GET['tipe'] ?? 'semua';
|
$tipe = $_GET['tipe'] ?? 'semua';
|
||||||
@@ -250,11 +250,12 @@ function openRusakModal(latlng) {
|
|||||||
document.getElementById('rusak-ket').value = '';
|
document.getElementById('rusak-ket').value = '';
|
||||||
clearFoto();
|
clearFoto();
|
||||||
|
|
||||||
fetch('https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=' + latlng.lat + '&lon=' + latlng.lng + '&zoom=17&accept-language=id')
|
fetch('api.php?action=reverse&lat=' + latlng.lat + '&lon=' + latlng.lng)
|
||||||
.then(function (r) { return r.json(); })
|
.then(function (r) { return r.json(); })
|
||||||
.then(function (geo) {
|
.then(function (geo) {
|
||||||
var road = geo.address && (geo.address.road || geo.address.residential || geo.address.path || geo.address.pedestrian || geo.address.footway || '');
|
var a = geo.address || {};
|
||||||
document.getElementById('rusak-nama-jalan').value = road || '(Jalan tidak teridentifikasi)';
|
var road = a.road || a.residential || a.path || a.pedestrian || a.footway || a.track || a.cycleway || a.neighbourhood || a.hamlet || a.village || a.suburb || '';
|
||||||
|
document.getElementById('rusak-nama-jalan').value = road || geo.display_name || '(Jalan tidak teridentifikasi)';
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
document.getElementById('rusak-nama-jalan').value = '(Gagal deteksi nama jalan)';
|
document.getElementById('rusak-nama-jalan').value = '(Gagal deteksi nama jalan)';
|
||||||
@@ -327,10 +328,11 @@ function handleFoto(input) {
|
|||||||
badge.textContent = 'GPS dari Foto';
|
badge.textContent = 'GPS dari Foto';
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch('https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=' + latDec + '&lon=' + lonDec + '&zoom=17&accept-language=id')
|
fetch('api.php?action=reverse&lat=' + latDec + '&lon=' + lonDec)
|
||||||
.then(function (r) { return r.json(); })
|
.then(function (r) { return r.json(); })
|
||||||
.then(function (geo) {
|
.then(function (geo) {
|
||||||
var road = geo.address && (geo.address.road || geo.address.residential || geo.address.path || geo.address.pedestrian || geo.address.footway || '');
|
var a = geo.address || {};
|
||||||
|
var road = a.road || a.residential || a.path || a.pedestrian || a.footway || a.track || a.cycleway || a.neighbourhood || a.hamlet || a.village || a.suburb || '';
|
||||||
document.getElementById('rusak-nama-jalan').value = road || geo.display_name || '(Jalan tidak teridentifikasi)';
|
document.getElementById('rusak-nama-jalan').value = road || geo.display_name || '(Jalan tidak teridentifikasi)';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -339,11 +341,31 @@ function handleFoto(input) {
|
|||||||
|
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
reader.onload = function (e) {
|
reader.onload = function (e) {
|
||||||
fotoBase64 = e.target.result;
|
// Kompres & resize foto agar payload kecil (hindari post_max_size & batas kolom DB)
|
||||||
var preview = document.getElementById('photo-preview');
|
var img = new Image();
|
||||||
preview.src = fotoBase64;
|
img.onload = function () {
|
||||||
preview.style.display = 'block';
|
var maxDim = 1280;
|
||||||
document.getElementById('foto-clear-btn').style.display = 'flex';
|
var w = img.width, h = img.height;
|
||||||
|
if (w > h && w > maxDim) { h = Math.round(h * maxDim / w); w = maxDim; }
|
||||||
|
else if (h >= w && h > maxDim) { w = Math.round(w * maxDim / h); h = maxDim; }
|
||||||
|
var canvas = document.createElement('canvas');
|
||||||
|
canvas.width = w; canvas.height = h;
|
||||||
|
canvas.getContext('2d').drawImage(img, 0, 0, w, h);
|
||||||
|
fotoBase64 = canvas.toDataURL('image/jpeg', 0.7);
|
||||||
|
var preview = document.getElementById('photo-preview');
|
||||||
|
preview.src = fotoBase64;
|
||||||
|
preview.style.display = 'block';
|
||||||
|
document.getElementById('foto-clear-btn').style.display = 'flex';
|
||||||
|
};
|
||||||
|
img.onerror = function () {
|
||||||
|
// Fallback bila gambar tak bisa di-decode (mis. format tak didukung canvas)
|
||||||
|
fotoBase64 = e.target.result;
|
||||||
|
var preview = document.getElementById('photo-preview');
|
||||||
|
preview.src = fotoBase64;
|
||||||
|
preview.style.display = 'block';
|
||||||
|
document.getElementById('foto-clear-btn').style.display = 'flex';
|
||||||
|
};
|
||||||
|
img.src = e.target.result;
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
}
|
}
|
||||||
@@ -381,27 +403,40 @@ function saveRusak() {
|
|||||||
formData.append('foto', fotoBase64);
|
formData.append('foto', fotoBase64);
|
||||||
|
|
||||||
fetch('api.php', { method: 'POST', body: formData })
|
fetch('api.php', { method: 'POST', body: formData })
|
||||||
.then(function (r) { return r.json(); })
|
.then(function (r) {
|
||||||
|
return r.text().then(function (txt) {
|
||||||
|
var res;
|
||||||
|
try { res = JSON.parse(txt); }
|
||||||
|
catch (e) {
|
||||||
|
// Respons bukan JSON: tampilkan status & potongan isi supaya jelas penyebabnya
|
||||||
|
var info = txt.trim() ? txt.trim().slice(0, 200) : '(respons kosong)';
|
||||||
|
throw new Error('HTTP ' + r.status + ' — ' + info);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
})
|
||||||
.then(function (res) {
|
.then(function (res) {
|
||||||
btn.textContent = 'Simpan Titik';
|
btn.textContent = 'Simpan Titik';
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
showToast('Titik jalan rusak disimpan!', 'success');
|
showToast('Titik jalan rusak disimpan!', 'success');
|
||||||
|
var savedFoto = fotoBase64; // simpan dulu: closeRusakModal() me-reset fotoBase64
|
||||||
closeRusakModal();
|
closeRusakModal();
|
||||||
var id = res.id;
|
var id = res.id;
|
||||||
var nama = res.nama || pelapor;
|
var nama = res.nama || pelapor;
|
||||||
var layer = makeRusakLayer(id, nama, tanggal, lat, lng, ket, fotoBase64, 'recent', false);
|
var layer = makeRusakLayer(id, nama, tanggal, lat, lng, ket, savedFoto, 'recent', false);
|
||||||
rusakLayers[id] = layer;
|
rusakLayers[id] = layer;
|
||||||
rusakData[id] = { id: id, nama: nama, tgl: tanggal, lat: lat, lng: lng, ket: ket, foto: fotoBase64, age: 'recent', recurring: false };
|
rusakData[id] = { id: id, nama: nama, tgl: tanggal, lat: lat, lng: lng, ket: ket, foto: savedFoto, age: 'recent', recurring: false };
|
||||||
if (res.tr !== undefined) updateStats(res);
|
if (res.tr !== undefined) updateStats(res);
|
||||||
cancelDraw();
|
cancelDraw();
|
||||||
} else {
|
} else {
|
||||||
showToast('Gagal: ' + (res.msg || 'unknown'), 'error');
|
showToast('Gagal: ' + (res.msg || 'unknown'), 'error');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function (err) {
|
||||||
btn.textContent = 'Simpan Titik';
|
btn.textContent = 'Simpan Titik';
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
showToast('Koneksi gagal!', 'error');
|
showToast('Gagal simpan: ' + (err && err.message ? err.message : 'koneksi gagal'), 'error');
|
||||||
|
console.error('saveRusak error:', err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1080,7 +1080,7 @@ function exitFloatingGeoEdit(save) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function spHandleFoto(input){var f=input.files[0];if(!f)return;if(f.size>5*1024*1024){showToast('Foto maks 5MB!','error');return;}var r=new FileReader();r.onload=function(e){spFotoBase64=e.target.result;el('sp-foto-preview').src=spFotoBase64;el('sp-foto-preview').style.display='block';el('sp-foto-clear').style.display='flex';};r.readAsDataURL(f);}
|
function spHandleFoto(input){var f=input.files[0];if(!f)return;if(f.size>5*1024*1024){showToast('Foto maks 5MB!','error');return;}var r=new FileReader();r.onload=function(e){var img=new Image();img.onload=function(){var maxDim=1280,w=img.width,h=img.height;if(w>h&&w>maxDim){h=Math.round(h*maxDim/w);w=maxDim;}else if(h>=w&&h>maxDim){w=Math.round(w*maxDim/h);h=maxDim;}var c=document.createElement('canvas');c.width=w;c.height=h;c.getContext('2d').drawImage(img,0,0,w,h);spFotoBase64=c.toDataURL('image/jpeg',0.7);el('sp-foto-preview').src=spFotoBase64;el('sp-foto-preview').style.display='block';el('sp-foto-clear').style.display='flex';};img.onerror=function(){spFotoBase64=e.target.result;el('sp-foto-preview').src=spFotoBase64;el('sp-foto-preview').style.display='block';el('sp-foto-clear').style.display='flex';};img.src=e.target.result;};r.readAsDataURL(f);}
|
||||||
function spClearFoto(){spFotoBase64='';el('sp-foto-preview').style.display='none';el('sp-foto-file').value='';el('sp-foto-clear').style.display='none';}
|
function spClearFoto(){spFotoBase64='';el('sp-foto-preview').style.display='none';el('sp-foto-file').value='';el('sp-foto-clear').style.display='none';}
|
||||||
|
|
||||||
/* ══ GEOMETRY EDIT ═══════════════════════════════════ */
|
/* ══ GEOMETRY EDIT ═══════════════════════════════════ */
|
||||||
@@ -1410,7 +1410,7 @@ if (document.getElementById('lnav-list')) {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<?php if ($mode === 'input'): ?>
|
<?php if ($mode === 'input'): ?>
|
||||||
<script src="assets/jalan-input.js"></script>
|
<script src="assets/jalan-input.js?v=<?= @filemtime(__DIR__ . '/assets/jalan-input.js') ?: time() ?>"></script>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -56,7 +56,7 @@ CREATE TABLE `jalan_rusak` (
|
|||||||
`tanggal` date NOT NULL,
|
`tanggal` date NOT NULL,
|
||||||
`latitude` double NOT NULL DEFAULT 0,
|
`latitude` double NOT NULL DEFAULT 0,
|
||||||
`longitude` double NOT NULL DEFAULT 0,
|
`longitude` double NOT NULL DEFAULT 0,
|
||||||
`foto` text DEFAULT NULL COMMENT 'base64 atau path file foto',
|
`foto` mediumtext DEFAULT NULL COMMENT 'base64 atau path file foto',
|
||||||
`keterangan` text DEFAULT NULL,
|
`keterangan` text DEFAULT NULL,
|
||||||
`koordinat` text NOT NULL COMMENT 'GeoJSON Point string',
|
`koordinat` text NOT NULL COMMENT 'GeoJSON Point string',
|
||||||
`created_at` timestamp NOT NULL DEFAULT current_timestamp()
|
`created_at` timestamp NOT NULL DEFAULT current_timestamp()
|
||||||
@@ -138,7 +138,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
<body>
|
<body>
|
||||||
|
|
||||||
<header class="login-header">
|
<header class="login-header">
|
||||||
<a href="../../index.php">
|
<a href="../index.php">
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
|
||||||
Portal WebGIS
|
Portal WebGIS
|
||||||
</a>
|
</a>
|
||||||
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 109 KiB |
Reference in New Issue
Block a user