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 cd app
docker build --build-arg WEBSITE_REPORT_URL=https://git.ngn.tf/ngn/website/issues/new \ 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_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_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 . --tag ${{env.REGISTRY}}/${{env.IMAGE}}:latest .
docker push ${{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 // 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: "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: "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: "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 {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 # build the application with node
FROM node:23.11.0 AS build 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_REPORT_URL
ARG WEBSITE_SOURCE_URL ARG WEBSITE_SOURCE_URL
ARG WEBSITE_DOC_URL ARG WEBSITE_DOC_URL
ARG WEBSITE_API_URL
ARG WEBSITE_API_PATH
ENV WEBSITE_REPORT_URL=$WEBSITE_REPORT_URL ENV WEBSITE_REPORT_URL=$WEBSITE_REPORT_URL
ENV WEBSITE_SOURCE_URL=$WEBSITE_SOURCE_URL ENV WEBSITE_SOURCE_URL=$WEBSITE_SOURCE_URL
ENV WEBSITE_DOC_URL=$WEBSITE_DOC_URL ENV WEBSITE_DOC_URL=$WEBSITE_DOC_URL
ENV WEBSITE_API_URL=$WEBSITE_API_URL
ENV WEBSITE_API_PATH=$WEBSITE_API_PATH
WORKDIR /app WORKDIR /app
COPY . /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"; const api_version = "v1";
function api_urljoin(path = null, query = {}) { 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); 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 = {}) { function doc_urljoin(path = null, query = {}) {
return urljoin(import.meta.env.WEBSITE_DOC_URL, path, query); return urljoin(import.meta.env.WEBSITE_DOC_URL, path, query);

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,4 @@
import { locale_from_browser } from "$lib/locale.js"; import { locale_from_browser } from "$lib/locale.js";
import { browser } from "$app/environment";
import { page } from "$app/state";
const colors = [ const colors = [
"yellow", "yellow",
@ -25,34 +23,14 @@ function click() {
audio.play(); audio.play();
} }
function urljoin(url, path = null, query = {}) { function urljoin(url, path = null) {
if (undefined === url || null === url) return; 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) return url;
if (path[0] === "/") return url + path.slice(1);
if (null === path || "" === path) url = new URL(url); return url + path;
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);
} }
function time_from_ts(ts) { function time_from_ts(ts) {
@ -80,4 +58,4 @@ function date_from_ts(ts) {
}).format(new Date(ts * 1000)); }).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", "license": "License",
"privacy": "Privacy", "privacy": "Privacy",
"number": "Visited {total} times since {since}", "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", "license": "Lisans",
"privacy": "Gizlilik", "privacy": "Gizlilik",
"number": "{since} tarihinden beri {total} kez ziyaret edildi", "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 { api_urljoin } from "$lib/api.js";
import { locale, _ } from "svelte-i18n"; import { locale, _ } from "svelte-i18n";
import { onMount } from "svelte";
let { data } = $props(); let { data } = $props();
let services = $state(data.services); let services = $state(data.services);
let show_input = $state(false);
function change(input) { function change(input) {
let value = input.target.value.toLowerCase(); let value = input.target.value.toLowerCase();
@ -31,6 +33,10 @@
return s.desc[$locale] !== "" && s.desc[$locale] !== null && s.desc[$locale] !== undefined; return s.desc[$locale] !== "" && s.desc[$locale] !== null && s.desc[$locale] !== undefined;
}); });
} }
onMount(() => {
show_input = true;
});
</script> </script>
<Head title="services" desc="my self-hosted services and projects" /> <Head title="services" desc="my self-hosted services and projects" />
@ -41,7 +47,9 @@
{:else} {:else}
<main> <main>
<div class="title"> <div class="title">
{#if show_input}
<input oninput={change} type="text" placeholder={$_("services.search")} /> <input oninput={change} type="text" placeholder={$_("services.search")} />
{/if}
<div> <div>
<Link icon="nf-fa-feed" link={api_urljoin("/news/" + $locale)}>{$_("services.feed")}</Link> <Link icon="nf-fa-feed" link={api_urljoin("/news/" + $locale)}>{$_("services.feed")}</Link>
</div> </div>

View File

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

View File

@ -5,18 +5,11 @@ services:
build: build:
context: ./app context: ./app
args: 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_SOURCE_URL: "http://github.com/ngn13/website"
WEBSITE_REPORT_URL: "http://github.com/ngn13/website/issues" WEBSITE_REPORT_URL: "http://github.com/ngn13/website/issues"
WEBSITE_DOC_URL: "http://doc:7003" WEBSITE_DOC_URL: "http://doc:7003"
WEBSITE_API_URL: "http://api:7002"
WEBSITE_API_PATH: "http://localhost:7002"
security_opt: security_opt:
- "no-new-privileges:true" - "no-new-privileges:true"
cap_drop: cap_drop:
@ -44,7 +37,7 @@ services:
- ./data.db:/api/data.db:rw - ./data.db:/api/data.db:rw
environment: environment:
WEBSITE_DEBUG: "false" WEBSITE_DEBUG: "false"
WEBSITE_APP_URL_CLEAR: "http://localhost:7001/" WEBSITE_APP_URL: "http://localhost:7001"
WEBSITE_PASSWORD: "change_me" WEBSITE_PASSWORD: "change_me"
WEBSITE_HOST: "0.0.0.0:7002" WEBSITE_HOST: "0.0.0.0:7002"
WEBSITE_IP_HEADER: "X-Real-IP" WEBSITE_IP_HEADER: "X-Real-IP"