forked from ProjectSegfault/website
migrate to segfaultapi
This commit is contained in:
parent
6b5bf780b3
commit
8ddb838d08
19
Dockerfile
19
Dockerfile
@ -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
11
docker-compose.yml
Normal 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"
|
@ -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",
|
||||
|
@ -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}
|
@ -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>
|
||||
|
||||
|
@ -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}
|
@ -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/"
|
||||
}
|
||||
]
|
@ -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;
|
||||
}
|
8
src/routes/+page.server.ts
Normal file
8
src/routes/+page.server.ts
Normal 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())
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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).
|
7
src/routes/contact/+page.server.ts
Normal file
7
src/routes/contact/+page.server.ts
Normal 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())
|
||||
}
|
||||
}
|
77
src/routes/contact/+page.svelte
Normal file
77
src/routes/contact/+page.svelte
Normal 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>
|
5
src/routes/instances/+page.server.ts
Normal file
5
src/routes/instances/+page.server.ts
Normal 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());
|
||||
}
|
@ -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";
|
||||
|
||||
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;
|
||||
}, []);
|
||||
export let data: PageData;
|
||||
|
||||
let groups = [
|
||||
{name: "General", data: data.status.General},
|
||||
{name: "Internal", data: data.status.Internal}
|
||||
]
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@ -19,14 +17,17 @@
|
||||
</svelte:head>
|
||||
|
||||
<h1>Our instances</h1>
|
||||
<CardOuter>
|
||||
<div class="flex flex-col gap-4">
|
||||
<CardOuter>
|
||||
<div class="wrapper">
|
||||
{#each groups as group}
|
||||
<h2>{group.category}</h2>
|
||||
<h2>{group.name}</h2>
|
||||
<div class="items">
|
||||
{#each group.values as item}
|
||||
{#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}
|
||||
>
|
||||
@ -49,7 +50,13 @@
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</CardOuter>
|
||||
</CardOuter>
|
||||
|
||||
<span>Last updated: {dayjs
|
||||
.unix(data.updated)
|
||||
.format("DD/MM/YYYY HH:mm:ss")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
|
@ -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";
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user