Is uw domein nog beschikbaar?
Controleer direct uw domeinnaam en registreer eenvoudig online.
STATUS CONTROLEREN
CHECKING
ACTIVE SERVICES
LIVE
UPTIME HISTORY
LAST 14 DAYS
(function(){
/* ========================================================== CONFIGURATIE ========================================================== */
const STATUS_URL = 'https://status.oil4.nl/index.json';
/* * Oil4 nieuwe server live: * 14 september 2026. * * Na 30 dagen schakelt SINCE LAUNCH * automatisch naar 30 DAYS. */
const LAUNCH_DATE = new Date('2026-09-14T00:00:00+02:00');
const REFRESH_INTERVAL = 60000;
const WANTED_SERVICES = [
{ key:'web', label:'Web' },
{ key:'plesk', label:'Plesk' },
{ key:'imap', label:'IMAP' },
{ key:'pop3', label:'POP3' },
{ key:'smtp', label:'SMTP' }
];
/* ========================================================== HELPERS ========================================================== */
function clamp(value,min,max){
return Math.min( Math.max(value,min), max );
}
/* * Better Stack availability kan afhankelijk * van output bijvoorbeeld 1.0 of 100 zijn. */
function normalizeAvailability(value){
if( value === null || value === undefined || value === '' ){ return null; }
let number = parseFloat(value);
if(isNaN(number)){ return null; }
if(number >= 0 && number <= 1){ number *= 100; } return clamp( number, 0, 100 ); } function normalizeName(name){ return String(name || '') .toLowerCase() .replace( /^oil4[\s\-_:]*/i, '' ) .replace( /[^a-z0-9]/g, '' ); } function isOperational(status){ const state = String(status || '') .toLowerCase(); return( state === 'operational' || state === 'up' || state === 'resolved' ); } function uptimeClass(value){ if( value === null || value === undefined || isNaN(value) ){ return 'status-na'; } value = Number(value); if(value >= 99.999){ return 'status-perfect'; }
if(value >= 99.90){ return 'status-good'; }
if(value >= 99){ return 'status-warning'; }
return 'status-bad';
}
function percentage(value){
if( value === null || value === undefined || isNaN(value) ){ return '--.--%'; }
return Number(value) .toFixed(2) + '%';
}
function displayDate(date){
return date .toLocaleDateString( 'nl-NL', { day:'2-digit', month:'short' } )
.replace('.','')
.toUpperCase();
}
function localDayString(date){
const year = date.getFullYear();
const month = String( date.getMonth() + 1 ).padStart(2,'0');
const day = String( date.getDate() ).padStart(2,'0');
return( year + '-' + month + '-' + day );
}
/* ========================================================== SINCE LAUNCH / 30 DAYS ========================================================== */
function updatePeriodLabel(){
const now = new Date();
const elapsedDays =
( now.getTime() - LAUNCH_DATE.getTime() ) / 86400000;
document.getElementById( 'oil4-period-label' ).textContent =
elapsedDays >= 30
? '30 DAYS'
: 'SINCE LAUNCH';
}
/* ========================================================== GAUGE ========================================================== */
function setGauge(value){
const gauge = document.getElementById( 'oil4-gauge-progress' );
gauge.classList.remove( 'status-perfect', 'status-good', 'status-warning', 'status-bad' );
if( value === null || isNaN(value) ){
gauge.style.strokeDashoffset = 553;
return;
}
const p = clamp( Number(value), 0, 100 );
gauge.style.strokeDashoffset =
553 -
( p / 100 ) * 553;
gauge.classList.add( uptimeClass(p) );
}
/* ========================================================== BETTER STACK RESOURCES ========================================================== */
function getResources(json){
if( !json || !Array.isArray(json.included) ){
return [];
}
return json.included
.filter( function(item){
return( item && item.type === 'status_page_resource' && item.attributes );
} )
.map( function(item){
const a = item.attributes;
return{
id:item.id,
name: a.public_name || '',
status: a.status || 'not_monitored',
availability: normalizeAvailability( a.availability ),
history: Array.isArray( a.status_history ) ? a.status_history : []
};
} );
}
/* ========================================================== SERVICE MATCHING ========================================================== */
function findResource( resources, service ){
const wanted = normalizeName( service.key );
/* * Eerst exacte match. */
let resource = resources.find( function(item){
return( normalizeName( item.name ) === wanted );
} );
if(resource){ return resource; }
/* * Daarna bijvoorbeeld: * * Oil4 Web * Oil4 - Web * Oil4 Web Server */
resource = resources.find( function(item){
const name = normalizeName( item.name );
return( name.includes( wanted ) );
} );
return resource || null;
}
/* ========================================================== SERVICES RENDEREN ========================================================== */
function renderServices(resources){
const container = document.getElementById( 'oil4-services-list' );
container.innerHTML = '';
const matched = [];
WANTED_SERVICES.forEach( function(service){
const resource = findResource( resources, service );
matched.push({ service:service, resource:resource });
const row = document.createElement( 'div' );
/* * Alleen expliciete downtime/degraded * wordt als probleem weergegeven. * * Niet gevonden = UNKNOWN, * dus niet automatisch OFFLINE. */
let status = resource ? resource.status : 'unknown';
let availability = resource ? resource.availability : null;
let online = resource ? isOperational(status) : null;
let cssClass;
if(online === false){
cssClass = 'status-bad';
} else if( availability !== null ){
cssClass = uptimeClass( availability );
} else if( online === true ){
cssClass = 'status-perfect';
} else{
cssClass = 'status-na';
}
row.className = 'oil4-service-row ' + cssClass;
let barWidth = 0;
if(online === false){
barWidth = 0;
} else if( availability !== null ){
barWidth = availability;
} else if( online === true ){
barWidth = 100;
}
let label;
/* * Gebruiker wilde rechts: * ONLINE / OFFLINE. */
if(online === true){
label = 'ONLINE';
} else if(online === false){
label = 'OFFLINE';
} else{
label = '---';
}
row.innerHTML =
'
'' +
'' + service.label + '' +
'
' +
'
'' +
'
' +
'
label +
'
';
container.appendChild( row );
} );
return matched;
}
/* ========================================================== DAG-UPTIME ========================================================== */
function calculateDayUptime( matchedResources, date ){
const day = localDayString(date);
const now = new Date();
const isToday = localDayString(now) === day;
let elapsedSeconds;
if(isToday){
const start = new Date(date);
start.setHours( 0,0,0,0 );
elapsedSeconds =
( now.getTime() - start.getTime() ) / 1000;
} else{
elapsedSeconds = 86400;
}
elapsedSeconds = Math.max( elapsedSeconds, 1 );
const uptimes = [];
matchedResources.forEach( function(match){
const resource = match.resource;
if(!resource){ return; }
const record = resource.history.find( function(entry){
return( String( entry.day || '' ) .substring(0,10) === day );
} );
if(!record){ return; }
const downtime = parseFloat( record.downtime_duration || 0 );
/* * Per service: * * uptime = * actieve seconden - downtime. */
const uptime =
( ( elapsedSeconds - downtime ) / elapsedSeconds ) * 100;
uptimes.push( clamp( uptime, 0, 100 ) );
} );
if(!uptimes.length){
return null;
}
/* * Voor totale infrastructuur nemen we * de slechtste service van die dag. * * Daarmee wordt een storing niet * weggemiddeld. */
return Math.min.apply( null, uptimes );
}
/* ========================================================== HISTORY ========================================================== */
function renderHistory( matchedResources ){
const container = document.getElementById( 'oil4-history' );
container.innerHTML = '';
const today = new Date();
today.setHours( 0,0,0,0 );
const launch = new Date( LAUNCH_DATE );
launch.setHours( 0,0,0,0 );
const values = [];
for( let offset = 13; offset >= 0; offset-- ){
const date = new Date(today);
date.setDate( date.getDate() - offset );
const beforeLaunch = date < launch; let uptime = null; if(!beforeLaunch){ uptime = calculateDayUptime( matchedResources, date ); } /* * Vandaag kan Better Stack vlak na * livegang nog geen history-record * hebben. * * Als alle gematchte services nu * operational zijn, tonen we voor * VANDAAG 100%. */ if( offset === 0 && uptime === null ){ const realResources = matchedResources .map( item => item.resource ) .filter(Boolean);
if( realResources.length && realResources.every( resource => isOperational( resource.status ) ) ){
uptime = 100;
}
}
if( !beforeLaunch && uptime !== null ){
values.push({ date:date, value:uptime });
}
const element = document.createElement( 'div' );
let cssClass;
if(beforeLaunch){
cssClass = 'status-na';
} else if( uptime === null ){
cssClass = 'status-na';
} else{
cssClass = uptimeClass( uptime );
}
element.className = 'oil4-day ' + cssClass;
if(offset === 0){
element.classList.add( 'today' );
}
let fillHeight;
if( beforeLaunch || uptime === null ){
fillHeight = 12;
} else{
/* * 99.9% moet visueel nog steeds * bijna vol zijn. */
fillHeight = Math.max( 15, uptime );
}
const uptimeText =
beforeLaunch ? 'N/A'
: uptime === null ? '--'
: percentage( uptime );
const label =
offset === 0 ? 'VANDAAG' : displayDate(date);
element.innerHTML =
'
'
' +
'
' +
'
' +
'
';
container.appendChild( element );
}
return values;
}
/* ========================================================== GROTE UPTIME ========================================================== */
function calculateOverall( historyValues, matchedResources ){
const now = new Date();
const daysSinceLaunch =
( now.getTime() - LAUNCH_DATE.getTime() ) / 86400000;
const maxDays = daysSinceLaunch >= 30 ? 30 : 30;
/* * We gebruiken echte historische * daggegevens wanneer beschikbaar. */
const validHistory = historyValues .slice( -maxDays );
if(validHistory.length){
let total = 0;
validHistory.forEach( item => {
total += item.value;
} );
return( total / validHistory.length );
}
/* * Dag 1: * history kan nog leeg zijn. * * Dan availability gebruiken. */
const availabilities =
matchedResources
.map( item => item.resource ? item.resource.availability : null )
.filter( value => value !== null && !isNaN(value) );
if(availabilities.length){
return Math.min.apply( null, availabilities );
}
/* * Zijn alle services operationeel, * maar heeft Better Stack nog geen * availability/history opgebouwd? * * Dan is vandaag vanaf launch 100%. */
const actualResources =
matchedResources
.map( item => item.resource )
.filter(Boolean);
if( actualResources.length && actualResources.every( resource => isOperational( resource.status ) ) ){
return 100;
}
return null;
}
/* ========================================================== MASTER STATUS ========================================================== */
function renderMasterStatus( json, matchedResources ){
const master = document.getElementById( 'oil4-master-status' );
const text = document.getElementById( 'oil4-master-text' );
const meter = document.getElementById( 'oil4-meter-online' );
/* * Better Stack heeft zelf ook een * aggregate_state op de statuspagina. */
const aggregateState =
json && json.data && json.data.attributes
? String( json.data.attributes .aggregate_state || '' ).toLowerCase()
: '';
const resources =
matchedResources
.map( item => item.resource )
.filter(Boolean);
let operational;
if(aggregateState){
operational = aggregateState === 'operational';
} else if(resources.length){
operational = resources.every( resource => isOperational( resource.status ) );
} else{
operational = null;
}
master.classList.remove( 'loading', 'operational', 'issue' );
meter.classList.remove( 'offline' );
if(operational === true){
master.classList.add( 'operational' );
text.textContent = 'ALL SYSTEMS OPERATIONAL';
meter.innerHTML = ' ONLINE';
} else if(operational === false){
master.classList.add( 'issue' );
text.textContent = 'SYSTEM ALERT';
meter.classList.add( 'offline' );
meter.innerHTML = ' ALERT';
} else{
text.textContent = 'STATUS UNKNOWN';
meter.innerHTML = ' CHECKING';
}
}
/* ========================================================== LAST UPDATE ========================================================== */
function updateClock(){
const now = new Date();
const date = now.toLocaleDateString( 'nl-NL', { day:'2-digit', month:'short', year:'numeric' } );
const time = now.toLocaleTimeString( 'nl-NL', { hour:'2-digit', minute:'2-digit', second:'2-digit' } );
document.getElementById( 'oil4-last-update' ).textContent =
'LAST UPDATE: ' + date.toUpperCase() + ' · ' + time;
}
/* ========================================================== LOAD STATUS ========================================================== */
async function loadOil4Status(){
try{
const response = await fetch( STATUS_URL, { cache:'no-store' } );
if(!response.ok){
throw new Error( 'HTTP ' + response.status );
}
const json = await response.json();
/* * Juiste Better Stack structuur: * * included[] * type=status_page_resource * attributes.public_name * attributes.status * attributes.availability * attributes.status_history */
const resources = getResources( json );
const matchedResources = renderServices( resources );
renderMasterStatus( json, matchedResources );
const historyValues = renderHistory( matchedResources );
const overall = calculateOverall( historyValues, matchedResources );
document.getElementById( 'oil4-main-uptime' ).textContent =
overall !== null ? percentage(overall) : '--.--%';
setGauge( overall );
updatePeriodLabel();
updateClock();
} catch(error){
console.error( 'Oil4 status error:', error );
document.getElementById( 'oil4-master-text' ).textContent = 'STATUS FEED OFFLINE';
const master = document.getElementById( 'oil4-master-status' );
master.classList.remove( 'operational' );
master.classList.add( 'issue' );
const meter = document.getElementById( 'oil4-meter-online' );
meter.classList.add( 'offline' );
meter.innerHTML = ' FEED OFFLINE';
document.getElementById( 'oil4-main-uptime' ).textContent = '--.--%';
setGauge(null);
}
}
/* ========================================================== START ========================================================== */
updatePeriodLabel();
loadOil4Status();
setInterval( loadOil4Status, REFRESH_INTERVAL );
})();
Controleer direct uw domeinnaam en registreer eenvoudig online.
