From 5fb3c03e4086e8b36becb262649f0569e1153019 Mon Sep 17 00:00:00 2001 From: ngn Date: Fri, 10 Jan 2025 00:16:06 +0300 Subject: [PATCH] finish up translations and setup doc server stuff --- api/Makefile | 2 +- api/config/config.go | 6 +- api/go.mod | 1 - api/go.sum | 2 - api/routes/index.go | 23 +-- api/routes/news.go | 12 +- api/util/res.go | 10 - api/views/news.xml | 4 +- app/package-lock.json | 17 +- app/package.json | 4 +- app/src/lib/api.js | 2 +- app/src/lib/error.svelte | 2 +- app/src/lib/footer.svelte | 14 +- app/src/lib/navbar_switch.svelte | 22 ++- app/src/lib/service.svelte | 8 +- app/src/lib/util.js | 4 +- app/src/locales/en.json | 17 +- app/src/locales/tr.json | 54 +++++- app/src/routes/+page.svelte | 6 +- app/src/routes/services/+page.svelte | 4 +- app/vite.config.js | 13 ++ doc/.clang-format | 225 +++++++++++++++++++++++ doc/.gitignore | 4 + doc/Makefile | 35 ++++ api/views/index.md => doc/docs/en/api.md | 0 doc/inc/config.h | 18 ++ doc/inc/routes.h | 4 + doc/src/config.c | 39 ++++ doc/src/main.c | 25 +++ doc/src/routes/read.c | 118 ++++++++++++ 30 files changed, 591 insertions(+), 104 deletions(-) create mode 100644 doc/.clang-format create mode 100644 doc/.gitignore create mode 100644 doc/Makefile rename api/views/index.md => doc/docs/en/api.md (100%) create mode 100644 doc/inc/config.h create mode 100644 doc/inc/routes.h create mode 100644 doc/src/config.c create mode 100644 doc/src/main.c create mode 100644 doc/src/routes/read.c diff --git a/api/Makefile b/api/Makefile index 5a9f3f2..e115a02 100644 --- a/api/Makefile +++ b/api/Makefile @@ -6,7 +6,7 @@ api.elf: $(GOSRCS) go build -o $@ 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: gofmt -s -w . diff --git a/api/config/config.go b/api/config/config.go index 52270fb..2913a04 100644 --- a/api/config/config.go +++ b/api/config/config.go @@ -38,10 +38,10 @@ func (c *Type) Load() (err error) { // default options c.Options = []Option{ {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: "frontend_url", Value: "http://localhost:5173/", Type: OPTION_TYPE_URL, Required: true}, // frontend application URL for the website + {Name: "url", Value: "http://localhost:7001/", Type: OPTION_TYPE_URL, Required: true}, // API 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: "host", Value: "0.0.0.0:7001", Type: OPTION_TYPE_STR, Required: true}, // host the server should listen on diff --git a/api/go.mod b/api/go.mod index 3d596b5..0e9af9d 100644 --- a/api/go.mod +++ b/api/go.mod @@ -5,7 +5,6 @@ go 1.21.3 require ( github.com/gofiber/fiber/v2 v2.52.5 github.com/mattn/go-sqlite3 v1.14.24 - github.com/russross/blackfriday/v2 v2.1.0 ) require ( diff --git a/api/go.sum b/api/go.sum index d1f72e1..2885b92 100644 --- a/api/go.sum +++ b/api/go.sum @@ -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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 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/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= diff --git a/api/routes/index.go b/api/routes/index.go index 970b2f2..786070c 100644 --- a/api/routes/index.go +++ b/api/routes/index.go @@ -3,30 +3,11 @@ package routes import ( "github.com/gofiber/fiber/v2" "github.com/ngn13/website/api/config" - "github.com/ngn13/website/api/util" ) func GET_Index(c *fiber.Ctx) error { - var ( - md []byte - err error - ) - conf := c.Locals("config").(*config.Type) + doc := conf.GetURL("doc_url") - if !conf.GetBool("index") { - 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) + return c.Redirect(doc.JoinPath("/api").String()) } diff --git a/api/routes/news.go b/api/routes/news.go index e58e32b..1e8cdca 100644 --- a/api/routes/news.go +++ b/api/routes/news.go @@ -40,7 +40,7 @@ func GET_News(c *fiber.Ctx) error { db := c.Locals("database").(*database.Type) conf := c.Locals("config").(*config.Type) - frontend := conf.GetURL("frontend_url") + app := conf.GetURL("app_url") lang := c.Params("lang") 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{ - "frontend": frontend, - "updated": time.Now().Format(time.RFC3339), - "entries": entries, - "lang": lang, + "updated": time.Now().Format(time.RFC3339), + "entries": entries, + "lang": lang, + "app": app, }); err != nil { return util.ErrInternal(c, err) } + c.Set("Content-Disposition", "attachment; filename=\"news.atom\"") c.Set("Content-Type", "application/atom+xml; charset=utf-8") + return c.Send(feed) } diff --git a/api/util/res.go b/api/util/res.go index 2166cf0..d9c63dd 100644 --- a/api/util/res.go +++ b/api/util/res.go @@ -6,7 +6,6 @@ import ( "github.com/gofiber/fiber/v2" "github.com/ngn13/website/api/config" - "github.com/russross/blackfriday/v2" ) func IP(c *fiber.Ctx) string { @@ -20,15 +19,6 @@ func IP(c *fiber.Ctx) string { 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 { if data == nil { data = fiber.Map{} diff --git a/api/views/news.xml b/api/views/news.xml index 251fa1f..a5a0a28 100644 --- a/api/views/news.xml +++ b/api/views/news.xml @@ -1,8 +1,8 @@ - {{.frontend.Host}} news + {{.app.Host}} news {{.updated}} News and updates about my projects and self-hosted services - + {{ range .entries }} {{.Title}} diff --git a/app/package-lock.json b/app/package-lock.json index 6f4b5f3..e9f213f 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,16 +1,14 @@ { "name": "website", - "version": "5.0.0", + "version": "6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "website", - "version": "5.0.0", + "version": "6.0", "dependencies": { "@types/dompurify": "^3.2.0", - "dompurify": "^3.2.3", - "marked": "^15.0.4", "svelte-i18n": "^4.0.1" }, "devDependencies": { @@ -1398,17 +1396,6 @@ "@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": { "version": "0.4.17", "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", diff --git a/app/package.json b/app/package.json index 75a6a10..1d47b98 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "version": "6.0", "private": true, "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", "preview": "vite preview --host", "lint": "prettier --check .", @@ -22,8 +22,6 @@ "type": "module", "dependencies": { "@types/dompurify": "^3.2.0", - "dompurify": "^3.2.3", - "marked": "^15.0.4", "svelte-i18n": "^4.0.1" } } diff --git a/app/src/lib/api.js b/app/src/lib/api.js index 2b9797a..2e24f92 100644 --- a/app/src/lib/api.js +++ b/app/src/lib/api.js @@ -1,7 +1,7 @@ import { urljoin } from "$lib/util.js"; 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 = {}) { return urljoin(url, path, query); diff --git a/app/src/lib/error.svelte b/app/src/lib/error.svelte index 64ffef5..7bea385 100644 --- a/app/src/lib/error.svelte +++ b/app/src/lib/error.svelte @@ -15,7 +15,7 @@ {error} {/if} - + {$_("error.report")} diff --git a/app/src/lib/footer.svelte b/app/src/lib/footer.svelte index 128b17a..0655b6b 100644 --- a/app/src/lib/footer.svelte +++ b/app/src/lib/footer.svelte @@ -1,5 +1,5 @@ -
+

{service.name}

@@ -44,7 +41,7 @@ {$_("services.status.up")} {:else if service.check_res == 2} - + {$_("services.status.slow")} {/if} @@ -70,6 +67,7 @@ align-items: center; justify-content: space-between; color: var(--white-1); + flex: 1; } main .info .title h1 { diff --git a/app/src/lib/util.js b/app/src/lib/util.js index 02e770e..c123b21 100644 --- a/app/src/lib/util.js +++ b/app/src/lib/util.js @@ -49,13 +49,13 @@ function urljoin(url, path = null, query = {}) { 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(query[k]); + for (let k in query) url.searchParams.append(k, query[k]); return url.href; } 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() { diff --git a/app/src/locales/en.json b/app/src/locales/en.json index 504a370..3f54a0a 100644 --- a/app/src/locales/en.json +++ b/app/src/locales/en.json @@ -2,7 +2,6 @@ "navbar": { "home": "home", "services": "services", - "news": "news", "donate": "donate" }, "home": { @@ -10,7 +9,7 @@ "welcome": { "title": "about", "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", "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", "privacy": "Accessible from clearnet, TOR and I2P, no region or network blocks", "bullshit": "No CDNs, no cloudflare, no CAPTCHA, no analytics, no bullshit", - "link": "Check them out!" + "link": "See all the services!" }, "projects": { "title": "projects", @@ -54,11 +53,11 @@ } }, "donate": { - "title": "donate money!", - "info": "I spend a lot of time working 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).", - "details": "So even a small donation would be highly appreciated 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!", + "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" @@ -66,7 +65,7 @@ }, "error": { "title": "Something went wrong!", - "report": "report this issue" + "report": "Report this issue" }, "footer": { "source": "Source", diff --git a/app/src/locales/tr.json b/app/src/locales/tr.json index 86baf3d..119e393 100644 --- a/app/src/locales/tr.json +++ b/app/src/locales/tr.json @@ -1,19 +1,21 @@ { "navbar": { "home": "anasayfa", - "news": "haberler", "services": "servisler", - "language": "dil" + "donate": "bağış" }, "home": { + "title": "Merhaba Dünya!", "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", "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": "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", "fix": "salak şeyleri düzeltmek", "ctf": "CTF challenge'ları çözmek", @@ -21,24 +23,56 @@ "wiki": "wikimi genişletmek" }, "links": { - "title": "Eğer benim ile iletişime geçmek istiyorsanız, işte bazı faydalı linkler", - "prefer": "tercihim" + "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" }, - "info": { - "title": "Salak şeyler inşa etmenin yanı sıra, herkes için kullanıma açık özgür ve ücretsiz servisler host ediyorum", + "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 saygı gösteriyor", "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" + "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": { "source": "Kaynak", "license": "Lisans", "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", "congrat": "tebrikler!!", "version": "Kullan API versiyonu {api_version}, arayüz versiyonu {frontend_version}" diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte index 9f2a6a9..452303b 100644 --- a/app/src/routes/+page.svelte +++ b/app/src/routes/+page.svelte @@ -54,7 +54,7 @@ - {$_("home.services.desc")} + {$_("home.services.desc")}:
  • @@ -81,7 +81,9 @@ {$_("home.projects.desc")}:
      - {#each projects as project} + {#each projects.filter((p) => { + return p.desc[$language] !== "" && p.desc[$language] !== null && p.desc[$language] !== undefined; + }) as project}
    • {project.name}: {project.desc[$language]} diff --git a/app/src/routes/services/+page.svelte b/app/src/routes/services/+page.svelte index 5f317eb..2b7f94b 100644 --- a/app/src/routes/services/+page.svelte +++ b/app/src/routes/services/+page.svelte @@ -38,7 +38,9 @@
- {#each services as service} + {#each services.filter((s) => { + return s.desc[$language] !== "" && s.desc[$language] !== null && s.desc[$language] !== undefined; + }) as service} {/each}
diff --git a/app/vite.config.js b/app/vite.config.js index 3467166..de31221 100644 --- a/app/vite.config.js +++ b/app/vite.config.js @@ -3,12 +3,25 @@ import { defineConfig } from "vite"; import { fileURLToPath } from "url"; 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 json = readFileSync(file, "utf8"); 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({ plugins: [sveltekit()], + envPrefix: "APP", define: { pkg: pkg, }, diff --git a/doc/.clang-format b/doc/.clang-format new file mode 100644 index 0000000..3169c4d --- /dev/null +++ b/doc/.clang-format @@ -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 +... + diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..18f0094 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,4 @@ +compile_commands.json +.cache +*.elf +dist diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..d0a0000 --- /dev/null +++ b/doc/Makefile @@ -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 diff --git a/api/views/index.md b/doc/docs/en/api.md similarity index 100% rename from api/views/index.md rename to doc/docs/en/api.md diff --git a/doc/inc/config.h b/doc/inc/config.h new file mode 100644 index 0000000..e8705e1 --- /dev/null +++ b/doc/inc/config.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +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); diff --git a/doc/inc/routes.h b/doc/inc/routes.h new file mode 100644 index 0000000..c99399c --- /dev/null +++ b/doc/inc/routes.h @@ -0,0 +1,4 @@ +#pragma once +#include + +void GET_read(req_t *req, res_t *res); diff --git a/doc/src/config.c b/doc/src/config.c new file mode 100644 index 0000000..eecf82d --- /dev/null +++ b/doc/src/config.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#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; +} diff --git a/doc/src/main.c b/doc/src/main.c new file mode 100644 index 0000000..371a2cf --- /dev/null +++ b/doc/src/main.c @@ -0,0 +1,25 @@ +#include +#include + +#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; +} diff --git a/doc/src/routes/read.c b/doc/src/routes/read.c new file mode 100644 index 0000000..a044dc6 --- /dev/null +++ b/doc/src/routes/read.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; +}