From 69af1e97c6535a2191d2429ff16989642ad46021 Mon Sep 17 00:00:00 2001 From: Manish Gupta <59428681+mguptahub@users.noreply.github.com> Date: Mon, 28 Oct 2024 22:46:39 +0530 Subject: [PATCH] Add ENV File support (#10) --- README.md | 7 +- docs/assets/css/mobile.css | 226 ++++++++++++++++ docs/assets/css/style.css | 469 ++++++++++++++++++++++++++++++++++ docs/assets/css/zeromd.css | 23 ++ docs/assets/js/main.js | 218 ++++++++++++++++ docs/assets/md/docker.md | 3 + docs/assets/md/env-sample.md | 53 ++++ docs/assets/styles/mobile.css | 22 -- docs/assets/styles/style.css | 305 ---------------------- docs/index.html | 256 +++++++++++-------- go.mod | 1 + go.sum | 2 + pkg/config/config.go | 11 + 13 files changed, 1157 insertions(+), 439 deletions(-) create mode 100644 docs/assets/css/mobile.css create mode 100644 docs/assets/css/style.css create mode 100644 docs/assets/css/zeromd.css create mode 100644 docs/assets/js/main.js create mode 100644 docs/assets/md/env-sample.md delete mode 100644 docs/assets/styles/mobile.css delete mode 100644 docs/assets/styles/style.css diff --git a/README.md b/README.md index ba31786..7f1bb82 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ ## Features -- Environment variable-based configuration +- Environment variable-based configuration (Support .env file) - Support for A, CNAME, MX, and TXT records - Docker service name resolution - Optional TTL configuration (default: 60 seconds) @@ -166,10 +166,11 @@ docker run -d \ --name nanodns \ -p 10053:10053/udp \ -e DNS_PORT=10053 \ - -e DNS_RELAY_SERVERS=8.8.8.8:53,1.1.1.1:53 \ # Optional relay configuration + -e DNS_RELAY_SERVERS=8.8.8.8:53,1.1.1.1:53 \ -e "A_REC1=app.example.com|10.10.0.1|300" \ -e "A_REC2=api.example.com|service:webapp" \ -e "TXT_REC1=example.com|v=spf1 include:_spf.example.com ~all" \ + -v ${PWD}/.env:/app/.env \ ghcr.io/mguptahub/nanodns:latest ``` @@ -191,6 +192,8 @@ services: - TXT_REC1=example.com|v=spf1 include:_spf.example.com ~all ports: - "${DNS_PORT:-10053}:${DNS_PORT:-10053}/udp" + volumes: + - ./.env:/app/.env networks: - app_network diff --git a/docs/assets/css/mobile.css b/docs/assets/css/mobile.css new file mode 100644 index 0000000..22b7002 --- /dev/null +++ b/docs/assets/css/mobile.css @@ -0,0 +1,226 @@ +@media (max-width: 768px) { + .markdown-body h1{ + font-size: x-large !important; + } + .navbar { + padding: 0.5rem; + } + + .nav-content { + padding: 0.5rem; + } + + .nav-logo img { + height: 32px; /* Slightly smaller logo */ + } + + .nav-links { + gap: 0.75rem; + } + + /* Hide text, show only icons for nav items */ + .nav-link span { + display: none; + } + + .nav-link { + font-size: 1.2rem; + padding: 0.5rem; + } + + .hero { + padding: 5rem 1rem 3rem; + } + + .hero-description { + font-size: 0.95rem; + line-height: 1.6; + padding: 0 1rem; + } + + .main-logo { + width: 140px; /* Slightly smaller logo */ + margin-bottom: 1.5rem; + } + + .hero-features { + display: flex; + flex-direction: row; /* Keep horizontal on tablets */ + justify-content: center; + flex-wrap: wrap; + gap: 0.75rem; + margin: 1.5rem 0; + } + + .hero-features span { + font-size: 0.85rem; + padding: 0.5rem 1rem; + background: rgba(74, 108, 247, 0.08); + border: 1px solid rgba(74, 108, 247, 0.15); + border-radius: 1.5rem; + white-space: nowrap; + display: inline-flex; + align-items: center; + gap: 0.5rem; + } + + .hero-features i { + font-size: 0.85rem; + } + + + .buttons { + flex-direction: column; + align-items: center; + } + + .btn { + font-size: 0.9rem; + padding: 0.7rem 1.2rem; + } + + .badges { + padding: 0.75rem; + gap: 0.4rem; + } + + .badges img { + height: 20px; /* Adjust badge size */ + } + + .feature-card { + padding: 1.5rem; + } + + .feature-card h3 { + font-size: 1.1rem; + margin-bottom: 0.75rem; + } + + .feature-card p { + font-size: 0.9rem; + } + + .tab-buttons { + padding: 0.5rem; + } + + .footer-content { + grid-template-columns: 1fr; + text-align: center; + } + + .modal-content { + padding: 1.25rem; + font-size: 0.8rem; + line-height: 1.5; + max-width: 100%; + } + +} + +@media (max-width: 480px) { + + .navbar { + padding: 0.25rem; + } + + .nav-content { + padding: 0.25rem; + } + + .nav-links { + gap: 0.5rem; + } + + .nav-logo img { + height: 28px; + } + + .hero { + padding: 4rem 0.75rem 2rem; + } + + .hero-description { + font-size: 0.9rem; + padding: 0 0.75rem; + } + + .main-logo { + width: 120px; + margin-bottom: 1rem; + } + + .hero-content { + padding: 0 1rem; + } + + .hero-features { + padding: 0 0.5rem; + gap: 0.5rem; + } + + .hero-features span { + font-size: 0.8rem; + padding: 0.4rem 0.8rem; + } + + .hero-features i { + font-size: 0.8rem; + } + + .badges { + flex-direction: row; + align-items: center; + gap: 0.5rem; + width: 100%; + } + + .tab-buttons { + overflow-x: scroll; + } + + .btn { + font-size: 0.85rem; + padding: 0.6rem 1rem; + } + + h2 { + font-size: 1.5rem; + margin-bottom: 1.5rem; + } + + .feature-card { + padding: 1.25rem; + } + + .feature-card h3 { + font-size: 1rem; + } + + .feature-card p { + font-size: 0.85rem; + } +} + +@media (max-width: 360px) { + .nav-links { + gap: 0.25rem; + } + + .nav-link { + padding: 0.4rem; + font-size: 1.1rem; + } + .hero-description { + font-size: 1rem; + } + + .feature-card h3 { + font-size: 1.2rem; + } + + .feature-card p { + font-size: 0.95rem; + } +} \ No newline at end of file diff --git a/docs/assets/css/style.css b/docs/assets/css/style.css new file mode 100644 index 0000000..961b143 --- /dev/null +++ b/docs/assets/css/style.css @@ -0,0 +1,469 @@ +:root { + --primary-color: #4a6cf7; + --background: #ffffff; + --text-primary: #111827; + --text-secondary: #4b5563; + --card-bg: #ffffff; + --nav-bg: rgba(255, 255, 255, 0.9); + --code-header-bg: #334155; + --footer-bg: #f8fafc; + --border-color: rgba(255, 255, 255, 0.1); + + --header-bg-dark: #0f1729; + --header-bg-light: #f8fafc; + --tab-inactive-light: #64748b; + --tab-inactive-dark: #94a3b8; + + --hero-bg-light: linear-gradient(135deg, #f0f4ff 0%, #f8faff 100%); + --hero-pattern-light: radial-gradient(circle at 1px 1px, #4a6cf710 1px, transparent 0); + --hero-bg-dark: linear-gradient(135deg, #0f172a 0%, #1e293b 100%); + --hero-pattern-dark: radial-gradient(circle at 1px 1px, #ffffff08 1px, transparent 0); + + --pill-bg-light: rgba(74, 108, 247, 0.1); + --pill-border-light: rgba(74, 108, 247, 0.2); + --badge-bg-light: rgba(0, 0, 0, 0.04); + --badge-border-light: rgba(0, 0, 0, 0.1); + --btn-border-light: rgba(0, 0, 0, 0.1); +} + +.dark-theme { + --background: #0f172a; + --text-primary: #f3f4f6; + --text-secondary: #d1d5db; + --card-bg: #1e293b; + --nav-bg: rgba(15, 23, 42, 0.9); + --footer-bg: #1e293b; + --border-color: rgba(255, 255, 255, 0.1); +} +.dark-theme .navbar { + background: var(--header-bg-dark); +} + + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; + line-height: 1.6; + background: var(--background); + color: var(--text-primary); + transition: background-color 0.3s, color 0.3s; +} + +/* Navbar Styles */ +.navbar { + position: fixed; + top: 0; + left: 0; + right: 0; + background: var(--header-bg-light); + backdrop-filter: blur(10px); + z-index: 1000; + box-shadow: 0 1px 3px rgba(0,0,0,0.1); + border-bottom: 1px solid var(--border-color); +} + +.nav-content { + max-width: 1200px; + margin: 0 auto; + padding: 1rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +.nav-logo img { + height: 40px; +} + +.nav-links { + display: flex; + gap: 2rem; + align-items: center; +} + +.nav-link { + color: var(--text-primary); + text-decoration: none; + display: flex; + align-items: center; + gap: 0.5rem; + transition: color 0.3s; + font-weight: 500; +} + +.nav-link:hover { + color: var(--primary-color); +} + +.theme-toggle { + background: none; + border: none; + color: var(--text-primary); + cursor: pointer; + padding: 0.5rem; + font-size: 1.2rem; + transition: color 0.3s; +} + +.theme-toggle:hover { + color: var(--primary-color); +} + +/* Hero Section */ +.hero { + position: relative; + padding: 8rem 1rem 4rem; + text-align: center; + background: var(--hero-bg-light); + overflow: hidden; +} + +.hero::before { + content: ''; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-image: var(--hero-pattern-light); + background-size: 24px 24px; + opacity: 0.8; + z-index: 0; +} + +.dark-theme .hero { + background: var(--hero-bg-dark); +} + +.dark-theme .hero::before { + background-image: var(--hero-pattern-dark); +} + +.main-logo { + position: relative; + z-index: 1; + width: 180px; + height: auto; + margin-bottom: 2rem; + animation: float 6s ease-in-out infinite; +} + +.hero-content { + position: relative; + z-index: 1; + max-width: 800px; + margin: 0 auto; +} + +.hero-features { + display: flex; + justify-content: center; + gap: 2rem; + margin: 1.5rem 0; + position: relative; + z-index: 1; +} + +.hero-features span { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.6rem 1.2rem; + background: var(--pill-bg-light); + border: 1px solid var(--pill-border-light); + border-radius: 2rem; + font-size: 0.9rem; + color: var(--text-primary); + transition: all 0.3s ease; +} + +.hero-features span:hover { + transform: translateY(-1px); + box-shadow: 0 4px 6px rgba(74, 108, 247, 0.1); +} + +.dark-theme .hero-features span { + background: rgba(255, 255, 255, 0.05); + border-color: rgba(255, 255, 255, 0.1); + color: var(--text-secondary); +} + +.hero-description { + font-size: 1.1rem; + color: var(--text-secondary); + margin: 2rem auto; + max-width: 700px; + line-height: 1.8; + position: relative; + z-index: 1; +} + +.badges { + display: flex; + gap: 0.5rem; + justify-content: center; + flex-wrap: wrap; + margin-top: 2rem; + padding: 1rem; + background: var(--badge-bg-light); + border: 1px solid var(--badge-border-light); + border-radius: 0.5rem; + display: inline-flex; +} + +.badges a { + transition: transform 0.3s ease; +} + +.badges a:hover { + transform: translateY(-1px); +} +.dark-theme .badges { + background: rgba(255, 255, 255, 0.05); + border-color: rgba(255, 255, 255, 0.1); +} + +/* Buttons */ +.buttons { + display: flex; + gap: 1rem; + justify-content: center; + margin: 2rem 0; + position: relative; + z-index: 1; +} + +.btn { + padding: 0.8rem 1.5rem; + border-radius: 0.5rem; + text-decoration: none; + font-weight: 500; + display: inline-flex; + align-items: center; + gap: 0.5rem; + transition: all 0.3s ease; + border: 1px solid transparent; +} + +.btn-primary { + background: var(--primary-color); + color: white; + box-shadow: 0 4px 6px rgba(74, 108, 247, 0.1); +} + +.btn-secondary { + background: var(--card-bg); + color: var(--text-primary); + border: 1px solid var(--btn-border-light); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); +} + +.btn-secondary:hover { + border-color: var(--primary-color); + color: var(--primary-color); +} + +.dark-theme .btn-secondary { + background: rgba(255, 255, 255, 0.05); + border-color: rgba(255, 255, 255, 0.1); + box-shadow: none; +} + +.btn:hover { + transform: translateY(-2px); + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); +} + +/* Container */ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 2rem 1rem; +} + +Section Headers +h2 { + font-size: 2rem; + margin-bottom: 2rem; + text-align: center; + color: var(--text-primary); +} + +/* Features Section */ +.features { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; + margin: 2rem 0; +} + +.feature-card { + background: var(--card-bg); + padding: 2rem; + border-radius: 1rem; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); + transition: transform 0.3s; + border: 1px solid var(--border-color); + text-decoration: none; + color: inherit; +} + +.feature-card:hover { + transform: translateY(-5px); +} + +.feature-icon { + font-size: 2rem; + color: var(--primary-color); + margin-bottom: 1rem; +} + +.feature-card h3 { + margin-bottom: 1rem; + color: var(--text-primary); +} + +.feature-card p { + color: var(--text-secondary); +} + +/* Quick Start Section */ +.code-tabs { + background: var(--card-bg); + border-radius: 0.5rem; + overflow: hidden; + margin: 2rem 0; + border: 1px solid var(--border-color); +} + +.tab-buttons { + display: flex; + gap: 1rem; + padding: 1rem; + background: var(--code-header-bg); + overflow-x: auto; +} + +.tab-btn { + background: none; + border: none; + color: var(--tab-inactive-light); + padding: 0.5rem 1rem; + cursor: pointer; + display: flex; + align-items: center; + gap: 0.5rem; + transition: all 0.3s ease; + font-weight: 500; + border-radius: 0.3rem; +} +.dark-theme .tab-btn { + color: var(--tab-inactive-dark); +} +.tab-btn:hover { + background: rgba(255, 255, 255, 0.1); + color: white; +} + +.tab-btn.active { + color: white; + background: rgba(255, 255, 255, 0.1); + border-radius: 0.3rem; +} + +.tab-btn.active { + color: white; + background: rgba(255, 255, 255, 0.15); + border-radius: 0.3rem; +} +.tab-btn:not(.active):hover { + background: rgba(255, 255, 255, 0.05); + color: rgba(255, 255, 255, 0.9); +} + +.tab-content.active { + display: block; +} + +/* Modal Styles */ +.modal-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.8); + z-index: 1000; + padding: 2rem; + align-items: center; + justify-content: center; +} + +.modal-content { + background: var(--card-bg); + color: var(--text-primary); + padding: 2rem; + border-radius: 0.4rem; + max-width: 80%; + width: 100%; + max-height: 80vh; + overflow-y: auto; + position: relative; +} + +.modal-close { + position: absolute; + top: 1rem; + right: 1rem; + font-size: 1.5rem; + cursor: pointer; + color: var(--text-secondary); + transition: color 0.3s; + z-index: 1; +} + +.modal-close:hover { + color: var(--primary-color); +} + +/* Footer */ +.footer { + background: var(--footer-bg); + padding: 4rem 1rem; + margin-top: 4rem; + border-top: 1px solid var(--border-color); +} + +.footer-content { + max-width: 1200px; + margin: 0 auto; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 2rem; +} + +.footer h3 { + color: var(--text-primary); + margin-bottom: 1rem; +} + +.footer p { + margin-bottom: 0.5rem; +} + +.footer a { + color: var(--text-secondary); + text-decoration: none; + transition: color 0.3s; +} + +.footer a:hover { + color: var(--primary-color); +} + diff --git a/docs/assets/css/zeromd.css b/docs/assets/css/zeromd.css new file mode 100644 index 0000000..a0877f3 --- /dev/null +++ b/docs/assets/css/zeromd.css @@ -0,0 +1,23 @@ +@media (max-width: 768px) { + + h1 { + font-size: 1.5em !important; + } + h2 { + font-size: 1.25em !important; + } + h3 { + font-size: 1.10em !important; + } +} + +.markdown-body { + padding: 0.5rem 1rem !important; +} + +.markdown-body pre[class*=language-] { + /* border: 1px solid green !important; */ + padding: 10px !important; + border-radius: 5px !important; + backdrop-filter: brightness(0.9); +} diff --git a/docs/assets/js/main.js b/docs/assets/js/main.js new file mode 100644 index 0000000..ca01ec1 --- /dev/null +++ b/docs/assets/js/main.js @@ -0,0 +1,218 @@ +// Theme Toggle Implementation +function initThemeToggle() { + const themeToggle = document.getElementById('themeToggle'); + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)'); + + // Set initial theme based on user preference + function setInitialTheme() { + if (localStorage.getItem('theme') === 'dark' || + (!localStorage.getItem('theme') && prefersDark.matches)) { + document.body.classList.add('dark-theme'); + themeToggle.querySelector('i').classList.replace('fa-moon', 'fa-sun'); + } + } + + // Toggle theme + function toggleTheme() { + document.body.classList.toggle('dark-theme'); + const icon = themeToggle.querySelector('i'); + + if (document.body.classList.contains('dark-theme')) { + icon.classList.replace('fa-moon', 'fa-sun'); + localStorage.setItem('theme', 'dark'); + } else { + icon.classList.replace('fa-sun', 'fa-moon'); + localStorage.setItem('theme', 'light'); + } + } + + setInitialTheme(); + themeToggle.addEventListener('click', toggleTheme); + prefersDark.addEventListener('change', setInitialTheme); +} + +// Tab Functionality Implementation +function initTabFunctionality() { + const tabButtons = document.querySelectorAll('.tab-btn'); + + function switchTab(tabId) { + // Update button states + tabButtons.forEach(btn => { + btn.classList.toggle('active', btn.getAttribute('data-tab') === tabId); + }); + + // Update content visibility + document.querySelectorAll('.tab-content').forEach(content => { + content.style.display = content.id === tabId ? 'block' : 'none'; + }); + } + + tabButtons.forEach(button => { + button.addEventListener('click', () => { + switchTab(button.getAttribute('data-tab')); + }); + }); +} + +// Modal Functionality Implementation +function initModalFunctionality() { + const modalOverlay = document.getElementById('modalOverlay'); + const modalMarkdown = document.getElementById('modalMarkdown'); + let currentScroll = 0; + + window.openModal = function(mdFile) { + currentScroll = window.scrollY; + modalMarkdown.setAttribute('src', mdFile); + modalOverlay.style.display = 'flex'; + document.body.style.position = 'fixed'; + document.body.style.top = `-${currentScroll}px`; + document.body.style.width = '100%'; + + }; + + window.closeModal = function() { + modalOverlay.style.display = 'none'; + modalMarkdown.removeAttribute('src'); + document.body.style.position = ''; + document.body.style.top = ''; + document.body.style.width = ''; + window.scrollTo(0, currentScroll); + }; + + // Close modal on escape key + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && modalOverlay.style.display === 'flex') { + closeModal(); + } + }); + + // Close modal when clicking outside + modalOverlay.addEventListener('click', (e) => { + if (e.target === modalOverlay) { + closeModal(); + } + }); +} + +// Smooth Scroll Implementation +function initSmoothScroll() { + document.querySelectorAll('a[href^="#"]').forEach(anchor => { + anchor.addEventListener('click', function(e) { + e.preventDefault(); + const target = document.querySelector(this.getAttribute('href')); + + if (target) { + const navHeight = document.querySelector('.navbar').offsetHeight; + const targetPosition = target.getBoundingClientRect().top + window.pageYOffset; + + window.scrollTo({ + top: targetPosition - navHeight, + behavior: 'smooth' + }); + } + }); + }); +} + +// Scroll Spy Implementation +function initScrollSpy() { + const navLinks = document.querySelectorAll('.nav-link[href^="#"]'); + const sections = document.querySelectorAll('section[id]'); + + function updateActiveLink() { + const navHeight = document.querySelector('.navbar').offsetHeight; + const fromTop = window.scrollY + navHeight; + + sections.forEach(section => { + const sectionTop = section.offsetTop - navHeight; + const sectionBottom = sectionTop + section.offsetHeight; + const id = section.getAttribute('id'); + const correspondingLink = document.querySelector(`.nav-link[href="#${id}"]`); + + if (fromTop >= sectionTop && fromTop < sectionBottom && correspondingLink) { + navLinks.forEach(link => link.classList.remove('active')); + correspondingLink.classList.add('active'); + } + }); + + // Special case for page bottom + const pageBottom = window.scrollY + window.innerHeight === document.documentElement.scrollHeight; + if (pageBottom) { + navLinks.forEach(link => link.classList.remove('active')); + navLinks[navLinks.length - 1].classList.add('active'); + } + } + + // Throttle scroll event + let ticking = false; + window.addEventListener('scroll', () => { + if (!ticking) { + window.requestAnimationFrame(() => { + updateActiveLink(); + ticking = false; + }); + ticking = true; + } + }); + + // Update active link on page load + updateActiveLink(); +} + +// Handle copy functionality for code blocks +function initCodeCopy() { + document.querySelectorAll('pre code').forEach((block) => { + const copyButton = document.createElement('button'); + copyButton.className = 'copy-button'; + copyButton.innerHTML = ''; + + copyButton.addEventListener('click', () => { + navigator.clipboard.writeText(block.textContent).then(() => { + copyButton.innerHTML = ''; + setTimeout(() => { + copyButton.innerHTML = ''; + }, 2000); + }); + }); + + const wrapper = document.createElement('div'); + wrapper.className = 'code-wrapper'; + block.parentNode.insertBefore(wrapper, block); + wrapper.appendChild(block); + wrapper.appendChild(copyButton); + }); +} + +// window.ZeroMD = { +// config: { +// cssUrls: [ +// 'https://cdn.jsdelivr.net/gh/sindresorhus/github-markdown-css/github-markdown.css' +// ], +// markedOptions: { +// breaks: true, +// gfm: true +// } +// } +// }; + +// Initialize code copy functionality when zero-md components are loaded + + +document.addEventListener('zero-md-rendered', initCodeCopy); + +// Wait for DOM to be fully loaded +document.addEventListener('DOMContentLoaded', function() { + // Initialize AOS (Animate On Scroll) + AOS.init({ + duration: 800, + once: true, + offset: 50 + }); + + initThemeToggle(); + initTabFunctionality(); + initModalFunctionality(); + initSmoothScroll(); + initScrollSpy(); +}); + diff --git a/docs/assets/md/docker.md b/docs/assets/md/docker.md index 727385c..c1edf09 100644 --- a/docs/assets/md/docker.md +++ b/docs/assets/md/docker.md @@ -9,6 +9,7 @@ docker run -d \ -e DNS_RELAY_SERVERS=8.8.8.8:53,1.1.1.1:53 \ -e "A_REC1=app.example.com|10.10.0.1|300" \ -e "TXT_REC1=example.com|v=spf1 include:_spf.example.com ~all" \ + -v ${PWD}/.env:/app/.env \ ghcr.io/mguptahub/nanodns:latest ``` @@ -29,6 +30,8 @@ services: - TXT_REC1=example.com|v=spf1 include:_spf.example.com ~all ports: - "${DNS_PORT:-10053}:${DNS_PORT:-10053}/udp" # Uses DNS_PORT if set, otherwise 10053 + volumes: + - ./.env:/app/.env networks: - app_network diff --git a/docs/assets/md/env-sample.md b/docs/assets/md/env-sample.md new file mode 100644 index 0000000..4c6cc86 --- /dev/null +++ b/docs/assets/md/env-sample.md @@ -0,0 +1,53 @@ + +### Sample .env file + + +```bash +# DNS Server Configuration +DNS_PORT=10053 + +# Relay Configuration +DNS_RELAY_SERVERS=8.8.8.8,1.1.1.1 + +# TTL Configuration (in seconds) +DNS_DEFAULT_TTL=60 + +# A Records +# Format: domain|ip|ttl or domain|service:servicename|ttl +A_REC1=app1.example.com|10.10.0.1|300 +A_REC2=app2.example.com|10.10.0.2|300 +# A_REC3=static.example.com|10.0.0.50 +# A_REC4=*.example.com|192.168.1.100|300 + +# CNAME Records +# Format: domain|target|ttl +CNAME_REC1=www.example.com|app.example.com|3600 +CNAME_REC2=docs.example.com|documentation.service.local +# CNAME_REC3=blog.example.com|app.example.com|600 + +# MX Records +# Format: domain|priority|mailserver|ttl +MX_REC1=example.com|10|mail1.example.com|3600 +MX_REC2=example.com|20|mail2.example.com|3600 +# MX_REC3=example.com|30|mail-backup.example.com|3600 + +# TXT Records +# Format: domain|"text value"|ttl +TXT_REC1=example.com|v=spf1 include:_spf.example.com ~all|3600 +TXT_REC2=_dmarc.example.com|v=DMARC1; p=reject; rua=mailto:dmarc@example.com +# TXT_REC3=_acme-challenge.example.com|validation-token-here|60 +# TXT_REC4=mail._domainkey.example.com|v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4|3600 + +# Service Discovery Examples +# A_REC5=db.local|service:postgres.default.svc.cluster.local|60 +# A_REC6=redis.local|service:redis.default.svc.cluster.local|60 +# A_REC7=kafka.local|service:kafka.kafka.svc.cluster.local|60 + +# Load Balancing Examples +# A_REC8=api.local|192.168.1.10|60 +# A_REC9=api.local|192.168.1.11|60 +# A_REC10=api.local|192.168.1.12|60 + +# Note: All TTL values are optional and will default to DNS_DEFAULT_TTL if not specified +# Note: Service references will be automatically resolved in Docker/Kubernetes environments +``` \ No newline at end of file diff --git a/docs/assets/styles/mobile.css b/docs/assets/styles/mobile.css deleted file mode 100644 index c570319..0000000 --- a/docs/assets/styles/mobile.css +++ /dev/null @@ -1,22 +0,0 @@ -@media (max-width: 768px) { - .hero { - margin-top: 1.5rem; - } - .hero .main-logo{ - height: 12rem; - } - .nav-content{ - justify-content: center; - } - .nav-links { - display: none; - } - - .hero h1 { - font-size: 2rem; - } - - .buttons { - flex-direction: column; - } -} \ No newline at end of file diff --git a/docs/assets/styles/style.css b/docs/assets/styles/style.css deleted file mode 100644 index 4761b04..0000000 --- a/docs/assets/styles/style.css +++ /dev/null @@ -1,305 +0,0 @@ -html { - scroll-padding-top: 75px; - /* Adjusts the scroll position by the height of your navbar */ - scroll-behavior: smooth; -} - -h2 { - scroll-margin-top: 75px; - /* Adds a margin at the top when an element is scrolled into view */ -} - -:root { - --primary: #0b75b5; - --secondary: #054a74; - --text: #1f2937; - --light: #f3f4f6; - --code-bg: #1f2937; - --code-text: #e5e7eb; -} - -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; - line-height: 1.6; - color: var(--text); - padding-top: 60px; -} - -.navbar { - position: fixed; - top: 0; - width: 100%; - background: white; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - z-index: 1000; - padding: 1rem 0; -} - -.nav-content { - max-width: 1200px; - margin: 0 auto; - padding: 0 2rem; - display: flex; - justify-content: space-between; - align-items: center; -} - -.nav-logo { - font-size: 1.5rem; - font-weight: bold; - color: var(--primary); - text-decoration: none; -} - -.nav-links a { - margin-left: 2rem; - color: var(--text); - text-decoration: none; - font-weight: 500; -} - -.nav-links a:hover { - color: var(--primary); -} - -.hero { - background: linear-gradient(0deg, #625df580 0%, #1e40af 100%); - /* background: linear-gradient(135deg, #2563eb 0%, #1e40af 100%); */ - color: white; - padding: 2% 2rem; - text-align: center; -} - -.hero h1 { - font-size: 3rem; - margin-bottom: 1rem; -} - -.hero p { - font-size: 1.2rem; - max-width: 600px; - margin: 0 auto 2rem; -} -.hero .main-logo{ - height: 25rem; -} - -.container { - max-width: 1200px; - margin: 0 auto; - padding: 2rem; -} - -.features { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: 2rem; - margin: 4rem 0; -} - -.feature-card { - background: white; - border-radius: 8px; - padding: 2rem; - box-shadow: 0 0px 11px rgba(0, 0, 0, 0.2); - transition: transform 0.3s ease; - text-decoration: none; - color: inherit; -} - -.feature-card:hover { - transform: translateY(-5px); -} - -.feature-icon { - font-size: 2rem; - color: var(--primary); - margin-bottom: 1rem; -} - -.buttons { - display: flex; - gap: 1rem; - justify-content: center; -} - -.btn { - display: inline-block; - padding: 0.8rem 1.5rem; - border-radius: 6px; - text-decoration: none; - font-weight: 500; - transition: all 0.3s ease; -} - -.btn-primary { - background: var(--primary); - color: white; -} - -.btn-primary:hover { - background: var(--secondary); -} - -.btn-secondary { - background: white; - color: var(--primary); - border: 2px solid var(--primary); -} - -.btn-secondary:hover { - background: var(--light); -} - -pre { - background: var(--code-bg); - color: var(--code-text); - padding: 1.5rem; - border-radius: 8px; - overflow-x: auto; - /* margin: 1.5rem 0; */ -} - -.code-tabs { - border: 1px solid #e5e7eb; - border-radius: 8px; - overflow: hidden; - margin: 0; - margin-top: 2rem; -} - -.tab-buttons { - display: flex; - background: #f9fafb; - border-bottom: 1px solid #e5e7eb; -} - -.tab-btn { - padding: 0.8rem 1.5rem; - border: none; - background: none; - cursor: pointer; - font-weight: 500; -} - -.tab-btn.active { - background: white; - border-bottom: 2px solid var(--primary); -} - -.tab-content { - padding: 1rem; - /* padding: 1.5rem; */ -} - -.badges { - display: flex; - gap: 0.5rem; - flex-wrap: wrap; - margin: 1rem 0; - justify-content: center; - margin-top: 2rem; -} - -.badge { - display: inline-block; - padding: 0.3rem 0.8rem; - border-radius: 9999px; - font-size: 0.875rem; - font-weight: 500; - background: var(--light); - color: var(--text); - text-decoration: none; -} - -.sections { - display: grid; - /* gap: 4rem; */ - margin: 4rem 0; -} - -h2 { - font-size: 2rem; - /* margin-bottom: 1.5rem; */ - color: var(--text); -} - -h3 { - font-size: 1.5rem; - margin: 2rem 0 1rem; - color: var(--text); -} - -p { - margin-bottom: 1rem; -} - -.footer { - background: var(--code-bg); - color: white; - padding: 4rem 2rem; - margin-top: 4rem; -} - -.footer-content { - max-width: 1200px; - margin: 0 auto; - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 2rem; -} - -.footer h3 { - color: white; - margin-top: 0; -} - -.footer a { - color: var(--light); - text-decoration: none; -} - -.footer a:hover { - text-decoration: underline; -} - -.modal-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.4); - display: none; - justify-content: center; - align-items: center; - z-index: 1001; -} - -.modal-content { - position: relative; - width: 90%; - height: 90%; - background: white; - padding: 2rem; - border-radius: 8px; - overflow-y: auto; - border: 5px solid #f2f2f2; - box-shadow: 0px 0px 11px #212121; -} - -.modal-close { - position: fixed; - top: 5%; - right: 6%; - font-size: 2rem; - cursor: pointer; - color: var(--text); - z-index: 1002; -} diff --git a/docs/index.html b/docs/index.html index 4ef4e06..117dad3 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,120 +1,170 @@ - NanoDNS - Lightweight DNS Server + + - - + + + + + + + - - -
- - -

A lightweight DNS server designed for container environments, allowing dynamic resolution of service - names and custom DNS records.

-
- - View on - GitHub - Quick Start Guide -
-
- - Build - - - Release - - - License - - - Go Version - + +
+
+ Lightweight + Containerized + Fast Setup +
+

+ An ultra-lightweight DNS server that runs anywhere - from Docker containers to Kubernetes pods to Linux services. Perfect for internal networks and ISVs distributing self-hosted applications, it provides custom domain resolution and service discovery without databases or external dependencies. +

+ +
-

Features

-
-
-
-

Easy Configuration

-

Configure DNS records using simple environment variables. Supports A, CNAME, MX, and TXT records with optional relay DNS.

-
-
-
-

Relay DNS

-

Forward unmatched queries to upstream DNS servers. Support for multiple fallback servers with automatic failover.

-
-
-
-

Docker Integration

-

Seamless integration with Docker and Kubernetes environments. Automatic service discovery and resolution.

+
+

Features

+
+
+
+

Easy Configuration

+

Configure DNS records using simple environment variables. Supports A, CNAME, MX, and TXT records with optional relay DNS.

+
+
+
+

Relay DNS

+

Forward unmatched queries to upstream DNS servers. Support for multiple fallback servers with automatic failover.

+
+
+
+

Docker Integration

+

Seamless integration with Docker and Kubernetes environments. Automatic service discovery and resolution.

+
-
+

Quick Start

- - -
-
- + + +
- -
+
+
+ + + + +
+
+ + + + +
+
+ - -
-
+
-
+

Documentation

+ + - + + + + + + + - \ No newline at end of file diff --git a/go.mod b/go.mod index be7c948..4abccf6 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/mguptahub/nanodns go 1.22.4 require ( + github.com/joho/godotenv v1.5.1 // indirect github.com/miekg/dns v1.1.62 // indirect golang.org/x/mod v0.18.0 // indirect golang.org/x/net v0.27.0 // indirect diff --git a/go.sum b/go.sum index 72d5473..dc32bbc 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= diff --git a/pkg/config/config.go b/pkg/config/config.go index 47990aa..b64c5df 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -6,6 +6,8 @@ import ( "os" "strings" "time" + + "github.com/joho/godotenv" ) const ( @@ -21,6 +23,15 @@ type RelayConfig struct { Timeout time.Duration } +func init() { + if err := godotenv.Load(); err != nil { + // Only log if file exists but couldn't be loaded + if !os.IsNotExist(err) { + log.Printf("Error loading .env file: %v", err) + } + } +} + func GetDNSPort() string { if port := os.Getenv("DNS_PORT"); port != "" { return port