OPEN-ISP v0.5 (pickle-lime) — rilis open-source perdana

Aplikasi billing & manajemen ISP (fiber optic) — PWA pelanggan, portal teknisi & kolektor,
admin panel lengkap, multi-vendor OLT, RADIUS, WhatsApp/Telegram, pembayaran QRIS.

Kredit & lisensi (ISC):
- Base kode & repo: alijayanet/billing-rtrw oleh Alijaya Net — terima kasih banyak
  atas base kode dan repo yang bermanfaat.
- Add-on: GoWA, LibreSpeed/OpenSpeedTest, Uptime Kuma, notification-forwarder.
- Dikembangkan menjadi OPEN-ISP oleh oxpoe — https://github.com/oxpoe/open-isp
This commit is contained in:
oxpoe committed 2026-10-09 05:51:21 +07:00
commit eaf05f2811
280 files changed
+123975

No files matched your search

+278
View File
@@ -0,0 +1,278 @@
/**
* Settings Validation Schema
* Validate setiap field sebelum di-save ke settings.json
*/
const { logger } = require('./logger');
// Validation rules untuk setiap field
const VALIDATION_RULES = {
// Server Configuration
server_port: {
type: 'number',
min: 1024,
max: 65535,
required: true,
description: 'Port server (1024-65535)'
},
server_host: {
type: 'string',
required: true,
description: 'Host server'
},
session_secret: {
type: 'string',
minLength: 32,
required: true,
description: 'Session secret (min 32 karakter)'
},
// Company Information
company_header: {
type: 'string',
maxLength: 100,
required: true,
description: 'Nama perusahaan'
},
public_base_url: {
type: 'string',
pattern: /^https?:\/\/.+/,
description: 'URL publik aplikasi (http://... atau https://...)'
},
company_manager: {
type: 'string',
maxLength: 100,
description: 'Nama manager'
},
company_phone: {
type: 'string',
pattern: /^[0-9\-\+\s]+$/,
description: 'Nomor telepon perusahaan'
},
company_email: {
type: 'string',
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
description: 'Email perusahaan'
},
company_address: {
type: 'string',
maxLength: 500,
description: 'Alamat perusahaan'
},
footer_info: {
type: 'string',
maxLength: 200,
description: 'Footer info'
},
operational_hours: {
type: 'string',
maxLength: 200,
description: 'Jam operasional'
},
// Admin Credentials
admin_username: {
type: 'string',
minLength: 3,
maxLength: 50,
required: true,
description: 'Username admin'
},
admin_password: {
type: 'string',
minLength: 6,
required: true,
description: 'Password admin (min 6 karakter)'
},
admin_api_key: {
type: 'string',
minLength: 10,
description: 'API key admin'
},
// GenieACS Configuration
genieacs_url: {
type: 'string',
pattern: /^https?:\/\/.+/,
description: 'URL GenieACS (http://...)'
},
genieacs_username: {
type: 'string',
description: 'Username GenieACS'
},
genieacs_password: {
type: 'string',
description: 'Password GenieACS'
},
// MikroTik Configuration
mikrotik_host: {
type: 'string',
pattern: /^(\d{1,3}\.){3}\d{1,3}$|^[a-zA-Z0-9\-\.]+$/,
description: 'Host MikroTik (IP atau hostname)'
},
mikrotik_user: {
type: 'string',
minLength: 1,
description: 'Username MikroTik'
},
mikrotik_password: {
type: 'string',
minLength: 1,
description: 'Password MikroTik'
},
mikrotik_port: {
type: 'number',
min: 1024,
max: 65535,
description: 'Port MikroTik'
},
isolir_day: {
type: 'number',
min: 1,
max: 365,
description: 'Hari isolir (1-365)'
},
// WhatsApp Configuration
whatsapp_enabled: {
type: 'boolean',
description: 'Enable WhatsApp'
},
whatsapp_auth_folder: {
type: 'string',
description: 'Folder auth WhatsApp'
},
whatsapp_broadcast_delay: {
type: 'number',
min: 10,
max: 5000,
description: 'Delay broadcast WhatsApp (ms)'
},
// Telegram Configuration
telegram_enabled: {
type: 'boolean',
description: 'Enable Telegram'
},
telegram_bot_token: {
type: 'string',
description: 'Telegram bot token'
},
telegram_admin_id: {
type: 'string',
pattern: /^\d+$/,
description: 'Telegram admin ID (numeric)'
},
// Tripay Configuration
office_lat: {
type: 'string',
pattern: /^-?\d+(\.\d+)?$|^$/,
description: 'Latitude kantor'
},
office_lng: {
type: 'string',
pattern: /^-?\d+(\.\d+)?$|^$/,
description: 'Longitude kantor'
},
// Other
auto_backup_enabled: {
type: 'boolean',
description: 'Enable auto backup'
},
login_otp_enabled: {
type: 'boolean',
description: 'Enable OTP login'
}
};
/**
* Validate single value
*/
function validateValue(field, value, rule) {
// Trim string values
let trimmedValue = value;
if (typeof value === 'string') {
trimmedValue = value.trim();
}
// Check type
if (rule.type === 'number') {
if (typeof trimmedValue !== 'number') return `${field} harus berupa angka`;
if (rule.min !== undefined && trimmedValue < rule.min) return `${field} minimal ${rule.min}`;
if (rule.max !== undefined && trimmedValue > rule.max) return `${field} maksimal ${rule.max}`;
}
if (rule.type === 'string') {
if (typeof trimmedValue !== 'string') return `${field} harus berupa string`;
if (rule.minLength && trimmedValue.length < rule.minLength) return `${field} minimal ${rule.minLength} karakter`;
if (rule.maxLength && trimmedValue.length > rule.maxLength) return `${field} maksimal ${rule.maxLength} karakter`;
if (rule.pattern && !rule.pattern.test(trimmedValue)) return `${field} format tidak valid`;
if (rule.enum && !rule.enum.includes(trimmedValue)) return `${field} harus salah satu dari: ${rule.enum.join(', ')}`;
}
if (rule.type === 'boolean') {
if (typeof trimmedValue !== 'boolean') return `${field} harus berupa boolean`;
}
return null; // Valid
}
/**
* Validate settings object
*/
function validateSettings(settings) {
const errors = [];
Object.keys(settings).forEach(field => {
const value = settings[field];
const rule = VALIDATION_RULES[field];
// Skip jika tidak ada rule (field baru atau optional)
if (!rule) return;
// Check required
if (rule.required && (value === undefined || value === null || value === '')) {
errors.push(`${field} wajib diisi`);
return;
}
// Skip validation jika value kosong dan tidak required
if (!rule.required && (value === undefined || value === null || value === '')) {
return;
}
// Validate value
const error = validateValue(field, value, rule);
if (error) errors.push(error);
});
return {
valid: errors.length === 0,
errors
};
}
/**
* Get validation rule untuk field
*/
function getFieldRule(field) {
return VALIDATION_RULES[field] || null;
}
/**
* Get all validation rules
*/
function getAllRules() {
return VALIDATION_RULES;
}
module.exports = {
validateValue,
validateSettings,
getFieldRule,
getAllRules,
VALIDATION_RULES
};