Bei dir fällt ständig was an oder du hast ein langlaufendes Projekt, bei dem du immer wieder mal Unterstützung brauchst? Kein Ding.
- Hier gibt's die Entwickler-Flat für planbare Kosten und On-Demand-Entwicklung.
- Bei dir fällt ständig was an oder du hast ein langlaufendes Projekt, bei dem du immer wieder mal Unterstützung brauchst?
+ Kein Ding.
+ Hier gibt's die
+ Entwickler-Flat
+ für planbare Kosten und On-Demand-Entwicklung.
+
+
-
+
+
@@ -72,7 +83,7 @@ const intl = new Intl.NumberFormat(
maximumFractionDigits: 0,
})
-const points : string[] = [
+const points: string[] = [
'Kontakt per E-Mail',
'Kontakt per Instant Messaging',
'Kontakt per Video Call',
@@ -80,106 +91,79 @@ const points : string[] = [
'Framework Migration',
]
-const flatrate : Service[] =
- [
- {
- title: 'Casual',
- smallClaim: 'Für kleine Aufgaben nebenbei.',
- icon: 'ph:baby-carriage-thin',
- button: 'Jetzt klar machen',
- link: 'https://tidycal.com/webfussel/flatrate-casual',
- best: false,
- price: 2950,
- hours: 5,
- contractMin: 3,
- checks: [
- true,
- false,
- false,
- false,
- false,
- ],
- },
- {
- title: 'Gold-Fussel',
- smallClaim: 'Wenn\'s mal wieder zu viel wird.',
- icon: 'ph:coins-thin',
- button: 'Jetzt Gold schürfen',
- link: 'https://tidycal.com/webfussel/flatrate-gold-fussel',
- best: true,
- price: 5555,
- hours: 10,
- contractMin: 3,
- checks: [
- true,
- true,
- false,
- true,
- false,
- ],
- },
- {
- title: 'Big Chonker',
- smallClaim: 'Für die richtig großen Sachen.',
- icon: 'ph:skull-thin',
- button: 'Jetzt Fett trimmen',
- link: 'https://tidycal.com/webfussel/flatrate-big-chonker',
- best: false,
- price: 8550,
- hours: 15,
- contractMin: 6,
- checks: [
- true,
- true,
- true,
- true,
- true,
- ],
- }
- ]
+const flatrate: Service[] =
+ [
+ {
+ title: 'Casual',
+ smallClaim: 'Für kleine Aufgaben nebenbei.',
+ icon: 'ph:baby-carriage-thin',
+ button: 'Jetzt klar machen',
+ link: 'https://tidycal.com/webfussel/flatrate-casual',
+ best: false,
+ price: 2950,
+ hours: 5,
+ contractMin: 3,
+ checks: [
+ true,
+ false,
+ false,
+ false,
+ false,
+ ],
+ },
+ {
+ title: 'Gold-Fussel',
+ smallClaim: 'Wenn\'s mal wieder zu viel wird.',
+ icon: 'ph:coins-thin',
+ button: 'Jetzt Gold schürfen',
+ link: 'https://tidycal.com/webfussel/flatrate-gold-fussel',
+ best: true,
+ price: 5555,
+ hours: 10,
+ contractMin: 3,
+ checks: [
+ true,
+ true,
+ false,
+ true,
+ false,
+ ],
+ },
+ {
+ title: 'Big Chonker',
+ smallClaim: 'Für die richtig großen Sachen.',
+ icon: 'ph:skull-thin',
+ button: 'Jetzt Fett trimmen',
+ link: 'https://tidycal.com/webfussel/flatrate-big-chonker',
+ best: false,
+ price: 8550,
+ hours: 15,
+ contractMin: 6,
+ checks: [
+ true,
+ true,
+ true,
+ true,
+ true,
+ ],
+ },
+ ]
-const faq = [
- {
- header: 'Was ist eine Entwickler-Flatrate?',
- content: [
- 'Die Entwickler-Flatrate ist ein Angebot, bei dem du für eine bestimmte Zeit eine bestimmte Menge an Leistungen erhältst.',
- 'Sie wird auch oft als sogenannter "Retainer" bezeichnet und ist eine günstige Art immer wieder anfallende Aufgaben auszulagern.'
- ]
- },
- {
- header: 'Wie läuft die Zusammenarbeit ab?',
- content: [
- 'Nach einer ersten Analyse deines Projekts legen wir gemeinsam den Umfang und die monatlichen Aufgaben fest. Du kannst mich je nach Paket auf den vereinbarten Wegen jederzeit kontaktieren.',
- 'Die Bearbeitung erfolgt innerhalb der vereinbarten Fristen.',
- ]
- },
- {
- header: 'Wie wird abgerechnet?',
- content: [
- 'Du zahlst monatlich einen festen Betrag im Prepaid-Format unabhängig davon wie viele Aufgaben tatsächlich anfallen.',
- 'Dadurch kannst du dein Budget besser einplanen und Aufgaben verteilen.'
- ]
- },
- {
- header: 'Kann ich den Retainer jederzeit kündigen?',
- content: [
- 'Ja klar. Du musst dabei nur die Mindestvertragslaufzeit beachten - die bezahlte Leistung steht dir weiterhin zu.',
- 'Falls wir merken, dass wir für eine Zusammenarbeit mehr als ungeeignet sind, bekommst du dein Geld anteilig der verbleibenden Zeit zurück und verlierst den Anspruch auf Leistungen ab diesem Zeitpunkt.'
- ]
- },
- {
- header: 'Was passiert, wenn ich dich mal weniger brauche?',
- content: [
- 'Der monatliche feste Betrag bleibt bestehen. Ähnlich wie bei einer Versicherung bezahlst du hier für den Fall, dass du mich brauchst und erhältst dann entsprechend die vereinbarten Leistungen.',
- 'Wenn du merkst, dass du mich nicht mehr brauchst, dann kannst du den Vertrag jederzeit kündigen und die Leistungen auslaufen lassen.'
- ]
- },
- {
- header: 'Für wen lohnt sich sowas überhaupt?',
- content: [
- 'Vor allem Unternehmen, bei denen immer wieder Aufgaben anfallen für die sich aber kein komplettes Projekt oder gar eine feste Stelle in der Firma lohnt, profitieren von dieser Art von Leistung.',
- 'Sie ist ideal für eine langfristige und zuverlässige Zusammenarbeit.'
- ]
- }
-]
+const { data: faq } = await useAsyncData('faq_flatrate', () => queryCollection('faq').path('/snippets/faq/flatrate').first())
+const texts = generatePlainText<['title']>(faq.value?.body.value)
+
+if (faq) {
+ useSchemaOrg({
+ '@context': 'https://schema.org',
+ '@type': 'FAQPage',
+ 'mainEntity': texts.map(entity => ({
+ '@type': 'Question',
+ 'name': entity.meta.title,
+ 'acceptedAnswer': {
+ '@type': 'Answer',
+ 'text': entity.text,
+ },
+ })),
+ })
+}
diff --git a/app/components/Section/Footer.vue b/app/components/Section/Footer.vue
index f01bdb7..4c71b98 100755
--- a/app/components/Section/Footer.vue
+++ b/app/components/Section/Footer.vue
@@ -1,18 +1,18 @@
diff --git a/app/components/Section/Header.vue b/app/components/Section/Header.vue
index 11368db..725fc68 100755
--- a/app/components/Section/Header.vue
+++ b/app/components/Section/Header.vue
@@ -12,7 +12,7 @@
-
+
diff --git a/app/components/Section/Intro.vue b/app/components/Section/Intro.vue
index 55609b2..396f561 100755
--- a/app/components/Section/Intro.vue
+++ b/app/components/Section/Intro.vue
@@ -3,10 +3,10 @@
Moin.
- Ich bin Fiona .
+ Ich bin Fiona .
- Component & API Entwicklerin
+ Component & API Entwicklerin
Ich unterstütze Unternehmen dabei, ihre Daten von verschiedenen Endpunkten sauber aufzubereiten
diff --git a/app/components/Section/Skills.vue b/app/components/Section/Skills.vue
index 53d8554..9eb7736 100755
--- a/app/components/Section/Skills.vue
+++ b/app/components/Section/Skills.vue
@@ -1,7 +1,7 @@
Meine Expertise.
- Dies sind meine Spezialgebiete - aber ich bin flexibel!
+ Dies sind meine Spezialgebiete - aber ich bin flexibel!
Neben den klassischen Webentwicklungsstandards JavaScript, HTML und CSS biete ich außerdem folgende Technologien.
-
+
diff --git a/app/components/Section/SkillsEasy.vue b/app/components/Section/SkillsEasy.vue
index d133c0a..2db7b93 100755
--- a/app/components/Section/SkillsEasy.vue
+++ b/app/components/Section/SkillsEasy.vue
@@ -1,13 +1,13 @@
Jetzt mal ganz konkret.
- In diesem Abschnitt ganz ohne Technik-Blabla .
+ In diesem Abschnitt ganz ohne Technik-Blabla .
Verwirkliche jetzt dein Webprojekt.
-
+
Lass mal reden
@@ -16,246 +16,5 @@
diff --git a/app/components/Spoiler.vue b/app/components/Spoiler.vue
index c064887..b136408 100644
--- a/app/components/Spoiler.vue
+++ b/app/components/Spoiler.vue
@@ -1,19 +1,19 @@
-
- {{ header }}
+
+ {{ title }}
diff --git a/app/pages/contact.vue b/app/pages/contact.vue
index a19ef5d..867bfbf 100755
--- a/app/pages/contact.vue
+++ b/app/pages/contact.vue
@@ -3,3 +3,10 @@
+
+
\ No newline at end of file
diff --git a/app/pages/flatrate.vue b/app/pages/flatrate.vue
index c1c2191..1eb8f68 100755
--- a/app/pages/flatrate.vue
+++ b/app/pages/flatrate.vue
@@ -3,3 +3,10 @@
+
+
\ No newline at end of file
diff --git a/app/pages/index.vue b/app/pages/index.vue
index 206f51b..72ab00e 100755
--- a/app/pages/index.vue
+++ b/app/pages/index.vue
@@ -14,4 +14,9 @@ useHead({
{ rel: 'icon', href: '/favicon.ico', type: 'image/x-icon' },
],
})
+
+useSeoMeta({
+ title: 'Home',
+ description: 'Webprojekte und Retainer mit Fusselqualität. Du brauchst eine Website mit CMS? Bock auf Flatrate? webfussel by Fiona Urban',
+})
\ No newline at end of file
diff --git a/app/pages/references.vue b/app/pages/references.vue
index e586151..3b724af 100755
--- a/app/pages/references.vue
+++ b/app/pages/references.vue
@@ -2,4 +2,11 @@
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/app/utils/markdown.ts b/app/utils/markdown.ts
new file mode 100644
index 0000000..adeaec8
--- /dev/null
+++ b/app/utils/markdown.ts
@@ -0,0 +1,27 @@
+import type { MinimalElement, MinimalNode } from '@nuxt/content'
+
+type TypedRecord = Record
+
+type PlainText = {
+ meta: TypedRecord
+ text: string
+}
+
+const extractText = (element ?: MinimalNode) : string => {
+ if (!element) return ''
+ if (typeof element === 'string') return element
+ const [,, ...nodes] = element
+ return nodes?.map((el : MinimalNode) => typeof el === 'string' ? el : extractText(el)).join(' ') ?? ''
+}
+
+export const generatePlainText = (body ?: MinimalNode[]) : PlainText[] => {
+ if (!body) return []
+ return body.map>(part => {
+ const [, meta] = part as MinimalElement
+ return {
+ meta : meta as TypedRecord,
+ text: extractText(part).replace(/\n/g, ' ')
+ }
+ })
+}
+
diff --git a/app/utils/socials.ts b/app/utils/socials.ts
index 8c5eb5e..8178280 100644
--- a/app/utils/socials.ts
+++ b/app/utils/socials.ts
@@ -18,12 +18,12 @@ export const socials = [
name: '@webfussel.de',
'aria-label': 'Externer Link: Bluesky Profil'
},
- {
- href: 'https://twitch.tv/webfussel',
- icon: 'ph:twitch-logo-duotone',
- name: 'webfussel',
- 'aria-label': 'Externer Link: Twitch Kanal'
- },
+ // {
+ // href: 'https://twitch.tv/webfussel',
+ // icon: 'ph:twitch-logo-duotone',
+ // name: 'webfussel',
+ // 'aria-label': 'Externer Link: Twitch Kanal'
+ // },
{
href: 'https://ko-fi.com/webfussel',
icon: 'wf:kofi',
diff --git a/content.config.ts b/content.config.ts
index 9ea361f..bcb3581 100644
--- a/content.config.ts
+++ b/content.config.ts
@@ -10,13 +10,13 @@ export default defineContentConfig({
skills: defineCollection({
type: 'page',
source: 'snippets/skills/*.md',
+ }),
+
+ faq: defineCollection({
+ type: 'page',
+ source: 'snippets/faq/*.md',
schema: z.object({
- title: z.string(),
- img: z.object({
- path: z.string(),
- name: z.string(),
- position: z.string(),
- }),
+ rawbody: z.string(),
})
})
}
diff --git a/content/snippets/faq/booking.md b/content/snippets/faq/booking.md
new file mode 100644
index 0000000..2af58b4
--- /dev/null
+++ b/content/snippets/faq/booking.md
@@ -0,0 +1,67 @@
+::spoiler
+---
+title: "Warum machst du keine Stundensätze?"
+---
+Ich finde, Stundensätze haben für beide Seiten nur Nachteile:
+
+Wenn ich schnell und gut arbeite, bekomme ich weniger Geld. Hab ich mal einen Knoten im Gehirn und brauche sehr lange, muss der Kunde mehr zahlen.
+
+Klar kann man sagen, dass sich das irgendwann ausgleichen könnte – aber so weit will ich es gar nicht erst kommen lassen.
+::
+
+::spoiler
+---
+title: "Was bekomme ich denn für die 999 €?"
+---
+Grundsätzlich kommt das natürlich immer darauf an, was du genau willst. Da spielen einige Faktoren eine Rolle.
+
+**Ein Beispiel für eine 999 € Seite wäre**
+
+---
+- Kein CMS
+- Eine Section auf der Landingpage
+- Impressum, Datenschutzerklärung
+- Standarddesign
+---
+
+Je nachdem was du willst, kann sich das aber auch anders zusammensetzen.
+Zu Beachten ist, dass es sich hier um reines HTML und CSS handelt. Keine Fancy Daten aus einem Backend, keine 3D-Animationen.
+Große und gute Websites werden normalerweise über einen längeren Zeitraum entwickelt und kosten entsprechend auch mehr.
+
+Bei diesem Startangebot handelt es sich also einfach nur um eine kleine Online-Visitenkarte.
+::
+
+::spoiler
+---
+title: "Welche Themen bietest du für deine Schulungen an?"
+---
+
+Sprachen
+ JavaScript, TypeScript, HTML, CSS
+
+
+
+Frameworks
+ Vue, Nuxt
+
+::
+
+::spoiler
+---
+title: "Wo finden die Schulungen statt?"
+---
+Die Schulungen finden online statt. Normalerweise nutze ich dafür Google Meet, aber wenn du oder deine Firma eine andere Plattform wünschen und bereitstellen bin ich natürlich flexibel.
+
+Wenn sich deine Firma in der Nähe meines Wohnortes befindet – und damit meine ich "In einer Stunde mit der Straßenbahn zu erreichen", dann kann alles natürlich auch vor Ort stattfinden.
+::
+
+::spoiler
+---
+title: "Ich hab ein cooles Projekt! Aber kein Geld …"
+---
+Tja.
+
+Ne, awas. Meld dich einfach trotzdem über meine E-Mail-Adresse und vielleicht finden wir eine Lösung.
+
+Du findest weitere Kontaktmöglichkeiten auf meiner [Kontakt-Seite ](/contact/){class="text inline-flex-row"}.
+::
\ No newline at end of file
diff --git a/content/snippets/faq/flatrate.md b/content/snippets/faq/flatrate.md
new file mode 100644
index 0000000..adee048
--- /dev/null
+++ b/content/snippets/faq/flatrate.md
@@ -0,0 +1,53 @@
+::spoiler
+---
+title: "Was ist eine Entwickler-Flatrate?"
+---
+Die Entwickler-Flatrate ist ein Angebot, bei dem du für eine bestimmte Zeit eine bestimmte Menge an Leistungen erhältst.
+
+Sie wird auch oft als sogenannter "Retainer" bezeichnet und ist eine günstige Art, immer wieder anfallende Aufgaben auszulagern.
+::
+
+::spoiler
+---
+title: "Wie läuft die Zusammenarbeit ab?"
+---
+Nach einer ersten Analyse deines Projekts legen wir gemeinsam den Umfang und die monatlichen Aufgaben fest. Du kannst mich je nach Paket auf den vereinbarten Wegen jederzeit kontaktieren.
+
+Die Bearbeitung erfolgt innerhalb der vereinbarten Fristen.
+::
+
+::spoiler
+---
+title: "Wie wird abgerechnet?"
+---
+Du zahlst monatlich einen festen Betrag im Prepaid-Format unabhängig davon, wie viele Aufgaben tatsächlich anfallen.
+
+Dadurch kannst du dein Budget besser einplanen und Aufgaben verteilen.
+::
+
+::spoiler
+---
+title: "Kann ich den Retainer jederzeit kündigen?"
+---
+Ja klar. Du musst dabei nur die Mindestvertragslaufzeit beachten – die bezahlte Leistung steht dir weiterhin zu.
+
+Falls wir merken, dass wir für eine Zusammenarbeit mehr als ungeeignet sind, bekommst du dein Geld anteilig der verbleibenden Zeit zurück und verlierst den Anspruch auf Leistungen ab diesem Zeitpunkt.
+::
+
+::spoiler
+---
+title: "Was passiert, wenn ich dich mal weniger brauche?"
+---
+Der monatliche feste Betrag bleibt bestehen. Ähnlich wie bei einer Versicherung bezahlst du hier für den Fall, dass du mich brauchst, und erhältst dann entsprechend die vereinbarten Leistungen.
+
+Wenn du merkst, dass du mich nicht mehr brauchst, dann kannst du den Vertrag jederzeit kündigen und die Leistungen auslaufen lassen.
+::
+
+::spoiler
+---
+title: "Für wen lohnt sich sowas überhaupt?"
+---
+Vor allem Unternehmen, bei denen immer wieder Aufgaben anfallen, für die sich aber kein komplettes Projekt oder gar eine feste Stelle in der Firma lohnt, profitieren von dieser Art von Leistung.
+
+Sie ist ideal für eine langfristige und zuverlässige Zusammenarbeit.
+::
\ No newline at end of file
diff --git a/content/snippets/skills/1.see.md b/content/snippets/skills/1.see.md
index fa0430c..0ea7a91 100644
--- a/content/snippets/skills/1.see.md
+++ b/content/snippets/skills/1.see.md
@@ -15,5 +15,5 @@ Diese Komponenten so zu entwickeln, dass sie wirklich flexibel sind und auch per
**Diese Voraussicht, für den Fall der Fälle vorzusorgen:**
**Die gibt's bei mir dazu.**
-Fussel-Ehrenwort.
+Fussel-Ehrenwort.
::
\ No newline at end of file
diff --git a/content/snippets/skills/2.edit.md b/content/snippets/skills/2.edit.md
index 0037d6a..b5b195a 100644
--- a/content/snippets/skills/2.edit.md
+++ b/content/snippets/skills/2.edit.md
@@ -7,12 +7,12 @@ image:
position: "right"
---
-In vielen Fällen ist es natürlich praktisch, wenn du einfach Inhalte einer Seite auf das ändern kannst, was gerade aktuell ist, ohne auf andere angewiesen zu sein. Damit das reibungslos möglich ist, entwickle ich dir gerne eine Anwendung oder Homepage, deren Inhalte du komplett selbst auf dem neuesten Stand halten kannst – und zwar mit einem sogenannten CMS – ein C ontent M anagement S ystem.
+In vielen Fällen ist es natürlich praktisch, wenn du einfach Inhalte einer Seite auf das ändern kannst, was gerade aktuell ist, ohne auf andere angewiesen zu sein. Damit das reibungslos möglich ist, entwickle ich dir gerne eine Anwendung oder Homepage, deren Inhalte du komplett selbst auf dem neuesten Stand halten kannst – und zwar mit einem sogenannten CMS – ein C ontent M anagement S ystem.
-Für CMS setze ich in erster Linie auf die cloudbasierte Lösung [Storyblok ](https://www.storyblok.com){class="inline-flex-row text"}. Dies stellt für die Meisten eine kostenlose bis kostengünstige Lösung dar ohne viel technisches Wissen mitbringen zu müssen.
+Für CMS setze ich in erster Linie auf die cloudbasierte Lösung [Storyblok ](https://www.storyblok.com){class="inline-flex-row text"}. Dies stellt für die Meisten eine kostenlose bis kostengünstige Lösung dar, ohne viel technisches Wissen mitbringen zu müssen.
-Falls du aber nicht möchtest, dass deine Daten auf irgendeinem Fremden Server liegen – was ich durchaus verstehen kann! – dann gibt es auch die Möglichkeit mit [Strapi ](https://strapi.io){class="inline-flex-row text"} selbst das eigene CMS zu hosten. Das musst du dann aber allerdings selbst erledigen oder ich erledige das für dich für einen Aufpreis.
+Falls du aber nicht möchtest, dass deine Daten auf irgendeinem Fremden Server liegen – was ich durchaus verstehen kann! – dann gibt es auch die Möglichkeit mit [Strapi ](https://strapi.io){class="inline-flex-row text"} selbst das eigene CMS zu hosten. Das musst du dann aber allerdings selbst erledigen oder ich erledige das für dich für einen Aufpreis.
**Nie wieder jemand anderen fragen zu müssen, um deine Website auf dem neuesten Stand zu halten.**
-Mit Fussel-Garantie.
+Mit Fussel-Garantie.
::
\ No newline at end of file
diff --git a/content/snippets/skills/3.result.md b/content/snippets/skills/3.result.md
index 02045db..bc2a49d 100644
--- a/content/snippets/skills/3.result.md
+++ b/content/snippets/skills/3.result.md
@@ -7,12 +7,12 @@ image:
position: "left"
---
-Grundsätzlich lässt sich das ganz einfach zusammenfassen: Dein persönlicher Webauftritt.
+Grundsätzlich lässt sich das ganz einfach zusammenfassen: Dein persönlicher Webauftritt.
-Ob du nun etwas kleineres brauchst, um ein paar Hobbies zu zeigen. Oder vielleicht etwas größeres, weil du dir ein eigenes Business aufbauen willst. Eventuell ein eigener Blog, eine komplette Applikation oder die Möglichkeit für andere Leute deine Daten bereit zu stellen. All das, das kann ich dir mit meinen Fähigkeiten und meiner Erfahrung bieten.
+Ob du nun etwas Kleineres brauchst, um ein paar Hobbys zu zeigen. Oder vielleicht etwas Größeres, weil du dir ein eigenes Business aufbauen willst. Eventuell ein eigener Blog, eine komplette Applikation oder die Möglichkeit für andere Leute deine Daten bereitzustellen. All das, das kann ich dir mit meinen Fähigkeiten und meiner Erfahrung bieten.
-Erkunde einfach meine [Referenzen ](references/){class="inline-flex-row text"} und dir wird auffallen, dass diese äußerst durchmischt und bunt sind. So individuell wie die Projekte meiner bisherigen Kunden wird auch dein Projekt behandelt. Und auch, wenn du glaubst, dass die Referenzen nicht zu deinem Projekt passen, werden wir deine ganz individuelle Lösung gemeinsam erarbeiten.
+Erkunde einfach meine [Referenzen ](references/){class="inline-flex-row text"} und dir wird auffallen, dass diese äußerst durchmischt und bunt sind. So individuell wie die Projekte meiner bisherigen Kunden wird auch dein Projekt behandelt. Und auch, wenn du glaubst, dass die Referenzen nicht zu deinem Projekt passen, werden wir deine ganz individuelle Lösung gemeinsam erarbeiten.
-**Denn jedes Projekt ist etwas eigenes und besonderes.**
-Auch deins.
+**Denn jedes Projekt ist etwas Eigenes und Besonderes.**
+Auch deins.
::
\ No newline at end of file
diff --git a/docker-compose.yaml b/docker-compose.yaml
new file mode 100644
index 0000000..7578ab9
--- /dev/null
+++ b/docker-compose.yaml
@@ -0,0 +1,47 @@
+version: '3'
+
+services:
+ wf4:
+ image: oven/bun:latest
+ container_name: wf4
+ working_dir: /app
+ ports:
+ - "1337:3000"
+ volumes:
+ - wf4_data:/app
+ environment:
+ - NODE_ENV=production
+ command: >
+ sh -c "
+ # Install git and curl if not already installed
+ if ! command -v git &> /dev/null || ! command -v curl &> /dev/null; then
+ echo 'Installing required packages...'
+ apt-get update && apt-get install -y git curl
+ fi &&
+
+ # Clone repository if not already cloned
+ if [ ! -d /app/.git ]; then
+ echo 'Cloning repository...'
+ git clone https://git.webfussel.de/webfussel/wf4 /tmp/wf4 &&
+ cp -r /tmp/wf4/. /app/ &&
+ rm -rf /tmp/wf4
+ fi &&
+
+ # Install dependencies and start application
+ echo 'Installing dependencies...' &&
+ bun install &&
+ echo 'Building application...' &&
+ bun run build &&
+ echo 'Starting application...' &&
+ bun .output/server/index.mjs
+ "
+ restart: unless-stopped
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:1337"]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+ start_period: 40s
+
+volumes:
+ wf4_data:
diff --git a/package-lock.json b/package-lock.json
index b36d5f2..35806ca 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3723,7 +3723,6 @@
},
"node_modules/@parcel/watcher-wasm/node_modules/napi-wasm": {
"version": "1.1.0",
- "dev": true,
"inBundle": true,
"license": "MIT"
},
diff --git a/public/img/network/orell@1x.webp b/public/img/network/orell@1x.webp
new file mode 100644
index 0000000..e47f07f
Binary files /dev/null and b/public/img/network/orell@1x.webp differ
diff --git a/public/img/network/orell@2x.webp b/public/img/network/orell@2x.webp
new file mode 100644
index 0000000..2425726
Binary files /dev/null and b/public/img/network/orell@2x.webp differ
diff --git a/public/img/network/orell@3x.webp b/public/img/network/orell@3x.webp
new file mode 100644
index 0000000..c011a41
Binary files /dev/null and b/public/img/network/orell@3x.webp differ