Perbaikan Mapping Obat Satu Sehat Dengan Query

— Perbaikan denominator_code untuk Tablet Salut Selaput
UPDATE `satu_sehat_mapping_obat`
SET `denominator_code` = ‘TAB’
WHERE `form_code` = ‘BS077’
AND `denominator_code` = ‘BTL’;

— Perbaikan denominator_code untuk Infus
UPDATE `satu_sehat_mapping_obat`
SET `denominator_code` = ‘BAG’
WHERE `form_code` = ‘BS035’
AND `denominator_code` = ‘BTL’;

— Perbaikan denominator_code untuk Larutan Injeksi (yang salah)
UPDATE `satu_sehat_mapping_obat`
SET `denominator_code` = ‘AMP’
WHERE `kode_brng` = ‘B000008028’;

— Perbaikan denominator_code untuk Sirup Kering
UPDATE `satu_sehat_mapping_obat`
SET `denominator_code` = ‘BTL’
WHERE `form_code` = ‘BS056’
AND `denominator_code` NOT IN (‘BTL’);

— Perbaikan denominator_code untuk Kapsul yang salah mapping
UPDATE `satu_sehat_mapping_obat`
SET `denominator_code` = ‘TAB’
WHERE `kode_brng` = ‘B000004186’;

— Perbaikan form display untuk Kapsul Lepas Tunda
UPDATE `satu_sehat_mapping_obat`
SET `form_display` = ‘Kapsul Lepas Tunda’
WHERE `form_code` = ‘BS032’
AND `obat_display` LIKE ‘%Lepas Tunda%’;

— Verifikasi hasil perbaikan
SELECT
form_code,
form_display,
denominator_code,
COUNT(*) as jumlah,
GROUP_CONCAT(DISTINCT kode_brng SEPARATOR ‘, ‘) as contoh_kode
FROM `satu_sehat_mapping_obat`
GROUP BY form_code, form_display, denominator_code
ORDER BY form_code;

— Standarisasi denominator_code berdasarkan form_code
UPDATE `satu_sehat_mapping_obat`
SET `denominator_code` = CASE
WHEN `form_code` = ‘BS066’ THEN ‘TAB’ — Tablet
WHEN `form_code` = ‘BS077’ THEN ‘TAB’ — Tablet Salut Selaput
WHEN `form_code` = ‘BS023’ THEN ‘CAPLET’ — Kaplet Salut Selaput
WHEN `form_code` = ‘BS069’ THEN ‘TAB’ — Tablet Kunyah
WHEN `form_code` = ‘BS073’ THEN ‘TAB’ — Tablet Dispersibel
WHEN `form_code` = ‘BS032’ THEN ‘CAP’ — Kapsul
WHEN `form_code` = ‘BS046’ THEN ‘BTL’ — Sirup
WHEN `form_code` = ‘BS056’ THEN ‘BTL’ — Sirup Kering
WHEN `form_code` = ‘BS060’ THEN ‘BTL’ — Suspensi
WHEN `form_code` = ‘BS034’ THEN ‘AMP’ — Larutan Injeksi
WHEN `form_code` = ‘BS049’ THEN ‘VIAL’ — Serbuk Injeksi
WHEN `form_code` = ‘BS035’ THEN ‘BAG’ — Infus
WHEN `form_code` = ‘BS011’ THEN ‘REC’ — Enema
WHEN `form_code` = ‘BS087’ THEN ‘BTL’ — Tetes Oral
WHEN `form_code` = ‘BS033’ THEN ‘AMP’ — Larutan Inhalasi
ELSE `denominator_code`
END;

— Cek data yang masih inconsistent
SELECT
kode_brng,
obat_display,
form_code,
form_display,
denominator_code,
CASE
WHEN form_code = ‘BS066’ AND denominator_code != ‘TAB’ THEN ‘PERLU PERBAIKAN’
WHEN form_code = ‘BS077’ AND denominator_code != ‘TAB’ THEN ‘PERLU PERBAIKAN’
WHEN form_code = ‘BS032’ AND denominator_code != ‘CAP’ THEN ‘PERLU PERBAIKAN’
ELSE ‘OK’
END as status
FROM `satu_sehat_mapping_obat`
HAVING status = ‘PERLU PERBAIKAN’;

Ditulis pada Tak Berkategori | Tinggalkan komentar

Mengatasi Masalah Audio Tidak Muncul di Pop Os 24.04

# Dokumentasi Perbaikan Audio Pop!_OS 24.04

**Tanggal:** 17 Desember 2025
**Sistem:** Pop!_OS 24.04 NVIDIA Edition
**Audio System:** PipeWire 1.4.2 + WirePlumber

## ๐Ÿ“‹ Ringkasan Masalah

Setelah instalasi Pop!_OS 24.04, audio tidak berfungsi baik untuk output analog (speaker/headphone) maupun HDMI.

### Hardware Audio Terdeteksi:
1. **NVIDIA GPU Audio** (card 0) – untuk HDMI/DisplayPort
– Device: `HDA NVidia (0x22eb)`
– 4 output HDMI tersedia

2. **AMD HD Audio** (card 2) – untuk speaker/headphone analog
– Device: `Realtek ALC897`
– Output: Line Out, Headphones
– Input: Front Mic, Rear Mic, Line In

3. **Logitech Webcam C310** (card 1) – microphone saja

## ๐Ÿ” Root Cause Analysis

### Masalah yang Ditemukan:

1. **Default Audio = Dummy Device**
– Default sink: `auto_null` (tidak menghasilkan suara)

2. **Kartu Audio Dalam Status “OFF”**
– NVIDIA card: Active Profile = `off`
– AMD card: Active Profile = `off`

3. **Jack Detection Gagal**
– Semua port menunjukkan `not available`
– Sistem tidak mendeteksi speaker/headphone terpasang
– Ini adalah bug umum di beberapa motherboard AMD

4. **Profile HDMI-Specific Tidak Available**
– Profile `output:hdmi-stereo` dst menunjukkan `available: no`
– Solusi: gunakan profile `pro-audio` yang selalu available

## โœ… Solusi yang Diterapkan

### 1. Aktivasi Manual Kartu Audio

“`bash
# Aktifkan AMD card (speaker/headphone)
pactl set-card-profile alsa_card.pci-0000_37_00.6 pro-audio

# Aktifkan NVIDIA card (HDMI)
pactl set-card-profile alsa_card.pci-0000_01_00.1 pro-audio
“`

### 2. Konfigurasi Permanent WirePlumber

**File:** `~/.config/wireplumber/main.lua.d/51-alsa-custom.lua`

“`lua
— Set AMD HD Audio to pro-audio profile by default
amd_rule = {
matches = {
{
{ “device.name”, “equals”, “alsa_card.pci-0000_37_00.6” },
},
},
apply_properties = {
[“device.profile”] = “pro-audio”,
},
}

table.insert(alsa_monitor.rules, amd_rule)

— Set NVIDIA HDMI to pro-audio profile by default
nvidia_rule = {
matches = {
{
{ “device.name”, “equals”, “alsa_card.pci-0000_01_00.1” },
},
},
apply_properties = {
[“device.profile”] = “pro-audio”,
},
}

table.insert(alsa_monitor.rules, nvidia_rule)
“`

**Fungsi:** Otomatis mengaktifkan kedua kartu audio setiap boot dengan profile `pro-audio`

### 3. Restart Audio Services

“`bash
systemctl –user restart pipewire wireplumber
“`

## ๐Ÿ”Š Output Audio yang Tersedia

Setelah perbaikan, output audio yang tersedia:

| ID | Device Name | Deskripsi | Lokasi Fisik |
|—|—|—|—|
| 58 | `alsa_output.pci-0000_37_00.6.pro-output-0` | Analog Output | Jack hijau motherboard (Line Out / Headphones) |
| 99 | `alsa_output.pci-0000_01_00.1.pro-output-3` | HDMI 1 | Port HDMI pertama GPU |
| 100 | `alsa_output.pci-0000_01_00.1.pro-output-7` | HDMI 2 | Port HDMI kedua GPU |
| 101 | `alsa_output.pci-0000_01_00.1.pro-output-8` | HDMI 3 | Port HDMI ketiga GPU |
| 102 | `alsa_output.pci-0000_01_00.1.pro-output-9` | HDMI 4 | Port HDMI keempat GPU |

**Catatan:** Nomor output HDMI (3,7,8,9) tidak berurutan karena mengikuti konvensi ALSA device numbering.

## ๐ŸŽฏ Cara Menggunakan

### Melihat Daftar Output Audio

“`bash
pactl list sinks short
“`

### Switch ke HDMI (Monitor/TV)

“`bash
# HDMI 1 (biasanya port pertama yang aktif)
pactl set-default-sink alsa_output.pci-0000_01_00.1.pro-output-3

# Atau lewat GUI: Settings โ†’ Sound โ†’ Output Device
“`

### Switch ke Speaker/Headphone Analog

“`bash
pactl set-default-sink alsa_output.pci-0000_37_00.6.pro-output-0
“`

### Cek Default Output Saat Ini

“`bash
pactl info | grep “Default Sink”
“`

### Test Audio

“`bash
# Speaker test (akan bunyi “Front Left” dan “Front Right”)
speaker-test -t wav -c 2 -l 1

# Atau putar file audio
paplay /usr/share/sounds/freedesktop/stereo/bell.oga
“`

### Cek Volume

“`bash
# Interactive mixer
alsamixer

# Cek volume Master
amixer sget Master
“`

## ๐Ÿ”ง Troubleshooting

### HDMI Tidak Bunyi

1. **Cek monitor/TV mendukung audio HDMI**
– Beberapa monitor tidak punya speaker built-in
– Cek volume di monitor/TV tidak mute

2. **Coba output HDMI lain**
“`bash
# Jika port fisik berbeda, coba output 7, 8, atau 9
pactl set-default-sink alsa_output.pci-0000_01_00.1.pro-output-7
speaker-test -t wav -c 2 -l 1
“`

3. **Cek kabel HDMI tersambung dengan baik**
“`bash
# Lihat port mana yang connected
for port in /sys/class/drm/card0-*/status; do
echo “$port: $(cat $port)”
done
“`

### Speaker/Headphone Tidak Bunyi

1. **Pastikan colok ke jack yang benar**
– Jack hijau = Line Out / Speaker
– Jack merah muda = Microphone
– Biasanya di belakang motherboard

2. **Cek volume tidak mute**
“`bash
alsamixer
# Tekan M untuk unmute jika ada “MM” di channel
“`

3. **Test dengan command**
“`bash
pactl set-default-sink alsa_output.pci-0000_37_00.6.pro-output-0
speaker-test -t wav -c 2 -l 1
“`

### Audio Hilang Setelah Reboot

1. **Cek konfigurasi WirePlumber masih ada**
“`bash
cat ~/.config/wireplumber/main.lua.d/51-alsa-custom.lua
“`

2. **Restart audio services**
“`bash
systemctl –user restart pipewire wireplumber
sleep 3
pactl list sinks short
“`

3. **Cek log WirePlumber untuk error**
“`bash
journalctl –user -u wireplumber -n 50
“`

### Aplikasi Tertentu Tidak Ada Suara

1. **Aplikasi mungkin menggunakan output lama**
– Restart aplikasi setelah switch output
– Atau atur output per-aplikasi di Settings โ†’ Sound โ†’ Applications

2. **Cek aplikasi tidak mute**
“`bash
pavucontrol # Install jika belum ada: sudo apt install pavucontrol
“`

## ๐Ÿ“Š Verifikasi Status Audio

### Command Diagnostik Lengkap

“`bash
# 1. Cek hardware audio terdeteksi
lspci | grep -i audio
cat /proc/asound/cards

# 2. Cek PipeWire running
systemctl –user status pipewire pipewire-pulse wireplumber

# 3. Cek kartu audio dan profil aktif
pactl list cards short
pactl list cards | grep -E “Name:|Active Profile:”

# 4. Cek output audio tersedia
pactl list sinks short

# 5. Cek default output
pactl info | grep “Default Sink”

# 6. Cek volume
amixer sget Master

# 7. Test suara
speaker-test -t wav -c 2 -l 1
“`

### Output yang Diharapkan

**Status Normal:**
“`
# pactl list sinks short
58 alsa_output.pci-0000_37_00.6.pro-output-0 PipeWire s32le 2ch 48000Hz SUSPENDED
99 alsa_output.pci-0000_01_00.1.pro-output-3 PipeWire s32le 8ch 48000Hz RUNNING
100 alsa_output.pci-0000_01_00.1.pro-output-7 PipeWire s32le 8ch 48000Hz SUSPENDED
101 alsa_output.pci-0000_01_00.1.pro-output-8 PipeWire s32le 8ch 48000Hz SUSPENDED
102 alsa_output.pci-0000_01_00.1.pro-output-9 PipeWire s32le 8ch 48000Hz SUSPENDED
“`

**Status:**
– `RUNNING` = sedang aktif digunakan
– `SUSPENDED` = tersedia tapi tidak aktif

## ๐ŸŽ›๏ธ Konfigurasi Lanjutan (Opsional)

### Install PulseAudio Volume Control (GUI)

“`bash
sudo apt install pavucontrol
“`

Kemudian jalankan: `pavucontrol`

**Fitur:**
– Switch output per aplikasi
– Atur volume individual per aplikasi
– Monitor input/output real-time

### Set Default Output Permanent

Jika ingin HDMI atau speaker sebagai default permanent, tambahkan ke `~/.config/pulse/default.pa`:

“`bash
# Buat file jika belum ada
mkdir -p ~/.config/pulse
echo “set-default-sink alsa_output.pci-0000_01_00.1.pro-output-3” >> ~/.config/pulse/default.pa
“`

**Catatan:** Biasanya tidak perlu, sistem akan ingat output terakhir yang digunakan.

## ๐Ÿ“ Catatan Penting

1. **Device ID mungkin berbeda di sistem lain**
– `pci-0000_01_00.1` dan `pci-0000_37_00.6` adalah PCI bus address spesifik untuk hardware ini
– Jika hardware berbeda, jalankan `pactl list cards` untuk mendapatkan device name yang benar

2. **Pro-audio profile vs stereo profile**
– Profile `pro-audio` memberikan kontrol granular per HDMI port
– Profile `output:hdmi-stereo` lebih sederhana tapi kadang tidak available karena jack detection

3. **Jack detection bug**
– Ini adalah bug driver/kernel yang umum di beberapa motherboard
– Workaround dengan `pro-audio` profile adalah solusi yang stabil

4. **NVIDIA driver dan audio HDMI**
– Audio HDMI NVIDIA bekerja dengan driver proprietary maupun nouveau
– Tidak perlu konfigurasi khusus di Pop!_OS karena sudah termasuk NVIDIA driver

## ๐Ÿ”— Referensi

– **PipeWire Documentation:** https://docs.pipewire.org/
– **WirePlumber Documentation:** https://pipewire.pages.freedesktop.org/wireplumber/
– **ALSA Project:** https://www.alsa-project.org/
– **Pop!_OS Support:** https://support.system76.com/

## ๐Ÿ“… Log Perubahan

| Tanggal | Perubahan | Status |
|—|—|—|
| 2025-12-17 | Initial fix – Aktivasi AMD & NVIDIA cards | โœ… Berhasil |
| 2025-12-17 | Buat konfigurasi WirePlumber permanent | โœ… Berhasil |
| 2025-12-17 | Test HDMI dan analog output | โœ… Berhasil |

**Dibuat oleh:** Claude Code
**Sistem:** Pop!_OS 24.04 LTS
**Kernel:** 6.17.9-76061709-generic

Ditulis pada Tak Berkategori | Tinggalkan komentar

Mengatasi Permasalahan Retrieve Failed Saat Ke Server Orthanc

Saya jelaskan ya mas, berdasarkan tampilan di layar:

Hasil Tes

  • Connection Test: Passed โœ…

  • QUERY: Passed โœ…

  • STORE: Passed โœ…

  • RETRIEVE:

    • Pertama kali: Passed โœ…

    • Di Configuration Test: Failed โŒ โ†’ Zero entities fetched

Analisis

  1. Tes koneksi berhasil โ†’ artinya komunikasi ke server Orthanc (192.168.20.221) tidak ada masalah.

  2. STORE berhasil โ†’ artinya server Orthanc bisa menerima data dari modalitas/mesin ini.

  3. QUERY juga berhasil โ†’ artinya server bisa di-ping untuk permintaan daftar data.

  4. RETRIEVE gagal (Zero entities fetched) โ†’ ini biasanya bukan karena jaringan, tapi karena tidak ada data (study/image) yang bisa diambil dari server Orthanc untuk AETitle yang dites.

Kemungkinan Penyebab

  • Orthanc belum punya study yang bisa diretrieve (kosong).

  • Query dilakukan dengan filter tertentu (misalnya PatientID) yang tidak ada di Orthanc.

  • Konfigurasi Retrieve AE Title di Orthanc belum diset dengan benar (misalnya tidak mengenali AE Title dari modalitas ini sebagai authorized peer).

  • Ada mismatch antara AE Title / Port / Hostname di setting mesin dengan yang didaftarkan di Orthanc.

Solusi yang bisa dicoba

  1. Cek isi Orthanc

    • Buka web interface Orthanc (http://192.168.20.221:8042 default) โ†’ login โ†’ pastikan ada study/image.

  2. Tes kirim data

    • Coba kirim 1 study dari mesin ke Orthanc, lalu lakukan retrieve lagi.

  3. Cek config Orthanc

    • Pastikan di file orthanc.json sudah ada konfigurasi AE Title dari modalitas ini di DicomModalities.

    • Contoh:

      "DicomModalities" : {
      "CT1" : [ "CT_AE", "192.168.20.100", 104 ]
      }
  4. Tes retrieve manual

    • Bisa pakai storescu atau getscu (jika ada toolkit DCMTK) untuk memastikan retrieve berjalan dari sisi lain.

๐Ÿ‘‰ Intinya: masalahnya bukan di koneksi, tapi Orthanc tidak ada data atau tidak bisa memberikan data balik ke modalitas karena konfigurasi AE Title/Retrieve-nya.

Ini contoh orthanc.json minimal yang siap untuk QUERY/STORE/RETRIEVE dari modalitas. Silakan sesuaikan IP, AE Title, dan port-nya.

{
"Name": "ORTHANC",
"DicomAet": "ORTHANC", // AE Title Orthanc (yang Anda tes)
"DicomPort": 4242, // Port DICOM Orthanc (default 4242)
"HttpServerEnabled": true,
"HttpPort": 8042, // Web UI Orthanc
"AuthenticationEnabled": false, // set true jika pakai auth

// DAFTAR PEER untuk C-MOVE (HARUS ada AE si modalitas/viewer tujuan retrieve)
// Format: "Alias": ["AETitleTujuan", "IP_Tujuan", PortTujuan]
"DicomModalities": {
"XRAYROOM": ["XRAY_AE", "192.168.20.30", 104],
"USROOM": ["US_AE", "192.168.20.40", 104],
"VIEWERPC": ["VIEWERAE", "192.168.20.50", 11112],

// Tambahkan modalitas yang sedang Anda pakai untuk test retrieve:
// Ganti dengan AE Title & IP PC/mesin Anda
"MY_MODALITY": ["MYMODAE", "192.168.20.100", 104]
},

// Opsi umum (boleh dibiarkan default)
"StoreAnonymous": false,
"OverwriteInstances": false,
"SslEnabled": false
}

Cara pakai & checklist

  1. Pastikan identitas Orthanc

    • AE Title Orthanc = ORTHANC

    • IP Orthanc = 192.168.20.221

    • Port DICOM Orthanc = 4242

  2. Daftarkan modalitas tujuan C-MOVE
    Di blok DicomModalities wajib ada entri untuk PC/mesin yang melakukan retrieve.
    Contoh jika mesin Anda: AE = MYMODAE, IP = 192.168.20.100, port = 104, maka baris:

    "MY_MODALITY": ["MYMODAE", "192.168.20.100", 104]
  3. Restart Orthanc setelah ubah file config.

  4. Buka firewall di PC/mesin tujuan pada port DICOM-nya (104/11112 dlsb) agar Orthanc bisa mendorong image saat C-MOVE.

  5. Uji alur:

    • Kirim 1 study ke Orthanc (STORE).

    • Dari modalitas, lakukan C-FIND (Query) โ†’ harus muncul study.

    • Lakukan C-MOVE (Retrieve) dengan Destination AE = AE modalitas Anda (MYMODAE). Orthanc akan push ke IP/port yang didefinisikan pada DicomModalities.

Catatan:

  • Banyak mesin hanya melakukan C-MOVE (bukan C-GET). Untuk C-MOVE, Orthanc harus tahu AE/IP/port tujuan di DicomModalities. Kalau tidak, muncul kasus โ€œZero entities fetchedโ€/gagal retrieve walau query lulus.

  • Jika tetap kosong: pastikan filter Query (PatientID/StudyDate) tidak menyaring semua data dan memang ada study di Orthanc.

Ditulis pada Tak Berkategori | Tinggalkan komentar

Source Code SSL Untuk TTE

import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.TrustAllStrategy;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;

private void BtnSimpanTandaTanganActionPerformed(java.awt.event.ActionEvent evt) {
R4.setSelected(true);
if (Phrase.getText().equals(“”)) {
Valid.textKosong(Phrase, “Phrase”);
} else {
try {
esign = true;
sertisign = false;
tampilPerawatan();
if (esign == true) {
File g = new File(“file.css”);
BufferedWriter bg = new BufferedWriter(new FileWriter(g));
bg.write(“.isi td{border-right: 1px solid #e2e7dd;font: 8.5px tahoma;height:12px;border-bottom: 1px solid #e2e7dd;background: #ffffff;color:#323232;}.isi a{text-decoration:none;color:#8b9b95;padding:0 0 0 0px;font-family: Tahoma;font-size: 8.5px;border: white;}”);
bg.close();

PdfWriter pdf = new PdfWriter(“RPP” + NoRawat.getText().trim().replaceAll(“/”, “”) + “.pdf”);
HtmlConverter.convertToPdf(
LoadHTMLRiwayatPerawatan.getText().replaceAll(“<head>”, “<head><link href=\”file.css\” rel=\”stylesheet\” type=\”text/css\” />”).
replaceAll(“<body>”,
“<body>”
+ “<table width=’100%’ align=’center’ border=’0′ class=’tbl_form’ cellspacing=’0′ cellpadding=’0′>”
+ “<tr>”
+ “<td width=’15%’ border=’0′>”
+ “<img width=’50’ height=’50’ src=’data:image/jpeg;base64,” + Base64.getEncoder().encodeToString(Sequel.cariGambar(“select setting.logo from setting”).readAllBytes()) + “‘/>”
+ “</td>”
+ “<td width=’85%’ border=’0′>”
+ “<center>”
+ “<font color=’000000′ size=’3′ face=’Tahoma’>” + akses.getnamars() + “</font><br>”
+ “<font color=’000000′ size=’1′ face=’Tahoma’>”
+ akses.getalamatrs() + “, ” + akses.getkabupatenrs() + “, ” + akses.getpropinsirs() + “<br/>”
+ akses.getkontakrs() + “, E-mail : ” + akses.getemailrs()
+ “<br>RIWAYAT PERAWATAN”
+ “</font> ”
+ “</center>”
+ “</td>”
+ “</tr>”
+ “</table><br>”
+ “<table width=’100%’ border=’0′ align=’center’ cellpadding=’3px’ cellspacing=’0′ class=’tbl_form’>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>No.RM</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + NoRM.getText().trim() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Nama Pasien</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + NmPasien.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Alamat</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + Alamat.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Jenis Kelamin</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + Jk.getText().replaceAll(“L”, “Laki-Laki”).replaceAll(“P”, “Perempuan”) + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Tempat & Tanggal Lahir</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + TempatLahir.getText() + ” ” + TanggalLahir.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Ibu Kandung</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + IbuKandung.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Golongan Darah</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + GD.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Status Nikah</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + StatusNikah.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Agama</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + Agama.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Pendidikan Terakhir</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + Pendidikan.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Bahasa Dipakai</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + Bahasa.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Cacat Fisik</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + CacatFisik.getText() + “</td>”
+ “</tr>”
+ “</table>”
).
replaceAll((getClass().getResource(“/picture/”)) + “”, “./gambar/”), pdf
);
System.out.println(“Membuat ulang File RPP” + NoRawat.getText().trim().replaceAll(“/”, “”) + “.pdf untuk dikirim ke server”);
File f = new File(“RPP” + NoRawat.getText().trim().replaceAll(“/”, “”) + “.pdf”);
try {
CloseableHttpClient httpClient = createHttpClientWithSSL();
HttpPost post = new HttpPost(koneksiDB.URLAKSESFILEESIGN());
post.setHeader(“Content-Type”, “application/json”);
post.addHeader(“username”, koneksiDB.USERNAMEAPIESIGN());
post.addHeader(“password”, koneksiDB.PASSAPIESIGN());
post.addHeader(“url”, koneksiDB.URLAPIESIGN());

byte[] fileContent = Files.readAllBytes(f.toPath());

json = “{”
+ “\”file\”:\”” + Base64.getEncoder().encodeToString(fileContent) + “\”,”
+ “\”nik\”:\”” + Sequel.cariIsi(“select pegawai.no_ktp from pegawai where pegawai.nik=?”, akses.getkode()) + “\”,”
+ “\”passphrase\”:\”” + Phrase.getText() + “\”,”
+ “\”tampilan\”:\”visible\”,”
+ “\”image\”:\”false\”,”
+ “\”linkQR\”:\”Dikeluarkan di ” + akses.getnamars() + “, Kabupaten/Kota ” + akses.getkabupatenrs() + “. Ditandatangani secara elektronik oleh ” + dpjp + ” ID ” + kddpjp + ” Tanggal ” + Tanggal.getSelectedItem().toString() + “\”,”
+ “\”width\”:\”70\”,”
+ “\”height\”:\”70\”,”
+ “\”tag_koordinat\”:\”#\””
+ “}”;

System.out.println(“URL Akses file :” + koneksiDB.URLAKSESFILEESIGN());
post.setEntity(new StringEntity(json));
try ( CloseableHttpResponse response = httpClient.execute(post)) {
System.out.println(“Response Status : ” + response.getCode());
json = EntityUtils.toString(response.getEntity());
root = mapper.readTree(json);
if (response.getCode() == 200) {
try ( FileOutputStream fos = new FileOutputStream(new File(“RPP” + NoRawat.getText().trim().replaceAll(“/”, “”) + “.pdf”))) {
byte[] fileBytes = Base64.getDecoder().decode(root.path(“response”).asText());
fos.write(fileBytes);
WindowPhrase.dispose();
JOptionPane.showMessageDialog(null, “Proses tanda tangan berhasil…”);
Desktop.getDesktop().browse(new File(“RPP” + NoRawat.getText().trim().replaceAll(“/”, “”) + “.pdf”).toURI());
} catch (Exception e) {
WindowPhrase.dispose();
JOptionPane.showMessageDialog(null, “Gagal mengkonversi base64 ke file…”);
System.out.println(“Notif : ” + e);
}
} else {
WindowPhrase.dispose();
JOptionPane.showMessageDialog(null, “Code : ” + root.path(“metadata”).path(“code”).asText() + ” Pesan : ” + root.path(“metadata”).path(“message”).asText());
}
} catch (IOException a) {
WindowPhrase.dispose();
System.out.println(“Notifikasi : ” + a);
JOptionPane.showMessageDialog(null, “” + a);
}
} catch (Exception e) {
WindowPhrase.dispose();
System.out.println(“Notifikasi : ” + e);
JOptionPane.showMessageDialog(null, “” + e);
}
}
} catch (Exception e) {
System.out.println(“Notifikasi : ” + e);
}
}
}

// Method untuk membuat HTTP client yang menerima semua SSL certificate
private CloseableHttpClient createHttpClientWithSSL() throws Exception {
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial(new TrustAllStrategy())
.build();

SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
sslContext,
NoopHostnameVerifier.INSTANCE
);

HttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(sslSocketFactory)
.build();

return HttpClients.custom()
.setConnectionManager(connectionManager)
.build();
}

Ditulis pada Tak Berkategori | Tinggalkan komentar

Contoh Script Text to Speech Di Antrian Farmasi Pake Webspeech API

<!DOCTYPE html>
<html lang=”id”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<title>Panggilan Penyerahan Resep</title>
<style>
.card {
background-color: #ff5722;
color: white;
padding: 20px;
border-radius: 8px;
margin: 10px 0;
}
.debug-info {
margin-top: 20px;
padding: 10px;
background-color: #f5f5f5;
border-radius: 4px;
font-size: 12px;
}
</style>
</head>
<body>
<div class=”row”>
<div class=”col s12″ id=”header-instansi”>
<div class=”card”>
<div class=”card-content”>
<h5>
<table border=’0′ width=’100%’>
<tr>
<td>Panggilan Penyerahan Resep</td>
<td>:</td>
<td>
<!– Simulasi data dari PHP –>
<span>12345 REG001 Nyonya Siti Aminah</span>
<span id=’namaPasien’ data-nama=’Nyonya Siti Aminah’></span>
</td>
</tr>
</table>
</h5>
</div>
</div>
</div>
</div>

<div class=”debug-info”>
<strong>Debug Info:</strong>
<div id=”debugInfo”></div>
<button onclick=”testSpeech()”>Test Suara Manual</button>
<button onclick=”showAvailableVoices()”>Tampilkan Voice Tersedia</button>
</div>

<script>
let speechReady = false;
let availableVoices = [];
let debugElement = document.getElementById(‘debugInfo’);

function log(message) {
console.log(message);
if (debugElement) {
debugElement.innerHTML += ‘<br>’ + message;
}
}

function initializeSpeech() {
// Cek dukungan browser
if (!(‘speechSynthesis’ in window)) {
log(‘โŒ Browser tidak mendukung Web Speech API’);
return false;
}

log(‘โœ… Web Speech API didukung’);
return true;
}

function loadVoices() {
availableVoices = window.speechSynthesis.getVoices();
log(`๐Ÿ“ข ${availableVoices.length} suara tersedia`);

// Cari suara bahasa Indonesia
const indonesiaVoices = availableVoices.filter(voice =>
voice.lang.includes(‘id’) || voice.lang.includes(‘ID’)
);

if (indonesiaVoices.length > 0) {
log(`๐Ÿ‡ฎ๐Ÿ‡ฉ Ditemukan ${indonesiaVoices.length} suara bahasa Indonesia`);
speechReady = true;

// Panggil pasien jika ada data
const namaPasienElem = document.getElementById(‘namaPasien’);
if (namaPasienElem && namaPasienElem.dataset.nama) {
setTimeout(() => {
panggilPasien(namaPasienElem.dataset.nama);
}, 500); // Delay sedikit untuk memastikan voices siap
}
} else {
log(‘โš ๏ธ Suara bahasa Indonesia tidak ditemukan, akan menggunakan suara default’);
speechReady = true;
}
}

function panggilPasien(namaPasien) {
if (!speechReady) {
log(‘โŒ Speech belum siap’);
return;
}

// Hentikan speech yang sedang berjalan
window.speechSynthesis.cancel();

const suara = new SpeechSynthesisUtterance();
suara.text = `Pasien dengan nama ${namaPasien}, silakan menuju loket penyerahan obat.`;

// Pengaturan suara
suara.lang = ‘id-ID’;
suara.rate = 0.8; // Sedikit lebih lambat agar jelas
suara.pitch = 1.0;
suara.volume = 1.0;

// Pilih suara terbaik
const indonesiaVoice = availableVoices.find(voice =>
voice.lang === ‘id-ID’ || voice.lang.includes(‘id’)
);

if (indonesiaVoice) {
suara.voice = indonesiaVoice;
log(`๐ŸŽค Menggunakan suara: ${indonesiaVoice.name} (${indonesiaVoice.lang})`);
} else {
log(‘๐ŸŽค Menggunakan suara default’);
}

// Event handlers
suara.onstart = function() {
log(‘โ–ถ๏ธ Mulai berbicara’);
};

suara.onend = function() {
log(‘โœ… Selesai berbicara’);
};

suara.onerror = function(event) {
log(`โŒ Error: ${event.error}`);
};

// Jalankan Text-to-Speech
try {
window.speechSynthesis.speak(suara);
log(`๐Ÿ“ข Memanggil: ${namaPasien}`);
} catch (error) {
log(`โŒ Gagal memanggil: ${error.message}`);
}
}

function testSpeech() {
panggilPasien(‘Test Pasien’);
}

function showAvailableVoices() {
log(‘— DAFTAR SUARA TERSEDIA —‘);
availableVoices.forEach((voice, index) => {
log(`${index + 1}. ${voice.name} (${voice.lang}) – ${voice.localService ? ‘Lokal’ : ‘Online’}`);
});
}

// Inisialisasi
if (initializeSpeech()) {
// Load voices saat halaman dimuat
if (window.speechSynthesis.getVoices().length > 0) {
loadVoices();
}

// Event listener untuk voices changed
window.speechSynthesis.onvoiceschanged = function() {
log(‘๐Ÿ”„ Voices berubah, memuat ulang…’);
loadVoices();
};

// Fallback: coba load voices setelah delay
setTimeout(() => {
if (!speechReady) {
log(‘โฐ Fallback: memuat voices setelah delay’);
loadVoices();
}
}, 1000);
}

// Cleanup saat halaman akan ditutup
window.addEventListener(‘beforeunload’, function() {
window.speechSynthesis.cancel();
});
</script>
</body>
</html>

Ditulis pada Tak Berkategori | Tinggalkan komentar

Memberikan Background Merah di Link Berkas Digital

private void menampilkanBerkasDigital(String norawat) {
try {
if(chkBerkasDigital.isSelected()==true){
try{
rs2=koneksi.prepareStatement(
“select master_berkas_digital.nama,berkas_digital_perawatan.lokasi_file “+
“from berkas_digital_perawatan inner join master_berkas_digital “+
“on berkas_digital_perawatan.kode=master_berkas_digital.kode “+
“where berkas_digital_perawatan.no_rawat='”+norawat+”‘”).executeQuery();
if(rs2.next()){
htmlContent.append(
“<tr class=’isi’>”).append(
“<td valign=’top’ width=’2%’></td>”).append(
“<td valign=’top’ width=’18%’>Berkas Digital Perawatan</td>”).append(
“<td valign=’top’ width=’1%’ align=’center’>:</td>”).append(
“<td valign=’top’ width=’79%’>”).append(
“<table width=’100%’ border=’0′ align=’center’ cellpadding=’3px’ cellspacing=’0′ class=’tbl_form’>”).append(
“<tr align=’center’>”).append(
“<td valign=’top’ width=’4%’ bgcolor=’#FFFAF8′>No.</td>”).append(
“<td valign=’top’ width=’96%’ bgcolor=’#FFFAF8′>Berkas Digital</td>”).append(
“</tr>”
);
w=1;
do{
if(rs2.getString(“lokasi_file”).toLowerCase().contains(“.jpg”)||rs2.getString(“lokasi_file”).toLowerCase().contains(“.jpeg”)){
htmlContent.append(
“<tr>”).append(
“<td valign=’top’ align=’center’>”).append(w).append(“</td>”).append(
“<td valign=’top’ align=’center’>”).append(rs2.getString(“nama”)).append(“<br><a href=’http://”).append(koneksiDB.HOSTHYBRIDWEB()).append(“:”).append(koneksiDB.PORTWEB()).append(“/”).append(koneksiDB.HYBRIDWEB()).append(“/berkasrawat/”).append(rs2.getString(“lokasi_file”)).append(“‘><img alt=’Berkas Digital’ src=’http://”).append(koneksiDB.HOSTHYBRIDWEB()).append(“:”).append(koneksiDB.PORTWEB()).append(“/”).append(koneksiDB.HYBRIDWEB()).append(“/berkasrawat/”).append(rs2.getString(“lokasi_file”)).append(“‘ width=’450′ height=’450’/></a></td>”).append(
“</tr>”);
}else{
htmlContent.append(
“<tr>”).append(
“<td valign=’top’ align=’center’>”).append(w).append(“</td>”).append(
“<td valign=’top’><a href=’http://”).append(koneksiDB.HOSTHYBRIDWEB()).append(“:”).append(koneksiDB.PORTWEB()).append(“/”).append(koneksiDB.HYBRIDWEB()).append(“/berkasrawat/”).append(rs2.getString(“lokasi_file”)).append(“‘ style=’background-color: #8B0000; color: white; font-weight: bold; text-decoration: none; display: inline-block; padding: 8px 12px; border-radius: 4px; transition: background-color 0.3s;’ onmouseover=’this.style.backgroundColor=\”#A00000\”‘ onmouseout=’this.style.backgroundColor=\”#8B0000\”‘>”).append(rs2.getString(“nama”)).append(“_”).append(rs2.getString(“lokasi_file”).replaceAll(“pages/upload/”,””)).append(“</a></td>”).append(
“</tr>”);
}
w++;
}while(rs2.next());
htmlContent.append(
“</table>”).append(
“</td>”).append(
“</tr>”);
}
} catch (Exception e) {
System.out.println(“Notifikasi : “+e);
} finally{
if(rs2!=null){
rs2.close();
}
}
}
}catch (Exception e) {
System.out.println(“Notif Berkas Digitaln: “+e);
}
}

Ditulis pada Tak Berkategori | Tinggalkan komentar

Membuat Format Nomor Mengandung Bulan dan Tanggal

// Tambahkan method baru untuk generate nomor rujukan dengan format yang diinginkan
private void generateNoRujukan() {
try {
// Ambil tanggal saat ini
Calendar cal = Calendar.getInstance();
int tahun = cal.get(Calendar.YEAR);
int bulan = cal.get(Calendar.MONTH) + 1; // Calendar.MONTH dimulai dari 0

// Konversi bulan ke angka romawi
String bulanRomawi = convertToRoman(bulan);

// Query untuk mencari nomor urut terakhir dalam bulan dan tahun yang sama
String sql = “SELECT MAX(CAST(SUBSTRING_INDEX(no_rujuk, ‘/’, 1) AS UNSIGNED)) as max_no ” +
“FROM rujuk ” +
“WHERE YEAR(tgl_rujuk) = ? AND MONTH(tgl_rujuk) = ?”;

PreparedStatement ps = koneksi.prepareStatement(sql);
ps.setInt(1, tahun);
ps.setInt(2, bulan);

ResultSet rs = ps.executeQuery();

int nomorUrut = 1; // Default nomor urut
if (rs.next()) {
Integer maxNo = rs.getInt(“max_no”);
if (!rs.wasNull()) {
nomorUrut = maxNo + 1;
}
}

// Format nomor urut dengan leading zero (3 digit)
String nomorUrutFormatted = String.format(“%03d”, nomorUrut);

// Buat nomor rujukan dengan format: 001/SR/VIII/2025
String noRujukan = nomorUrutFormatted + “/SR/” + bulanRomawi + “/” + tahun;

TNoRj.setText(noRujukan);

rs.close();
ps.close();

} catch (Exception e) {
System.out.println(“Error generating nomor rujukan: ” + e);
// Fallback ke nomor default jika ada error
TNoRj.setText(“001/SR/I/” + Calendar.getInstance().get(Calendar.YEAR));
}
}

// Method untuk konversi bulan ke angka romawi
private String convertToRoman(int bulan) {
String[] bulanRomawi = {
“I”, “II”, “III”, “IV”, “V”, “VI”,
“VII”, “VIII”, “IX”, “X”, “XI”, “XII”
};

if (bulan >= 1 && bulan <= 12) {
return bulanRomawi[bulan – 1];
}
return “I”; // Default ke Januari jika ada error
}

// Modifikasi method emptTeks() untuk menggunakan format baru
public void emptTeks() {
TNoRj.setText(“”);
TTmpRujuk.setText(“”);
TNoRw.setText(“”);
TNoRM.setText(“”);
TPasien.setText(“”);
KdDok.setText(“”);
TDokter.setText(“”);
TDiagnosa.setText(“”);
DTPRujuk.setDate(new Date());

// Ganti baris ini:
// Valid.autoNomer(“rujuk”,”R”,9,TNoRj);
// Dengan:
generateNoRujukan();

TNoRj.requestFocus();
ktrujuk.setSelectedIndex(0);
ambulance.setSelectedIndex(0);
ket.setText(“”);
}

// Jika Anda ingin nomor rujukan juga ter-generate saat tanggal rujukan berubah,
// tambahkan modifikasi pada DTPRujukKeyPressed atau buat listener untuk DTPRujuk:
private void DTPRujukKeyPressed(java.awt.event.KeyEvent evt) {
Valid.pindah(evt,TNoRj,CmbJam);

// Regenerate nomor rujukan jika tanggal berubah
if (evt.getKeyCode() == KeyEvent.VK_ENTER || evt.getKeyCode() == KeyEvent.VK_TAB) {
generateNoRujukan();
}
}

// Atau bisa tambahkan ActionListener untuk DTPRujuk di constructor:
// Tambahkan ini di constructor DlgRujuk setelah inisialisasi komponen:
/*
DTPRujuk.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
generateNoRujukan();
}
});
*/

Ditulis pada Tak Berkategori | Tinggalkan komentar

Membuat HTTPS localhost (SSL) Menggunakan mkcert di Ubuntu 22.04

Berikut cara membuat HTTPS localhost menggunakan mkcert di Ubuntu 22.04:

1. Install mkcert

bash
# Install menggunakan curl
curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64"
chmod +x mkcert-v*-linux-amd64
sudo mv mkcert-v*-linux-amd64 /usr/local/bin/mkcert

# Atau install dari snap
sudo snap install mkcert

2. Setup Certificate Authority (CA)

bash
# Install CA root certificate ke system trust store
mkcert -install

Perintah ini akan membuat CA lokal dan menambahkannya ke trust store browser dan sistem.

3. Generate Certificate untuk Localhost

bash
# Generate certificate untuk localhost
mkcert localhost 127.0.0.1 ::1

# Atau untuk domain custom
mkcert example.local "*.example.local"

Ini akan menghasilkan dua file:

  • localhost+2.pem (certificate)
  • localhost+2-key.pem (private key)

4. Konfigurasi Web Server

Untuk Nginx:

nginx
server {
    listen 443 ssl;
    server_name localhost;
    
    ssl_certificate /path/to/localhost+2.pem;
    ssl_certificate_key /path/to/localhost+2-key.pem;
    
    location / {
        root /var/www/html;
        index index.html;
    }
}

Untuk Apache:

apache
<VirtualHost *:443>
    ServerName localhost
    DocumentRoot /var/www/html
    
    SSLEngine on
    SSLCertificateFile /path/to/localhost+2.pem
    SSLCertificateKeyFile /path/to/localhost+2-key.pem
</VirtualHost>

Untuk Node.js/Express:

javascript
const https = require('https');
const fs = require('fs');
const express = require('express');

const app = express();

const options = {
    key: fs.readFileSync('localhost+2-key.pem'),
    cert: fs.readFileSync('localhost+2.pem')
};

https.createServer(options, app).listen(3000, () => {
    console.log('HTTPS Server running on https://localhost:3000');
});

5. Test Certificate

Buka browser dan akses https://localhost. Anda seharusnya melihat:

  • Tidak ada warning SSL
  • Icon gembok hijau di address bar
  • Certificate valid

Tips Tambahan:

Lokasi CA root certificate:

bash
# Cek lokasi CA
mkcert -CAROOT

Generate untuk multiple domains:

bash
mkcert localhost 127.0.0.1 ::1 example.local "*.example.local"

Uninstall CA (jika diperlukan):

bash
mkcert -uninstall

Dengan setup ini, Anda akan memiliki HTTPS localhost yang valid tanpa warning browser, sangat berguna untuk development aplikasi yang memerlukan HTTPS.

SM

untuk apache copykan kemana filenya dan virtual host dibuat dimana

Berikut detail lengkap untuk konfigurasi Apache di Ubuntu 22.04:

1. Lokasi File Certificate

Salin file certificate ke direktori yang aman:

bash
# Buat direktori untuk SSL certificates
sudo mkdir -p /etc/ssl/private
sudo mkdir -p /etc/ssl/certs

# Copy certificate files
sudo cp localhost+2.pem /etc/ssl/certs/
sudo cp localhost+2-key.pem /etc/ssl/private/

# Set permissions yang tepat
sudo chmod 644 /etc/ssl/certs/localhost+2.pem
sudo chmod 600 /etc/ssl/private/localhost+2-key.pem
sudo chown root:root /etc/ssl/certs/localhost+2.pem
sudo chown root:root /etc/ssl/private/localhost+2-key.pem

2. Buat Virtual Host

Lokasi Virtual Host di Ubuntu:

  • Available sites: /etc/apache2/sites-available/
  • Enabled sites: /etc/apache2/sites-enabled/

Buat file konfigurasi:

bash
# Buat file virtual host baru
sudo nano /etc/apache2/sites-available/localhost-ssl.conf

Isi file konfigurasi:

apache
<VirtualHost *:443>
    ServerName localhost
    DocumentRoot /var/www/html
    
    # SSL Configuration
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/localhost+2.pem
    SSLCertificateKeyFile /etc/ssl/private/localhost+2-key.pem
    
    # Optional: SSL Security Headers
    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
    Header always set X-Content-Type-Options nosniff
    Header always set X-Frame-Options DENY
    
    # Log files
    ErrorLog ${APACHE_LOG_DIR}/localhost_ssl_error.log
    CustomLog ${APACHE_LOG_DIR}/localhost_ssl_access.log combined
</VirtualHost>

# Optional: Redirect HTTP to HTTPS
<VirtualHost *:80>
    ServerName localhost
    Redirect permanent / https://localhost/
</VirtualHost>

3. Enable Modules dan Site

bash
# Enable SSL module
sudo a2enmod ssl
sudo a2enmod headers

# Enable site
sudo a2ensite localhost-ssl.conf

# Test konfigurasi
sudo apache2ctl configtest

# Restart Apache
sudo systemctl restart apache2

4. Struktur Direktori Lengkap

/etc/apache2/
โ”œโ”€โ”€ sites-available/
โ”‚   โ”œโ”€โ”€ 000-default.conf
โ”‚   โ”œโ”€โ”€ default-ssl.conf
โ”‚   โ””โ”€โ”€ localhost-ssl.conf          # File yang kita buat
โ”œโ”€โ”€ sites-enabled/
โ”‚   โ”œโ”€โ”€ 000-default.conf -> ../sites-available/000-default.conf
โ”‚   โ””โ”€โ”€ localhost-ssl.conf -> ../sites-available/localhost-ssl.conf
โ””โ”€โ”€ ...

/etc/ssl/
โ”œโ”€โ”€ certs/
โ”‚   โ””โ”€โ”€ localhost+2.pem            # Certificate file
โ””โ”€โ”€ private/
    โ””โ”€โ”€ localhost+2-key.pem        # Private key

5. Verifikasi Setup

bash
# Cek apakah Apache listening di port 443
sudo netstat -tlnp | grep :443

# Cek enabled sites
sudo a2ensite --list

# Cek SSL certificate
openssl x509 -in /etc/ssl/certs/localhost+2.pem -text -noout

6. Test di Browser

  1. Buka https://localhost
  2. Pastikan tidak ada SSL warning
  3. Cek certificate details di browser

7. Troubleshooting

Jika ada error permission:

bash
sudo chown -R www-data:www-data /var/www/html
sudo chmod -R 755 /var/www/html

Jika Apache tidak start:

bash
# Cek syntax error
sudo apache2ctl configtest

# Cek Apache status
sudo systemctl status apache2

# Cek log error
sudo tail -f /var/log/apache2/error.log

Disable default SSL site jika konflik:

bash
sudo a2dissite default-ssl.conf
sudo systemctl reload apache2

Dengan konfigurasi ini, Anda akan memiliki HTTPS localhost yang berfungsi dengan baik di Apache pada Ubuntu 22.04.

Ditulis pada Tak Berkategori | Tinggalkan komentar

Cara Mengatur Agar Audio di Izinkan di Google Chrome

Berikut adalah cara mengatur agar audio diizinkan di Google Chrome:

Mengatur Izin Audio untuk Situs Tertentu:
1. Buka Google Chrome dan kunjungi situs yang ingin Anda izinkan audio.
2. Klik ikon kunci atau ikon info di sebelah kiri alamat situs di bilah alamat.
3. Klik pada “Site settings” atau “Pengaturan situs”.
4. Cari bagian “Permissions” atau “Izin” dan cari opsi “Sound” atau “Suara”.
5. Pilih opsi “Allow” atau “Izinkan” untuk mengizinkan audio pada situs tersebut.

Mengatur Izin Audio untuk Semua Situs:
1. Buka Google Chrome dan ketik chrome://settings/content/sound di bilah alamat.
2. Anda akan diarahkan ke halaman pengaturan suara.
3. Pilih opsi “Sites can ask to play sound” atau “Situs dapat meminta untuk memutar suara”.

Mengatur Izin Audio untuk Situs yang Diblokir:
1. Jika Anda telah memblokir audio untuk situs tertentu dan ingin mengizinkannya kembali, Anda dapat melakukan langkah-langkah berikut.
2. Buka Google Chrome dan ketik chrome://settings/content/sound di bilah alamat.
3. Cari bagian “Block” atau “Blokir” dan cari situs yang ingin Anda izinkan audio.
4. Klik pada ikon tiga titik di sebelah situs dan pilih “Remove” atau “Hapus”.

Dengan mengikuti langkah-langkah di atas, Anda dapat mengatur agar audio diizinkan di Google Chrome untuk situs tertentu atau semua situs.

Ditulis pada Tak Berkategori | Tinggalkan komentar

Cara Mengambil Tahun Pakai Query Mariadb dari Kolom Tanggal

Untuk mengambil tahun dari kolom tanggal di tabel pengajuan_cuti, Anda dapat menggunakan fungsi bawaan SQL seperti YEAR(). Berikut adalah query-nya:

sql
SELECT YEAR(tanggal) AS tahun
FROM pengajuan_cuti;

Penjelasan:

  • YEAR(tanggal): Mengambil tahun dari kolom tanggal, yang diasumsikan bertipe DATE, DATETIME, atau tipe data serupa.
  • AS tahun: Memberi alias untuk kolom hasil agar lebih mudah dibaca (opsional).

Jika Anda ingin hanya mengambil tahun tertentu atau data lainnya berdasarkan tahun, Anda bisa menambahkan klausa WHERE atau GROUP BY. Contoh:

Filter Berdasarkan Tahun Tertentu

sql
SELECT *
FROM pengajuan_cuti
WHERE YEAR(tanggal) = 2025;

Mengelompokkan Berdasarkan Tahun

sql
SELECT YEAR(tanggal) AS tahun, COUNT(*) AS jumlah_pengajuan
FROM pengajuan_cuti
GROUP BY YEAR(tanggal);

Silakan sesuaikan dengan kebutuhan Anda! ๐Ÿ˜Š

Ditulis pada Tak Berkategori | Tinggalkan komentar