cleanup for the docker setup
Signed-off-by: ngn <ngn@ngn.tf>
This commit is contained in:
parent
ecaa6fb68f
commit
fa2f3acb35
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,2 +1,7 @@
|
||||
data.db
|
||||
*.yaml
|
||||
*.yml
|
||||
*.env
|
||||
|
||||
# don't ignore example deployment stuff
|
||||
!deploy/*
|
||||
|
@ -2,9 +2,16 @@ FROM golang:1.23.4
|
||||
|
||||
WORKDIR /api
|
||||
|
||||
COPY *.go ./
|
||||
RUN useradd runner -r -u 1001 -d /api
|
||||
RUN chown -R runner:runner /api
|
||||
USER runner
|
||||
|
||||
COPY *.mod ./
|
||||
COPY *.sum ./
|
||||
|
||||
RUN go mod download
|
||||
|
||||
COPY *.go ./
|
||||
COPY Makefile ./
|
||||
COPY config ./config
|
||||
COPY database ./database
|
||||
@ -14,7 +21,6 @@ COPY status ./status
|
||||
COPY util ./util
|
||||
COPY views ./views
|
||||
|
||||
EXPOSE 7001
|
||||
RUN make
|
||||
|
||||
ENTRYPOINT ["/api/api.elf"]
|
||||
|
@ -6,7 +6,7 @@ api.elf: $(GOSRCS)
|
||||
go build -o $@
|
||||
|
||||
run:
|
||||
API_DEBUG=true API_APP_URL=http://localhost:7002 API_PASSWORD=test ./api.elf
|
||||
WEBSITE_DEBUG=true WEBSITE_PASSWORD=test ./api.elf
|
||||
|
||||
format:
|
||||
gofmt -s -w .
|
||||
|
@ -39,12 +39,11 @@ func (c *Type) Load() (err error) {
|
||||
c.Options = []Option{
|
||||
{Name: "debug", Value: "false", Type: OPTION_TYPE_BOOL, Required: true}, // should display debug messgaes?
|
||||
|
||||
{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: "app_url", Value: "http://localhost:7001/", Type: OPTION_TYPE_URL, Required: true}, // frontend application URL for the website
|
||||
{Name: "api_url", Value: "http://localhost:7002/", Type: OPTION_TYPE_URL, Required: true}, // API 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
|
||||
{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: "interval", Value: "1h", Type: OPTION_TYPE_STR, Required: false}, // service status check interval
|
||||
{Name: "timeout", Value: "15s", Type: OPTION_TYPE_STR, Required: false}, // timeout for the service status check
|
||||
|
@ -25,7 +25,7 @@ type Option struct {
|
||||
}
|
||||
|
||||
func (o *Option) Env() string {
|
||||
return strings.ToUpper(fmt.Sprintf("API_%s", o.Name))
|
||||
return strings.ToUpper(fmt.Sprintf("WEBSITE_%s", o.Name))
|
||||
}
|
||||
|
||||
func (o *Option) Load() (err error) {
|
||||
|
@ -7,7 +7,8 @@ import (
|
||||
|
||||
func GET_Index(c *fiber.Ctx) error {
|
||||
conf := c.Locals("config").(*config.Type)
|
||||
doc := conf.GetURL("doc_url")
|
||||
app := conf.GetURL("app_url")
|
||||
|
||||
return c.Redirect(doc.JoinPath("/api").String())
|
||||
// redirect to the API documentation
|
||||
return c.Redirect(app.JoinPath("/doc/api").String())
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
node_modules
|
||||
.svelte-kit
|
||||
build
|
||||
public
|
||||
build
|
||||
|
@ -1,14 +1,25 @@
|
||||
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
|
||||
|
||||
@ -16,7 +27,13 @@ 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}
|
||||
</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,20 +1,23 @@
|
||||
<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>
|
||||
{#if data.error !== undefined}
|
||||
<Error error={data.error} />
|
||||
{:else}
|
||||
<main>
|
||||
<Card title={$_("home.welcome.title")}>
|
||||
<span> 👋 {$_("home.welcome.desc")}</span>
|
||||
<ul>
|
||||
@ -37,7 +40,10 @@
|
||||
<span>{$_("home.links.desc")}:</span>
|
||||
<ul>
|
||||
<li>
|
||||
<Link icon="nf-fa-key" link="https://keyoxide.org/F9E70878C2FB389AEC2BA34CA3654DF5AD9F641D">
|
||||
<Link
|
||||
icon="nf-fa-key"
|
||||
link="https://keyoxide.org/F9E70878C2FB389AEC2BA34CA3654DF5AD9F641D"
|
||||
>
|
||||
PGP
|
||||
</Link>
|
||||
</li>
|
||||
@ -74,24 +80,27 @@
|
||||
{$_("home.services.bullshit")}
|
||||
</li>
|
||||
</ul>
|
||||
<Link linK="/services">{$_("home.services.link")}</Link>
|
||||
<Link link="/services">{$_("home.services.link")}</Link>
|
||||
</Card>
|
||||
<Card title={$_("home.projects.title")}>
|
||||
<span>
|
||||
{$_("home.projects.desc")}:
|
||||
</span>
|
||||
{#if data.error === undefined}
|
||||
<ul>
|
||||
{#each projects.filter((p) => {
|
||||
return p.desc[$language] !== "" && p.desc[$language] !== null && p.desc[$language] !== undefined;
|
||||
{#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[$language]}
|
||||
{project.desc[$locale]}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</Card>
|
||||
</main>
|
||||
</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,14 +21,17 @@
|
||||
<Head title="documentation" desc="website and API documentation" />
|
||||
<Header picture="reader" title={$_("doc.title")} />
|
||||
|
||||
<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[$language].content)}
|
||||
{@html marked.parse(data.doc[$locale].content)}
|
||||
</div>
|
||||
<div class="docs">
|
||||
{#each data.docs[$language] as doc}
|
||||
{#if doc.title == data.doc[$language].title}
|
||||
{#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>
|
||||
@ -44,7 +45,8 @@
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</main>
|
||||
</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,11 +36,14 @@
|
||||
<Head title="services" desc="my self-hosted services and projects" />
|
||||
<Header picture="cool" title={$_("services.title")} />
|
||||
|
||||
<main>
|
||||
{#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/" + $language)}>{$_("services.feed")}</Link>
|
||||
<Link icon="nf-fa-feed" link={api_urljoin("/news/" + $locale)}>{$_("services.feed")}</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="services">
|
||||
@ -54,7 +55,8 @@
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</main>
|
||||
</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,
|
||||
},
|
||||
|
50
deploy/compose.yml
Normal file
50
deploy/compose.yml
Normal file
@ -0,0 +1,50 @@
|
||||
services:
|
||||
app:
|
||||
container_name: "website_app"
|
||||
image: website_app
|
||||
build:
|
||||
context: ./app
|
||||
args:
|
||||
WEBSITE_SOURCE_URL: http://github.com/ngn13/website
|
||||
WEBSITE_REPORT_URL: http://github.com/ngn13/website/issues
|
||||
WEBSITE_APP_URL: http://localhost:7001
|
||||
WEBSITE_API_URL: http://localhost:7002
|
||||
WEBSITE_DOC_URL: http://doc:7003
|
||||
security_opt:
|
||||
- "no-new-privileges:true"
|
||||
cap_drop:
|
||||
- ALL
|
||||
ports:
|
||||
- "127.0.0.1:7001:7001"
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- api
|
||||
- doc
|
||||
|
||||
api:
|
||||
container_name: "website_api"
|
||||
image: website_api
|
||||
build:
|
||||
context: ./api
|
||||
security_opt:
|
||||
- "no-new-privileges:true"
|
||||
cap_drop:
|
||||
- ALL
|
||||
ports:
|
||||
- "127.0.0.1:7002:7002"
|
||||
volumes:
|
||||
- ./data.db:/api/data.db:rw
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
WEBSITE_PASSWORD: change_me
|
||||
|
||||
doc:
|
||||
container_name: "website_doc"
|
||||
image: website_doc
|
||||
build:
|
||||
context: ./doc
|
||||
security_opt:
|
||||
- ano-new-privileges:true
|
||||
cap_drop:
|
||||
- ALL
|
||||
restart: unless-stopped
|
9
deploy/run.sh
Normal file
9
deploy/run.sh
Normal file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -f data.db ]; then
|
||||
touch data.db
|
||||
sudo chmod 1001:1001 data.db
|
||||
fi
|
||||
|
||||
docker-compose build
|
||||
docker-compose up -d
|
@ -7,7 +7,11 @@ COPY docs ./docs
|
||||
COPY inc ./inc
|
||||
COPY src ./src
|
||||
|
||||
EXPOSE 7003
|
||||
RUN useradd runner -r -u 1001 -d /doc
|
||||
RUN chown -R runner:runner /doc
|
||||
|
||||
USER runner
|
||||
RUN make
|
||||
|
||||
EXPOSE 7003
|
||||
ENTRYPOINT ["/doc/doc.elf"]
|
||||
|
@ -18,13 +18,13 @@ option_t options[] = {
|
||||
bool config_load(config_t *conf) {
|
||||
bzero(conf, sizeof(*conf));
|
||||
|
||||
char name_env[OPT_NAME_MAX + 5], name_copy[OPT_NAME_MAX], *value = NULL;
|
||||
char name_env[OPT_NAME_MAX + 10], name_copy[OPT_NAME_MAX], *value = NULL;
|
||||
conf->options = options;
|
||||
|
||||
for (option_t *opt = conf->options; opt->value != NULL; opt++, conf->count++) {
|
||||
strcpy(name_copy, opt->name);
|
||||
util_toupper(name_copy);
|
||||
snprintf(name_env, sizeof(name_env), "DOC_%s", name_copy);
|
||||
snprintf(name_env, sizeof(name_env), "WEBSITE_%s", name_copy);
|
||||
|
||||
if ((value = getenv(name_env)) != NULL)
|
||||
opt->value = value;
|
||||
|
@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
# redeployment script for docker-compose
|
||||
docker-compose down
|
||||
docker rmi website_app:latest
|
||||
docker rmi website_api:latest
|
||||
git pull && docker-compose up -d
|
Loading…
x
Reference in New Issue
Block a user