make stuff work without js
All checks were successful
Build the docker image for the API / build (push) Successful in 2m7s
Build the docker image for the frontend application / build (push) Successful in 42s

Signed-off-by: ngn <ngn@ngn.tf>
This commit is contained in:
ngn 2025-04-08 02:39:37 +03:00
parent b312e40204
commit ccf0d8abf9
Signed by: ngn
GPG Key ID: A3654DF5AD9F641D
15 changed files with 95 additions and 113 deletions

View File

@ -28,12 +28,8 @@ jobs:
cd app
docker build --build-arg WEBSITE_REPORT_URL=https://git.ngn.tf/ngn/website/issues/new \
--build-arg WEBSITE_SOURCE_URL=https://git.ngn.tf/ngn/website \
--build-arg WEBSITE_APP_URL_CLEAR=https://ngn.tf \
--build-arg WEBSITE_APP_URL_ONION=http://ngntfwmwovvku6eqi7dzzgzv2wzlvq2cqtqha7ccgzub2xnivsuxnuyd.onion \
--build-arg WEBSITE_APP_URL_I2P=http://ngn.i2p \
--build-arg WEBSITE_API_URL_CLEAR=https://api.ngn.tf \
--build-arg WEBSITE_API_URL_ONION=http://api.ngntfwmwovvku6eqi7dzzgzv2wzlvq2cqtqha7ccgzub2xnivsuxnuyd.onion \
--build-arg WEBSITE_API_URL_I2P=https://api.ngn.i2p \
--build-arg WEBSITE_DOC_URL=http://doc:7003 \
--build-arg WEBSITE_API_URL=https://api:7002 \
--build-arg WEBSITE_API_PATH=/api \
--tag ${{env.REGISTRY}}/${{env.IMAGE}}:latest .
docker push ${{env.REGISTRY}}/${{env.IMAGE}}:latest

View File

@ -38,7 +38,7 @@ 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: "app_url_clear", Value: "http://localhost:7001/", Type: OPTION_TYPE_URL, Required: true}, // frontend application URL for the website
{Name: "app_url", Value: "http://localhost:7001/", Type: OPTION_TYPE_URL, Required: true}, // frontend application URL for the website
{Name: "password", Value: "", Type: OPTION_TYPE_STR, Required: true}, // admin password
{Name: "host", Value: "0.0.0.0:7002", Type: OPTION_TYPE_STR, Required: true}, // host the server should listen on
{Name: "ip_header", Value: "X-Real-IP", Type: OPTION_TYPE_STR, Required: false}, // header that should be checked for obtaining the client IP

View File

@ -1,32 +1,17 @@
# build the application with node
FROM node:23.11.0 AS build
# app URLs
ARG WEBSITE_APP_URL_CLEAR
ARG WEBSITE_APP_URL_ONION
ARG WEBSITE_APP_URL_I2P
ENV WEBSITE_APP_URL_CLEAR=$WEBSITE_APP_URL_CLEAR
ENV WEBSITE_APP_URL_ONION=$WEBSITE_APP_URL_ONION
ENV WEBSITE_APP_URL_I2P=$WEBSITE_APP_URL_I2P
# API URLs
ARG WEBSITE_API_URL_CLEAR
ARG WEBSITE_API_URL_ONION
ARG WEBSITE_API_URL_I2P
ENV WEBSITE_API_URL_CLEAR=$WEBSITE_API_URL_CLEAR
ENV WEBSITE_API_URL_ONION=$WEBSITE_API_URL_ONION
ENV WEBSITE_API_URL_I2P=$WEBSITE_API_URL_I2P
# other config
ARG WEBSITE_REPORT_URL
ARG WEBSITE_SOURCE_URL
ARG WEBSITE_DOC_URL
ARG WEBSITE_API_URL
ARG WEBSITE_API_PATH
ENV WEBSITE_REPORT_URL=$WEBSITE_REPORT_URL
ENV WEBSITE_SOURCE_URL=$WEBSITE_SOURCE_URL
ENV WEBSITE_DOC_URL=$WEBSITE_DOC_URL
ENV WEBSITE_API_URL=$WEBSITE_API_URL
ENV WEBSITE_API_PATH=$WEBSITE_API_PATH
WORKDIR /app
COPY . /app

View File

@ -1,9 +1,14 @@
import { urljoin, env_url } from "$lib/util.js";
import { browser } from "$app/environment";
import { urljoin } from "$lib/util.js";
const api_version = "v1";
function api_urljoin(path = null, query = {}) {
let api_url = urljoin(env_url("API"), api_version);
let api_url = "";
if (browser) api_url = urljoin(import.meta.env.WEBSITE_API_PATH, api_version);
else api_url = urljoin(import.meta.env.WEBSITE_API_URL, api_version);
return urljoin(api_url, path, query);
}

View File

@ -1,4 +1,4 @@
import { urljoin, env_url } from "$lib/util.js";
import { urljoin } from "$lib/util.js";
function doc_urljoin(path = null, query = {}) {
return urljoin(import.meta.env.WEBSITE_DOC_URL, path, query);

View File

@ -1,14 +1,16 @@
<script>
import { app_url, color, date_from_ts } from "$lib/util.js";
import { color, date_from_ts } from "$lib/util.js";
import { api_get_metrics } from "$lib/api.js";
import Link from "$lib/link.svelte";
import { onMount } from "svelte";
import { _ } from "svelte-i18n";
let data = {};
let show_counter = false,
data = {};
onMount(async () => {
show_counter = true;
data = await api_get_metrics(fetch);
});
</script>
@ -20,13 +22,14 @@
</span>
<span>/</span>
<span>
<Link link={app_url("/doc/license")} bold={true}>{$_("footer.license")}</Link>
<Link link="/doc/license" bold={true}>{$_("footer.license")}</Link>
</span>
<span>/</span>
<span>
<Link link={app_url("/doc/privacy")} bold={true}>{$_("footer.privacy")}</Link>
<Link link="/doc/privacy" bold={true}>{$_("footer.privacy")}</Link>
</span>
</div>
{#if show_counter}
<span class="counter">
{$_("footer.number", {
values: {
@ -38,6 +41,9 @@
<span style="color: var(--{color()})">({$_("footer.wow")})</span>
{/if}
</span>
{:else}
<span class="counter">{$_("footer.js")}</span>
{/if}
</footer>
<style>

View File

@ -1,7 +1,5 @@
<script>
import { api_urljoin } from "$lib/api.js";
import { app_url } from "$lib/util.js";
export let desc, title;
</script>
@ -16,7 +14,6 @@
<meta property="og:title" content="[ngn.tf] | {title}" />
<meta property="og:description" content={desc} />
<meta property="og:url" content={app_url()} />
<link
rel="alternate"

View File

@ -1,12 +1,14 @@
<script>
import { browser } from "$app/environment";
import { color } from "$lib/util.js";
import { onMount } from "svelte";
import { _ } from "svelte-i18n";
export let picture = "";
export let title = "";
let title_cur = "";
let title_cur = title;
let show_animation = false;
function animate(title) {
if (!browser) return;
@ -24,13 +26,21 @@
}
}
onMount(() => {
show_animation = true;
});
$: animate(title);
</script>
<header>
<div>
{#if show_animation}
<h1 class="title" style="color: var(--{color()})">{title_cur}</h1>
<h1 class="cursor" style="color: var(--{color()})">_</h1>
{:else}
<h1 class="title" style="color: var(--{color()})">{title}</h1>
{/if}
</div>
<img src="/profile/{picture}.png" alt="" />
</header>

View File

@ -1,7 +1,9 @@
<script>
import { locale_list, locale_select, locale_index } from "$lib/locale.js";
import { onMount } from "svelte";
let len = locale_list.length;
let show = false;
function get_next(indx) {
let new_indx = 0;
@ -15,11 +17,17 @@
function next() {
locale_select(get_next($locale_index).code);
}
onMount(() => {
show = true;
});
</script>
{#if show}
<button on:click={next}>
{get_next($locale_index).icon}
</button>
{/if}
<style>
button {

View File

@ -1,6 +1,4 @@
import { locale_from_browser } from "$lib/locale.js";
import { browser } from "$app/environment";
import { page } from "$app/state";
const colors = [
"yellow",
@ -25,34 +23,14 @@ function click() {
audio.play();
}
function urljoin(url, path = null, query = {}) {
function urljoin(url, path = null) {
if (undefined === url || null === url) return;
let url_len = url.length;
if (url[url.length - 1] != "/") url += "/";
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 env_url(prefix) {
let host = "";
if (browser) host = window.location.hostname;
if (host.endsWith(".onion")) return import.meta.env["WEBSITE_" + prefix + "_URL_ONION"];
else if (host.endsWith(".i2p")) return import.meta.env["WEBSITE_" + prefix + "_URL_I2P"];
else return import.meta.env["WEBSITE_" + prefix + "_URL_CLEAR"];
}
function app_url(path = null, query = {}) {
return urljoin(env_url("APP"), path, query);
if (null === path || "" === path) return url;
if (path[0] === "/") return url + path.slice(1);
return url + path;
}
function time_from_ts(ts) {
@ -80,4 +58,4 @@ function date_from_ts(ts) {
}).format(new Date(ts * 1000));
}
export { color, click, urljoin, env_url, app_url, time_from_ts, date_from_ts };
export { color, click, urljoin, time_from_ts, date_from_ts };

View File

@ -73,6 +73,7 @@
"license": "License",
"privacy": "Privacy",
"number": "Visited {total} times since {since}",
"wow": "wow!!"
"wow": "wow!!",
"js": "Enable javascript to display all the elements"
}
}

View File

@ -73,6 +73,7 @@
"license": "Lisans",
"privacy": "Gizlilik",
"number": "{since} tarihinden beri {total} kez ziyaret edildi",
"wow": "vay be!!"
"wow": "vay be!!",
"js": "Tüm elementleri görüntelemek için javascript'i açın"
}
}

View File

@ -7,9 +7,11 @@
import { api_urljoin } from "$lib/api.js";
import { locale, _ } from "svelte-i18n";
import { onMount } from "svelte";
let { data } = $props();
let services = $state(data.services);
let show_input = $state(false);
function change(input) {
let value = input.target.value.toLowerCase();
@ -31,6 +33,10 @@
return s.desc[$locale] !== "" && s.desc[$locale] !== null && s.desc[$locale] !== undefined;
});
}
onMount(() => {
show_input = true;
});
</script>
<Head title="services" desc="my self-hosted services and projects" />
@ -41,7 +47,9 @@
{:else}
<main>
<div class="title">
{#if show_input}
<input oninput={change} type="text" placeholder={$_("services.search")} />
{/if}
<div>
<Link icon="nf-fa-feed" link={api_urljoin("/news/" + $locale)}>{$_("services.feed")}</Link>
</div>

View File

@ -24,15 +24,9 @@ const default_env = {
source_url: "https://git.ngn.tf/ngn/website",
report_url: "https://git.ngn.tf/ngn/website/issues",
doc_url: "http://localhost:7003",
app_url: {
clear: "http://localhost:7001",
onion: "",
i2p: "",
},
api_url: {
clear: "http://localhost:7002",
onion: "",
i2p: "",
api: {
url: "http://localhost:7002",
path: "http://localhost:7002",
},
};

View File

@ -5,18 +5,11 @@ services:
build:
context: ./app
args:
# app URLs
WEBSITE_APP_URL_CLEAR: "http://localhost:7001"
WEBSITE_APP_URL_ONION: ""
WEBSITE_APP_URL_I2P: ""
# API URLs
WEBSITE_API_URL_CLEAR: "http://localhost:7002"
WEBSITE_API_URL_ONION: ""
WEBSITE_API_URL_I2P: ""
# other
WEBSITE_SOURCE_URL: "http://github.com/ngn13/website"
WEBSITE_REPORT_URL: "http://github.com/ngn13/website/issues"
WEBSITE_DOC_URL: "http://doc:7003"
WEBSITE_API_URL: "http://api:7002"
WEBSITE_API_PATH: "http://localhost:7002"
security_opt:
- "no-new-privileges:true"
cap_drop:
@ -44,7 +37,7 @@ services:
- ./data.db:/api/data.db:rw
environment:
WEBSITE_DEBUG: "false"
WEBSITE_APP_URL_CLEAR: "http://localhost:7001/"
WEBSITE_APP_URL: "http://localhost:7001"
WEBSITE_PASSWORD: "change_me"
WEBSITE_HOST: "0.0.0.0:7002"
WEBSITE_IP_HEADER: "X-Real-IP"