test version fetcher
All checks were successful
Deploy to Production / deploy (push) Successful in 6s
All checks were successful
Deploy to Production / deploy (push) Successful in 6s
This commit is contained in:
parent
aeccf99ad3
commit
0dca4e8ddc
43
index.html
43
index.html
@ -5,6 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>pyserve - Documentation</title>
|
<title>pyserve - Documentation</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<script src="scripts/version-fetcher.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="container">
|
<div id="container">
|
||||||
@ -13,7 +14,8 @@
|
|||||||
<div class="tagline">python application orchestrator</div>
|
<div class="tagline">python application orchestrator</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="content">
|
<div id="main-wrapper">
|
||||||
|
<div id="content">
|
||||||
<h2>About</h2>
|
<h2>About</h2>
|
||||||
<p>
|
<p>
|
||||||
pyserve is a Python application orchestrator and HTTP server.
|
pyserve is a Python application orchestrator and HTTP server.
|
||||||
@ -102,7 +104,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>Version</h2>
|
<h2>Version</h2>
|
||||||
<p>Current version: <strong>0.9.10</strong></p>
|
<p>Current version: <strong id="current-version">0.9.10</strong></p>
|
||||||
|
|
||||||
<h2>Requirements</h2>
|
<h2>Requirements</h2>
|
||||||
<ul class="indent">
|
<ul class="indent">
|
||||||
@ -110,6 +112,43 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- <aside id="sidebar">
|
||||||
|
<div class="partner-block">
|
||||||
|
<h3>Sponsors & Partners</h3>
|
||||||
|
|
||||||
|
<div class="partner-card">
|
||||||
|
<div class="partner-icon">🚀</div>
|
||||||
|
<h4>Cloud Hosting</h4>
|
||||||
|
<p>Deploy your pyserve applications with enterprise-grade reliability.</p>
|
||||||
|
<a href="#" class="partner-link">Learn More →</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="partner-card">
|
||||||
|
<div class="partner-icon">📊</div>
|
||||||
|
<h4>Monitoring</h4>
|
||||||
|
<p>Real-time monitoring and analytics for your production apps.</p>
|
||||||
|
<a href="#" class="partner-link">Get Started →</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="partner-card">
|
||||||
|
<div class="partner-icon">🔐</div>
|
||||||
|
<h4>Security</h4>
|
||||||
|
<p>Advanced security scanning and vulnerability detection.</p>
|
||||||
|
<a href="#" class="partner-link">Secure Now →</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="community-block">
|
||||||
|
<h4>Community</h4>
|
||||||
|
<ul class="community-links">
|
||||||
|
<li><a href="https://git.pyserve.org">Git Repository</a></li>
|
||||||
|
<li><a href="#">Discord Server</a></li>
|
||||||
|
<li><a href="#">Stack Overflow</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside> -->
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
<p>pyserve © 2024-2025 | MIT License</p>
|
<p>pyserve © 2024-2025 | MIT License</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
149
scripts/version-fetcher.js
Normal file
149
scripts/version-fetcher.js
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
/**
|
||||||
|
* Version Fetcher - Automatically fetches and displays latest pyserveX version
|
||||||
|
* from Gitea releases page
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
const RELEASES_URL = 'https://git.pyserve.org/Shifty/pyserveX/releases/latest';
|
||||||
|
const VERSION_ELEMENT_ID = 'current-version';
|
||||||
|
const CACHE_KEY = 'pyserve_version_cache';
|
||||||
|
const CACHE_DURATION = 3600000; // 1 hour
|
||||||
|
const FALLBACK_VERSION = 'offline';
|
||||||
|
|
||||||
|
async function fetchLatestVersion() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(RELEASES_URL, {
|
||||||
|
method: 'GET',
|
||||||
|
mode: 'cors',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'text/html'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.warn(`Failed to fetch: ${response.status}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const html = await response.text();
|
||||||
|
|
||||||
|
const patterns = [
|
||||||
|
/<title>PyServeX\s+v([\d.]+)/i,
|
||||||
|
/releases\/tag\/v([\d.]+)/,
|
||||||
|
/aria-label="PyServeX\s+v([\d.]+)/i,
|
||||||
|
/<h4[^>]*>.*?v([\d.]+).*?<\/h4>/i
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const pattern of patterns) {
|
||||||
|
const match = html.match(pattern);
|
||||||
|
if (match && match[1]) {
|
||||||
|
console.log(`✓ Version found: ${match[1]}`);
|
||||||
|
return match[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn('Version not found in HTML');
|
||||||
|
return null;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Fetch error:', error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCachedVersion() {
|
||||||
|
try {
|
||||||
|
const cached = localStorage.getItem(CACHE_KEY);
|
||||||
|
if (!cached) return null;
|
||||||
|
|
||||||
|
const data = JSON.parse(cached);
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
if (now - data.timestamp < CACHE_DURATION) {
|
||||||
|
return data.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.removeItem(CACHE_KEY);
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Cache read error:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cacheVersion(version) {
|
||||||
|
try {
|
||||||
|
const data = {
|
||||||
|
version: version,
|
||||||
|
timestamp: Date.now()
|
||||||
|
};
|
||||||
|
localStorage.setItem(CACHE_KEY, JSON.stringify(data));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Cache write error:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateVersionDisplay(version) {
|
||||||
|
const element = document.getElementById(VERSION_ELEMENT_ID);
|
||||||
|
if (!element) {
|
||||||
|
console.warn('Version element not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionLink = document.createElement('a');
|
||||||
|
versionLink.href = 'https://git.pyserve.org/Shifty/pyserveX/releases/tag/v' + version;
|
||||||
|
versionLink.textContent = version;
|
||||||
|
versionLink.target = '_blank';
|
||||||
|
versionLink.rel = 'noopener noreferrer';
|
||||||
|
versionLink.className = 'version-link';
|
||||||
|
|
||||||
|
element.innerHTML = '';
|
||||||
|
element.appendChild(versionLink);
|
||||||
|
|
||||||
|
const badge = document.createElement('span');
|
||||||
|
badge.textContent = 'latest';
|
||||||
|
badge.className = 'version-badge';
|
||||||
|
badge.title = 'Fetched from Git repository';
|
||||||
|
element.appendChild(document.createTextNode(' '));
|
||||||
|
element.appendChild(badge);
|
||||||
|
|
||||||
|
element.style.opacity = '1';
|
||||||
|
}
|
||||||
|
function showLoadingState() {
|
||||||
|
const element = document.getElementById(VERSION_ELEMENT_ID);
|
||||||
|
if (element) {
|
||||||
|
element.style.opacity = '0.6';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
showLoadingState();
|
||||||
|
|
||||||
|
const cachedVersion = getCachedVersion();
|
||||||
|
if (cachedVersion) {
|
||||||
|
updateVersionDisplay(cachedVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
const latestVersion = await fetchLatestVersion();
|
||||||
|
if (latestVersion) {
|
||||||
|
cacheVersion(latestVersion);
|
||||||
|
updateVersionDisplay(latestVersion);
|
||||||
|
} else if (!cachedVersion) {
|
||||||
|
const element = document.getElementById(VERSION_ELEMENT_ID);
|
||||||
|
if (element) {
|
||||||
|
element.textContent = FALLBACK_VERSION;
|
||||||
|
element.style.opacity = '1';
|
||||||
|
element.style.color = '#c9c9c9';
|
||||||
|
element.title = 'Version fetch failed (CORS or network issue)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
209
style.css
209
style.css
@ -13,7 +13,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#container {
|
#container {
|
||||||
max-width: 800px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#content {
|
#content {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
@ -235,3 +235,208 @@ dd {
|
|||||||
color: #666;
|
color: #666;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Main wrapper for two-column layout */
|
||||||
|
#main-wrapper {
|
||||||
|
display: flex;
|
||||||
|
gap: 30px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar */
|
||||||
|
#sidebar {
|
||||||
|
width: 300px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-block {
|
||||||
|
position: sticky;
|
||||||
|
top: 20px;
|
||||||
|
background: #0d0d0d;
|
||||||
|
border: 1px solid #333;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-block h3 {
|
||||||
|
color: #3cb371;
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0 0 15px 0;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 2px solid #2e8b57;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-card {
|
||||||
|
background: #1a1a1a;
|
||||||
|
border: 1px solid #2a2a2a;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-card:hover {
|
||||||
|
border-color: #3cb371;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(60, 179, 113, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-icon {
|
||||||
|
font-size: 32px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-card h4 {
|
||||||
|
color: #e0e0e0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-card p {
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-link {
|
||||||
|
display: inline-block;
|
||||||
|
color: #5fba7d;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-link:hover {
|
||||||
|
color: #7ccd9a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-block {
|
||||||
|
background: #1a2a1a;
|
||||||
|
border: 1px solid #2e4a2e;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-block h4 {
|
||||||
|
color: #3cb371;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-links {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-links li {
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-links li:before {
|
||||||
|
content: "► ";
|
||||||
|
color: #2e8b57;
|
||||||
|
font-size: 10px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-links a {
|
||||||
|
color: #5fba7d;
|
||||||
|
font-size: 12px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-links a:hover {
|
||||||
|
color: #7ccd9a;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive design */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
#container {
|
||||||
|
max-width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
#main-wrapper {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-block {
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* On mobile, show partner cards in a more compact grid */
|
||||||
|
.partner-card {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
#container {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-block {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-card {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-icon {
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#current-version {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-link {
|
||||||
|
color: #5fba7d;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-link:hover {
|
||||||
|
color: #7ccd9a;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-badge {
|
||||||
|
display: inline-block;
|
||||||
|
background: linear-gradient(135deg, #2e8b57 0%, #3cb371 100%);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user