@ -1,4 +1,4 @@
|
||||
node_modules
|
||||
.svelte-kit
|
||||
build
|
||||
public
|
||||
build
|
||||
|
@ -1,22 +1,39 @@
|
||||
FROM node:23.5.0 as build
|
||||
# build the application with node
|
||||
FROM node:23.5.0 AS build
|
||||
|
||||
ARG WEBSITE_REPORT_URL
|
||||
ARG WEBSITE_SOURCE_URL
|
||||
ARG WEBSITE_APP_URL
|
||||
ARG WEBSITE_API_URL
|
||||
ARG WEBSITE_DOC_URL
|
||||
|
||||
ENV WEBSITE_REPORT_URL=$WEBSITE_REPORT_URL
|
||||
ENV WEBSITE_SOURCE_URL=$WEBSITE_SOURCE_URL
|
||||
ENV WEBSITE_APP_URL=$WEBSITE_APP_URL
|
||||
ENV WEBSITE_API_URL=$WEBSITE_API_URL
|
||||
ENV WEBSITE_DOC_URL=$WEBSITE_DOC_URL
|
||||
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
|
||||
ARG API_URL
|
||||
ENV VITE_API_URL_DEV $API_URL
|
||||
|
||||
RUN npm install && npm run build
|
||||
|
||||
FROM oven/bun:1.1.20 as main
|
||||
# run it with bun (a lot faster)
|
||||
FROM oven/bun:latest AS main
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=build /app/build ./build
|
||||
COPY --from=build /app/package.json ./package.json
|
||||
COPY --from=build /app/build ./build
|
||||
COPY --from=build /app/package.json ./package.json
|
||||
COPY --from=build /app/package-lock.json ./package-lock.json
|
||||
|
||||
EXPOSE 7001
|
||||
RUN useradd runner -r -u 1001 -d /app
|
||||
RUN chown -R runner:runner /app
|
||||
|
||||
USER runner
|
||||
RUN bun install
|
||||
|
||||
EXPOSE 7001
|
||||
|
||||
ENV PORT=7001
|
||||
CMD ["bun", "build/index.js"]
|
||||
|
@ -3,9 +3,9 @@
|
||||
"version": "6.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --port 7002 dev",
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview --host",
|
||||
"preview": "vite preview",
|
||||
"lint": "prettier --check .",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
@ -21,7 +21,6 @@
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@types/dompurify": "^3.2.0",
|
||||
"dompurify": "^3.2.3",
|
||||
"marked": "^15.0.6",
|
||||
"svelte-i18n": "^4.0.1"
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { urljoin } from "$lib/util.js";
|
||||
|
||||
const api_version = "v1";
|
||||
const api_url = urljoin(import.meta.env.APP_API_URL, api_version);
|
||||
const api_url = urljoin(import.meta.env.WEBSITE_API_URL, api_version);
|
||||
|
||||
function api_urljoin(path = null, query = {}) {
|
||||
return urljoin(api_url, path, query);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { urljoin } from "$lib/util.js";
|
||||
|
||||
function doc_urljoin(path = null, query = {}) {
|
||||
return urljoin(import.meta.env.APP_DOC_URL, path, query);
|
||||
return urljoin(import.meta.env.WEBSITE_DOC_URL, path, query);
|
||||
}
|
||||
|
||||
function doc_check_err(json) {
|
||||
|
@ -15,7 +15,7 @@
|
||||
{error}
|
||||
{/if}
|
||||
</code>
|
||||
<Link link={import.meta.env.APP_REPORT_URL}>
|
||||
<Link link={import.meta.env.WEBSITE_REPORT_URL}>
|
||||
{$_("error.report")}
|
||||
</Link>
|
||||
<img src="/profile/sad.png" alt="" />
|
||||
|
@ -17,17 +17,17 @@
|
||||
<div class="info">
|
||||
<div class="links">
|
||||
<span>
|
||||
<Link link={import.meta.env.APP_SOURCE_URL} bold={true}>{$_("footer.source")}</Link>
|
||||
<Link link={import.meta.env.WEBSITE_SOURCE_URL} bold={true}>{$_("footer.source")}</Link>
|
||||
</span>
|
||||
<span>/</span>
|
||||
<span>
|
||||
<Link link={urljoin(import.meta.env.APP_URL, "doc/license")} bold={true}
|
||||
<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.APP_URL, "doc/privacy")} bold={true}
|
||||
<Link link={urljoin(import.meta.env.WEBSITE_APP_URL, "doc/privacy")} bold={true}
|
||||
>{$_("footer.privacy")}</Link
|
||||
>
|
||||
</span>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { frontend_url } from "$lib/util.js";
|
||||
import { api_urljoin } from "$lib/api.js";
|
||||
import { app_url } from "$lib/util.js";
|
||||
|
||||
export let desc, title;
|
||||
</script>
|
||||
@ -10,7 +10,7 @@
|
||||
|
||||
<meta content="[ngn.tf] | {title}" property="og:title" />
|
||||
<meta content={desc} property="og:description" />
|
||||
<meta content={frontend_url()} property="og:url" />
|
||||
<meta content={app_url()} property="og:url" />
|
||||
<meta content="#000000" data-react-helmet="true" name="theme-color" />
|
||||
|
||||
<link
|
||||
|
@ -1,4 +0,0 @@
|
||||
export default [
|
||||
{ code: "en", name: "English", icon: "🇬🇧", path: "../locales/en.json" },
|
||||
{ code: "tr", name: "Turkish", icon: "🇹🇷", path: "../locales/tr.json" },
|
||||
];
|
@ -21,11 +21,11 @@
|
||||
<Icon {icon} />
|
||||
{/if}
|
||||
{#if highlight}
|
||||
<a {style} href={link}>
|
||||
<a data-sveltekit-preload-data {style} href={link}>
|
||||
<slot></slot>
|
||||
</a>
|
||||
{:else}
|
||||
<a {style} class="no-highlight" href={link}>
|
||||
<a data-sveltekit-preload-data {style} class="no-highlight" href={link}>
|
||||
<slot></slot>
|
||||
</a>
|
||||
{/if}
|
||||
|
66
app/src/lib/locale.js
Normal file
66
app/src/lib/locale.js
Normal file
@ -0,0 +1,66 @@
|
||||
import { init, locale, register, waitLocale } from "svelte-i18n";
|
||||
import { browser } from "$app/environment";
|
||||
import { get, writable } from "svelte/store";
|
||||
|
||||
const locale_default = "en";
|
||||
let locale_index = writable(0);
|
||||
let locale_list = [];
|
||||
|
||||
function locale_setup() {
|
||||
// english
|
||||
register("en", () => import("../locales/en.json"));
|
||||
locale_list.push({ code: "en", name: "English", icon: "🇬🇧" });
|
||||
|
||||
// turkish
|
||||
register("tr", () => import("../locales/tr.json"));
|
||||
locale_list.push({ code: "tr", name: "Turkish", icon: "🇹🇷" });
|
||||
|
||||
init({
|
||||
fallbackLocale: locale_default,
|
||||
initialLocale: get(locale),
|
||||
});
|
||||
}
|
||||
|
||||
function locale_from_browser() {
|
||||
if (browser) return window.navigator.language.slice(0, 2).toLowerCase();
|
||||
else return locale_default;
|
||||
}
|
||||
|
||||
function locale_select(l = null) {
|
||||
if (l === null) {
|
||||
if (browser && null !== (l = localStorage.getItem("locale"))) locale_select(l);
|
||||
else locale_select(locale_from_browser());
|
||||
return;
|
||||
}
|
||||
|
||||
l = l.slice(0, 2);
|
||||
|
||||
for (let i = 0; i < locale_list.length; i++) {
|
||||
if (l !== locale_list[i].code) continue;
|
||||
|
||||
if (browser) localStorage.setItem("locale", l);
|
||||
|
||||
locale.set(l);
|
||||
locale_index.set(i);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
locale.set(locale_default);
|
||||
locale_index.set(0);
|
||||
}
|
||||
|
||||
async function locale_wait() {
|
||||
await waitLocale();
|
||||
}
|
||||
|
||||
export {
|
||||
locale,
|
||||
locale_list,
|
||||
locale_index,
|
||||
locale_default,
|
||||
locale_setup,
|
||||
locale_wait,
|
||||
locale_select,
|
||||
locale_from_browser,
|
||||
};
|
@ -1,38 +1,24 @@
|
||||
<script>
|
||||
import { language, set_lang } from "$lib/util.js";
|
||||
import languages from "$lib/lang.js";
|
||||
import { locale_list, locale_select, locale_index } from "$lib/locale.js";
|
||||
|
||||
let icon = null,
|
||||
indx = 0,
|
||||
len = languages.length;
|
||||
let len = locale_list.length;
|
||||
|
||||
function next_indx() {
|
||||
if (indx + 1 >= len) return 0;
|
||||
return indx + 1;
|
||||
}
|
||||
function get_next(indx) {
|
||||
let new_indx = 0;
|
||||
|
||||
function next_lang(inc) {
|
||||
let new_indx = next_indx();
|
||||
if (inc) indx = new_indx;
|
||||
return languages[new_indx];
|
||||
if (indx + 1 >= len) indx = 0;
|
||||
else new_indx = indx + 1;
|
||||
|
||||
return locale_list[new_indx];
|
||||
}
|
||||
|
||||
function next() {
|
||||
set_lang(next_lang(true).code);
|
||||
icon = next_lang(false).icon;
|
||||
}
|
||||
|
||||
for (indx = 0; indx < len; indx++) {
|
||||
if (languages[indx].code == $language) {
|
||||
set_lang(languages[indx].code);
|
||||
icon = next_lang(false).icon;
|
||||
break;
|
||||
}
|
||||
locale_select(get_next($locale_index).code);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={next}>
|
||||
{icon}
|
||||
{get_next($locale_index).icon}
|
||||
</button>
|
||||
|
||||
<style>
|
||||
|
@ -2,8 +2,8 @@
|
||||
import Icon from "$lib/icon.svelte";
|
||||
import Link from "$lib/link.svelte";
|
||||
|
||||
import { color, time_from_ts, language } from "$lib/util.js";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { color, time_from_ts } from "$lib/util.js";
|
||||
import { locale, _ } from "svelte-i18n";
|
||||
|
||||
export let service = {};
|
||||
</script>
|
||||
@ -12,7 +12,7 @@
|
||||
<div class="info">
|
||||
<div class="title">
|
||||
<h1>{service.name}</h1>
|
||||
<p>{service.desc[$language]}</p>
|
||||
<p>{service.desc[$locale]}</p>
|
||||
</div>
|
||||
<div class="links">
|
||||
<Link highlight={false} link={service.clear}><Icon icon="nf-oct-link" /></Link>
|
||||
|
@ -1,9 +1,5 @@
|
||||
import { browser } from "$app/environment";
|
||||
import { locale } from "svelte-i18n";
|
||||
import languages from "$lib/lang.js";
|
||||
import { writable, get } from "svelte/store";
|
||||
import { locale_from_browser } from "$lib/locale.js";
|
||||
|
||||
const default_language = languages[0].code;
|
||||
const colors = [
|
||||
"yellow",
|
||||
"cyan",
|
||||
@ -13,54 +9,8 @@ const colors = [
|
||||
// "blue" (looks kinda ass)
|
||||
];
|
||||
|
||||
let language = writable(default_language);
|
||||
let colors_pos = -1;
|
||||
|
||||
function browser_lang() {
|
||||
if (browser) return window.navigator.language.slice(0, 2).toLowerCase();
|
||||
else return get(language);
|
||||
}
|
||||
|
||||
function set_lang(lang) {
|
||||
language.set(default_language);
|
||||
locale.set(default_language);
|
||||
|
||||
if (lang === null || lang === undefined) {
|
||||
if (browser && null !== (lang = localStorage.getItem("language"))) set_lang(lang);
|
||||
else if (browser) set_lang(browser_lang());
|
||||
return;
|
||||
}
|
||||
|
||||
lang = lang.slice(0, 2);
|
||||
|
||||
for (let i = 0; i < languages.length; i++) {
|
||||
if (lang !== languages[i].code) continue;
|
||||
|
||||
language.set(lang);
|
||||
locale.set(lang);
|
||||
|
||||
if (browser) localStorage.setItem("language", lang);
|
||||
}
|
||||
}
|
||||
|
||||
function urljoin(url, path = null, query = {}) {
|
||||
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 frontend_url(path = null, query = {}) {
|
||||
return urljoin(import.meta.env.APP_URL, path, query);
|
||||
}
|
||||
|
||||
function color() {
|
||||
if (colors_pos < 0) colors_pos = Math.floor(Math.random() * colors.length);
|
||||
else if (colors_pos >= colors.length) colors_pos = 0;
|
||||
@ -73,6 +23,26 @@ function click() {
|
||||
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;
|
||||
|
||||
@ -80,7 +50,7 @@ function time_from_ts(ts) {
|
||||
let ts_zone = ts_date.toString().match(/([A-Z]+[\+-][0-9]+)/)[1];
|
||||
|
||||
return (
|
||||
new Intl.DateTimeFormat(browser_lang(), {
|
||||
new Intl.DateTimeFormat(locale_from_browser(), {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
@ -91,22 +61,11 @@ function time_from_ts(ts) {
|
||||
function date_from_ts(ts) {
|
||||
if (ts === 0 || ts === undefined) return;
|
||||
|
||||
return new Intl.DateTimeFormat(browser_lang(), {
|
||||
return new Intl.DateTimeFormat(locale_from_browser(), {
|
||||
month: "2-digit",
|
||||
year: "2-digit",
|
||||
day: "2-digit",
|
||||
}).format(new Date(ts * 1000));
|
||||
}
|
||||
|
||||
export {
|
||||
default_language,
|
||||
browser_lang,
|
||||
language,
|
||||
set_lang,
|
||||
urljoin,
|
||||
frontend_url,
|
||||
click,
|
||||
color,
|
||||
time_from_ts,
|
||||
date_from_ts,
|
||||
};
|
||||
export { color, click, urljoin, app_url, time_from_ts, date_from_ts };
|
||||
|
@ -1,37 +1,6 @@
|
||||
import { default_language, language, set_lang } from "$lib/util.js";
|
||||
import { api_get_services, api_get_projects } from "$lib/api.js";
|
||||
import { doc_get_list } from "$lib/doc.js";
|
||||
import languages from "$lib/lang.js";
|
||||
import { locale_setup, locale_wait } from "$lib/locale.js";
|
||||
|
||||
import { init, register, waitLocale } from "svelte-i18n";
|
||||
import { get } from "svelte/store";
|
||||
|
||||
// setup the locale
|
||||
for (let i = 0; i < languages.length; i++)
|
||||
register(languages[i].code, () => import(/* @vite-ignore */ languages[i].path));
|
||||
|
||||
// set the language
|
||||
set_lang();
|
||||
|
||||
init({
|
||||
fallbackLocale: default_language,
|
||||
initialLocale: get(language),
|
||||
});
|
||||
|
||||
// load locales & load data from the API
|
||||
export async function load({ fetch }) {
|
||||
await waitLocale();
|
||||
|
||||
try {
|
||||
return {
|
||||
services: await api_get_services(fetch),
|
||||
projects: await api_get_projects(fetch),
|
||||
docs: await doc_get_list(fetch),
|
||||
error: null,
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
error: err.toString(),
|
||||
};
|
||||
}
|
||||
export async function load() {
|
||||
locale_setup();
|
||||
await locale_wait();
|
||||
}
|
||||
|
@ -1,25 +1,27 @@
|
||||
<script>
|
||||
import Navbar from "$lib/navbar.svelte";
|
||||
import Footer from "$lib/footer.svelte";
|
||||
import Error from "$lib/error.svelte";
|
||||
|
||||
let { data, children } = $props();
|
||||
import { locale_select } from "$lib/locale.js";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
onMount(() => {
|
||||
locale_select();
|
||||
});
|
||||
</script>
|
||||
|
||||
<main>
|
||||
{#if data.error === null}
|
||||
<Navbar />
|
||||
<div class="content">
|
||||
{@render children()}
|
||||
</div>
|
||||
<Footer />
|
||||
{:else}
|
||||
<Error error={data.error} />>
|
||||
{/if}
|
||||
<Navbar />
|
||||
<div class="content">
|
||||
{@render children()}
|
||||
</div>
|
||||
<Footer />
|
||||
</main>
|
||||
|
||||
<style>
|
||||
@import "../../static/global.css";
|
||||
@import "/global.css";
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
|
14
app/src/routes/+page.js
Normal file
14
app/src/routes/+page.js
Normal file
@ -0,0 +1,14 @@
|
||||
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,
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
error: err.toString(),
|
||||
};
|
||||
}
|
||||
}
|
@ -1,97 +1,106 @@
|
||||
<script>
|
||||
import Header from "$lib/header.svelte";
|
||||
import Error from "$lib/error.svelte";
|
||||
import Head from "$lib/head.svelte";
|
||||
import Card from "$lib/card.svelte";
|
||||
import Link from "$lib/link.svelte";
|
||||
|
||||
import { color, language } from "$lib/util.js";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { _, locale } from "svelte-i18n";
|
||||
import { color } from "$lib/util.js";
|
||||
|
||||
const { data } = $props();
|
||||
let projects = $state(data.projects);
|
||||
let { data } = $props();
|
||||
</script>
|
||||
|
||||
<Head title="home" desc="home page of my personal website" />
|
||||
<Header picture="tired" title={$_("home.title")} />
|
||||
|
||||
<main>
|
||||
<Card title={$_("home.welcome.title")}>
|
||||
<span> 👋 {$_("home.welcome.desc")}</span>
|
||||
<ul>
|
||||
<li>🇹🇷 {$_("home.welcome.whoami")}</li>
|
||||
<li>🖥️ {$_("home.welcome.interest")}</li>
|
||||
<li>❤️ {$_("home.welcome.support")}</li>
|
||||
</ul>
|
||||
</Card>
|
||||
<Card title={$_("home.work.title")}>
|
||||
<span>{$_("home.work.desc")}</span>
|
||||
<ul>
|
||||
<li>⌨️ {$_("home.work.build")}</li>
|
||||
<li>🤦 {$_("home.work.fix")}</li>
|
||||
<li>🚩 {$_("home.work.ctf")}</li>
|
||||
<li>👥 {$_("home.work.contribute")}</li>
|
||||
<li>📑 {$_("home.work.wiki")}</li>
|
||||
</ul>
|
||||
</Card>
|
||||
<Card title={$_("home.links.title")}>
|
||||
<span>{$_("home.links.desc")}:</span>
|
||||
<ul>
|
||||
<li>
|
||||
<Link icon="nf-fa-key" link="https://keyoxide.org/F9E70878C2FB389AEC2BA34CA3654DF5AD9F641D">
|
||||
PGP
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link icon="nf-md-email" link="mailto:ngn@ngn.tf">Email</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link icon="nf-md-mastodon" link="https://defcon.social/@ngn">Mastodon</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<span>
|
||||
{$_("home.links.prefer")}
|
||||
</span>
|
||||
</Card>
|
||||
<Card title={$_("home.services.title")}>
|
||||
<span>
|
||||
{$_("home.services.desc")}:
|
||||
</span>
|
||||
<ul>
|
||||
<li>
|
||||
<i style="color: var(--{color()});" class="nf nf-md-speedometer_slow"></i>
|
||||
{$_("home.services.speed")}
|
||||
</li>
|
||||
<li>
|
||||
<i style="color: var(--{color()});" class="nf nf-fa-lock"></i>
|
||||
{$_("home.services.security")}
|
||||
</li>
|
||||
<li>
|
||||
<i style="color: var(--{color()});" class="nf nf-fa-network_wired"></i>
|
||||
{$_("home.services.privacy")}
|
||||
</li>
|
||||
<li>
|
||||
<i style="color: var(--{color()});" class="nf nf-md-eye_off"></i>
|
||||
{$_("home.services.bullshit")}
|
||||
</li>
|
||||
</ul>
|
||||
<Link linK="/services">{$_("home.services.link")}</Link>
|
||||
</Card>
|
||||
<Card title={$_("home.projects.title")}>
|
||||
<span>
|
||||
{$_("home.projects.desc")}:
|
||||
</span>
|
||||
<ul>
|
||||
{#each projects.filter((p) => {
|
||||
return p.desc[$language] !== "" && p.desc[$language] !== null && p.desc[$language] !== undefined;
|
||||
}) as project}
|
||||
{#if data.error !== undefined}
|
||||
<Error error={data.error} />
|
||||
{:else}
|
||||
<main>
|
||||
<Card title={$_("home.welcome.title")}>
|
||||
<span> 👋 {$_("home.welcome.desc")}</span>
|
||||
<ul>
|
||||
<li>🇹🇷 {$_("home.welcome.whoami")}</li>
|
||||
<li>🖥️ {$_("home.welcome.interest")}</li>
|
||||
<li>❤️ {$_("home.welcome.support")}</li>
|
||||
</ul>
|
||||
</Card>
|
||||
<Card title={$_("home.work.title")}>
|
||||
<span>{$_("home.work.desc")}</span>
|
||||
<ul>
|
||||
<li>⌨️ {$_("home.work.build")}</li>
|
||||
<li>🤦 {$_("home.work.fix")}</li>
|
||||
<li>🚩 {$_("home.work.ctf")}</li>
|
||||
<li>👥 {$_("home.work.contribute")}</li>
|
||||
<li>📑 {$_("home.work.wiki")}</li>
|
||||
</ul>
|
||||
</Card>
|
||||
<Card title={$_("home.links.title")}>
|
||||
<span>{$_("home.links.desc")}:</span>
|
||||
<ul>
|
||||
<li>
|
||||
<Link active={true} link={project.url}>{project.name}</Link>:
|
||||
{project.desc[$language]}
|
||||
<Link
|
||||
icon="nf-fa-key"
|
||||
link="https://keyoxide.org/F9E70878C2FB389AEC2BA34CA3654DF5AD9F641D"
|
||||
>
|
||||
PGP
|
||||
</Link>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</Card>
|
||||
</main>
|
||||
<li>
|
||||
<Link icon="nf-md-email" link="mailto:ngn@ngn.tf">Email</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link icon="nf-md-mastodon" link="https://defcon.social/@ngn">Mastodon</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<span>
|
||||
{$_("home.links.prefer")}
|
||||
</span>
|
||||
</Card>
|
||||
<Card title={$_("home.services.title")}>
|
||||
<span>
|
||||
{$_("home.services.desc")}:
|
||||
</span>
|
||||
<ul>
|
||||
<li>
|
||||
<i style="color: var(--{color()});" class="nf nf-md-speedometer_slow"></i>
|
||||
{$_("home.services.speed")}
|
||||
</li>
|
||||
<li>
|
||||
<i style="color: var(--{color()});" class="nf nf-fa-lock"></i>
|
||||
{$_("home.services.security")}
|
||||
</li>
|
||||
<li>
|
||||
<i style="color: var(--{color()});" class="nf nf-fa-network_wired"></i>
|
||||
{$_("home.services.privacy")}
|
||||
</li>
|
||||
<li>
|
||||
<i style="color: var(--{color()});" class="nf nf-md-eye_off"></i>
|
||||
{$_("home.services.bullshit")}
|
||||
</li>
|
||||
</ul>
|
||||
<Link link="/services">{$_("home.services.link")}</Link>
|
||||
</Card>
|
||||
<Card title={$_("home.projects.title")}>
|
||||
<span>
|
||||
{$_("home.projects.desc")}:
|
||||
</span>
|
||||
{#if data.error === undefined}
|
||||
<ul>
|
||||
{#each data.projects.filter((p) => {
|
||||
return p.desc[$locale] !== "" && p.desc[$locale] !== null && p.desc[$locale] !== undefined;
|
||||
}) as project}
|
||||
<li>
|
||||
<Link active={true} link={project.url}>{project.name}</Link>:
|
||||
{project.desc[$locale]}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</Card>
|
||||
</main>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
main {
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { doc_get } from "$lib/doc";
|
||||
import { doc_get_list, doc_get } from "$lib/doc";
|
||||
|
||||
export async function load({ fetch, params }) {
|
||||
try {
|
||||
return { doc: await doc_get(fetch, params.name) };
|
||||
return {
|
||||
docs: await doc_get_list(fetch),
|
||||
doc: await doc_get(fetch, params.name),
|
||||
};
|
||||
} catch (err) {
|
||||
return { error: err.toString() };
|
||||
}
|
||||
|
@ -1,20 +1,18 @@
|
||||
<script>
|
||||
import Header from "$lib/header.svelte";
|
||||
import Error from "$lib/error.svelte";
|
||||
import Head from "$lib/head.svelte";
|
||||
|
||||
import { language, color } from "$lib/util.js";
|
||||
import { goto } from "$app/navigation";
|
||||
import { locale, _ } from "svelte-i18n";
|
||||
import { color } from "$lib/util.js";
|
||||
import DOMPurify from "dompurify";
|
||||
import { onMount } from "svelte";
|
||||
import { marked } from "marked";
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
let { data } = $props();
|
||||
marked.use({ breaks: true });
|
||||
|
||||
onMount(async () => {
|
||||
if (data.error !== null) return await goto("/");
|
||||
|
||||
for (let key in data.doc)
|
||||
data.doc[key]["content"] = DOMPurify.sanitize(data.doc[key]["content"]);
|
||||
});
|
||||
@ -23,28 +21,32 @@
|
||||
<Head title="documentation" desc="website and API documentation" />
|
||||
<Header picture="reader" title={$_("doc.title")} />
|
||||
|
||||
<main>
|
||||
{#if data.doc !== undefined}
|
||||
<div class="markdown-body" style="--link-color: var(--{color()})">
|
||||
{@html marked.parse(data.doc[$language].content)}
|
||||
</div>
|
||||
<div class="docs">
|
||||
{#each data.docs[$language] as doc}
|
||||
{#if doc.title == data.doc[$language].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}
|
||||
</main>
|
||||
{#if data.error !== undefined}
|
||||
<Error error={data.error} />
|
||||
{: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}
|
||||
</main>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
@import "/markdown.css";
|
||||
|
14
app/src/routes/services/+page.js
Normal file
14
app/src/routes/services/+page.js
Normal file
@ -0,0 +1,14 @@
|
||||
import { api_get_services } from "$lib/api.js";
|
||||
|
||||
export async function load({ fetch }) {
|
||||
try {
|
||||
let services = await api_get_services(fetch)
|
||||
return {
|
||||
services: null === services ? [] : services,
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
error: err.toString(),
|
||||
};
|
||||
}
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
<script>
|
||||
import Service from "$lib/service.svelte";
|
||||
import Header from "$lib/header.svelte";
|
||||
import Error from "$lib/error.svelte";
|
||||
import Link from "$lib/link.svelte";
|
||||
import Head from "$lib/head.svelte";
|
||||
|
||||
import { api_urljoin } from "$lib/api.js";
|
||||
import { language } from "$lib/util.js";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { locale, _ } from "svelte-i18n";
|
||||
|
||||
let { data } = $props();
|
||||
let services = $state(data.services);
|
||||
@ -22,15 +22,13 @@
|
||||
|
||||
data.services.forEach((s) => {
|
||||
if (s.name.toLowerCase().includes(value)) services.push(s);
|
||||
else if (s.desc[$language].toLowerCase().includes(value)) services.push(s);
|
||||
else if (s.desc[$locale].toLowerCase().includes(value)) services.push(s);
|
||||
});
|
||||
}
|
||||
|
||||
function get_services() {
|
||||
return services.filter((s) => {
|
||||
return (
|
||||
s.desc[$language] !== "" && s.desc[$language] !== null && s.desc[$language] !== undefined
|
||||
);
|
||||
return s.desc[$locale] !== "" && s.desc[$locale] !== null && s.desc[$locale] !== undefined;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@ -38,23 +36,27 @@
|
||||
<Head title="services" desc="my self-hosted services and projects" />
|
||||
<Header picture="cool" title={$_("services.title")} />
|
||||
|
||||
<main>
|
||||
<div class="title">
|
||||
<input oninput={change} type="text" placeholder={$_("services.search")} />
|
||||
<div>
|
||||
<Link icon="nf-fa-feed" link={api_urljoin("/news/" + $language)}>{$_("services.feed")}</Link>
|
||||
{#if data.error !== undefined}
|
||||
<Error error={data.error} />
|
||||
{:else}
|
||||
<main>
|
||||
<div class="title">
|
||||
<input oninput={change} type="text" placeholder={$_("services.search")} />
|
||||
<div>
|
||||
<Link icon="nf-fa-feed" link={api_urljoin("/news/" + $locale)}>{$_("services.feed")}</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="services">
|
||||
{#if get_services().length == 0}
|
||||
<h3 class="none">{$_("services.none")}</h3>
|
||||
{:else}
|
||||
{#each get_services() as service}
|
||||
<Service {service} />
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</main>
|
||||
<div class="services">
|
||||
{#if get_services().length == 0}
|
||||
<h3 class="none">{$_("services.none")}</h3>
|
||||
{:else}
|
||||
{#each get_services() as service}
|
||||
<Service {service} />
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</main>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
main {
|
||||
|
@ -6,8 +6,8 @@ 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",
|
||||
APP_URL: "http://localhost:7001",
|
||||
API_URL: "http://localhost:7002",
|
||||
DOC_URL: "http://localhost:7003",
|
||||
};
|
||||
|
||||
@ -16,12 +16,20 @@ 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];
|
||||
if (process.env["WEBSITE_" + env] === undefined) process.env["WEBSITE_" + env] = default_env[env];
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()],
|
||||
envPrefix: "APP",
|
||||
envPrefix: "WEBSITE",
|
||||
preview: {
|
||||
port: 7001,
|
||||
strictPort: true,
|
||||
},
|
||||
server: {
|
||||
port: 7001,
|
||||
strictPort: true,
|
||||
},
|
||||
define: {
|
||||
pkg: pkg,
|
||||
},
|
||||
|
Reference in New Issue
Block a user