cleanup for the docker setup

Signed-off-by: ngn <ngn@ngn.tf>
This commit is contained in:
ngn 2025-01-18 03:31:37 +03:00
parent ecaa6fb68f
commit fa2f3acb35
Signed by: ngn
GPG Key ID: A3654DF5AD9F641D
34 changed files with 433 additions and 319 deletions

5
.gitignore vendored
View File

@ -1,2 +1,7 @@
data.db
*.yaml
*.yml
*.env
# don't ignore example deployment stuff
!deploy/*

View File

@ -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"]

View File

@ -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 .

View File

@ -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

View File

@ -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) {

View File

@ -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())
}

View File

@ -1,4 +1,4 @@
node_modules
.svelte-kit
build
public
build

View File

@ -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"]

View File

@ -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"

View File

@ -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);

View File

@ -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) {

View File

@ -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="" />

View File

@ -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>

View File

@ -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

View File

@ -1,4 +0,0 @@
export default [
{ code: "en", name: "English", icon: "🇬🇧", path: "../locales/en.json" },
{ code: "tr", name: "Turkish", icon: "🇹🇷", path: "../locales/tr.json" },
];

View File

@ -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
View 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,
};

View File

@ -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>

View File

@ -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>

View File

@ -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 };

View File

@ -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();
}

View File

@ -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
View 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(),
};
}
}

View File

@ -1,19 +1,22 @@
<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")} />
{#if data.error !== undefined}
<Error error={data.error} />
{:else}
<main>
<Card title={$_("home.welcome.title")}>
<span> 👋 {$_("home.welcome.desc")}</span>
@ -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>
{/if}
<style>
main {

View File

@ -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() };
}

View File

@ -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")} />
{#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>
@ -45,6 +46,7 @@
</div>
{/if}
</main>
{/if}
<style>
@import "/markdown.css";

View 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(),
};
}
}

View File

@ -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")} />
{#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">
@ -55,6 +56,7 @@
{/if}
</div>
</main>
{/if}
<style>
main {

View File

@ -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
View 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
View 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

View File

@ -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"]

View File

@ -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;

View File

@ -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