mirror of
https://github.com/ION606/static-site-hosting.git
synced 2026-05-14 22:16:54 +00:00
Split routes into multiple files (#2)
* split into files * attempted path fix * perms fix * added missed functions * fixed circular dependancy * I hate splitting * env fix * path fix
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
.error-container {
|
||||
text-align: center;
|
||||
padding: 100px 20px;
|
||||
min-height: 60vh;
|
||||
}
|
||||
|
||||
.error-emoji {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
max-width: 600px;
|
||||
margin: 0 auto 2rem;
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.alien {
|
||||
font-size: 5rem;
|
||||
margin: 2rem 0;
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const emojis = ["👽", "🚀", "🛸", "🌌", "🌠", "🔭", "🪐", "🌍", "✨", "👾"];
|
||||
const randomEmoji = emojis[Math.floor(Math.random() * emojis.length)];
|
||||
|
||||
const alienElement = document.querySelector(".alien");
|
||||
if (alienElement) {
|
||||
alienElement.textContent = randomEmoji;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="error-container">
|
||||
<div class="alien">👽</div>
|
||||
<h1>Houston, We Have a Problem!</h1>
|
||||
|
||||
<div class="error-message">
|
||||
<p>The site you're looking for doesn't exist... yet!</p>
|
||||
<p>But don't worry, this corner of the internet is just waiting for your creativity.</p>
|
||||
</div>
|
||||
|
||||
{% if current_user.is_authenticated %}
|
||||
{% if domain %}
|
||||
<a href="{{ url_for('main.dashboard', _external=True) }}" class="btn cta-button">
|
||||
🚀 Create {{ domain.split('.')[0]|default("Site", true) }}
|
||||
</a>
|
||||
{% else %}
|
||||
<div class="auth-buttons">
|
||||
{% if is_main_domain %}
|
||||
<p>Start your web hosting journey today!</p>
|
||||
<a href="{{ url_for('main.register', _external=True) }}" class="btn cta-button">Sign Up</a>
|
||||
{% else %}
|
||||
<p>The page you requested doesn't exist on this site.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="auth-buttons">
|
||||
<p>Start your web hosting journey today!</p>
|
||||
<a href="{{ url_for('main.login', _external=True) }}" class="btn cta-button">Login</a>
|
||||
<a href="{{ url_for('main.register', _external=True) }}" class="btn cta-button">Sign Up</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div style="margin-top: 3rem;">
|
||||
<small>PS: If you were looking for someone else's site, maybe they forgot to launch it! 🚀</small>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,82 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description"
|
||||
content="ION Static Site Hosting offers effortless hosting and sharing of static sites with instant deployment, dark mode interface, and secure, private data handling!">
|
||||
<meta name="keywords"
|
||||
content="static site hosting, instant deployment, dark mode, secure hosting, private data, file management, blazing fast">
|
||||
<meta name="author" content="ION606">
|
||||
<meta property="og:title" content="ION Static Site Hosting">
|
||||
<meta property="og:description"
|
||||
content="Host and share your static sites effortlessly with ION Static Site Hosting. Enjoy instant deployment, a sleek dark mode interface, and secure, private data handling!">
|
||||
<meta property="og:image" content="{{ url_for('static', filename='hosting.png') }}">
|
||||
<meta property="og:url" content="https://{{ SERVERNAME }}/">
|
||||
<meta name="twitter:card" content="{{ url_for('static', filename='hosting.png') }}">
|
||||
<meta name="twitter:title" content="ION Static Site Hosting">
|
||||
<meta name="twitter:description"
|
||||
content="Host and share your static sites effortlessly with ION Static Site Hosting. Enjoy instant deployment, a sleek dark mode interface, and secure, private data handling!">
|
||||
<meta name="twitter:image" content="{{ url_for('static', filename='hosting.png') }}">
|
||||
|
||||
<title>ION Static Site Hosting - {% block title %}{% endblock %}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" id="theme-style">
|
||||
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const themeToggle = document.querySelector('#theme-toggle');
|
||||
const body = document.body;
|
||||
|
||||
// Get saved theme from localStorage
|
||||
const savedTheme = localStorage.getItem('theme') || 'dark';
|
||||
body.setAttribute('data-theme', savedTheme);
|
||||
|
||||
if (!savedTheme) localStorage.setItem('theme', savedTheme);
|
||||
|
||||
// Set initial button text
|
||||
themeToggle.textContent = savedTheme === 'dark' ? 'Light Mode' : 'Dark Mode';
|
||||
|
||||
themeToggle.addEventListener('click', () => {
|
||||
const isDark = body.getAttribute('data-theme') === 'dark';
|
||||
body.setAttribute('data-theme', isDark ? 'light' : 'dark');
|
||||
localStorage.setItem('theme', isDark ? 'light' : 'dark');
|
||||
themeToggle.textContent = isDark ? 'Dark Mode' : 'Light Mode';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar">
|
||||
<div class="container">
|
||||
<a href="{{ url_for('main.home', _external=True) }}">Home</a>
|
||||
{% if current_user.is_authenticated %}
|
||||
<a href="{{ url_for('main.dashboard', _external=True) }}">Dashboard</a>
|
||||
<a href="{{ url_for('main.logout', _external=True) }}">Logout</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('main.login', _external=True) }}">Login</a>
|
||||
<a href="{{ url_for('main.register', _external=True) }}">Register</a>
|
||||
{% endif %}
|
||||
<button id="theme-toggle" class="btn">Toggle Dark Mode</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="flash-message flash-{{ category }}">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,133 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Dashboard{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Your Sites</h2>
|
||||
|
||||
<h3>Create New Site</h3>
|
||||
<form method="POST" action="{{ url_for('main.upload_site') }}" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label>Site Name:</label>
|
||||
<input type="text" name="name" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Site URL:</label>
|
||||
<div style="display: flex; align-items: center;">
|
||||
<input type="text" name="subdomain" required pattern="[a-zA-Z0-9\-_]+"
|
||||
title="Letters, numbers, hyphens, and underscores only" value="{{ subdomain }}"
|
||||
style="flex: 1;">
|
||||
<span style="margin-left: 5px;">.{{ hostname }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" onclick="document.querySelector('#file-input').click()">
|
||||
<label class="upload-label">
|
||||
Upload Files
|
||||
<input type="file" name="files" style="display: none;" id="file-input" multiple accept=".html,.css,.js"
|
||||
required>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="file-preview" class="file-preview">
|
||||
<h5>Selected Files:</h5>
|
||||
<div id="file-grid" class="file-grid"></div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn" style="margin-top: 20px;">Create Site</button>
|
||||
</form>
|
||||
|
||||
<div style="margin-bottom: 30px;"></div>
|
||||
|
||||
<h1>My Sites</h1>
|
||||
|
||||
<div class="sites-list">
|
||||
{% for site in sites %}
|
||||
<div class="site-card">
|
||||
<h4>{{ site.name }}</h4>
|
||||
<p>Created: {{ site.created_at.strftime('%Y-%m-%d') }}</p>
|
||||
|
||||
<!-- Display uploaded files -->
|
||||
<div class="uploaded-files">
|
||||
<h5>Uploaded Files:</h5>
|
||||
<div class="file-grid">
|
||||
{% set site_dir = 'sites/' + current_user.id|string + '/' + site.id|string %}
|
||||
{% for file in list_files(site_dir) %}
|
||||
<div class="file-card">
|
||||
<div
|
||||
class="file-icon {{ 'html' if file.endswith('.html') else 'css' if file.endswith('.css') else 'js' }}">
|
||||
</div>
|
||||
<span class="file-name">{{ file }}</span>
|
||||
<form method="POST" action="{{ url_for('main.delete_file', site_id=site.id, filename=file) }}"
|
||||
style="display: inline;">
|
||||
<button type="submit" class="file-close">×</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Site Actions -->
|
||||
<div class="site-actions">
|
||||
<a href="{{ url_for('main.edit_site', site_id=site.id) }}" class="btn">Edit</a>
|
||||
<a href="{{ url_for('main.serve_site_content', filename='index.html', _external=True, subdomain=site.subdomain) }}">Visit
|
||||
{{ site.name }}
|
||||
</a>
|
||||
<form method="POST" action="{{ url_for('main.delete_site', site_id=site.id) }}" style="display: inline;">
|
||||
<button type="submit" class="btn btn-danger">Delete Site</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- for File Management -->
|
||||
<script>
|
||||
const fileInput = document.querySelector('#file-input');
|
||||
const fileGrid = document.querySelector('#file-grid');
|
||||
|
||||
function getFileIconClass(filename) {
|
||||
if (filename.endsWith('.html')) return 'html';
|
||||
if (filename.endsWith('.css')) return 'css';
|
||||
if (filename.endsWith('.js')) return 'js';
|
||||
return 'generic';
|
||||
}
|
||||
|
||||
function updateFilePreview() {
|
||||
fileGrid.innerHTML = '';
|
||||
const files = Array.from(fileInput.files);
|
||||
|
||||
files.forEach((file, index) => {
|
||||
const fileCard = document.createElement('div');
|
||||
fileCard.className = 'file-card';
|
||||
|
||||
fileCard.innerHTML = `
|
||||
<div class="file-icon ${getFileIconClass(file.name)}"></div>
|
||||
<span class="file-name">${file.name}</span>
|
||||
<button type="button" class="file-close" onclick="removeFile(${index})">×</button>
|
||||
`;
|
||||
|
||||
fileGrid.appendChild(fileCard);
|
||||
});
|
||||
}
|
||||
|
||||
function removeFile(index) {
|
||||
const files = Array.from(fileInput.files);
|
||||
files.splice(index, 1);
|
||||
|
||||
const newFileList = new DataTransfer();
|
||||
files.forEach(file => newFileList.items.add(file));
|
||||
fileInput.files = newFileList.files;
|
||||
|
||||
updateFilePreview();
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', updateFilePreview);
|
||||
fileInput.addEventListener('change', () => {
|
||||
document.querySelector('#file-preview').style.display = fileInput.files.length ? 'block' : 'non'
|
||||
updateFilePreview();
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,61 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Edit Site{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Edit {{ site.name }}</h2>
|
||||
<a href="{{ url_for('main.serve_site_content', subdomain=site.subdomain) }}" class="btn" target="_blank">View Site</a>
|
||||
|
||||
<div style="margin-bottom: 30px;"></div>
|
||||
|
||||
<label for="theme-selector">Select Theme:</label>
|
||||
<select id="theme-selector" style="width: 200px;">
|
||||
<!-- Theme options will be populated here -->
|
||||
</select>
|
||||
|
||||
<form method="POST">
|
||||
<div class="form-group">
|
||||
<label>Site Name:</label>
|
||||
<input type="text" name="name" value="{{ site.name }}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Files:</label>
|
||||
<ul id="file-list" class="file-grid" style="padding-left: 0; list-style: none;">
|
||||
{% for filename, content in files.items() %}
|
||||
<li class="file-card" style="cursor: pointer;" data-index="{{ loop.index0 }}" data-file="{{ filename }}">
|
||||
<div class="file-icon {% if filename.endswith('.html') %}html{% elif filename.endswith('.css') %}css{% elif filename.endswith('.js') %}js{% endif %}"></div>
|
||||
<span class="file-name">{{ filename }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Hidden textareas to retain file content (one per file) -->
|
||||
{% for filename, content in files.items() %}
|
||||
<textarea hidden name="{{ filename }}" id="textarea-{{ loop.index0 }}" data-filename="{{ filename }}">{{ content|trim }}</textarea>
|
||||
{% endfor %}
|
||||
|
||||
<!-- Ace Editor Container -->
|
||||
<div id="editor-container" style="width: 100%; height: 400px; border: 1px solid #ddd; margin-top: 20px;"></div>
|
||||
|
||||
<button type="submit" class="btn" style="margin-top:20px;">Save Changes</button>
|
||||
</form>
|
||||
|
||||
<!-- Ace Editor -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.14/ace.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.14/ext-themelist.js"></script>
|
||||
|
||||
<!-- Select2 -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css" rel="stylesheet" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js"></script>
|
||||
<script src="{{ url_for('static', filename='editor.js') }}"></script>
|
||||
|
||||
<style>
|
||||
/* Style for the Select2 dropdown */
|
||||
.select2-container {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,114 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<style>
|
||||
header {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
background: var(--card-bg);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
header p {
|
||||
font-size: 1.2rem;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.main-container {
|
||||
max-width: 900px;
|
||||
margin: 40px auto;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.feature-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.feature {
|
||||
background: var(--card-bg);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin: 10px;
|
||||
width: 260px;
|
||||
text-align: center;
|
||||
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.feature h3 {
|
||||
color: var(--text-color);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.cta-section {
|
||||
margin-top: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 40px;
|
||||
padding: 20px;
|
||||
background: var(--nav-bg);
|
||||
text-align: center;
|
||||
color: var(--nav-text);
|
||||
}
|
||||
</style>
|
||||
|
||||
<header>
|
||||
<h1>🚀 ION Static Site Hosting</h1>
|
||||
<p>Host and share your static sites effortlessly.</p>
|
||||
</header>
|
||||
|
||||
<div class="main-container">
|
||||
<h2>Why Choose Us?</h2>
|
||||
<p>Whether you’re a developer, designer, or just need to showcase your work, we make it easy to host and share
|
||||
static sites.</p>
|
||||
|
||||
<div class="feature-list">
|
||||
<div class="feature">
|
||||
<h3>⚡ Instant Hosting</h3>
|
||||
<p>Upload your static files and get a live site in seconds.</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3>🌙 Dark Mode</h3>
|
||||
<p>A sleek, modern interface that's easy on the eyes.</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3>✏️ Edit in Browser</h3>
|
||||
<p>Make changes directly to your site with our built-in editor.</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3>🔒 Secure & Private</h3>
|
||||
<p>Your data stays yours, no hidden tracking or ads.</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3>📂 Simple File Management</h3>
|
||||
<p>Easily organize and update your projects.</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3>🚀 Blazing Fast</h3>
|
||||
<p>Minimal setup, maximum performance.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cta-section">
|
||||
<h2>Get Started Now</h2>
|
||||
<p>No signup required. Upload your first site and go live instantly!</p>
|
||||
<a href="{{ url_for('main.dashboard') }}" class="btn">Upload Your Site</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p>© 2024 ION Static Hosting. All rights reserved.</p>
|
||||
</footer>
|
||||
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,18 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Login{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Login</h2>
|
||||
<form method="POST" action="{{ url_for('main.login') }}">
|
||||
<div class="form-group">
|
||||
<label>Email:</label>
|
||||
<input type="email" name="email" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Password:</label>
|
||||
<input type="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,18 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Register{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Register</h2>
|
||||
<form method="POST" action="{{ url_for('main.register') }}">
|
||||
<div class="form-group">
|
||||
<label>Email:</label>
|
||||
<input type="email" name="email" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Password:</label>
|
||||
<input type="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit">Register</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user