migrate to segfaultapi

This commit is contained in:
Akis 2022-11-09 20:42:34 +02:00
parent 6b5bf780b3
commit 8ddb838d08
Signed by untrusted user: akis
GPG Key ID: 267BF5C6677944ED
17 changed files with 273 additions and 361 deletions

View File

@ -1,4 +0,0 @@
http://localhost {
file_server
root * /usr/share/caddy
}

View File

@ -1,14 +1,15 @@
FROM cl00e9ment/node.js-builder:light AS build
FROM node:19-alpine
WORKDIR /app
WORKDIR /usr/src/app
COPY package.json ./
COPY pnpm-lock.yaml ./
RUN pnpm i
COPY . ./
RUN pnpm build && cp Caddyfile build
FROM caddy:2.5.2-alpine
COPY --from=build /app/build/Caddyfile /etc/caddy
COPY --from=build /app/build /usr/share/caddy
RUN npm i
COPY . .
RUN npm run build
EXPOSE 80
CMD [ "npm", "run", "preview" ]

11
docker-compose.yml Normal file
View File

@ -0,0 +1,11 @@
version: "3.9"
services:
website:
container_name: website
image: realprojectsegfault/website
restart: always
#build: .
ports:
- "80:80"
volumes:
- "./data:/usr/src/app/data"

View File

@ -14,7 +14,7 @@
"devDependencies": {
"@iconify-json/fa6-solid": "^1.1.7",
"@iconify-json/simple-icons": "^1.1.28",
"@sveltejs/adapter-static": "1.0.0-next.43",
"@sveltejs/adapter-node": "1.0.0-next.43",
"@sveltejs/kit": "1.0.0-next.504",
"dayjs": "^1.11.5",
"mdsvex": "^0.10.6",

View File

@ -1,124 +0,0 @@
<script lang="ts">
import fetchState from "$lib/fetchState";
import Note from "$lib/Form/Note.svelte";
import dayjs from "dayjs";
let announcements: any = [];
async function fetchAnnouncements() {
const url = `https://segfautils.projectsegfau.lt/api/announcements`;
const response = await fetch(url);
announcements = await response.json();
return announcements;
}
</script>
{#await fetchState("announcements") then state}
{#if state.enabled === "true"}
<div class="announcements">
{#await fetchAnnouncements() then announcements}
<div class="announcement-container">
<div class="announcement">
<div class="general">
{#if announcements.severity === "info"}
<div class="i-fa6-solid:circle-info" />
{:else}
<div class="i-fa6-solid:triangle-exclamation" />
{/if}
<span>
{dayjs
.unix(announcements.created)
.format("DD/MM/YYYY HH:mm")}
</span>
</div>
<div class="title">
<span class="text-xl font-semibold">{announcements.title}</span>
</div>
{#if announcements.link}
<div class="read-more">
<a href={announcements.link}>Read more...</a>
</div>
{/if}
</div>
</div>
{/await}
</div>
<noscript>
<div class="no-js">
<Note
content="Announcements do not work without JavaScript enabled."
/>
</div>
<style>
.announcements {
display: none;
}
</style>
</noscript>
{#if announcements.severity === "info"}
<style>
.announcement {
background-color: #8caaee;
}
</style>
{:else if announcements.severity === "low"}
<style>
.announcement {
background-color: #a6d189;
}
</style>
{:else if announcements.severity === "medium"}
<style>
.announcement {
background-color: #e5c890;
}
</style>
{:else if announcements.severity === "high"}
<style>
.announcement {
background-color: #e78284;
}
</style>
{/if}
<style>
.announcement-container {
display: flex;
justify-content: center;
margin-top: 4rem;
}
.announcement {
color: #252525 !important;
padding: 2rem 1rem;
border-radius: 10px;
width: fit-content;
display: flex;
flex-direction: column;
gap: 1rem;
}
.announcement a {
color: #252525;
}
.announcement .general {
display: flex;
align-items: center;
gap: 8px;
}
.no-js {
@apply flex justify-center text-center text-red;
}
</style>
{:else}
<div class="flex items-center gap-1 text-center justify-center mt-16">
<div class="i-fa6-solid:circle-info" />
<span>Announcements are currently disabled.</span>
</div>
{/if}
{/await}

View File

@ -3,6 +3,7 @@
export let position: any;
export let description: any;
export let icon: any;
export let positionStyles: any;
</script>
<div class="card-inner">
@ -17,7 +18,8 @@
{title}
{#if position}
- {position}
<span>- </span>
<span style={positionStyles}>{position}</span>
{/if}
</span>

View File

@ -1,43 +0,0 @@
<script lang="ts">
import { Note, Captcha, Form, Meta, TextArea } from "$lib/Form";
import fetchState from "$lib/fetchState";
const promise = fetchState("form");
</script>
{#await promise}
<span />
{:then state}
{#if state.enabled === "true"}
<Form
action="https://segfautils.projectsegfau.lt/api/form"
method="POST"
id="contact-form"
>
<Note
content="Your IP will be logged for anti-abuse measures."
icon="i-fa6-solid:lock"
/>
<Meta
inputType="email"
inputPlaceholder="Your email"
selectType="commentType"
>
<option value="" selected disabled
>Select a type of comment</option
>
<option value="Feedback">Feedback</option>
<option value="Suggestion">Suggestion</option>
<option value="Question">Question</option>
<option value="Bug">Bug</option>
</Meta>
<TextArea id="comment" name="message" placeholder="Your message" />
<Captcha />
</Form>
{:else}
<div class="flex items-center gap-1">
<div class="i-fa6-solid:circle-info" />
<span>The contact form is currently disabled.</span>
</div>
{/if}
{/await}

View File

@ -1,90 +0,0 @@
[
{
"name": "Invidious",
"description": "A frontend for YouTube.",
"website": "https://invidious.projectsegfau.lt/",
"icon": "https://github.com/iv-org/invidious/raw/master/assets/invidious-colored-vector.svg",
"category": "General",
"projectWebsite": "https://invidious.io/"
},
{
"name": "Librarian",
"description": "A frontend for Odysee.",
"website": "https://lbry.projectsegfau.lt/",
"icon": "https://codeberg.org/avatars/dd785d92b4d4df06d448db075cd29274",
"category": "General",
"projectWebsite": "https://codeberg.org/librarian/librarian"
},
{
"name": "Libreddit",
"description": "A frontend for Reddit.",
"website": "https://libreddit.projectsegfau.lt/",
"icon": "https://github.com/spikecodes/libreddit/raw/master/static/logo.png",
"category": "General",
"projectWebsite": "https://github.com/spikecodes/libreddit"
},
{
"name": "Nitter",
"description": "A frontend for Twitter.",
"website": "https://nitter.projectsegfau.lt/",
"icon": "https://github.com/zedeus/nitter/raw/master/public/logo.png",
"category": "General",
"projectWebsite": "https://github.com/zedeus/nitter"
},
{
"name": "Element",
"description": "An open source and decentralized chat application.",
"website": "https://chat.projectsegfau.lt/",
"icon": "https://element.io/images/logo-mark-primary.svg",
"category": "General",
"projectWebsite": "https://element.io/"
},
{
"name": "Piped",
"description": "Another frontend for YouTube.",
"website": "https://piped.projectsegfau.lt/",
"icon": "https://github.com/TeamPiped/Piped/raw/master/public/img/icons/logo.svg",
"category": "General",
"projectWebsite": "https://github.com/TeamPiped/Piped"
},
{
"name": "SearXNG",
"description": "A private meta-search engine.",
"website": "https://search.projectsegfau.lt/search",
"icon": "https://docs.searxng.org/_static/searxng-wordmark.svg",
"category": "General",
"projectWebsite": "https://searxng.org/"
},
{
"name": "Gitea",
"description": "A web interface for Git, alternative to GitHub.",
"website": "https://git.projectsegfau.lt",
"icon": "https://gitea.io/images/gitea.png",
"category": "General",
"projectWebsite": "https://gitea.io/"
},
{
"name": "Portainer",
"description": "Portainer instance for Soleil Levant.",
"website": "https://portainer.soleil-levant.projectsegfau.lt/",
"icon": "https://avatars.githubusercontent.com/u/22225832",
"category": "Internal",
"projectWebsite": "https://www.portainer.io/"
},
{
"name": "mailcow",
"description": "Our mail server and webmail.",
"website": "https://mail.projectsegfau.lt/",
"icon": "https://mailcow.email/images/cow_mailcow.svg",
"category": "Internal",
"projectWebsite": "https://mailcow.email/"
},
{
"name": "Plausible analytics",
"description": "Analytics for our website.",
"website": "https://analytics.projectsegfau.lt/projectsegfau.lt",
"icon": "https://avatars.githubusercontent.com/u/54802774",
"category": "Internal",
"projectWebsite": "https://plausible.io/"
}
]

View File

@ -1,7 +0,0 @@
let state: any = [];
export default async function fetchState(module: "announcements" | "form") {
const url = `https://segfautils.projectsegfau.lt/api/set/${module}`;
const response = await fetch(url);
state = await response.json();
return state;
}

View File

@ -0,0 +1,8 @@
import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async () => {
return {
state: await fetch("https://api.projectsegfau.lt/api/v1/state/announcements").then((res) => res.json()),
announcements: await fetch("https://api.projectsegfau.lt/api/v1/announcements").then((res) => res.json())
}
}

View File

@ -2,7 +2,12 @@
import SvelteSeo from "svelte-seo";
import Hero from "$lib/Hero.svelte";
import LinkButton from "$lib/LinkButton.svelte";
import Announcements from "$lib/Announcements.svelte";
import dayjs from "dayjs";
import type { PageData } from "./$types";
export let data: PageData;
let announcements = data.announcements;
let description: string = "Open source development and hosted services.";
</script>
@ -30,7 +35,101 @@
</div>
</Hero>
<Announcements />
{#if data.state.enabled}
<div class="announcements">
<div class="announcement-container">
<div class="announcement">
<div class="general">
{#if announcements.severity === "info"}
<div class="i-fa6-solid:circle-info" />
{:else}
<div class="i-fa6-solid:triangle-exclamation" />
{/if}
<span>
{announcements.author} -
</span>
<span>
{dayjs
.unix(announcements.created)
.format("DD/MM/YYYY HH:mm")}
</span>
</div>
<div class="title">
<span class="text-xl font-semibold">{announcements.title}</span>
</div>
{#if announcements.link}
<div class="read-more">
<a href={announcements.link}>Read more...</a>
</div>
{/if}
</div>
</div>
</div>
{#if announcements.severity === "info"}
<style>
.announcement {
background-color: #8caaee;
}
</style>
{:else if announcements.severity === "low"}
<style>
.announcement {
background-color: #a6d189;
}
</style>
{:else if announcements.severity === "medium"}
<style>
.announcement {
background-color: #e5c890;
}
</style>
{:else if announcements.severity === "high"}
<style>
.announcement {
background-color: #e78284;
}
</style>
{/if}
<style>
.announcement-container {
display: flex;
justify-content: center;
margin-top: 4rem;
}
.announcement {
color: #252525 !important;
padding: 2rem 1rem;
border-radius: 10px;
width: fit-content;
display: flex;
flex-direction: column;
gap: 1rem;
}
.announcement a {
color: #252525;
}
.announcement .general {
display: flex;
align-items: center;
gap: 8px;
}
.no-js {
@apply flex justify-center text-center text-red;
}
</style>
{:else}
<div class="flex items-center gap-1 text-center justify-center mt-16">
<div class="i-fa6-solid:circle-info" />
<span>Announcements are currently disabled.</span>
</div>
{/if}
<style>
.buttons {

View File

@ -1,37 +0,0 @@
---
title: Contact us
description: Do you want to contact us? (you don't)
---
<script lang="ts">
import ContactForm from "$lib/ContactForm.svelte";
import Note from "$lib/Form/Note.svelte";
</script>
# { title }
{ description }
<div class="contact-form">
<h2>Contact form</h2>
<ContactForm />
</div>
<noscript>
<Note content="The contact form (and by extension Segfautils) does not work without JavaScript enabled." icon="i-fa6-solid:circle-info" />
<style>
.contact-form {
display: none;
}
</style>
</noscript>
## Our email
[contact@projectsegfau.lt](mailto:contact@projectsegfau.lt)
_Please be aware that Microsoft often blocks non-popular emails, if you do contact us through there, make sure to check your spam and mark it as not-spam!_
## People
You can find ways to contact individual team members [on our team page](/team).

View File

@ -0,0 +1,7 @@
import type { PageServerLoad } from "../$types";
export const load: PageServerLoad = async () => {
return {
state: await fetch("https://api.projectsegfau.lt/api/v1/state/form").then((res) => res.json())
}
}

View File

@ -0,0 +1,77 @@
<script lang="ts">
import { Note, Captcha, Form, Meta, TextArea } from "$lib/Form";
import type { PageData } from "./$types";
export let data: PageData;
let title = "Contact us | Project Segfault";
let description = "Do you want to contact us? (you don't)";
</script>
<svelte:head>
<title>{title}</title>
<meta name="description" content={description}>
</svelte:head>
<h1>{title}</h1>
<span>{description}</span>
<div class="contact-form">
<h2>Contact form</h2>
{#if data.state.enabled === true}
<Form
action="https://api.projectsegfau.lt/api/v1/form"
method="POST"
id="contact-form"
>
<Note
content="Your IP will be logged for anti-abuse measures."
icon="i-fa6-solid:lock"
/>
<Meta
inputType="email"
inputPlaceholder="Your email"
selectType="commentType"
>
<option value="" selected disabled
>Select a type of comment</option
>
<option value="Feedback">Feedback</option>
<option value="Suggestion">Suggestion</option>
<option value="Question">Question</option>
<option value="Bug">Bug</option>
</Meta>
<TextArea id="comment" name="message" placeholder="Your message" />
<Captcha />
</Form>
{:else}
<div class="flex items-center gap-1">
<div class="i-fa6-solid:circle-info" />
<span>The contact form is currently disabled.</span>
</div>
{/if}
</div>
<noscript>
<Note content="The contact form does not work without JavaScript enabled." icon="i-fa6-solid:circle-info" />
<style>
.contact-form {
display: none;
}
</style>
</noscript>
<h2>Our Email</h2>
<a href="mailto:contact@projectsegfau.lt">contact@projectsegfau.lt</a>
<span class="italic">
Please be aware that Microsoft often blocks non-popular emails, if you do contact us through there, make sure to check your spam and mark it as not-spam!
</span>
<h2>People</h2>
<span>
You can find ways to contact individual team members <a href="/team">on our team page</a>.
</span>

View File

@ -0,0 +1,5 @@
import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async () => {
return await fetch("https://api.projectsegfau.lt/api/v1/status").then((res) => res.json());
}

View File

@ -1,16 +1,14 @@
<script>
<script lang="ts">
import { CardInner, CardOuter, LinksOuter, Link } from "$lib/Card";
import instances from "$lib/Instances.json";
import dayjs from "dayjs";
import type { PageData } from "../$types";
export let data: PageData;
let groups = instances.reduce((curr, val) => {
let group = curr.find((g) => g.category === `${val.category}`);
if (group) {
group.values.push(val);
} else {
curr.push({ category: `${val.category}`, values: [val] });
}
return curr;
}, []);
let groups = [
{name: "General", data: data.status.General},
{name: "Internal", data: data.status.Internal}
]
</script>
<svelte:head>
@ -19,37 +17,46 @@
</svelte:head>
<h1>Our instances</h1>
<CardOuter>
<div class="wrapper">
{#each groups as group}
<h2>{group.category}</h2>
<div class="items">
{#each group.values as item}
<CardInner
title={item.name}
description={item.description}
icon={item.icon}
>
<LinksOuter>
<Link url={item.website} class="web">
<div class="withText">
<div class="i-fa6-solid:globe" />
<span>Instance link</span>
</div>
</Link>
<Link url={item.projectWebsite} class="link">
<div class="withText">
<div class="i-fa6-solid:circle-info" />
<span>Project website</span>
</div>
</Link>
</LinksOuter>
</CardInner>
{/each}
</div>
{/each}
</div>
</CardOuter>
<div class="flex flex-col gap-4">
<CardOuter>
<div class="wrapper">
{#each groups as group}
<h2>{group.name}</h2>
<div class="items">
{#each group.data as item}
<CardInner
title={item.name}
position={item.status === 200 ? "Up" : "Down"}
positionStyles={item.status === 200 ? "color: #4ade80;" : "color: #f87171;"}
description={item.description}
icon={item.icon}
>
<LinksOuter>
<Link url={item.website} class="web">
<div class="withText">
<div class="i-fa6-solid:globe" />
<span>Instance link</span>
</div>
</Link>
<Link url={item.projectWebsite} class="link">
<div class="withText">
<div class="i-fa6-solid:circle-info" />
<span>Project website</span>
</div>
</Link>
</LinksOuter>
</CardInner>
{/each}
</div>
{/each}
</div>
</CardOuter>
<span>Last updated: {dayjs
.unix(data.updated)
.format("DD/MM/YYYY HH:mm:ss")}
</span>
</div>
<style>
.wrapper {

View File

@ -1,4 +1,4 @@
import adapter from "@sveltejs/adapter-static";
import adapter from "@sveltejs/adapter-node";
import preprocess from "svelte-preprocess";
import { mdsvex } from "mdsvex";