finish up translations and setup doc server stuff

This commit is contained in:
ngn 2025-01-10 00:16:06 +03:00
parent ac307de76c
commit 5fb3c03e40
30 changed files with 591 additions and 104 deletions

View File

@ -6,7 +6,7 @@ api.elf: $(GOSRCS)
go build -o $@ go build -o $@
run: run:
API_DEBUG=true API_FRONTEND_URL=http://localhost:5173/ API_PASSWORD=test ./api.elf API_DEBUG=true API_APP_URL=http://localhost:5173/ API_PASSWORD=test ./api.elf
format: format:
gofmt -s -w . gofmt -s -w .

View File

@ -38,10 +38,10 @@ func (c *Type) Load() (err error) {
// default options // default options
c.Options = []Option{ c.Options = []Option{
{Name: "debug", Value: "false", Type: OPTION_TYPE_BOOL, Required: true}, // should display debug messgaes? {Name: "debug", Value: "false", Type: OPTION_TYPE_BOOL, Required: true}, // should display debug messgaes?
{Name: "index", Value: "true", Type: OPTION_TYPE_BOOL, Required: false}, // should display the index page (view/index.md)?
{Name: "api_url", Value: "http://localhost:7001/", Type: OPTION_TYPE_URL, Required: true}, // API URL for the website {Name: "url", Value: "http://localhost:7001/", Type: OPTION_TYPE_URL, Required: true}, // API URL for the website
{Name: "frontend_url", Value: "http://localhost:5173/", Type: OPTION_TYPE_URL, Required: true}, // frontend application URL for the website {Name: "app_url", Value: "http://localhost:7002/", Type: OPTION_TYPE_URL, Required: true}, // frontend application URL for the website
{Name: "doc_url", Value: "http://localhost:7003/", Type: OPTION_TYPE_URL, Required: true}, // documentation URL for the website
{Name: "password", Value: "", Type: OPTION_TYPE_STR, Required: true}, // admin password {Name: "password", Value: "", Type: OPTION_TYPE_STR, Required: true}, // admin password
{Name: "host", Value: "0.0.0.0:7001", Type: OPTION_TYPE_STR, Required: true}, // host the server should listen on {Name: "host", Value: "0.0.0.0:7001", Type: OPTION_TYPE_STR, Required: true}, // host the server should listen on

View File

@ -5,7 +5,6 @@ go 1.21.3
require ( require (
github.com/gofiber/fiber/v2 v2.52.5 github.com/gofiber/fiber/v2 v2.52.5
github.com/mattn/go-sqlite3 v1.14.24 github.com/mattn/go-sqlite3 v1.14.24
github.com/russross/blackfriday/v2 v2.1.0
) )
require ( require (

View File

@ -17,8 +17,6 @@ github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBW
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=

View File

@ -3,30 +3,11 @@ package routes
import ( import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/ngn13/website/api/config" "github.com/ngn13/website/api/config"
"github.com/ngn13/website/api/util"
) )
func GET_Index(c *fiber.Ctx) error { func GET_Index(c *fiber.Ctx) error {
var (
md []byte
err error
)
conf := c.Locals("config").(*config.Type) conf := c.Locals("config").(*config.Type)
doc := conf.GetURL("doc_url")
if !conf.GetBool("index") { return c.Redirect(doc.JoinPath("/api").String())
return util.ErrNotFound(c)
}
frontend := conf.GetURL("frontend_url")
api := conf.GetURL("api_url")
if md, err = util.Render("views/index.md", fiber.Map{
"frontend": frontend,
"api": api,
}); err != nil {
return util.ErrInternal(c, err)
}
return util.Markdown(c, md)
} }

View File

@ -40,7 +40,7 @@ func GET_News(c *fiber.Ctx) error {
db := c.Locals("database").(*database.Type) db := c.Locals("database").(*database.Type)
conf := c.Locals("config").(*config.Type) conf := c.Locals("config").(*config.Type)
frontend := conf.GetURL("frontend_url") app := conf.GetURL("app_url")
lang := c.Params("lang") lang := c.Params("lang")
if lang == "" || len(lang) != 2 { if lang == "" || len(lang) != 2 {
@ -63,14 +63,16 @@ func GET_News(c *fiber.Ctx) error {
}) })
if feed, err = util.Render("views/news.xml", fiber.Map{ if feed, err = util.Render("views/news.xml", fiber.Map{
"frontend": frontend, "updated": time.Now().Format(time.RFC3339),
"updated": time.Now().Format(time.RFC3339), "entries": entries,
"entries": entries, "lang": lang,
"lang": lang, "app": app,
}); err != nil { }); err != nil {
return util.ErrInternal(c, err) return util.ErrInternal(c, err)
} }
c.Set("Content-Disposition", "attachment; filename=\"news.atom\"")
c.Set("Content-Type", "application/atom+xml; charset=utf-8") c.Set("Content-Type", "application/atom+xml; charset=utf-8")
return c.Send(feed) return c.Send(feed)
} }

View File

@ -6,7 +6,6 @@ import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/ngn13/website/api/config" "github.com/ngn13/website/api/config"
"github.com/russross/blackfriday/v2"
) )
func IP(c *fiber.Ctx) string { func IP(c *fiber.Ctx) string {
@ -20,15 +19,6 @@ func IP(c *fiber.Ctx) string {
return c.IP() return c.IP()
} }
func Markdown(c *fiber.Ctx, raw []byte) error {
exts := blackfriday.FencedCode
exts |= blackfriday.NoEmptyLineBeforeBlock
exts |= blackfriday.HardLineBreak
c.Set("Content-Type", "text/html; charset=utf-8")
return c.Send(blackfriday.Run(raw, blackfriday.WithExtensions(exts)))
}
func JSON(c *fiber.Ctx, code int, data fiber.Map) error { func JSON(c *fiber.Ctx, code int, data fiber.Map) error {
if data == nil { if data == nil {
data = fiber.Map{} data = fiber.Map{}

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom"> <?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">
<title>{{.frontend.Host}} news</title> <title>{{.app.Host}} news</title>
<updated>{{.updated}}</updated> <updated>{{.updated}}</updated>
<subtitle>News and updates about my projects and self-hosted services</subtitle> <subtitle>News and updates about my projects and self-hosted services</subtitle>
<link href="{{.frontend.JoinPath "/news"}}"></link> <link href="{{.app.JoinPath "/news"}}"></link>
{{ range .entries }} {{ range .entries }}
<entry> <entry>
<title>{{.Title}}</title> <title>{{.Title}}</title>

17
app/package-lock.json generated
View File

@ -1,16 +1,14 @@
{ {
"name": "website", "name": "website",
"version": "5.0.0", "version": "6.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "website", "name": "website",
"version": "5.0.0", "version": "6.0",
"dependencies": { "dependencies": {
"@types/dompurify": "^3.2.0", "@types/dompurify": "^3.2.0",
"dompurify": "^3.2.3",
"marked": "^15.0.4",
"svelte-i18n": "^4.0.1" "svelte-i18n": "^4.0.1"
}, },
"devDependencies": { "devDependencies": {
@ -1398,17 +1396,6 @@
"@jridgewell/sourcemap-codec": "^1.5.0" "@jridgewell/sourcemap-codec": "^1.5.0"
} }
}, },
"node_modules/marked": {
"version": "15.0.4",
"resolved": "https://registry.npmjs.org/marked/-/marked-15.0.4.tgz",
"integrity": "sha512-TCHvDqmb3ZJ4PWG7VEGVgtefA5/euFmsIhxtD0XsBxI39gUSKL81mIRFdt0AiNQozUahd4ke98ZdirExd/vSEw==",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/memoizee": { "node_modules/memoizee": {
"version": "0.4.17", "version": "0.4.17",
"resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz",

View File

@ -3,7 +3,7 @@
"version": "6.0", "version": "6.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "VITE_BUG_REPORT_URL=https://github.com/ngn13/website/issues VITE_API_URL=http://127.0.0.1:7001 VITE_FRONTEND_URL=http://localhost:5173 vite dev", "dev": "vite --port 7002 dev",
"build": "vite build", "build": "vite build",
"preview": "vite preview --host", "preview": "vite preview --host",
"lint": "prettier --check .", "lint": "prettier --check .",
@ -22,8 +22,6 @@
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@types/dompurify": "^3.2.0", "@types/dompurify": "^3.2.0",
"dompurify": "^3.2.3",
"marked": "^15.0.4",
"svelte-i18n": "^4.0.1" "svelte-i18n": "^4.0.1"
} }
} }

View File

@ -1,7 +1,7 @@
import { urljoin } from "$lib/util.js"; import { urljoin } from "$lib/util.js";
const version = "v1"; const version = "v1";
const url = urljoin(import.meta.env.VITE_API_URL, version); const url = urljoin(import.meta.env.APP_API_URL, version);
function api_url(path = null, query = {}) { function api_url(path = null, query = {}) {
return urljoin(url, path, query); return urljoin(url, path, query);

View File

@ -15,7 +15,7 @@
{error} {error}
{/if} {/if}
</code> </code>
<Link link={import.meta.env.VITE_BUG_REPORT_URL}> <Link link={import.meta.env.APP_REPORT_URL}>
{$_("error.report")} {$_("error.report")}
</Link> </Link>
<img src="/profile/sad.png" alt="" /> <img src="/profile/sad.png" alt="" />

View File

@ -1,5 +1,5 @@
<script> <script>
import { color, date_from_ts } from "$lib/util.js"; import { urljoin, color, date_from_ts, language } from "$lib/util.js";
import { get_metrics } from "$lib/api.js"; import { get_metrics } from "$lib/api.js";
import Link from "$lib/link.svelte"; import Link from "$lib/link.svelte";
@ -17,15 +17,21 @@
<div class="info"> <div class="info">
<div class="links"> <div class="links">
<span> <span>
<Link href="/" bold={true}>{$_("footer.source")}</Link> <Link link={import.meta.env.APP_SOURCE_URL} bold={true}>{$_("footer.source")}</Link>
</span> </span>
<span>/</span> <span>/</span>
<span> <span>
<Link href="/" bold={true}>{$_("footer.license")}</Link> <Link
link={urljoin(import.meta.env.APP_DOC_URL, "license", { lang: $language })}
bold={true}>{$_("footer.license")}</Link
>
</span> </span>
<span>/</span> <span>/</span>
<span> <span>
<Link href="/" bold={true}>{$_("footer.privacy")}</Link> <Link
link={urljoin(import.meta.env.APP_DOC_URL, "privacy", { lang: $language })}
bold={true}>{$_("footer.privacy")}</Link
>
</span> </span>
</div> </div>
<span> <span>

View File

@ -2,20 +2,30 @@
import { language, set_lang } from "$lib/util.js"; import { language, set_lang } from "$lib/util.js";
import languages from "$lib/lang.js"; import languages from "$lib/lang.js";
let icon = "", let icon = null,
indx = 0, indx = 0,
len = languages.length; len = languages.length;
function next() { function next_indx() {
if (indx >= languages.length) indx = 0; if (indx + 1 >= len) return 0;
return indx + 1;
}
icon = languages[indx].icon; function next_lang(inc) {
set_lang(languages[indx++].code); let new_indx = next_indx();
if (inc) indx = new_indx;
return languages[new_indx];
}
function next() {
set_lang(next_lang(true).code);
icon = next_lang(false).icon;
} }
for (indx = 0; indx < len; indx++) { for (indx = 0; indx < len; indx++) {
if (languages[indx].code == $language) { if (languages[indx].code == $language) {
icon = languages[indx++].icon; set_lang(languages[indx].code);
icon = next_lang(false).icon;
break; break;
} }
} }

View File

@ -6,12 +6,9 @@
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
export let service = {}; export let service = {};
let style = "";
if (service.check_res == 0) style = "opacity: 70%";
</script> </script>
<main {style}> <main>
<div class="info"> <div class="info">
<div class="title"> <div class="title">
<h1>{service.name}</h1> <h1>{service.name}</h1>
@ -44,7 +41,7 @@
{$_("services.status.up")} {$_("services.status.up")}
</span> </span>
{:else if service.check_res == 2} {:else if service.check_res == 2}
<span style="background: var(--white-2)"> <span style="background: var(--{color()}); filter: brightness(50%);">
{$_("services.status.slow")} {$_("services.status.slow")}
</span> </span>
{/if} {/if}
@ -70,6 +67,7 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
color: var(--white-1); color: var(--white-1);
flex: 1;
} }
main .info .title h1 { main .info .title h1 {

View File

@ -49,13 +49,13 @@ function urljoin(url, path = null, query = {}) {
else if (path[0] === "/") url = new URL(path.slice(1), url); else if (path[0] === "/") url = new URL(path.slice(1), url);
else url = new URL(path, url); else url = new URL(path, url);
for (let k in query) url.searchParams.append(query[k]); for (let k in query) url.searchParams.append(k, query[k]);
return url.href; return url.href;
} }
function frontend_url(path = null, query = {}) { function frontend_url(path = null, query = {}) {
return urljoin(import.meta.env.VITE_FRONTEND_URL, path, query); return urljoin(import.meta.env.APP_URL, path, query);
} }
function color() { function color() {

View File

@ -2,7 +2,6 @@
"navbar": { "navbar": {
"home": "home", "home": "home",
"services": "services", "services": "services",
"news": "news",
"donate": "donate" "donate": "donate"
}, },
"home": { "home": {
@ -10,7 +9,7 @@
"welcome": { "welcome": {
"title": "about", "title": "about",
"desc": "Welcome to my website, I'm ngn", "desc": "Welcome to my website, I'm ngn",
"whoami": "I'm a privacy, security and freedom addvocate high-schooler from Turkey", "whoami": "I'm a security, privacy and freedom advocate high-schooler from Turkey",
"interest": "I'm interested in system security and software development", "interest": "I'm interested in system security and software development",
"support": "I love and support Free/Libre and Open Source Software (FLOSS)" "support": "I love and support Free/Libre and Open Source Software (FLOSS)"
}, },
@ -35,7 +34,7 @@
"security": "All use SSL encrypted connection and they are all privacy-respecting", "security": "All use SSL encrypted connection and they are all privacy-respecting",
"privacy": "Accessible from clearnet, TOR and I2P, no region or network blocks", "privacy": "Accessible from clearnet, TOR and I2P, no region or network blocks",
"bullshit": "No CDNs, no cloudflare, no CAPTCHA, no analytics, no bullshit", "bullshit": "No CDNs, no cloudflare, no CAPTCHA, no analytics, no bullshit",
"link": "Check them out!" "link": "See all the services!"
}, },
"projects": { "projects": {
"title": "projects", "title": "projects",
@ -54,11 +53,11 @@
} }
}, },
"donate": { "donate": {
"title": "donate money!", "title": "Donate Money!",
"info": "I spend a lot of time working on different projects and maintaining different services.", "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, that is Turkish Lira, equals to ~$15 at time of writing (ik the economy is great).", "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 highly appreciated and it would help me keep everything up and running.", "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 has donated so far, as I said, I highly appreciate it. Thank you!", "thanks": "Also huge thanks to all of you who have donated so far!",
"table": { "table": {
"platform": "Platform", "platform": "Platform",
"address": "Adress/Link" "address": "Adress/Link"
@ -66,7 +65,7 @@
}, },
"error": { "error": {
"title": "Something went wrong!", "title": "Something went wrong!",
"report": "report this issue" "report": "Report this issue"
}, },
"footer": { "footer": {
"source": "Source", "source": "Source",

View File

@ -1,19 +1,21 @@
{ {
"navbar": { "navbar": {
"home": "anasayfa", "home": "anasayfa",
"news": "haberler",
"services": "servisler", "services": "servisler",
"language": "dil" "donate": "bağış"
}, },
"home": { "home": {
"title": "Merhaba Dünya!",
"welcome": { "welcome": {
"title": "Websiteme hoşgeldiniz, ben ngn", "title": "hakkımda",
"desc": "Websiteme hoşgeldiniz, ben ngn",
"whoami": "Türkiye'den, güvenlik, gizlik ve özgürlük savunucusu bir liseliyim", "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", "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" "support": "Özgür/Libre ve Açık Kaynaklı Yazılımı (FLOSS) seviyorum ve destekliyorum"
}, },
"work": { "work": {
"title": "Zamanım çoğunlukla şunlar ile geçiyor...", "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", "build": "salak şeyler inşa etmek",
"fix": "salak şeyleri düzeltmek", "fix": "salak şeyleri düzeltmek",
"ctf": "CTF challenge'ları çözmek", "ctf": "CTF challenge'ları çözmek",
@ -21,24 +23,56 @@
"wiki": "wikimi genişletmek" "wiki": "wikimi genişletmek"
}, },
"links": { "links": {
"title": "Eğer benim ile iletişime geçmek istiyorsanız, işte bazı faydalı linkler", "title": "iletişim",
"prefer": "tercihim" "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"
}, },
"info": { "services": {
"title": "Salak şeyler inşa etmenin yanı sıra, herkes için kullanıma açık özgür ve ücretsiz servisler host ediyorum", "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", "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 saygı gösteriyor", "security": "Hepsi SSL şifreli bağlantı kullanıyor ve hepsi gizliğinize saygı gösteriyor",
"privacy": "Accessible from clearnet, TOR and I2P, no region or network blocks", "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", "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 yok, boktan saçmalıklar yok",
"link": "tüm servisleri incele" "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",
"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ı"
}
},
"error": {
"title": "Birşeyler yanlış gitti!",
"report": "Bu sorunu raporlayın"
},
"footer": { "footer": {
"source": "Kaynak", "source": "Kaynak",
"license": "Lisans", "license": "Lisans",
"privacy": "Gizlilik", "privacy": "Gizlilik",
"powered": "Svelte, Go, SQLite ve yemek param tarafından destekleniyor", "powered": "Svelte, Go, SQLite ve bağışlar tarafından destekleniyor",
"number": "{since} tarihinden beri {number}. ziyaretçisiniz", "number": "{since} tarihinden beri {number}. ziyaretçisiniz",
"congrat": "tebrikler!!", "congrat": "tebrikler!!",
"version": "Kullan API versiyonu {api_version}, arayüz versiyonu {frontend_version}" "version": "Kullan API versiyonu {api_version}, arayüz versiyonu {frontend_version}"

View File

@ -54,7 +54,7 @@
</Card> </Card>
<Card title={$_("home.services.title")}> <Card title={$_("home.services.title")}>
<span> <span>
{$_("home.services.desc")} {$_("home.services.desc")}:
</span> </span>
<ul> <ul>
<li> <li>
@ -81,7 +81,9 @@
{$_("home.projects.desc")}: {$_("home.projects.desc")}:
</span> </span>
<ul> <ul>
{#each projects as project} {#each projects.filter((p) => {
return p.desc[$language] !== "" && p.desc[$language] !== null && p.desc[$language] !== undefined;
}) as project}
<li> <li>
<Link active={true} link={project.url}>{project.name}</Link>: <Link active={true} link={project.url}>{project.name}</Link>:
{project.desc[$language]} {project.desc[$language]}

View File

@ -38,7 +38,9 @@
</div> </div>
</div> </div>
<div class="services"> <div class="services">
{#each services as service} {#each services.filter((s) => {
return s.desc[$language] !== "" && s.desc[$language] !== null && s.desc[$language] !== undefined;
}) as service}
<Service {service} /> <Service {service} />
{/each} {/each}
</div> </div>

View File

@ -3,12 +3,25 @@ import { defineConfig } from "vite";
import { fileURLToPath } from "url"; import { fileURLToPath } from "url";
import { readFileSync } from "fs"; import { readFileSync } from "fs";
const default_env = {
REPORT_URL: "https://github.com/ngn13/website/issues",
SOURCE_URL: "https://github.com/ngn13/website",
API_URL: "http://localhost:7001",
URL: "http://localhost:7002",
DOC_URL: "http://localhost:7003",
};
const file = fileURLToPath(new URL("package.json", import.meta.url)); const file = fileURLToPath(new URL("package.json", import.meta.url));
const json = readFileSync(file, "utf8"); const json = readFileSync(file, "utf8");
const pkg = JSON.parse(json); const pkg = JSON.parse(json);
for (let env in default_env) {
if (process.env["APP_" + env] === undefined) process.env["APP_" + env] = default_env[env];
}
export default defineConfig({ export default defineConfig({
plugins: [sveltekit()], plugins: [sveltekit()],
envPrefix: "APP",
define: { define: {
pkg: pkg, pkg: pkg,
}, },

225
doc/.clang-format Normal file
View File

@ -0,0 +1,225 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: DontAlign
AlignArrayOfStructures: Left
AlignConsecutiveAssignments:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: false
BinPackParameters: true
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAfterAttributes: Never
BreakAfterJavaFieldAnnotations: false
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Attach
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: BinPack
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
PPIndentWidth: -1
QualifierAlignment: Leave
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: false
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseTab: Never
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
...

4
doc/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
compile_commands.json
.cache
*.elf
dist

35
doc/Makefile Normal file
View File

@ -0,0 +1,35 @@
# dirs
DIRS = $(shell find src/* -type d)
DISTDIR = dist
OUTDIRS = $(patsubst src/%,$(DISTDIR)/%,$(DIRS))
# sources
HSRCS = $(wildcard inc/*.h)
CSRCS = $(shell find -type f -name '*.c')
OBJS = $(patsubst ./src/%.c,./$(DISTDIR)/%.o,$(CSRCS))
# compiler flags
CFLAGS = -O3 -fstack-protector-strong -fcf-protection=full -fstack-clash-protection
LIBS = -lctorm -lcmark
INCLUDE = -I./inc
all: doc.elf
doc.elf: $(OBJS)
echo $(OBJS) $(OUTDIRS)
gcc $(CFLAGS) -o $@ $^ $(LIBS)
$(DISTDIR)/%.o: src/%.c
@mkdir -pv $(OUTDIRS)
gcc $(CFLAGS) $(INCLUDE) -c -o $@ $^ $(LIBS)
format:
clang-format -i -style=file $(CSRCS) $(HSRCS)
clean:
rm -rf $(DISTDIR)
run:
./doc.elf
.PHONY: format clean run

18
doc/inc/config.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
typedef struct option {
const char *name;
char *value;
bool required;
} option_t;
typedef struct config {
option_t *options;
int32_t count;
} config_t;
int32_t config_load(config_t *conf);
char *config_get(config_t *conf, const char *name);

4
doc/inc/routes.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
#include <ctorm/all.h>
void GET_read(req_t *req, res_t *res);

39
doc/src/config.c Normal file
View File

@ -0,0 +1,39 @@
#include <ctorm/log.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "config.h"
option_t options[] = {
{"host", "0.0.0.0:7003", true }, // host the server should listen on
{NULL, NULL, false},
};
int32_t config_load(config_t *conf) {
bzero(conf, sizeof(*conf));
char *value = NULL;
conf->options = options;
for (option_t *opt = conf->options; opt->name != NULL; opt++) {
if ((value = getenv(opt->name)) != NULL)
opt->value = value;
if (opt->required && *opt->value == 0) {
error("please specify a value for the required config option %s", opt->name);
errno = EFAULT;
return -1;
}
conf->count++;
}
return 0;
}
char *config_get(config_t *conf, const char *name) {
for (int32_t i = 0; i < conf->count; i++)
if (strcmp(conf->options[i].name, name) == 0)
return conf->options[i].value;
return NULL;
}

25
doc/src/main.c Normal file
View File

@ -0,0 +1,25 @@
#include <ctorm/all.h>
#include <stdlib.h>
#include "routes.h"
#include "config.h"
int main() {
config_t conf;
app_config_t config;
if (config_load(&conf) < 0)
return EXIT_FAILURE;
app_config_new(&config);
config.disable_logging = true;
app_t *app = app_new(&config);
GET(app, "/read", GET_read);
if (!app_run(app, config_get(&conf, "host")))
error("failed to start the app: %s", app_geterror());
app_free(app);
return EXIT_SUCCESS;
}

118
doc/src/routes/read.c Normal file
View File

@ -0,0 +1,118 @@
#include <linux/limits.h>
#include <ctorm/all.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <cmark.h>
#include <fcntl.h>
#include "routes.h"
void GET_read(req_t *req, res_t *res) {
const char *lang = REQ_QUERY("lang");
const char *name = REQ_QUERY("name");
char fp[PATH_MAX + 1], md[NAME_MAX + 1];
struct dirent *dirent = NULL;
int32_t name_len = 0, ent_len = 0;
DIR *dir = NULL;
bzero(fp, sizeof(fp));
bzero(md, sizeof(md));
if (NULL == name)
return RES_REDIRECT("/");
name_len = strlen(name);
if (NULL == lang)
lang = "en";
if (NULL == (dir = opendir("docs"))) {
error("failed to open the docs dir: %s", strerror(errno));
RES_SENDFILE("html/internal.html");
res->code = 500;
return;
}
while ((dirent = readdir(dir)) != NULL) {
if (strncmp(dirent->d_name, lang, PATH_MAX) == 0)
break;
}
closedir(dir);
if (NULL == dirent) {
RES_SENDFILE("html/notfound.html");
return;
}
snprintf(fp, sizeof(fp), "docs/%s", lang);
if (NULL == (dir = opendir(fp))) {
error("failed to open the language dir: %s", strerror(errno));
RES_SENDFILE("html/internal.html");
res->code = 500;
return;
}
while ((dirent = readdir(dir)) != NULL) {
if ((ent_len = strlen(dirent->d_name) - 3) != name_len)
continue;
if (strncmp(dirent->d_name, name, name_len) == 0) {
memcpy(md, dirent->d_name, ent_len + 3);
break;
}
}
closedir(dir);
if (NULL == dirent) {
RES_SENDFILE("html/notfound.html");
return;
}
char *md_content = NULL, md_fp[PATH_MAX + 1];
struct stat md_st;
int md_fd = 0;
snprintf(md_fp, sizeof(fp), "%s/%s", fp, md);
if ((md_fd = open(md_fp, O_RDONLY)) < 0) {
error("failed to open %s: %s", fp, strerror(errno));
goto err_internal_close;
}
if (fstat(md_fd, &md_st) < 0) {
error("failed to fstat %s: %s", fp, strerror(errno));
goto err_internal_close;
}
if ((md_content = mmap(0, md_st.st_size, PROT_READ, MAP_PRIVATE, md_fd, 0)) == NULL) {
error("failed to mmap %s: %s", fp, strerror(errno));
goto err_internal_close;
}
char *parsed = cmark_markdown_to_html(md_content, md_st.st_size, CMARK_OPT_DEFAULT);
RES_SEND(parsed);
free(parsed);
munmap(md_content, md_st.st_size);
close(md_fd);
return;
err_internal_close:
close(md_fd);
RES_SENDFILE("html/internal.html");
return;
}