fix service info, update images, add meta tags

Signed-off-by: ngn <ngn@ngn.tf>
This commit is contained in:
ngn 2025-04-05 19:39:10 +03:00
parent fe6b3512fd
commit 037a4405b9
Signed by: ngn
GPG Key ID: A3654DF5AD9F641D
22 changed files with 110 additions and 182 deletions

View File

@ -14,44 +14,34 @@
</script>
<footer style="border-top: solid 2px var(--{color()});">
<div class="info">
<div class="links">
<span>
<Link link={import.meta.env.WEBSITE_SOURCE_URL} bold={true}>{$_("footer.source")}</Link>
</span>
<span>/</span>
<span>
<Link link={urljoin(import.meta.env.WEBSITE_APP_URL, "doc/license")} bold={true}
>{$_("footer.license")}</Link
>
</span>
<span>/</span>
<span>
<Link link={urljoin(import.meta.env.WEBSITE_APP_URL, "doc/privacy")} bold={true}
>{$_("footer.privacy")}</Link
>
</span>
</div>
<div class="links">
<span>
{$_("footer.powered")}
</span>
</div>
<div class="useless">
<span>
{$_("footer.number", {
values: {
total: data.total,
since: date_from_ts(data.since),
},
})}
{#if data.number % 1000 == 0}
<span style="color: var(--{color()})">({$_("footer.wow")})</span>
{/if}
</span>
<span>
{$_("footer.version", { values: { api_version: "v1", frontend_version: pkg.version } })}
<Link link={import.meta.env.WEBSITE_SOURCE_URL} bold={true}>{$_("footer.source")}</Link>
</span>
<span>/</span>
<span>
<Link link={urljoin(import.meta.env.WEBSITE_APP_URL, "doc/license")} bold={true}
>{$_("footer.license")}</Link
>
</span>
<span>/</span>
<span>
<Link link={urljoin(import.meta.env.WEBSITE_APP_URL, "doc/privacy")} bold={true}
>{$_("footer.privacy")}</Link
>
</span>
</div>
<span class="counter">
{$_("footer.number", {
values: {
total: data.total,
since: date_from_ts(data.since),
},
})}
{#if data.number % 1000 == 0}
<span style="color: var(--{color()})">({$_("footer.wow")})</span>
{/if}
</span>
</footer>
<style>
@ -61,27 +51,29 @@
justify-content: space-between;
align-items: center;
background: var(--black-1);
box-sizing: border-box;
padding: 20px 50px 20px 50px;
}
div {
display: flex;
color: var(--white-2);
font-size: var(--size-2);
flex-direction: column;
gap: 5px;
}
.useless {
margin: 25px 50px 25px 0;
span {
color: var(--white-2);
font-size: 15px;
}
.counter {
text-align: right;
}
.info {
margin: 25px 0 25px 50px;
.links {
text-align: left;
}
.info .links {
display: flex;
flex-direction: row;
gap: 5px;

View File

@ -8,15 +8,20 @@
<svelte:head>
<title>[ngn.tf] | {title}</title>
<meta content="[ngn.tf] | {title}" property="og:title" />
<meta content={desc} property="og:description" />
<meta content={app_url()} property="og:url" />
<meta content="#000000" data-react-helmet="true" name="theme-color" />
<meta name="description" content={desc} />
<meta name="author" content="ngn" />
<meta name="keywords" content="ngn,ngn13,ngn1,ngn.tf" />
<meta name="color-scheme" content="only dark" />
<meta name="theme-color" content="#000000" />
<meta property="og:title" content="[ngn.tf] | {title}" />
<meta property="og:description" content={desc} />
<meta property="og:url" content={app_url()} />
<link
rel="alternate"
type="application/atom+xml"
href={api_urljoin("/news/en")}
title="Atom Feed"
title="Service news and updates"
/>
</svelte:head>

View File

@ -1,14 +1,35 @@
<script>
import { browser } from "$app/environment";
import { color } from "$lib/util.js";
import { _ } from "svelte-i18n";
export let picture = "";
export let title = "";
let title_cur = "";
function animate(title) {
if (!browser) return;
let id = window.setTimeout(function () {}, 0);
while (id--) clearTimeout(id);
title_cur = "";
for (let i = 0; i < title.length; i++) {
setTimeout(() => {
title_cur += title[i];
}, i * 70);
}
}
$: animate(title);
</script>
<header>
<div>
<h1 class="title" style="color: var(--{color()})">{title.toLowerCase()}</h1>
<h1 class="title" style="color: var(--{color()})">{title_cur}</h1>
<h1 class="cursor" style="color: var(--{color()})">_</h1>
</div>
<img src="/profile/{picture}.png" alt="" />
@ -50,9 +71,6 @@
header div .title {
text-shadow: var(--text-shadow);
overflow: hidden;
width: 0;
animation: typing 1s steps(20, end) forwards;
animation-delay: 0.3s;
}
header div .cursor {

View File

@ -5,7 +5,7 @@
"donate": "donate"
},
"home": {
"title": "Hello world!",
"title": "hello world!",
"welcome": {
"title": "about",
"desc": "Welcome to my website, I'm ngn",
@ -29,9 +29,9 @@
},
"services": {
"title": "services",
"desc": "A part from working on stupid shit, I host free (as in freedom, and price) services available for all",
"speed": "All of these services are available over a 600 Mbit/s interface",
"security": "All use SSL encrypted connection and they are all privacy-respecting",
"desc": "A part from working on stupid shit, I host free (as in freedom and price) services available for all",
"speed": "All of these services are available over an 1 Gbit interface",
"security": "All use SSL encrypted connection and they respect your privacy and freedom",
"privacy": "Accessible from clearnet, TOR and I2P, no region or network blocks",
"bullshit": "No CDNs, no cloudflare, no CAPTCHA, no analytics, no bullshit",
"link": "See all the services!"
@ -42,7 +42,7 @@
}
},
"services": {
"title": "Service Status",
"title": "service status",
"none": "No services found",
"search": "Search for a service",
"feed": "News and updates",
@ -54,7 +54,7 @@
}
},
"donate": {
"title": "Donate Money!",
"title": "donate!",
"info": "I spend a lot of time and money on different projects and maintaining different services.",
"price": "I mostly pay for hosting and electricity. Which when added up costs around 550₺ per month (~$15 at the time of writing).",
"details": "So even a small donation would be useful. And it would help me keep everything up and running.",
@ -64,20 +64,15 @@
"address": "Adress/Link"
}
},
"doc": {
"title": "Documentation"
},
"error": {
"title": "Something went wrong!",
"title": "something went wrong!",
"report": "Report this issue"
},
"footer": {
"source": "Source",
"license": "License",
"privacy": "Privacy",
"powered": "Powered by Svelte, Go, SQLite and donations",
"number": "Visited {total} times since {since}",
"wow": "wow!!",
"version": "Using API version {api_version}, frontend version {frontend_version}"
"wow": "wow!!"
}
}

View File

@ -5,7 +5,7 @@
"donate": "bağış"
},
"home": {
"title": "Merhaba Dünya!",
"title": "merhaba dünya!",
"welcome": {
"title": "hakkımda",
"desc": "Websiteme hoşgeldiniz, ben ngn",
@ -30,11 +30,10 @@
"services": {
"title": "servisler",
"desc": "Salak şeyler inşa etmenin yanı sıra, herkes için kullanıma açık özgür ve ücretsiz servisler host ediyorum",
"speed": "Tüm servisler 600 Mbit/s ağ arayüzü üzerinden erişilebilir",
"security": "Hepsi SSL şifreli bağlantı kullanıyor ve hepsi gizliğinize önem veriyor",
"privacy": "Accessible from clearnet, TOR and I2P, no region or network blocks",
"speed": "Tüm servisler 1 Gbit ağ arayüzü üzerinden erişilebilir",
"security": "Hepsi SSL şifreli bağlantı kullanıyor ve hepsi gizliliğinize ve özgürlüğünüze saygı gösteriyor",
"privacy": "Açık ağdan, TOR ve I2P'den erişilebilirler, bölge ya da ağ blokları yok",
"bullshit": "CDN yok, cloudflare yok, CAPTCHA yok, analitikler yok, boktan saçmalıklar yok",
"bullshit": "CDN yok, cloudflare yok, CAPTCHA yok, analitikler ve diğer saçmalıklar yok",
"link": "Tüm servisleri incele!"
},
"projects": {
@ -43,7 +42,7 @@
}
},
"services": {
"title": "Servis Durumu",
"title": "servis durumu",
"none": "Servis bulunamadı",
"search": "Bir servisi ara",
"feed": "Yenilikler ve güncellemeler",
@ -55,7 +54,7 @@
}
},
"donate": {
"title": "Para Bağışla!",
"title": "bağış yap!",
"info": "Farklı projeler ve farklı servisleri yönetmek için oldukça zaman ve para harcıyorum.",
"price": "Çoğunlukla hosting ve elektrik için ödeme yapıyorum. Bunlar eklendiği zaman aylık 550₺ civarı bir miktar oluyor (yazdığım sırada ~15$).",
"details": "Bu sebepten küçük bir bağış bile oldukça faydalı olacaktır. Ve herşeyi açık ve çalışmakta tutmama yardımcı olacaktır.",
@ -65,20 +64,15 @@
"address": "Adres/Bağlantı"
}
},
"doc": {
"title": "Dökümantasyon"
},
"error": {
"title": "Birşeyler yanlış gitti!",
"title": "birşeyler yanlış gitti!",
"report": "Bu sorunu raporlayın"
},
"footer": {
"source": "Kaynak",
"license": "Lisans",
"privacy": "Gizlilik",
"powered": "Svelte, Go, SQLite ve bağışlar tarafından destekleniyor",
"number": "{since} tarihinden beri {total} kez ziyaret edildi",
"wow": "vay be!!",
"version": "Kullan API versiyonu {api_version}, arayüz versiyonu {frontend_version}"
"wow": "vay be!!"
}
}

View File

@ -1,9 +1,8 @@
import { doc_get_list, doc_get } from "$lib/doc";
import { doc_get } from "$lib/doc";
export async function load({ fetch, params }) {
try {
return {
docs: await doc_get_list(fetch),
doc: await doc_get(fetch, params.name),
error: "",
};

View File

@ -22,7 +22,7 @@
</script>
<Head title="documentation" desc="website and API documentation" />
<Header picture="reader" title={$_("doc.title")} />
<Header picture="reader" title={data.doc[$locale].title} />
{#if data.error.length !== 0}
{#if !data.error.includes("not found")}
@ -30,26 +30,9 @@
{/if}
{:else}
<main>
{#if data.doc !== undefined}
<div class="markdown-body" style="--link-color: var(--{color()})">
{@html marked.parse(data.doc[$locale].content)}
</div>
<div class="docs">
{#each data.docs[$locale] as doc}
{#if doc.title == data.doc[$locale].title}
<a href="/doc/{doc.name}" style="border-color: var(--{color()})">
<h1>{doc.title}</h1>
<h3>{doc.desc}</h3>
</a>
{:else}
<a href="/doc/{doc.name}" style="border-color: var(--white-3)">
<h1>{doc.title}</h1>
<h3>{doc.desc}</h3>
</a>
{/if}
{/each}
</div>
{/if}
<div class="markdown-body" style="--link-color: var(--{color()})">
{@html marked.parse(data.doc[$locale].content)}
</div>
</main>
{/if}
@ -58,66 +41,10 @@
main {
padding: 50px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: start;
gap: 30px;
}
main .docs {
display: flex;
flex-direction: column;
align-items: end;
gap: 6px;
}
main .docs a {
display: flex;
flex-direction: column;
background: var(--black-3);
text-decoration: none;
box-sizing: border-box;
border-right-style: solid;
padding: 15px;
width: 100%;
gap: 4px;
}
main .docs a:hover {
box-shadow: var(--box-shadow-2);
}
main .docs a h1 {
font-size: var(--size-3);
color: var(--white-1);
font-weight: 900;
}
main .docs a h3 {
font-size: var(--size-2);
color: var(--white-3);
font-weight: 100;
text-decoration: none;
}
main .markdown-body :global(a) {
color: var(--link-color);
}
@media only screen and (max-width: 900px) {
main {
flex-direction: column-reverse;
}
main .docs {
width: 100%;
}
main .docs a {
border-right-style: none;
border-left-style: solid;
width: 100%;
}
}
</style>

View File

@ -9,13 +9,3 @@
border-color: transparent;
}
}
@keyframes typing {
from {
width: 0%;
}
to {
width: 100%;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 22 KiB

2
app/static/robots.txt Normal file
View File

@ -0,0 +1,2 @@
User-Agent: *
Disallow: /doc/

View File

@ -1,4 +1,4 @@
{
"title": "API documentation",
"title": "API",
"desc": "Website's API documentation"
}

View File

@ -1,6 +1,7 @@
My website's API stores information about my self-hosted services, it also allows me to
publish news and updates about these services using an Atom feed and it keeps track of
visitor metrics. The API itself is written in Go and uses SQLite for storage.
My website's API, [api.ngn.tf](https://api.ngn.tf), stores information about my
self-hosted services, it also allows me to publish news and updates about these
services using an Atom feed and it keeps track of visitor metrics. The API itself is
written in Go and uses SQLite for storage.
This documentation contains information about all the available API endpoints.

View File

@ -1,4 +1,4 @@
{
"title": "API dökümantasyonu",
"title": "API",
"desc": "Websitesinin API dökümantasyonu"
}

View File

@ -1,6 +1,7 @@
Websitemin API self-host edilen servisler hakkında bilgileri tutuyor, bu servisler hakkında
haberleri ve güncellemeleri bir Atom feed'i aracılığı ile paylaşmama izin veriyor ve ziyartçi
metriklerini takip ediyor. API'ın kendisi Go ile yazıldı ve veritabanı olarak SQLite kullanıyor.
Websitemin API, [api.ngn.tf](https://api.ngn.tf), self-host edilen servisler hakkında bilgileri
tutuyor, bu servisler hakkında haberleri ve güncellemeleri bir Atom feed'i aracılığı ile
paylaşmama izin veriyor ve ziyartçi metriklerini takip ediyor. API'ın kendisi Go ile yazıldı ve
veritabanı olarak SQLite kullanıyor.
Bu dökümentasyon tüm erişeme açık API endpoint'leri hakkında bilgiler içeriyor.

View File

@ -1,4 +1,4 @@
{
"title": "License",
"title": "source license",
"desc": "Source code license"
}

View File

@ -1,4 +1,4 @@
{
"title": "Lisans",
"title": "kaynak lisansı",
"desc": "Kaynak kodu lisansı"
}

View File

@ -1,4 +1,4 @@
{
"title": "Privacy",
"title": "privacy",
"desc": "Learn how I respect your privacy"
}

View File

@ -1,4 +1,4 @@
{
"title": "Gizlilik",
"title": "gizlilik",
"desc": "Gizliliğinize nasıl önem verdiğimi öğrenin"
}

View File

@ -10,6 +10,7 @@
#include "util.h"
option_t options[] = {
// name value requied
{"host", "0.0.0.0:7003", true }, // host the server should listen on
{"docs_dir", "./docs", true }, // documentation directory
{"", NULL, false},

View File

@ -13,7 +13,11 @@ int main() {
if (!config_load(&conf))
return EXIT_FAILURE;
host = config_get(&conf, "host");
if (NULL == (host = config_get(&conf, "host"))) {
ctorm_fail("failed to get the host configuration");
return EXIT_FAILURE;
}
ctorm_config_new(&app_config);
app_config.disable_logging = true;
@ -22,7 +26,6 @@ int main() {
// middlewares
MIDDLEWARE_ALL(app, "/*", route_cors);
MIDDLEWARE_ALL(app, "/*/*", route_cors);
MIDDLEWARE_ALL(app, "/*/*/*", route_cors);
// routes
GET(app, "/list", route_list);