Compare commits
4 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5aea1d3946 | ||
|
|
13c5193344 | ||
|
|
e459bb20ee | ||
|
|
4b7ec853b1 |
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
/graphics
|
/graphics
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
**/*.pw
|
|
||||||
**/*.secret
|
**/*.secret
|
||||||
|
**/*.pw
|
||||||
mounts/
|
mounts/
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,7 @@ RUN --mount=type=cache,target=/usr/local/cargo,from=rust:latest,source=/usr/loca
|
||||||
cargo build --release --features $adaptor && mv ./target/release/crabfit-api ./api
|
cargo build --release --features $adaptor && mv ./target/release/crabfit-api ./api
|
||||||
|
|
||||||
# Runtime image
|
# Runtime image
|
||||||
FROM debian:bookworm-slim
|
FROM debian:bullseye-slim
|
||||||
|
|
||||||
# install libssl3
|
|
||||||
RUN apt-get update &&\
|
|
||||||
apt-get install -yq libssl3 &&\
|
|
||||||
apt-get clean &&\
|
|
||||||
rm -rf /var/cache/apt/lists/*
|
|
||||||
|
|
||||||
# Run as "app" user
|
# Run as "app" user
|
||||||
RUN useradd -ms /bin/bash app
|
RUN useradd -ms /bin/bash app
|
||||||
|
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
services:
|
|
||||||
crabfit-api:
|
|
||||||
build:
|
|
||||||
context: ./api
|
|
||||||
# args:
|
|
||||||
# adaptor: sql-adaptor (default) | memory-adaptor | datastore-adaptor
|
|
||||||
# # datastore is for Google Datastore
|
|
||||||
secrets:
|
|
||||||
- crabfit-database-password
|
|
||||||
- crabfit-cron-key
|
|
||||||
environment:
|
|
||||||
DATABASE_PASSWORD_FILE: /run/secrets/crabfit-database-password
|
|
||||||
DATABASE_URL: postgresql://crabfit@crabfit-database:5432/crabfit
|
|
||||||
FRONTEND_URL: https://availability.techwork.zone
|
|
||||||
CRON_KEY_FILE: /run/secrets/crabfit-cron-key
|
|
||||||
labels:
|
|
||||||
traefik.enable: true
|
|
||||||
traefik.http.routers.crabfit-api.rule: Host(`api.a10y.techwork.zone`)
|
|
||||||
traefik.http.routers.crabfit-api.tls: true
|
|
||||||
traefik.http.routers.crabfit-api.tls.certresolver: letsencrypt_standalone
|
|
||||||
networks:
|
|
||||||
- crabfit
|
|
||||||
- public
|
|
||||||
|
|
||||||
crabfit-database:
|
|
||||||
image: postgres:17
|
|
||||||
secrets: [ 'crabfit-database-password' ]
|
|
||||||
environment:
|
|
||||||
POSTGRES_PASSWORD_FILE: /run/secrets/crabfit-database-password
|
|
||||||
POSTGRES_USER: crabfit
|
|
||||||
POSTGRES_DB: crabfit
|
|
||||||
volumes:
|
|
||||||
- ./mounts/database:/var/lib/postgresql/data
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "pg_isready"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 20s
|
|
||||||
retries: 3
|
|
||||||
networks: [ crabfit ]
|
|
||||||
|
|
||||||
crabfit-frontend:
|
|
||||||
build:
|
|
||||||
context: ./frontend
|
|
||||||
labels:
|
|
||||||
traefik.enable: true
|
|
||||||
traefik.http.routers.crabfit-frontend.rule: Host(`a10y.techwork.zone`) || Host(`availability.techwork.zone`)
|
|
||||||
traefik.http.routers.crabfit-frontend.tls: true
|
|
||||||
traefik.http.routers.crabfit-frontend.tls.certresolver: letsencrypt_standalone
|
|
||||||
networks: [ public ]
|
|
||||||
|
|
||||||
|
|
||||||
networks:
|
|
||||||
crabfit:
|
|
||||||
internal: true
|
|
||||||
|
|
||||||
secrets:
|
|
||||||
crabfit-database-password:
|
|
||||||
file: ./postgres.pw
|
|
||||||
crabfit-cron-key:
|
|
||||||
file: ./cron.secret
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
NEXT_PUBLIC_API_URL="https://api.a10y.techwork.zone"
|
NEXT_PUBLIC_API_URL="http://127.0.0.1:3000"
|
||||||
NEXT_PUBLIC_HOSTNAME=availability.techwork.zone
|
|
||||||
|
|
||||||
# Google auth for calendar syncing, feature will be disabled if these aren't set
|
# Google auth for calendar syncing, feature will be disabled if these aren't set
|
||||||
# NEXT_PUBLIC_GOOGLE_CLIENT_ID=""
|
# NEXT_PUBLIC_GOOGLE_CLIENT_ID=""
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
"@giraugh/tools": "^1.6.0",
|
"@giraugh/tools": "^1.6.0",
|
||||||
"@js-temporal/polyfill": "^0.4.4",
|
"@js-temporal/polyfill": "^0.4.4",
|
||||||
"@microsoft/microsoft-graph-client": "^3.0.5",
|
"@microsoft/microsoft-graph-client": "^3.0.5",
|
||||||
|
"@vercel/analytics": "^1.0.1",
|
||||||
"accept-language": "^3.0.18",
|
"accept-language": "^3.0.18",
|
||||||
"chroma.ts": "^1.0.10",
|
"chroma.ts": "^1.0.10",
|
||||||
"hue-map": "^1.0.0",
|
"hue-map": "^1.0.0",
|
||||||
|
|
@ -46,10 +47,5 @@
|
||||||
"sass": "^1.63.4",
|
"sass": "^1.63.4",
|
||||||
"typescript": "^5.1.3",
|
"typescript": "^5.1.3",
|
||||||
"typescript-plugin-css-modules": "^5.0.1"
|
"typescript-plugin-css-modules": "^5.0.1"
|
||||||
},
|
|
||||||
"prettier": {
|
|
||||||
"semi": false,
|
|
||||||
"arrowParens": "avoid",
|
|
||||||
"singleQuote": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import { makeClass, relativeTimeFormat } from '/src/utils'
|
||||||
|
|
||||||
import EventAvailabilities from './EventAvailabilities'
|
import EventAvailabilities from './EventAvailabilities'
|
||||||
import styles from './page.module.scss'
|
import styles from './page.module.scss'
|
||||||
import appLink from '/src/utils/appLink'
|
|
||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: { id: string }
|
params: { id: string }
|
||||||
|
|
@ -50,10 +49,10 @@ const Page = async ({ params }: PageProps) => {
|
||||||
>{t('common:created', { date: relativeTimeFormat(Temporal.Instant.fromEpochSeconds(event.created_at), i18n.language) })}</span>
|
>{t('common:created', { date: relativeTimeFormat(Temporal.Instant.fromEpochSeconds(event.created_at), i18n.language) })}</span>
|
||||||
|
|
||||||
<Copyable className={styles.info}>
|
<Copyable className={styles.info}>
|
||||||
{appLink(event.id)}
|
{`https://crab.fit/${event.id}`}
|
||||||
</Copyable>
|
</Copyable>
|
||||||
<p className={makeClass(styles.info, styles.noPrint)}>
|
<p className={makeClass(styles.info, styles.noPrint)}>
|
||||||
<Trans i18nKey="event:nav.shareinfo" t={t} i18n={i18n}>_<a href={`mailto:?subject=${encodeURIComponent(t('event:nav.email_subject', { event_name: event.name }))}&body=${encodeURIComponent(`${t('event:nav.email_body')} ${appLink(event.id)}`)}`}>_</a>_</Trans>
|
<Trans i18nKey="event:nav.shareinfo" t={t} i18n={i18n}>_<a href={`mailto:?subject=${encodeURIComponent(t('event:nav.email_subject', { event_name: event.name }))}&body=${encodeURIComponent(`${t('event:nav.email_body')} https://crab.fit/${event.id}`)}`}>_</a>_</Trans>
|
||||||
</p>
|
</p>
|
||||||
</Content>
|
</Content>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Metadata } from 'next'
|
import { Metadata } from 'next'
|
||||||
import { Karla } from 'next/font/google'
|
import { Karla } from 'next/font/google'
|
||||||
|
import { Analytics } from '@vercel/analytics/react'
|
||||||
|
|
||||||
import Egg from '/src/components/Egg/Egg'
|
import Egg from '/src/components/Egg/Egg'
|
||||||
import Settings from '/src/components/Settings/Settings'
|
import Settings from '/src/components/Settings/Settings'
|
||||||
|
|
@ -8,12 +9,11 @@ import { fallbackLng } from '/src/i18n/options'
|
||||||
import { useTranslation } from '/src/i18n/server'
|
import { useTranslation } from '/src/i18n/server'
|
||||||
|
|
||||||
import './global.css'
|
import './global.css'
|
||||||
import appLink from '../utils/appLink'
|
|
||||||
|
|
||||||
const karla = Karla({ subsets: ['latin'] })
|
const karla = Karla({ subsets: ['latin'] })
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
metadataBase: new URL(appLink('')),
|
metadataBase: new URL('https://crab.fit'),
|
||||||
title: {
|
title: {
|
||||||
absolute: 'Crab Fit',
|
absolute: 'Crab Fit',
|
||||||
template: '%s - Crab Fit',
|
template: '%s - Crab Fit',
|
||||||
|
|
@ -44,6 +44,7 @@ const RootLayout = async ({ children }: { children: React.ReactNode }) => {
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
|
<Analytics />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,10 @@ const Page = async () => {
|
||||||
|
|
||||||
<Video />
|
<Video />
|
||||||
|
|
||||||
{/* <DownloadButtons /> */}
|
<DownloadButtons />
|
||||||
|
|
||||||
<P><Trans i18nKey="about.content.p3" t={t} i18n={i18n}>_<a href="https://bengrant.dev" target="_blank" rel="noreferrer noopener author">_</a><a href="https://techwork.zone" target="_blank" rel="noreferrer noopener">_</a>_</Trans></P>
|
<P><Trans i18nKey="about.content.p3" t={t} i18n={i18n}>_<a href="https://bengrant.dev" target="_blank" rel="noreferrer noopener author">_</a>_</Trans></P>
|
||||||
<P><Trans i18nKey="about.content.p4" t={t} i18n={i18n}>_<a href="https://git.techwork.zone/scott/crabfit" target="_blank" rel="noreferrer noopener">_</a>_<Link href="/privacy" rel="license">_</Link>_</Trans></P>
|
<P><Trans i18nKey="about.content.p4" t={t} i18n={i18n}>_<a href="https://github.com/GRA0007/crab.fit" target="_blank" rel="noreferrer noopener">_</a>_<Link href="/privacy" rel="license">_</Link>_</Trans></P>
|
||||||
<P>{t('about.content.p6')}</P>
|
<P>{t('about.content.p6')}</P>
|
||||||
<P>{t('about.content.p5')}</P>
|
<P>{t('about.content.p5')}</P>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import { EventResponse } from '/src/config/api'
|
||||||
import { useTranslation } from '/src/i18n/client'
|
import { useTranslation } from '/src/i18n/client'
|
||||||
|
|
||||||
import styles from './EventInfo.module.scss'
|
import styles from './EventInfo.module.scss'
|
||||||
import appLink from '/src/utils/appLink'
|
|
||||||
|
|
||||||
interface EventInfoProps {
|
interface EventInfoProps {
|
||||||
event: EventResponse
|
event: EventResponse
|
||||||
|
|
@ -17,10 +16,10 @@ const EventInfo = ({ event }: EventInfoProps) => {
|
||||||
return <div className={styles.wrapper}>
|
return <div className={styles.wrapper}>
|
||||||
<h2>{event.name}</h2>
|
<h2>{event.name}</h2>
|
||||||
<Copyable className={styles.info}>
|
<Copyable className={styles.info}>
|
||||||
{appLink(event.id)}
|
{`https://crab.fit/${event.id}`}
|
||||||
</Copyable>
|
</Copyable>
|
||||||
<p className={styles.info}>
|
<p className={styles.info}>
|
||||||
<Trans i18nKey="event:nav.shareinfo_alt" t={t} i18n={i18n}>_<a href={`mailto:?subject=${encodeURIComponent(t('nav.email_subject', { event_name: event.name }))}&body=${encodeURIComponent(`${t('nav.email_body')} ${appLink(event.id)}`)}`} target="_blank">_</a>_</Trans>
|
<Trans i18nKey="event:nav.shareinfo_alt" t={t} i18n={i18n}>_<a href={`mailto:?subject=${encodeURIComponent(t('nav.email_subject', { event_name: event.name }))}&body=${encodeURIComponent(`${t('nav.email_body')} https://crab.fit/${event.id}`)}`} target="_blank">_</a>_</Trans>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import logo from '/src/res/logo.svg'
|
||||||
import { makeClass } from '/src/utils'
|
import { makeClass } from '/src/utils'
|
||||||
|
|
||||||
import styles from './Header.module.scss'
|
import styles from './Header.module.scss'
|
||||||
import appLink from '/src/utils/appLink'
|
|
||||||
|
|
||||||
const samuraiBob = localFont({
|
const samuraiBob = localFont({
|
||||||
src: './samuraibob.woff2',
|
src: './samuraibob.woff2',
|
||||||
|
|
@ -30,11 +29,11 @@ const Header = async ({ isFull, isSmall }: HeaderProps) => {
|
||||||
{isFull ? <>
|
{isFull ? <>
|
||||||
{!isSmall && <img className={styles.bigLogo} src={logo.src} height={512} width={512} alt="" />}
|
{!isSmall && <img className={styles.bigLogo} src={logo.src} height={512} width={512} alt="" />}
|
||||||
<span className={makeClass(styles.subtitle, samuraiBob.className, !/^[A-Za-z ]+$/.test(t('home:create')) && styles.hasAltChars)}>{t('home:create')}</span>
|
<span className={makeClass(styles.subtitle, samuraiBob.className, !/^[A-Za-z ]+$/.test(t('home:create')) && styles.hasAltChars)}>{t('home:create')}</span>
|
||||||
<h1 className={makeClass(styles.bigTitle, molot.className)}>MEETING</h1>
|
<h1 className={makeClass(styles.bigTitle, molot.className)}>CRAB FIT</h1>
|
||||||
</> : <Link href="/" className={styles.link}>
|
</> : <Link href="/" className={styles.link}>
|
||||||
<div className={styles.top}>
|
<div className={styles.top}>
|
||||||
<img className={styles.logo} src={logo.src} height={512} width={512} alt="" />
|
<img className={styles.logo} src={logo.src} height={512} width={512} alt="" />
|
||||||
<span className={makeClass(styles.title, molot.className)}>MEETING</span>
|
<span className={makeClass(styles.title, molot.className)}>CRAB FIT</span>
|
||||||
</div>
|
</div>
|
||||||
<span className={styles.tagline}>{t('common:tagline')}</span>
|
<span className={styles.tagline}>{t('common:tagline')}</span>
|
||||||
</Link>}
|
</Link>}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"cta": "Create your own Crab Fit!",
|
"cta": "Create your own Crab Fit!",
|
||||||
"created": "Created {{date}}",
|
"created": "Created {{date}}",
|
||||||
"donate": {
|
"donate": {
|
||||||
"info": "Thank you for using Crab Fit. If you like it, consider donating to the upstream author.",
|
"info": "Thank you for using Crab Fit. If you like it, consider donating.",
|
||||||
"button": "Donate",
|
"button": "Donate",
|
||||||
"title": "Every amount counts :)"
|
"title": "Every amount counts :)"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@
|
||||||
"events": "Events created",
|
"events": "Events created",
|
||||||
"availabilities": "Availabilities entered",
|
"availabilities": "Availabilities entered",
|
||||||
"content": {
|
"content": {
|
||||||
"p1": "This service is a soft fork of Crab Fit. Crab Fit helps you fit your event around everyone's schedules. Simply create an event above and send the link to everyone that is participating. Results update live and you will be able to see a heat-map of when everyone is free.<1/><2>Learn more about how to Crab Fit</2>.",
|
"p1": "Crab Fit helps you fit your event around everyone's schedules. Simply create an event above and send the link to everyone that is participating. Results update live and you will be able to see a heat-map of when everyone is free.<1/><2>Learn more about how to Crab Fit</2>.",
|
||||||
"p3": "Created by <1>Ben Grant</1> and hosted by the <2>Tech Workers' Syndicate</2>, Crab Fit is the modern-day solution to your group event planning debates.",
|
"p3": "Created by <1>Ben Grant</1>, Crab Fit is the modern-day solution to your group event planning debates.",
|
||||||
"p4": "The code for Crab Fit is open source, if you find any issues or want to contribute, you can visit the <1>repository</1>. By using Crab Fit you agree to the <3>privacy policy</3>.",
|
"p4": "The code for Crab Fit is open source, if you find any issues or want to contribute, you can visit the <1>repository</1>. By using Crab Fit you agree to the <3>privacy policy</3>.",
|
||||||
"p6": "To protect your privacy, events are deleted after 3 months of inactivity, and all passwords are securely hashed.",
|
"p6": "To protect your privacy, events are deleted after 3 months of inactivity, and all passwords are securely hashed.",
|
||||||
"p5": "Consider donating below if it helped you out so Crab Fit can stay free for everyone. 🦀"
|
"p5": "Consider donating below if it helped you out so Crab Fit can stay free for everyone. 🦀"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
export default function appLink(path: string) {
|
|
||||||
const proto = process?.env?.NODE_ENV === 'production' ? 'https' : 'http'
|
|
||||||
const host =
|
|
||||||
process?.env?.NEXT_PUBLIC_HOSTNAME ??
|
|
||||||
(process?.env?.NODE_ENV === 'production' ? 'crab.fit' : 'localhost:3000')
|
|
||||||
return `${proto}://${host}/${path}`
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue