Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions app/Http/Controllers/Surat/PermohonanController.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,18 @@
use App\Models\DataDesa;
use App\Models\LogTte;
use App\Models\Surat;
use Endroid\QrCode\Builder\Builder;
use Endroid\QrCode\Encoding\Encoding;
use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\RoundBlockSizeMode;
use Endroid\QrCode\Writer\PngWriter;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Psr7;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use setasign\Fpdi\Fpdi;
use Yajra\DataTables\DataTables;

class PermohonanController extends Controller
Expand Down Expand Up @@ -289,6 +295,95 @@ public function passphrase(Request $request, $id)
}
}

public function tandatanganQr($id)
{
$surat = Surat::findOrFail($id);

if ($surat->log_verifikasi != LogVerifikasiSurat::ProsesTTE) {
return response()->json(['status' => false, 'pesan_error' => 'Surat tidak dalam tahap penandatanganan.'], 400);
}

$user = auth()->user()->pengurus_id;
if ($user != $this->akun_camat->id) {
return response()->json(['status' => false, 'pesan_error' => 'Hanya camat yang dapat menandatangani surat.'], 403);
}

DB::beginTransaction();

try {
$file_path = public_path("storage/surat/{$surat->file}");
$file_info = pathinfo($file_path);
$signed_path = public_path("storage/surat/{$file_info['filename']}_signed.pdf");

$verificationUrl = route('surat.arsip.qrcode', $surat->id);

$qrCode = Builder::create()
->writer(new PngWriter())
->data($verificationUrl)
->encoding(new Encoding('UTF-8'))
->errorCorrectionLevel(ErrorCorrectionLevel::High)
->size(200)
->margin(10)
->roundBlockSizeMode(RoundBlockSizeMode::Margin)
->build();

$qrTempPath = public_path('storage/surat/qr_temp_' . $surat->id . '.png');
$qrCode->saveToFile($qrTempPath);

$pdf = new Fpdi();
$pageCount = $pdf->setSourceFile($file_path);

for ($i = 1; $i <= $pageCount; $i++) {
$templateId = $pdf->importPage($i);
$size = $pdf->getTemplateSize($templateId);
$pdf->AddPage($size['orientation'], [$size['width'], $size['height']]);
$pdf->useTemplate($templateId);

if ($i === $pageCount) {
$qrSize = 40;
$margin = 10;
$pdf->Image($qrTempPath, $size['width'] - $qrSize - $margin, $size['height'] - $qrSize - $margin, $qrSize, $qrSize);
}
}

$pdf->Output('F', $signed_path);

@unlink($qrTempPath);

$fileHash = hash_file('sha256', $signed_path);

@unlink($file_path);
rename($signed_path, $file_path);

$surat->update([
'status' => StatusSurat::Arsip,
'log_verifikasi' => LogVerifikasiSurat::SudahTTE,
'file_hash' => $fileHash,
]);

DB::commit();

return response()->json([
'status' => true,
'pesan_error' => 'success',
'jenis' => 'success',
]);
} catch (\Exception $e) {
DB::rollback();
Log::error('QR Code signing failed', [
'error' => $e->getMessage(),
'user_id' => auth()->id(),
'surat_id' => $id,
]);

return response()->json([
'status' => false,
'pesan_error' => $e->getMessage(),
'jenis' => 'Exception',
]);
}
}

protected function response($notif = [])
{
LogTte::create([
Expand Down
43 changes: 42 additions & 1 deletion app/Http/Controllers/Surat/SuratController.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
use App\Models\Profil;
use App\Models\SettingAplikasi;
use App\Models\Surat;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Yajra\DataTables\DataTables;
Expand Down Expand Up @@ -79,7 +80,13 @@ public function getData()
}
return $row->nama_penduduk;
})
->rawColumns(['aksi'])->make();
->addColumn('hash', function ($row) {
if ($row->file_hash) {
return '<code style="font-size: 10px;">' . substr($row->file_hash, 0, 16) . '...</code>';
}
return '-';
})
->rawColumns(['aksi', 'hash'])->make();
}

public function download($id)
Expand Down Expand Up @@ -136,4 +143,38 @@ public function qrcode($id)

return view('surat.qrcode', compact('surat', 'profil'));
}

public function verifikasi()
{
$page_title = 'Verifikasi Surat';
$page_description = 'Verifikasi keaslian surat digital';

return view('surat.verifikasi.index', compact('page_title', 'page_description'));
}

public function verifikasiStore(Request $request)
{
$request->validate([
'file' => 'required|mimes:pdf|max:5120',
]);

try {
$uploadedFile = $request->file('file');
$uploadedHash = hash_file('sha256', $uploadedFile->getRealPath());

$surat = Surat::where('file_hash', $uploadedHash)->where('status', StatusSurat::Arsip)->first();

if (!$surat) {
return back()->with('error', 'Surat tidak ditemukan atau file tidak sesuai dengan surat yang diterbitkan.');
}

return view('surat.verifikasi.hasil', compact('surat'));
} catch (\Exception $e) {
Log::error('Verifikasi surat failed', [
'error' => $e->getMessage(),
]);

return back()->with('error', 'Terjadi kesalahan saat memverifikasi surat.');
}
}
}
1 change: 1 addition & 0 deletions app/Models/Surat.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class Surat extends Model
'nomor',
'nama',
'file',
'file_hash',
'keterangan',
'log_verifikasi',
'verifikasi_operator',
Expand Down
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"cocur/slugify": "4.6.0",
"cviebrock/eloquent-sluggable": "^11.0",
"doctrine/dbal": "^3.6",
"endroid/qr-code": "^5.0",
"setasign/fpdi": "^2.3",
"guzzlehttp/guzzle": "^7.2",
"hexadog/laravel-themes-manager": "^1.13",
"jaybizzle/crawler-detect": "1.*",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddFileHashToLogSuratTable extends Migration
{
public function up()
{
Schema::table('das_log_surat', function (Blueprint $table) {
$table->string('file_hash', 64)->nullable()->after('file');
});
}

public function down()
{
Schema::table('das_log_surat', function (Blueprint $table) {
$table->dropColumn('file_hash');
});
}
}
2 changes: 2 additions & 0 deletions resources/views/layouts/fragments/sidebar.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,8 @@
</li>
<li {{ Request::is(['surat/arsip*']) ? 'class=active' : '' }}><a href="{{ route('surat.arsip') }}"><i class="fa fa-folder-open"></i>Arsip</a>
</li>
<li {{ Request::is(['surat/verifikasi*']) ? 'class=active' : '' }}><a href="{{ route('surat.verifikasi') }}"><i class="fa fa-qrcode"></i>Verifikasi</a>
</li>
<li {{ Request::is(['surat/pengaturan*']) ? 'class=active' : '' }}><a href="{{ route('surat.pengaturan') }}"><i class="fa fa-gear"></i>Pengaturan</a>
</li>
</ul>
Expand Down
7 changes: 7 additions & 0 deletions resources/views/surat/arsip.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<th>Nama Penduduk</th>
<th>Ditandatangani oleh</th>
<th>Tanggal</th>
<th>Hash</th>
</tr>
</thead>
</table>
Expand Down Expand Up @@ -105,6 +106,12 @@ class: 'text-center',
data: 'tanggal',
name: 'tanggal'
},
{
data: 'hash',
name: 'hash',
orderable: false,
searchable: false
},
]
});

Expand Down
59 changes: 58 additions & 1 deletion resources/views/surat/permohonan/show.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@
<div class="form-group">
<div class="text-center">
@if ($surat->log_verifikasi == 4)
<button id="passphrase" class="btn btn-primary">Tandatangani</button>
<button id="tandatangan-qr" class="btn btn-primary">Tandatangani dengan QR Code</button>
@if ($settings['tte'] && $settings['tte_api'] !== 'demo')
<button id="passphrase" class="btn btn-success">Tandatangani (TTE)</button>
@endif
@else
<button id="setujui" class="btn btn-primary">Setujui</button>
<button id="tolak" class="btn btn-danger">Tolak</button>
Expand Down Expand Up @@ -154,6 +157,60 @@
})
});

$('#tandatangan-qr').on('click', function() {
Swal.fire({
title: 'Apakah anda yakin ingin menandatangani surat ini dengan QR Code?',
text: 'Tanda tangan QR Code akan disematkan pada halaman terakhir surat.',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Ya, Tandatangani!',
cancelButtonText: 'Batal',
showLoaderOnConfirm: true,
preConfirm: () => {
return fetch(`{{ route('surat.permohonan.tandatangan_qr', $surat->id) }}`, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json',
},
})
.then(response => {
if (!response.ok) {
return response.json().then(err => { throw new Error(err.pesan_error) });
}
return response.json()
})
.catch(error => {
Swal.showValidationMessage(
`Request failed: ${error}`
)
})
},
allowOutsideClick: () => !Swal.isLoading()
}).then((result) => {
if (result.isConfirmed) {
let response = result.value
if (response.status == false) {
Swal.fire({
icon: 'error',
title: 'Gagal',
text: response.pesan_error,
})
} else {
Swal.fire({
icon: 'success',
title: 'Surat berhasil ditandatangani dengan QR Code',
showConfirmButton: true,
}).then((result) => {
return window.location.replace(`{{ route('surat.arsip') }}`);
})
}
}
})
});

$('#passphrase').on('click', function() {
Swal.fire({
title: 'Apakah anda yakin ingin menandatangani surat ini?',
Expand Down
23 changes: 16 additions & 7 deletions resources/views/surat/qrcode.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{{ $page_title ?? config('app.name', 'Laravel') }} | {{ $browser_title }}</title>
<title>Document</title>
<link rel="stylesheet" href="{{ asset('/bower_components/bootstrap/dist/css/bootstrap.min.css') }}">
<link rel="stylesheet" href="{{ asset('/bower_components/admin-lte/dist/css/AdminLTE.min.css') }}">

Expand Down Expand Up @@ -72,7 +71,7 @@
<tr>
<td></td>
<td></td>
<td><?= 'a/n ' . $surat->penduduk->nama ?></td>
<td><?= 'a/n ' . ($surat->penduduk->nama ?? $surat->nama_penduduk) ?></td>
</tr>
<tr>
<td colspan="3"><u><b>Ditandatangani oleh :</b></u></td>
Expand All @@ -87,15 +86,25 @@
<td>:</td>
<td>{{ $surat->pengurus->jabatan->nama }}</td>
</tr>
@if ($surat->file_hash)
<tr>
<td>Hash File</td>
<td>:</td>
<td><code style="font-size: 11px;">{{ $surat->file_hash }}</code></td>
</tr>
@endif
</tbody>
</table>
<br />
<div class="callout callout-success row">
<div class="col-md-8 no-float">
<h5><b>Telah ditandatangani secara elektronik</b></h5>
</div>
<div class="col-md-4 no-float">
<img src="{{ asset('img/bsre.png') }}" alt="logo bsre" width="120px" height="auto">
<div class="col-md-12 no-float">
<h5><b>Telah ditandatangani</b></h5>
<p style="font-size: 12px;">
Surat ini telah ditandatangani dan diverifikasi oleh Kecamatan {{ $profil->nama_kecamatan }}.
@if ($surat->file_hash)
Keaslian file dapat diverifikasi dengan mengunggah file pada halaman <a href="{{ route('surat.verifikasi') }}">Verifikasi Surat</a>.
@endif
</p>
</div>
</div>
</center>
Expand Down
Loading