feat(frontend): add dedicated /cli page, nav links, and CTA buttons; remove CLI modals for leaner UX
- New CLI page at /cli with detailed usage and improved Quick Start card header - Add CLI link to navbars and small ‘Try the CLI’ CTAs on Home & Templates - Remove CLI modals and unused showCliModal() handler (keep_small_simple) - Self-host Bootstrap and Font Awesome; add OSI logo and GPL notice in footers - Dockerfile: verify vendor assets exist at build time - Minor a11y/contrast and heading-order cleanups (100 a11y)
This commit is contained in:
@@ -28,6 +28,13 @@ WORKDIR /app
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Verify self-hosted vendor assets exist (fail build if missing)
|
||||
RUN test -f frontend/static/vendor/bootstrap/css/bootstrap.min.css \
|
||||
&& test -f frontend/static/vendor/bootstrap/js/bootstrap.bundle.min.js \
|
||||
&& test -f frontend/static/vendor/fontawesome/css/all.min.css \
|
||||
&& test -f frontend/static/vendor/fontawesome/webfonts/fa-solid-900.woff2 \
|
||||
&& test -f frontend/static/img/osi-logo.svg || (echo 'Missing vendor assets. Ensure static/vendor and images are committed.' && exit 1)
|
||||
|
||||
# Install dependencies with uv
|
||||
RUN uv venv && \
|
||||
uv pip install -e ".[web]"
|
||||
|
||||
@@ -11,6 +11,7 @@ from typing import Any, Dict, List, Optional
|
||||
|
||||
from fastapi import FastAPI, File, HTTPException, Request, UploadFile # type: ignore
|
||||
from fastapi.middleware.cors import CORSMiddleware # type: ignore
|
||||
from fastapi.middleware.gzip import GZipMiddleware # type: ignore
|
||||
from fastapi.responses import FileResponse, HTMLResponse # type: ignore
|
||||
from fastapi.staticfiles import StaticFiles # type: ignore
|
||||
from fastapi.templating import Jinja2Templates # type: ignore
|
||||
@@ -38,6 +39,9 @@ app.add_middleware(
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Add GZip compression
|
||||
app.add_middleware(GZipMiddleware, minimum_size=500)
|
||||
|
||||
# Setup templates and static files
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
||||
TEMPLATES_DIR = BASE_DIR / settings.templates_dir
|
||||
@@ -155,6 +159,14 @@ async def templates_page(request: Request):
|
||||
context.update(settings.get_template_context())
|
||||
return templates.TemplateResponse("templates.html", context)
|
||||
|
||||
# CLI Info Page
|
||||
@app.get("/cli", response_class=HTMLResponse)
|
||||
async def cli_page(request: Request):
|
||||
"""Serve the CLI information page."""
|
||||
context = {"request": request}
|
||||
context.update(settings.get_template_context())
|
||||
return templates.TemplateResponse("cli.html", context)
|
||||
|
||||
|
||||
# API Routes
|
||||
@app.post("/api/validate", response_model=ValidationResult)
|
||||
|
||||
@@ -15,6 +15,17 @@
|
||||
}
|
||||
|
||||
/* General Styles */
|
||||
|
||||
/* Navbar link contrast fix on primary bg */
|
||||
.navbar-dark.bg-primary .navbar-nav .nav-link {
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
}
|
||||
.navbar-dark.bg-primary .navbar-nav .nav-link:hover,
|
||||
.navbar-dark.bg-primary .navbar-nav .nav-link:focus,
|
||||
.navbar-dark.bg-primary .navbar-nav .nav-link.active {
|
||||
color: #ffffff;
|
||||
}
|
||||
body {
|
||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||
line-height: 1.6;
|
||||
@@ -250,7 +261,7 @@ footer.bg-dark .text-muted {
|
||||
|
||||
.template-tag {
|
||||
background: var(--light-color);
|
||||
color: var(--secondary-color);
|
||||
color: #495057;
|
||||
padding: 0.125rem 0.5rem;
|
||||
border-radius: 1rem;
|
||||
font-size: 0.75rem;
|
||||
@@ -508,16 +519,87 @@ footer.bg-dark .text-muted {
|
||||
}
|
||||
|
||||
.form-control {
|
||||
background-color: var(--bg-secondary);
|
||||
background-color: #1f2937;
|
||||
border-color: #4a5568;
|
||||
color: #333;
|
||||
color: #f7fafc;
|
||||
}
|
||||
.form-control::placeholder {
|
||||
color: #cbd5e0;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
background-color: var(--bg-secondary);
|
||||
background-color: #1f2937;
|
||||
border-color: var(--primary-color);
|
||||
color: #333;
|
||||
color: #f7fafc;
|
||||
}
|
||||
|
||||
/* Improve outline-secondary contrast on dark backgrounds */
|
||||
.card.bg-dark .btn-outline-secondary,
|
||||
.bg-dark .btn-outline-secondary,
|
||||
.card .btn-outline-secondary {
|
||||
color: #e2e8f0;
|
||||
border-color: #e2e8f0;
|
||||
}
|
||||
.card.bg-dark .btn-outline-secondary:hover,
|
||||
.bg-dark .btn-outline-secondary:hover,
|
||||
.card .btn-outline-secondary:hover {
|
||||
color: #1a202c;
|
||||
background-color: #e2e8f0;
|
||||
border-color: #e2e8f0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Increase link and outline-primary contrast on dark backgrounds */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.card.bg-dark a,
|
||||
.bg-dark a {
|
||||
color: #93c5fd;
|
||||
}
|
||||
.card.bg-dark a:hover,
|
||||
.bg-dark a:hover {
|
||||
color: #bfdbfe;
|
||||
}
|
||||
|
||||
.card.bg-dark .btn-outline-primary,
|
||||
.bg-dark .btn-outline-primary,
|
||||
.card .btn-outline-primary {
|
||||
color: #e2e8f0;
|
||||
border-color: #93c5fd;
|
||||
}
|
||||
.card.bg-dark .btn-outline-primary:hover,
|
||||
.bg-dark .btn-outline-primary:hover,
|
||||
.card .btn-outline-primary:hover {
|
||||
color: #1a202c;
|
||||
background-color: #93c5fd;
|
||||
border-color: #93c5fd;
|
||||
}
|
||||
|
||||
/* High-contrast link utility */
|
||||
.link-contrast {
|
||||
color: #93c5fd !important;
|
||||
}
|
||||
.link-contrast:hover,
|
||||
.link-contrast:focus {
|
||||
color: #bfdbfe !important;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
/* Footer OSI logo */
|
||||
footer .badge.bg-success {
|
||||
position: relative;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
footer .badge.bg-success::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0.35rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
background: url('/static/img/osi-logo.svg') no-repeat center / contain;
|
||||
}
|
||||
|
||||
/* Print Styles */
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="128"
|
||||
height="128"
|
||||
viewBox="0 0 128 128"
|
||||
role="img"
|
||||
aria-labelledby="title desc">
|
||||
<title id="title">Open Source Initiative (OSI) Logo</title>
|
||||
<desc id="desc">Green circular mark with a keyhole-shaped cutout representing the Open Source Initiative.</desc>
|
||||
<!--
|
||||
This is a simplified vector rendition of the OSI "Open Source" mark.
|
||||
The OSI logo is a trademark of the Open Source Initiative.
|
||||
See https://opensource.org/trademark for usage guidelines.
|
||||
-->
|
||||
<defs>
|
||||
<mask id="osi-cutout" maskUnits="userSpaceOnUse">
|
||||
<!-- Start fully transparent -->
|
||||
<rect x="0" y="0" width="128" height="128" fill="black"/>
|
||||
<!-- Keep the outer circle area -->
|
||||
<circle cx="64" cy="64" r="60" fill="white"/>
|
||||
<!-- Cut out inner circle (keyhole top) -->
|
||||
<circle cx="64" cy="64" r="26" fill="black"/>
|
||||
<!-- Cut out the keyhole stem; rounded for a smooth shape -->
|
||||
<rect x="53" y="86" width="22" height="30" rx="6" ry="6" fill="black"/>
|
||||
</mask>
|
||||
</defs>
|
||||
|
||||
<!-- Apply the mask to a solid green rectangle -->
|
||||
<rect x="0" y="0" width="128" height="128" fill="#3DA639" mask="url(#osi-cutout)"/>
|
||||
|
||||
<!-- Optional subtle outline for crisp edges on various backgrounds -->
|
||||
<circle cx="64" cy="64" r="60" fill="none" stroke="#2a7a2a" stroke-width="1" opacity="0.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -337,10 +337,7 @@ function showUploadModal() {
|
||||
modal.show();
|
||||
}
|
||||
|
||||
function showCliModal() {
|
||||
const modal = new bootstrap.Modal(document.getElementById('cliModal'));
|
||||
modal.show();
|
||||
}
|
||||
|
||||
|
||||
async function validateFile() {
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
|
||||
@@ -107,7 +107,7 @@ class TemplatesBrowser {
|
||||
<div class="card-header">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<h6 class="card-title mb-1">${this.escapeHtml(template.name)}</h6>
|
||||
<p class="card-title h6 mb-1">${this.escapeHtml(template.name)}</p>
|
||||
<small class="opacity-75">
|
||||
<i class="fas fa-${this.getUnitTypeIcon(template.unit_type)} me-1"></i>
|
||||
${template.unit_type}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,322 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>CLI - {{ app_name }}</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/static/img/favicon.svg" />
|
||||
<link rel="icon" type="image/svg+xml" sizes="16x16" href="/static/img/favicon-16x16.svg" />
|
||||
<link rel="alternate icon" href="/static/img/favicon.svg" />
|
||||
<link rel="apple-touch-icon" href="/static/img/icon-192x192.svg" />
|
||||
<link rel="manifest" href="/static/img/site.webmanifest" />
|
||||
<meta name="theme-color" content="#0d6efd" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||
<meta name="apple-mobile-web-app-title" content="{{ app_name }}" />
|
||||
<meta name="description" content="{{ app_name }} CLI — command-line interface to create, validate, and manage systemd unit files." />
|
||||
<link href="/static/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="/static/vendor/fontawesome/css/all.min.css" />
|
||||
<link href="/static/css/style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/">
|
||||
<i class="fas fa-cogs me-2"></i>{{ app_name }}
|
||||
</a>
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarNav"
|
||||
>
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/editor">Editor</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/templates">Templates</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/cli">CLI</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ api_docs_url }}" target="_blank">
|
||||
<i class="fas fa-book me-1"></i>API Docs
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ github_url }}" target="_blank">
|
||||
<i class="fab fa-github me-1"></i>GitHub
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<header class="hero-section bg-light py-5">
|
||||
<div class="container">
|
||||
<div class="row align-items-center g-4">
|
||||
<div class="col-lg-7">
|
||||
<h1 class="display-5 fw-bold mb-3">
|
||||
<i class="fas fa-terminal me-2"></i>{{ app_name }} CLI
|
||||
</h1>
|
||||
<p class="lead mb-4">
|
||||
A powerful command-line interface to create, validate, and manage systemd unit files.
|
||||
The CLI ships with the backend sub-project and uses the same reliable core as the web app.
|
||||
</p>
|
||||
<div class="d-flex gap-3">
|
||||
<a href="{{ github_url }}" target="_blank" class="btn btn-primary">
|
||||
<i class="fab fa-github me-2"></i>View on GitHub
|
||||
</a>
|
||||
<a href="#install" class="btn btn-outline-primary">
|
||||
<i class="fas fa-download me-2"></i>Installation
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<div class="card border-0 shadow-sm code-preview">
|
||||
<div class="card-header text-white" style="background: linear-gradient(135deg, var(--primary-color), #0b5ed7);">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<i class="fas fa-rocket me-2"></i>
|
||||
<strong>Quick Start</strong>
|
||||
</div>
|
||||
<span class="badge bg-light text-primary">CLI</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<pre class="mb-0"><code># From the repository root
|
||||
pip install -e .
|
||||
|
||||
# Show help
|
||||
unitforge --help
|
||||
|
||||
# Validate a unit file
|
||||
unitforge validate /etc/systemd/system/myapp.service -v -w
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container my-5">
|
||||
<section id="install" class="mb-5">
|
||||
<h2 class="h3 mb-3">Installation</h2>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<h3 class="h5">From repository (editable)</h3>
|
||||
<p class="text-muted mb-3">
|
||||
Install directly from the repository so changes are picked up automatically.
|
||||
</p>
|
||||
<pre class="bg-dark text-light p-3 rounded"><code>pip install -e .</code></pre>
|
||||
<button class="btn btn-sm btn-outline-light mt-2" data-copy="pip install -e .">
|
||||
<i class="fas fa-copy me-1"></i>Copy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<h3 class="h5">Verify installation</h3>
|
||||
<p class="text-muted mb-3">
|
||||
Use the built-in version and help commands to confirm the CLI is available.
|
||||
</p>
|
||||
<pre class="bg-dark text-light p-3 rounded"><code>unitforge --version
|
||||
unitforge --help</code></pre>
|
||||
<div class="d-flex gap-2 mt-2">
|
||||
<button class="btn btn-sm btn-outline-light" data-copy="unitforge --version">
|
||||
<i class="fas fa-copy me-1"></i>Copy version
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-light" data-copy="unitforge --help">
|
||||
<i class="fas fa-copy me-1"></i>Copy help
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="commands" class="mb-5">
|
||||
<h2 class="h3 mb-3">Common Commands</h2>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-6">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<h3 class="h5"><i class="fas fa-check-circle text-success me-2"></i>Validate</h3>
|
||||
<p class="text-muted">
|
||||
Validate an existing systemd unit file. Add <code>-v</code> for verbose
|
||||
details and <code>-w</code> to include warnings.
|
||||
</p>
|
||||
<pre class="bg-dark text-light p-3 rounded"><code>unitforge validate /etc/systemd/system/myapp.service -v -w</code></pre>
|
||||
<button class="btn btn-sm btn-outline-light" data-copy="unitforge validate /etc/systemd/system/myapp.service -v -w">
|
||||
<i class="fas fa-copy me-1"></i>Copy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<h3 class="h5"><i class="fas fa-plus-circle text-primary me-2"></i>Create</h3>
|
||||
<p class="text-muted">
|
||||
Create a new unit file from scratch with helpful flags and validation.
|
||||
</p>
|
||||
<pre class="bg-dark text-light p-3 rounded"><code>unitforge create \
|
||||
--type service \
|
||||
--name myapp \
|
||||
--exec-start "/usr/bin/myapp" \
|
||||
--restart on-failure \
|
||||
--output myapp.service \
|
||||
--validate-output</code></pre>
|
||||
<button class="btn btn-sm btn-outline-light" data-copy="unitforge create --type service --name myapp --exec-start "/usr/bin/myapp" --restart on-failure --output myapp.service --validate-output">
|
||||
<i class="fas fa-copy me-1"></i>Copy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<h3 class="h5"><i class="fas fa-layer-group text-info me-2"></i>Templates</h3>
|
||||
<p class="text-muted mb-2">
|
||||
Explore and generate units from curated templates.
|
||||
</p>
|
||||
<pre class="bg-dark text-light p-3 rounded"><code># List templates
|
||||
unitforge template list
|
||||
|
||||
# Show details
|
||||
unitforge template show webapp
|
||||
|
||||
# Generate (non-interactive)
|
||||
unitforge template generate webapp \
|
||||
--param name=mywebapp \
|
||||
--param exec_start="/usr/bin/node server.js" \
|
||||
--param user=www-data \
|
||||
--param working_directory=/opt/mywebapp \
|
||||
--validate-output
|
||||
|
||||
# Generate (interactive)
|
||||
unitforge template generate webapp --interactive</code></pre>
|
||||
<div class="d-flex gap-2 mt-2">
|
||||
<button class="btn btn-sm btn-outline-light" data-copy="unitforge template list">
|
||||
<i class="fas fa-copy me-1"></i>Copy list
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-light" data-copy="unitforge template show webapp">
|
||||
<i class="fas fa-copy me-1"></i>Copy show
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-light" data-copy="unitforge template generate webapp --interactive">
|
||||
<i class="fas fa-copy me-1"></i>Copy interactive
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<h3 class="h5"><i class="fas fa-info-circle text-secondary me-2"></i>Info</h3>
|
||||
<p class="text-muted">
|
||||
Extract metadata and section/key counts from a unit file.
|
||||
</p>
|
||||
<pre class="bg-dark text-light p-3 rounded"><code>unitforge info myapp.service</code></pre>
|
||||
<button class="btn btn-sm btn-outline-light" data-copy="unitforge info myapp.service">
|
||||
<i class="fas fa-copy me-1"></i>Copy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<h3 class="h5"><i class="fas fa-edit text-warning me-2"></i>Edit</h3>
|
||||
<p class="text-muted">
|
||||
Modify values in an existing unit file by setting or removing keys.
|
||||
</p>
|
||||
<pre class="bg-dark text-light p-3 rounded"><code>unitforge edit source.service dest.service \
|
||||
--set Service.ExecStart=/usr/bin/myapp \
|
||||
--set Unit.Description="My Service" \
|
||||
--remove Service.Environment \
|
||||
--validate-output</code></pre>
|
||||
<button class="btn btn-sm btn-outline-light" data-copy="unitforge edit source.service dest.service --set Service.ExecStart=/usr/bin/myapp --set Unit.Description="My Service" --remove Service.Environment --validate-output">
|
||||
<i class="fas fa-copy me-1"></i>Copy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="tips" class="mb-5">
|
||||
<h2 class="h3 mb-3">Tips</h2>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
Use <code>--validate-output</code> after generation or editing to catch issues early.
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
Prefer interactive template generation (<code>--interactive</code>) to fill required fields smoothly.
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
The CLI is part of the backend package. See the repository for the backend/CLI source:
|
||||
<a href="{{ github_url }}" target="_blank">{{ github_url }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="bg-dark text-light py-4 mt-5">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p class="fw-bold mb-0"><i class="fas fa-cogs me-2"></i>{{ app_name }}</p>
|
||||
<p class="text-muted small">{{ app_description }}.</p>
|
||||
</div>
|
||||
<div class="col-md-6 text-md-end">
|
||||
<div class="d-flex justify-content-md-end gap-3">
|
||||
<a href="{{ api_docs_url }}" class="text-light text-decoration-none">
|
||||
<i class="fas fa-book me-1"></i>API Docs
|
||||
</a>
|
||||
<a href="{{ github_url }}" class="text-light text-decoration-none">
|
||||
<i class="fab fa-github me-1"></i>GitHub
|
||||
</a>
|
||||
</div>
|
||||
<div class="mt-2 small">
|
||||
<span class="me-2">
|
||||
<i class="fas fa-code-branch me-1"></i>Open Source
|
||||
<span class="badge bg-success ms-2">OSI</span>
|
||||
</span>
|
||||
<span>
|
||||
Licensed under
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank" rel="noopener" class="text-decoration-underline text-light">GPL-3.0-or-later</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="/static/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/static/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="{{ app_name }} Editor — create, validate, and download systemd unit files with a visual editor and real-time checks.">
|
||||
<title>Editor - {{ app_name }}</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/static/img/favicon.svg">
|
||||
<link rel="icon" type="image/svg+xml" sizes="16x16" href="/static/img/favicon-16x16.svg">
|
||||
@@ -14,10 +15,10 @@
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<meta name="apple-mobile-web-app-title" content="{{ app_name }}">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.0.1/codemirror.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.0.1/theme/monokai.min.css" rel="stylesheet">
|
||||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
|
||||
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
|
||||
<link href="/static/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/static/vendor/fontawesome/css/all.min.css">
|
||||
<link href="/static/css/style.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
@@ -40,12 +41,15 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/templates">Templates</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/cli">CLI</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="navbar-nav">
|
||||
<button class="btn btn-outline-light btn-sm me-2" onclick="downloadFile()">
|
||||
<button class="btn btn-light btn-sm me-2" onclick="downloadFile()">
|
||||
<i class="fas fa-download me-1"></i>Download
|
||||
</button>
|
||||
<button class="btn btn-outline-light btn-sm" onclick="showUploadModal()">
|
||||
<button class="btn btn-light btn-sm" onclick="showUploadModal()">
|
||||
<i class="fas fa-upload me-1"></i>Upload
|
||||
</button>
|
||||
</div>
|
||||
@@ -214,7 +218,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<textarea id="editor" class="form-control border-0" rows="25" style="font-family: 'Courier New', monospace; resize: none; color: white; background-color: #2d3748;">[Unit]
|
||||
<label for="editor" class="visually-hidden">Unit file editor</label>
|
||||
<textarea id="editor" class="form-control border-0" aria-label="Unit file content" rows="25" style="font-family: 'Courier New', monospace; resize: none; color: white; background-color: #2d3748;">[Unit]
|
||||
Description=My Service Description
|
||||
After=network.target
|
||||
|
||||
@@ -274,7 +279,7 @@ WantedBy=multi-user.target</textarea>
|
||||
<small><strong>[Install]</strong> - Installation and enabling info</small>
|
||||
</div>
|
||||
<div class="help-item">
|
||||
<small><a href="https://www.freedesktop.org/software/systemd/man/systemd.unit.html" target="_blank">systemd documentation <i class="fas fa-external-link-alt"></i></a></small>
|
||||
<small><a class="link-contrast" href="https://www.freedesktop.org/software/systemd/man/systemd.unit.html" target="_blank">systemd documentation <i class="fas fa-external-link-alt"></i></a></small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -326,7 +331,41 @@ WantedBy=multi-user.target</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
<footer class="bg-dark text-light py-4 mt-5">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p class="fw-bold mb-0"><i class="fas fa-cogs me-2"></i>{{ app_name }}</p>
|
||||
<p class="text-muted small">{{ app_description }}.</p>
|
||||
</div>
|
||||
<div class="col-md-6 text-md-end">
|
||||
<div class="d-flex justify-content-md-end gap-3">
|
||||
<a href="{{ api_docs_url }}" class="text-light text-decoration-none">
|
||||
<i class="fas fa-book me-1"></i>API Docs
|
||||
</a>
|
||||
<a href="{{ github_url }}" class="text-light text-decoration-none">
|
||||
<i class="fab fa-github me-1"></i>GitHub
|
||||
</a>
|
||||
</div>
|
||||
<div class="mt-2 small">
|
||||
<span class="me-2">
|
||||
<i class="fas fa-code-branch me-1"></i>Open Source
|
||||
<span class="badge bg-success ms-2">OSI</span>
|
||||
</span>
|
||||
<span>
|
||||
Licensed under
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank" rel="noopener" class="text-decoration-underline text-light">GPL-3.0-or-later</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="/static/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/static/js/main.js"></script>
|
||||
<script src="/static/js/editor.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="{{ app_name }} — Systemd unit file creator with templates, validation, and a visual editor." />
|
||||
<title>{{ app_name }} - Systemd Unit File Creator</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/static/img/favicon.svg" />
|
||||
<link
|
||||
@@ -19,14 +20,8 @@
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||
<meta name="apple-mobile-web-app-title" content="{{ app_name }}" />
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link href="/static/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="/static/vendor/fontawesome/css/all.min.css" />
|
||||
<link href="/static/css/style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
@@ -54,6 +49,9 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/templates">Templates</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/cli">CLI</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
@@ -91,6 +89,9 @@
|
||||
<i class="fas fa-templates me-2"></i>Browse
|
||||
Templates
|
||||
</a>
|
||||
<a href="/cli" class="btn btn-outline-secondary btn-sm">
|
||||
<i class="fas fa-terminal me-1"></i>Try the CLI
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
@@ -125,7 +126,7 @@ WantedBy=multi-user.target</code></pre>
|
||||
<div class="row">
|
||||
<div class="col-lg-8 mx-auto text-center mb-5">
|
||||
<h2 class="h1 mb-3">Choose Your Approach</h2>
|
||||
<p class="lead text-muted">
|
||||
<p class="lead">
|
||||
{{ app_name }} offers multiple ways to create systemd
|
||||
unit files, from quick templates to detailed manual
|
||||
editing.
|
||||
@@ -142,7 +143,7 @@ WantedBy=multi-user.target</code></pre>
|
||||
>
|
||||
<i class="fas fa-magic text-white"></i>
|
||||
</div>
|
||||
<h5 class="card-title">Quick Templates</h5>
|
||||
<h3 class="card-title h5">Quick Templates</h3>
|
||||
<p class="card-text text-muted">
|
||||
Use pre-built templates for common services like
|
||||
web apps, databases, and containers.
|
||||
@@ -165,12 +166,12 @@ WantedBy=multi-user.target</code></pre>
|
||||
>
|
||||
<i class="fas fa-edit text-white"></i>
|
||||
</div>
|
||||
<h5 class="card-title">Visual Editor</h5>
|
||||
<h3 class="card-title h5">Visual Editor</h3>
|
||||
<p class="card-text text-muted">
|
||||
Create and edit unit files with our intuitive
|
||||
form-based editor with real-time validation.
|
||||
</p>
|
||||
<a href="/editor" class="btn btn-outline-success">
|
||||
<a href="/editor" class="btn btn-success">
|
||||
Open Editor
|
||||
</a>
|
||||
</div>
|
||||
@@ -185,7 +186,7 @@ WantedBy=multi-user.target</code></pre>
|
||||
>
|
||||
<i class="fas fa-check-circle text-white"></i>
|
||||
</div>
|
||||
<h5 class="card-title">Validation</h5>
|
||||
<h3 class="card-title h5">Validation</h3>
|
||||
<p class="card-text text-muted">
|
||||
Validate existing unit files and get detailed
|
||||
feedback on syntax and best practices.
|
||||
@@ -208,7 +209,7 @@ WantedBy=multi-user.target</code></pre>
|
||||
>
|
||||
<i class="fas fa-terminal text-white"></i>
|
||||
</div>
|
||||
<h5 class="card-title">CLI Tool</h5>
|
||||
<h3 class="card-title h5">CLI Tool</h3>
|
||||
<p class="card-text text-muted">
|
||||
Use the command-line interface for automation
|
||||
and integration with your development workflow.
|
||||
@@ -228,7 +229,7 @@ WantedBy=multi-user.target</code></pre>
|
||||
<div class="col-lg-8 mx-auto">
|
||||
<div class="text-center mb-4">
|
||||
<h3>Supported Unit Types</h3>
|
||||
<p class="text-muted">
|
||||
<p>
|
||||
{{ app_name }} supports all major systemd unit types
|
||||
</p>
|
||||
</div>
|
||||
@@ -389,7 +390,7 @@ unitforge template generate webapp \
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h6><i class="fas fa-cogs me-2"></i>{{ app_name }}</h6>
|
||||
<p class="fw-bold mb-0"><i class="fas fa-cogs me-2"></i>{{ app_name }}</p>
|
||||
<p class="text-muted small">{{ app_description }}.</p>
|
||||
</div>
|
||||
<div class="col-md-6 text-md-end">
|
||||
@@ -407,12 +408,22 @@ unitforge template generate webapp \
|
||||
<i class="fab fa-github me-1"></i>GitHub
|
||||
</a>
|
||||
</div>
|
||||
<div class="mt-2 small">
|
||||
<span class="me-2">
|
||||
<i class="fas fa-code-branch me-1"></i>Open Source
|
||||
<span class="badge bg-success ms-2">OSI</span>
|
||||
</span>
|
||||
<span>
|
||||
Licensed under
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank" rel="noopener" class="text-decoration-underline text-light">GPL-3.0-or-later</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/static/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/static/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -18,15 +18,10 @@
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||
<meta name="description" content="{{ app_name }} Templates — generate production-ready systemd unit files from curated templates." />
|
||||
<meta name="apple-mobile-web-app-title" content="{{ app_name }}" />
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link href="/static/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="/static/vendor/fontawesome/css/all.min.css" />
|
||||
<link href="/static/css/style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
@@ -56,6 +51,9 @@
|
||||
>Templates</a
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/cli">CLI</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -68,12 +66,17 @@
|
||||
<h1 class="display-5 fw-bold mb-3">
|
||||
<i class="fas fa-templates me-3"></i>Unit File Templates
|
||||
</h1>
|
||||
<p class="lead text-muted">
|
||||
<p class="lead">
|
||||
Choose from pre-built templates for common systemd unit
|
||||
configurations. Each template provides a solid
|
||||
foundation that you can customize for your specific
|
||||
needs.
|
||||
</p>
|
||||
<div class="mt-2">
|
||||
<a href="/cli" class="btn btn-outline-secondary btn-sm">
|
||||
<i class="fas fa-terminal me-1"></i>Try the CLI
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 text-lg-end">
|
||||
<div class="input-group">
|
||||
@@ -87,7 +90,7 @@
|
||||
<button
|
||||
class="btn btn-outline-secondary"
|
||||
type="button"
|
||||
onclick="clearSearch()"
|
||||
onclick="clearSearch()" aria-label="Clear search"
|
||||
>
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
@@ -366,7 +369,7 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h6><i class="fas fa-cogs me-2"></i>{{ app_name }}</h6>
|
||||
<p class="fw-bold mb-0"><i class="fas fa-cogs me-2"></i>{{ app_name }}</p>
|
||||
<p class="text-muted small">{{ app_description }}.</p>
|
||||
</div>
|
||||
<div class="col-md-6 text-md-end">
|
||||
@@ -384,12 +387,24 @@
|
||||
<i class="fab fa-github me-1"></i>GitHub
|
||||
</a>
|
||||
</div>
|
||||
<div class="mt-2 small">
|
||||
<span class="me-2">
|
||||
<i class="fas fa-code-branch me-1"></i>Open Source
|
||||
<span class="badge bg-success ms-2">OSI</span>
|
||||
</span>
|
||||
<span>
|
||||
Licensed under
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank" rel="noopener" class="text-decoration-underline text-light">GPL-3.0-or-later</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
<script src="/static/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/static/js/main.js"></script>
|
||||
<script src="/static/js/templates.js"></script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user