-
-
+
+
+ {$_("services.last", {
+ values: { time: time_from_ts(service.check_time) },
+ })}
+
+ {#if service.check_res == 0}
+
+ {$_("services.status.down")}
+
+ {:else if service.check_res == 1}
+
+ {$_("services.status.up")}
+
+ {:else if service.check_res == 2}
+
+ {$_("services.status.slow")}
+
+ {/if}
diff --git a/app/src/lib/util.js b/app/src/lib/util.js
new file mode 100644
index 0000000..6d5d083
--- /dev/null
+++ b/app/src/lib/util.js
@@ -0,0 +1,71 @@
+import { locale_from_browser } from "$lib/locale.js";
+
+const colors = [
+ "yellow",
+ "cyan",
+ "green",
+ "pinkish",
+ "red",
+ // "blue" (looks kinda ass)
+];
+
+let colors_pos = -1;
+
+function color() {
+ if (colors_pos < 0) colors_pos = Math.floor(Math.random() * colors.length);
+ else if (colors_pos >= colors.length) colors_pos = 0;
+
+ return colors[colors_pos];
+}
+
+function click() {
+ let audio = new Audio("/click.wav");
+ audio.play();
+}
+
+function urljoin(url, path = null, query = {}) {
+ if (undefined === url || null === url) return;
+
+ let url_len = url.length;
+
+ if (url[url_len - 1] != "/") url += "/";
+
+ if (null === path || "" === path) url = new URL(url);
+ else if (path[0] === "/") url = new URL(path.slice(1), url);
+ else url = new URL(path, url);
+
+ for (let k in query) url.searchParams.append(k, query[k]);
+
+ return url.href;
+}
+
+function app_url(path = null, query = {}) {
+ return urljoin(import.meta.env.WEBSITE_APP_URL, path, query);
+}
+
+function time_from_ts(ts) {
+ if (ts === 0 || ts === undefined) return;
+
+ let ts_date = new Date(ts * 1000);
+ let ts_zone = ts_date.toString().match(/([A-Z]+[\+-][0-9]+)/)[1];
+
+ return (
+ new Intl.DateTimeFormat(locale_from_browser(), {
+ hour: "2-digit",
+ minute: "2-digit",
+ second: "2-digit",
+ }).format(ts_date) + ` (${ts_zone})`
+ );
+}
+
+function date_from_ts(ts) {
+ if (ts === 0 || ts === undefined) return;
+
+ return new Intl.DateTimeFormat(locale_from_browser(), {
+ month: "2-digit",
+ year: "2-digit",
+ day: "2-digit",
+ }).format(new Date(ts * 1000));
+}
+
+export { color, click, urljoin, app_url, time_from_ts, date_from_ts };
diff --git a/app/src/locales/en.json b/app/src/locales/en.json
new file mode 100644
index 0000000..e18fd24
--- /dev/null
+++ b/app/src/locales/en.json
@@ -0,0 +1,83 @@
+{
+ "navbar": {
+ "home": "home",
+ "services": "services",
+ "donate": "donate"
+ },
+ "home": {
+ "title": "Hello world!",
+ "welcome": {
+ "title": "about",
+ "desc": "Welcome to my website, I'm ngn",
+ "whoami": "I'm a security, privacy and freedom advocate high-schooler from Turkey",
+ "interest": "I'm interested in system security and software development",
+ "support": "I love and support Free/Libre and Open Source Software (FLOSS)"
+ },
+ "work": {
+ "title": "work",
+ "desc": "I don't currently have a job, so I spend most of my time...",
+ "build": "building stupid shit",
+ "fix": "fixing stupid shit",
+ "ctf": "solving CTF challenges",
+ "contribute": "contributing to random projects",
+ "wiki": "expanding my wiki"
+ },
+ "links": {
+ "title": "contact",
+ "desc": "Here are some useful links if you want to get in contact with me",
+ "prefer": "I highly prefer email, you can send encrypted emails using my PGP key"
+ },
+ "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",
+ "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!"
+ },
+ "projects": {
+ "title": "projects",
+ "desc": "I mostly work on free software projects, here are some of projects that you might find interesting"
+ }
+ },
+ "services": {
+ "title": "Service Status",
+ "none": "No services found",
+ "search": "Search for a service",
+ "feed": "News and updates",
+ "last": "Last checked at {time}",
+ "status": {
+ "up": "Up",
+ "down": "Down",
+ "slow": "Slow"
+ }
+ },
+ "donate": {
+ "title": "Donate Money!",
+ "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.",
+ "thanks": "Also huge thanks to all of you who have donated so far!",
+ "table": {
+ "platform": "Platform",
+ "address": "Adress/Link"
+ }
+ },
+ "doc": {
+ "title": "Documentation"
+ },
+ "error": {
+ "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}"
+ }
+}
diff --git a/app/src/locales/tr.json b/app/src/locales/tr.json
new file mode 100644
index 0000000..0ed217b
--- /dev/null
+++ b/app/src/locales/tr.json
@@ -0,0 +1,84 @@
+{
+ "navbar": {
+ "home": "anasayfa",
+ "services": "servisler",
+ "donate": "bağış"
+ },
+ "home": {
+ "title": "Merhaba Dünya!",
+ "welcome": {
+ "title": "hakkımda",
+ "desc": "Websiteme hoşgeldiniz, ben ngn",
+ "whoami": "Türkiye'den, güvenlik, gizlik ve özgürlük savunucusu bir liseliyim",
+ "interest": "Sistem güvenliği ve yazılım geliştirmek ile ilgileniyorum",
+ "support": "Özgür/Libre ve Açık Kaynaklı Yazılımı (FLOSS) seviyorum ve destekliyorum"
+ },
+ "work": {
+ "title": "iş",
+ "desc": "Şuan bir işim yok, o yüzden zamanımın çoğunu şunlarla geçiriyorum:",
+ "build": "salak şeyler inşa etmek",
+ "fix": "salak şeyleri düzeltmek",
+ "ctf": "CTF challenge'ları çözmek",
+ "contribute": "rastgele projelere katkıda bulunmak",
+ "wiki": "wikimi genişletmek"
+ },
+ "links": {
+ "title": "iletişim",
+ "desc": "Eğer benim ile iletişime geçmek istiyorsanız, işte bazı faydalı linkler",
+ "prefer": "Email'i fazlasıyla tercih ediyorum, PGP anahtarım ile şifreli email'ler gönderebilirsiniz"
+ },
+ "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",
+ "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",
+ "link": "Tüm servisleri incele!"
+ },
+ "projects": {
+ "title": "projeler",
+ "desc": "Çoğunlukla özgür yazılım projeleri üzerinde çalışıyorum, işte ilginç bulabileceğiniz bazı projelerim"
+ }
+ },
+ "services": {
+ "title": "Servis Durumu",
+ "none": "Servis bulunamadı",
+ "search": "Bir servisi ara",
+ "feed": "Yenilikler ve güncellemeler",
+ "last": "Son kontrol zamanı {time}",
+ "status": {
+ "up": "Çalışıyor",
+ "down": "Kapalı",
+ "slow": "Yavaş"
+ }
+ },
+ "donate": {
+ "title": "Para Bağışla!",
+ "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.",
+ "thanks": "Ayrıca şuana kadar bağışta bulunan herkese çok teşekkür ederim!",
+ "table": {
+ "platform": "Platform",
+ "address": "Adres/Bağlantı"
+ }
+ },
+ "doc": {
+ "title": "Dökümantasyon"
+ },
+ "error": {
+ "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}"
+ }
+}
diff --git a/app/src/routes/+error.svelte b/app/src/routes/+error.svelte
index f43feb2..0c20b5a 100644
--- a/app/src/routes/+error.svelte
+++ b/app/src/routes/+error.svelte
@@ -1,8 +1,8 @@
diff --git a/app/src/routes/+layout.js b/app/src/routes/+layout.js
new file mode 100644
index 0000000..423b270
--- /dev/null
+++ b/app/src/routes/+layout.js
@@ -0,0 +1,6 @@
+import { locale_setup, locale_wait } from "$lib/locale.js";
+
+export async function load() {
+ locale_setup();
+ await locale_wait();
+}
diff --git a/app/src/routes/+layout.svelte b/app/src/routes/+layout.svelte
index 4d1a561..718adcc 100644
--- a/app/src/routes/+layout.svelte
+++ b/app/src/routes/+layout.svelte
@@ -1,12 +1,36 @@
+ import Navbar from "$lib/navbar.svelte";
+ import Footer from "$lib/footer.svelte";
+
+ import { locale_select } from "$lib/locale.js";
+ import { onMount } from "svelte";
+
+ let { children } = $props();
+
+ onMount(() => {
+ locale_select();
+ });
+
-
+
+ {@render children()}
+
+
diff --git a/app/src/routes/+page.js b/app/src/routes/+page.js
new file mode 100644
index 0000000..90a2715
--- /dev/null
+++ b/app/src/routes/+page.js
@@ -0,0 +1,15 @@
+import { api_get_projects } from "$lib/api.js";
+
+export async function load({ fetch }) {
+ try {
+ let projects = await api_get_projects(fetch);
+ return {
+ projects: null === projects ? [] : projects,
+ error: "",
+ };
+ } catch (err) {
+ return {
+ error: err.toString(),
+ };
+ }
+}
diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte
index 0637943..d4b13db 100644
--- a/app/src/routes/+page.svelte
+++ b/app/src/routes/+page.svelte
@@ -1,172 +1,121 @@
-
- [ngn.tf] | homepage
-
-
-
-
-
-
-
+
+
+{/if}
diff --git a/app/src/routes/blog/+page.js b/app/src/routes/blog/+page.js
deleted file mode 100644
index cf1f72c..0000000
--- a/app/src/routes/blog/+page.js
+++ /dev/null
@@ -1,9 +0,0 @@
-export async function load({ fetch }) {
- const api = import.meta.env.VITE_API_URL_DEV
- const res = await fetch(api+"/blog/sum")
- const data = await res.json()
-
- return {
- posts: data["result"]
- }
-}
diff --git a/app/src/routes/blog/+page.svelte b/app/src/routes/blog/+page.svelte
deleted file mode 100644
index 7851e9f..0000000
--- a/app/src/routes/blog/+page.svelte
+++ /dev/null
@@ -1,105 +0,0 @@
-
-
-
-
-
diff --git a/app/src/routes/blog/[id]/+page.server.js b/app/src/routes/blog/[id]/+page.server.js
deleted file mode 100644
index 7592e51..0000000
--- a/app/src/routes/blog/[id]/+page.server.js
+++ /dev/null
@@ -1,14 +0,0 @@
-export async function load({ fetch, params }) {
- const id = params.id
- const api = import.meta.env.VITE_API_URL_DEV
- const res = await fetch(api+"/blog/get?id="+id)
- const data = await res.json()
-
- if (data["error"] != "") {
- return {
- error: data["error"]
- }
- }
-
- return data["result"]
-}
diff --git a/app/src/routes/blog/[id]/+page.svelte b/app/src/routes/blog/[id]/+page.svelte
deleted file mode 100644
index d05b41c..0000000
--- a/app/src/routes/blog/[id]/+page.svelte
+++ /dev/null
@@ -1,191 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/routes/doc/[name]/+page.server.js b/app/src/routes/doc/[name]/+page.server.js
new file mode 100644
index 0000000..a414da7
--- /dev/null
+++ b/app/src/routes/doc/[name]/+page.server.js
@@ -0,0 +1,13 @@
+import { doc_get_list, 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: "",
+ };
+ } catch (err) {
+ return { error: err.toString() };
+ }
+}
diff --git a/app/src/routes/doc/[name]/+page.svelte b/app/src/routes/doc/[name]/+page.svelte
new file mode 100644
index 0000000..11448d4
--- /dev/null
+++ b/app/src/routes/doc/[name]/+page.svelte
@@ -0,0 +1,123 @@
+
+
+