first commit
This commit is contained in:
668
index.html
Normal file
668
index.html
Normal file
@@ -0,0 +1,668 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="tr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Netplan Yapılandırma Oluşturucu</title>
|
||||
<!-- Tailwind CSS -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<!-- Vue.js -->
|
||||
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
||||
<!-- js-yaml -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js"></script>
|
||||
<!-- Custom CSS -->
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'sans-serif'],
|
||||
},
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#f0fdf4',
|
||||
100: '#dcfce7',
|
||||
200: '#bbf7d0',
|
||||
300: '#86efac',
|
||||
400: '#4ade80',
|
||||
500: '#22c55e',
|
||||
600: '#16a34a',
|
||||
700: '#15803d',
|
||||
800: '#166534',
|
||||
900: '#14532d',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-50 dark:bg-gray-900 text-gray-800 dark:text-gray-200 transition-colors duration-300 font-sans">
|
||||
<div id="app" class="min-h-screen flex flex-col">
|
||||
<!-- Header -->
|
||||
<header class="bg-white dark:bg-gray-800 shadow-sm sticky top-0 z-50">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="bg-primary-600 text-white p-2 rounded-lg shadow-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
||||
</svg>
|
||||
</div>
|
||||
<h1
|
||||
class="text-2xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-primary-600 to-primary-400">
|
||||
Netplan Config Generator
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-4">
|
||||
<button @click="toggleTheme"
|
||||
class="p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
|
||||
<span v-if="isDark">🌞</span>
|
||||
<span v-else>🌙</span>
|
||||
</button>
|
||||
<button @click="toggleLanguage"
|
||||
class="flex items-center space-x-1 px-3 py-1.5 rounded-md text-sm font-medium bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">
|
||||
<span>{{ currentLang === 'tr' ? '🇹🇷 TR' : '🇬🇧 EN' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="flex-grow container mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8">
|
||||
|
||||
<!-- Configuration Form -->
|
||||
<div class="lg:col-span-7 space-y-6">
|
||||
|
||||
<!-- Global Settings -->
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
|
||||
<div
|
||||
class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 flex justify-between items-center">
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
|
||||
<span class="mr-2">🌐</span> {{ t('globalSettings') }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="p-6 grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{{
|
||||
t('renderer') }}</label>
|
||||
<select v-model="config.renderer"
|
||||
class="w-full rounded-lg border-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-primary-500 focus:ring-primary-500 shadow-sm transition-colors">
|
||||
<option value="networkd">networkd (Server Default)</option>
|
||||
<option value="NetworkManager">NetworkManager (Desktop Default)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{{
|
||||
t('version') }}</label>
|
||||
<input type="number" v-model.number="config.version"
|
||||
class="w-full rounded-lg border-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-primary-500 focus:ring-primary-500 shadow-sm"
|
||||
readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ethernets -->
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
|
||||
<div
|
||||
class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 flex justify-between items-center">
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
|
||||
<span class="mr-2">🔌</span> {{ t('ethernets') }}
|
||||
</h2>
|
||||
<button @click="addEthernet"
|
||||
class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 transition-colors">
|
||||
<svg class="-ml-0.5 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
{{ t('addInterface') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="config.ethernets.length === 0"
|
||||
class="p-8 text-center text-gray-500 dark:text-gray-400">
|
||||
{{ t('noInterfaces') }}
|
||||
</div>
|
||||
|
||||
<div v-else class="divide-y divide-gray-100 dark:divide-gray-700">
|
||||
<div v-for="(eth, index) in config.ethernets" :key="index"
|
||||
class="p-6 hover:bg-gray-50 dark:hover:bg-gray-700/30 transition-colors relative group">
|
||||
<button @click="removeEthernet(index)"
|
||||
class="absolute top-4 right-4 text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('interfaceName') }}</label>
|
||||
<input type="text" v-model="eth.name" placeholder="e.g. eth0"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
<div class="flex items-center pt-6">
|
||||
<input type="checkbox" v-model="eth.dhcp4" :id="'dhcp4-'+index"
|
||||
class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded">
|
||||
<label :for="'dhcp4-'+index"
|
||||
class="ml-2 block text-sm text-gray-900 dark:text-gray-300">DHCPv4</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!eth.dhcp4" class="space-y-4 animate-fade-in-down">
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('ipAddresses') }}</label>
|
||||
<div class="flex space-x-2">
|
||||
<input type="text" v-model="eth.addressesInput"
|
||||
placeholder="192.168.1.10/24, 10.0.0.5/24"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-gray-400">{{ t('commaSeparated') }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('gateway4') }}</label>
|
||||
<input type="text" v-model="eth.gateway4" placeholder="192.168.1.1"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('nameservers') }}</label>
|
||||
<input type="text" v-model="eth.nameserversInput" placeholder="8.8.8.8, 1.1.1.1"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 pt-4 border-t border-gray-100 dark:border-gray-700">
|
||||
<button @click="eth.showAdvanced = !eth.showAdvanced"
|
||||
class="text-xs text-primary-600 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300 font-medium flex items-center">
|
||||
<span class="mr-1">{{ eth.showAdvanced ? '−' : '+' }}</span> {{
|
||||
t('advancedSettings') }}
|
||||
</button>
|
||||
|
||||
<div v-if="eth.showAdvanced"
|
||||
class="mt-4 grid grid-cols-1 md:grid-cols-2 gap-4 animate-fade-in-down">
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">MTU</label>
|
||||
<input type="number" v-model.number="eth.mtu" placeholder="1500"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">MAC
|
||||
Address (Optional)</label>
|
||||
<input type="text" v-model="eth.macaddress" placeholder="XX:XX:XX:XX:XX:XX"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
<div class="col-span-1 md:col-span-2">
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('routes') }}</label>
|
||||
<div v-for="(route, rIndex) in eth.routes" :key="rIndex"
|
||||
class="flex space-x-2 mb-2">
|
||||
<input type="text" v-model="route.to" placeholder="To: 10.0.0.0/24"
|
||||
class="flex-1 rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
<input type="text" v-model="route.via" placeholder="Via: 192.168.1.1"
|
||||
class="flex-1 rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
<button @click="eth.routes.splice(rIndex, 1)"
|
||||
class="text-red-500 hover:text-red-700">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5"
|
||||
viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<button @click="eth.routes.push({to: '', via: ''})"
|
||||
class="text-xs text-primary-600 hover:text-primary-700 font-medium flex items-center mt-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 mr-1"
|
||||
viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Add Route
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Wi-Fi Settings -->
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
|
||||
<div
|
||||
class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 flex justify-between items-center">
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
|
||||
<span class="mr-2">📶</span> {{ t('wifis') }}
|
||||
</h2>
|
||||
<button @click="addWifi"
|
||||
class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none transition-colors">
|
||||
<svg class="-ml-0.5 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
{{ t('addWifi') }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="config.wifis.length === 0" class="p-8 text-center text-gray-500 dark:text-gray-400">
|
||||
{{ t('noWifis') }}
|
||||
</div>
|
||||
<div v-else class="divide-y divide-gray-100 dark:divide-gray-700">
|
||||
<div v-for="(wifi, index) in config.wifis" :key="index"
|
||||
class="p-6 hover:bg-gray-50 dark:hover:bg-gray-700/30 transition-colors relative group">
|
||||
<button @click="removeWifi(index)"
|
||||
class="absolute top-4 right-4 text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('interfaceName') }}</label>
|
||||
<input type="text" v-model="wifi.name" placeholder="wlan0"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">SSID</label>
|
||||
<input type="text" v-model="wifi.accessPoints[0].ssid"
|
||||
placeholder="Network Name"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">Password</label>
|
||||
<input type="password" v-model="wifi.accessPoints[0].password" placeholder="Key"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
<div class="flex items-center pt-6">
|
||||
<input type="checkbox" v-model="wifi.dhcp4" :id="'wifi-dhcp4-'+index"
|
||||
class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded">
|
||||
<label :for="'wifi-dhcp4-'+index"
|
||||
class="ml-2 block text-sm text-gray-900 dark:text-gray-300">DHCPv4</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bridges -->
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
|
||||
<div
|
||||
class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 flex justify-between items-center">
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
|
||||
<span class="mr-2">🌉</span> {{ t('bridges') }}
|
||||
</h2>
|
||||
<button @click="addBridge"
|
||||
class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none transition-colors">
|
||||
<svg class="-ml-0.5 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
{{ t('addBridge') }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="config.bridges.length === 0"
|
||||
class="p-8 text-center text-gray-500 dark:text-gray-400">
|
||||
No Bridges added yet.
|
||||
</div>
|
||||
<div v-else class="divide-y divide-gray-100 dark:divide-gray-700">
|
||||
<div v-for="(br, index) in config.bridges" :key="index"
|
||||
class="p-6 hover:bg-gray-50 dark:hover:bg-gray-700/30 transition-colors relative group">
|
||||
<button @click="removeBridge(index)"
|
||||
class="absolute top-4 right-4 text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('interfaceName') }}</label>
|
||||
<input type="text" v-model="br.name" placeholder="br0"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('interfaces') }}</label>
|
||||
<input type="text" v-model="br.interfacesInput" placeholder="eth0, eth1"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
<div class="flex items-center pt-6">
|
||||
<input type="checkbox" v-model="br.dhcp4" :id="'br-dhcp4-'+index"
|
||||
class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded">
|
||||
<label :for="'br-dhcp4-'+index"
|
||||
class="ml-2 block text-sm text-gray-900 dark:text-gray-300">DHCPv4</label>
|
||||
</div>
|
||||
<div v-if="!br.dhcp4">
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('ipAddresses') }}</label>
|
||||
<input type="text" v-model="br.addressesInput" placeholder="192.168.1.10/24"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bonds -->
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
|
||||
<div
|
||||
class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 flex justify-between items-center">
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
|
||||
<span class="mr-2">🔗</span> {{ t('bonds') }}
|
||||
</h2>
|
||||
<button @click="addBond"
|
||||
class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none transition-colors">
|
||||
<svg class="-ml-0.5 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
{{ t('addBond') }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="config.bonds.length === 0" class="p-8 text-center text-gray-500 dark:text-gray-400">
|
||||
No Bonds added yet.
|
||||
</div>
|
||||
<div v-else class="divide-y divide-gray-100 dark:divide-gray-700">
|
||||
<div v-for="(bond, index) in config.bonds" :key="index"
|
||||
class="p-6 hover:bg-gray-50 dark:hover:bg-gray-700/30 transition-colors relative group">
|
||||
<button @click="removeBond(index)"
|
||||
class="absolute top-4 right-4 text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('interfaceName') }}</label>
|
||||
<input type="text" v-model="bond.name" placeholder="bond0"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('interfaces') }}</label>
|
||||
<input type="text" v-model="bond.interfacesInput" placeholder="eth0, eth1"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('mode') }}</label>
|
||||
<select v-model="bond.parameters.mode"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
<option value="balance-rr">balance-rr</option>
|
||||
<option value="active-backup">active-backup</option>
|
||||
<option value="balance-xor">balance-xor</option>
|
||||
<option value="broadcast">broadcast</option>
|
||||
<option value="802.3ad">802.3ad</option>
|
||||
<option value="balance-tlb">balance-tlb</option>
|
||||
<option value="balance-alb">balance-alb</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center pt-6">
|
||||
<input type="checkbox" v-model="bond.dhcp4" :id="'bond-dhcp4-'+index"
|
||||
class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded">
|
||||
<label :for="'bond-dhcp4-'+index"
|
||||
class="ml-2 block text-sm text-gray-900 dark:text-gray-300">DHCPv4</label>
|
||||
</div>
|
||||
<div v-if="!bond.dhcp4">
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('ipAddresses') }}</label>
|
||||
<input type="text" v-model="bond.addressesInput" placeholder="192.168.1.10/24"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- VLANs -->
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
|
||||
<div
|
||||
class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 flex justify-between items-center">
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center">
|
||||
<span class="mr-2">🏷️</span> {{ t('vlans') }}
|
||||
</h2>
|
||||
<button @click="addVlan"
|
||||
class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none transition-colors">
|
||||
<svg class="-ml-0.5 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
{{ t('addVlan') }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="config.vlans.length === 0" class="p-8 text-center text-gray-500 dark:text-gray-400">
|
||||
No VLANs added yet.
|
||||
</div>
|
||||
<div v-else class="divide-y divide-gray-100 dark:divide-gray-700">
|
||||
<div v-for="(vlan, index) in config.vlans" :key="index"
|
||||
class="p-6 hover:bg-gray-50 dark:hover:bg-gray-700/30 transition-colors relative group">
|
||||
<button @click="removeVlan(index)"
|
||||
class="absolute top-4 right-4 text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('interfaceName') }}</label>
|
||||
<input type="text" v-model="vlan.name" placeholder="vlan10"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('id') }}</label>
|
||||
<input type="number" v-model.number="vlan.id" placeholder="10"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('link') }}</label>
|
||||
<input type="text" v-model="vlan.link" placeholder="eth0"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
<div class="flex items-center pt-6">
|
||||
<input type="checkbox" v-model="vlan.dhcp4" :id="'vlan-dhcp4-'+index"
|
||||
class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded">
|
||||
<label :for="'vlan-dhcp4-'+index"
|
||||
class="ml-2 block text-sm text-gray-900 dark:text-gray-300">DHCPv4</label>
|
||||
</div>
|
||||
<div v-if="!vlan.dhcp4">
|
||||
<label
|
||||
class="block text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1">{{
|
||||
t('ipAddresses') }}</label>
|
||||
<input type="text" v-model="vlan.addressesInput" placeholder="192.168.10.1/24"
|
||||
class="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Preview Sidebar -->
|
||||
<div class="lg:col-span-5 space-y-6">
|
||||
<div class="sticky top-24">
|
||||
<div class="bg-gray-900 rounded-xl shadow-2xl overflow-hidden border border-gray-700">
|
||||
<div
|
||||
class="bg-gray-800 px-4 py-3 border-b border-gray-700 flex justify-between items-center">
|
||||
<div class="flex space-x-2">
|
||||
<div class="w-3 h-3 rounded-full bg-red-500"></div>
|
||||
<div class="w-3 h-3 rounded-full bg-yellow-500"></div>
|
||||
<div class="w-3 h-3 rounded-full bg-green-500"></div>
|
||||
</div>
|
||||
<span class="text-xs text-gray-400 font-mono">01-netcfg.yaml</span>
|
||||
</div>
|
||||
<div class="relative group">
|
||||
<pre
|
||||
class="p-6 text-sm font-mono leading-relaxed text-gray-300 overflow-x-auto min-h-[400px] max-h-[calc(100vh-300px)] scrollbar-thin scrollbar-thumb-gray-600 scrollbar-track-transparent"><code>{{ generatedYaml }}</code></pre>
|
||||
|
||||
<div
|
||||
class="absolute top-4 right-4 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<button @click="copyToClipboard"
|
||||
class="bg-gray-700 hover:bg-gray-600 text-white p-2 rounded-lg shadow-lg transition-colors"
|
||||
title="Kopyala">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex space-x-4">
|
||||
<button @click="downloadYaml"
|
||||
class="flex-1 bg-primary-600 hover:bg-primary-700 text-white font-medium py-3 px-4 rounded-xl shadow-lg shadow-primary-500/30 flex justify-center items-center transition-all transform hover:-translate-y-0.5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
{{ t('downloadYaml') }}
|
||||
</button>
|
||||
<button @click="copyToClipboard"
|
||||
class="flex-1 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-200 font-medium py-3 px-4 rounded-xl shadow-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 flex justify-center items-center transition-all transform hover:-translate-y-0.5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
|
||||
</svg>
|
||||
{{ t('copyClipboard') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Instructions -->
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
|
||||
<div
|
||||
class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50">
|
||||
<h3 class="font-semibold text-gray-900 dark:text-white flex items-center">
|
||||
<span class="mr-2">📝</span> {{ t('instructions') }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="p-6 text-sm text-gray-600 dark:text-gray-300 space-y-4">
|
||||
<p>{{ t('step1') }}</p>
|
||||
|
||||
<div>
|
||||
<p class="mb-2">{{ t('step2') }}</p>
|
||||
<div
|
||||
class="bg-gray-100 dark:bg-gray-900 p-2 rounded border border-gray-200 dark:border-gray-700 font-mono text-xs select-all break-all">
|
||||
/etc/netplan/01-netcfg.yaml
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="mb-2">{{ t('step3') }}</p>
|
||||
<div
|
||||
class="bg-gray-100 dark:bg-gray-900 p-2 rounded border border-gray-200 dark:border-gray-700 font-mono text-xs select-all">
|
||||
sudo chmod 600 /etc/netplan/01-netcfg.yaml
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="mb-2">{{ t('step4') }}</p>
|
||||
<div
|
||||
class="bg-gray-100 dark:bg-gray-900 p-2 rounded border border-gray-200 dark:border-gray-700 font-mono text-xs select-all">
|
||||
sudo netplan try
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="mb-2">{{ t('step5') }}</p>
|
||||
<div
|
||||
class="bg-gray-100 dark:bg-gray-900 p-2 rounded border border-gray-200 dark:border-gray-700 font-mono text-xs select-all">
|
||||
sudo netplan apply
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-yellow-50 dark:bg-yellow-900/30 border-l-4 border-yellow-400 p-3">
|
||||
<p class="text-xs text-yellow-700 dark:text-yellow-200">
|
||||
{{ t('note') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="mt-auto py-8 text-center text-sm text-gray-500 dark:text-gray-400">
|
||||
<p>Netplan Config Generator © 2024</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user