Compare commits

..

4 Commits

Author SHA1 Message Date
b8e77b2a54
Docker and Docker compose
All checks were successful
Build and deploy website / build (push) Successful in 3m26s
Updated dependencies

Updated workflow to use docker compose

Signed-off-by: Martin Berg Alstad <git@martials.no>
2024-10-12 19:07:55 +02:00
740cba625d
SSR and i18n
Signed-off-by: Martin Berg Alstad <git@martials.no>
2024-10-12 18:29:44 +02:00
1a2fec6a59
security.txt
Signed-off-by: Martin Berg Alstad <git@martials.no>
2024-10-12 17:12:12 +02:00
c701a510f7
Button group to switch languages.
Localized pathname function for links.

inlang/paraglide-astro package

Signed-off-by: Martin Berg Alstad <git@martials.no>
2024-10-09 21:30:05 +02:00
27 changed files with 1803 additions and 687 deletions

5
.dockerignore Normal file
View File

@ -0,0 +1,5 @@
.astro
.gitea
.DS_Store
node_modules
dist

5
.env
View File

@ -1,2 +1,3 @@
GIT_URL="https://git.martials.no" DOMAIN="martials.no"
STATUS_URL="https://status.martials.no/status/home" GIT_URL=https://git.$DOMAIN
STATUS_URL="https://status.$DOMAIN/status/home"

View File

@ -1,40 +0,0 @@
name: Build and deploy website
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Install dependencies
run: echo y | npm exec -- pnpm install
- name: Build
run: npm exec -- pnpm build
- name: Upload artifacts
uses: actions/upload-artifact@v3 # Deprecated and v4+ is not supported for GHES
with:
name: dist
path: dist
deploy:
runs-on: host
steps:
- name: Download artifacts
uses: actions/download-artifact@v3
with:
name: dist
path: dist
- name: Move files to server
run: |
rm -rf /var/www/beta.martials.no/*
cp -r dist/* /var/www/beta.martials.no

View File

@ -0,0 +1,19 @@
name: Build and deploy website
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: host
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Run docker-compose
run: docker compose up -d --build

26
Dockerfile Normal file
View File

@ -0,0 +1,26 @@
FROM node:lts-alpine AS base
WORKDIR /app
# By copying only the package.json and package-lock.json here, we ensure that the following `-deps` steps are independent of the source code.
# Therefore, the `-deps` steps will be skipped if only the source code changes.
COPY package.json pnpm-lock.yaml ./
COPY project.inlang ./project.inlang
FROM base AS prod-deps
RUN echo y | npm exec -- pnpm install --prod
FROM base AS build-deps
RUN npm exec -- pnpm install
FROM build-deps AS build
COPY . .
RUN npm exec -- pnpm run build
FROM base AS runtime
COPY --from=prod-deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
ENV HOST=0.0.0.0
ENV PORT=4321
EXPOSE 4321
CMD node ./dist/server/entry.mjs

View File

@ -1,13 +1,15 @@
// @ts-check // @ts-check
import { defineConfig } from "astro/config" import paraglide from "@inlang/paraglide-astro"
import tailwind from "@astrojs/tailwind" import tailwind from "@astrojs/tailwind"
import sitemap from "@astrojs/sitemap" import sitemap from "@astrojs/sitemap"
import { loadEnv } from "vite"
import mdx from "@astrojs/mdx"
import svelte from "@astrojs/svelte" import svelte from "@astrojs/svelte"
import node from "@astrojs/node"
import mdx from "@astrojs/mdx"
import icon from "astro-icon" import icon from "astro-icon"
import { defineConfig } from "astro/config"
import { loadEnv } from "vite"
const { url } = process.env.URL const { url } = process.env.URL
? loadEnv(process.env.URL, process.cwd(), "") ? loadEnv(process.env.URL, process.cwd(), "")
: { url: "http://localhost:3000" } : { url: "http://localhost:3000" }
@ -15,10 +17,24 @@ const { url } = process.env.URL
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
site: url, site: url,
// output: "server", TODO server | also required for i18n output: "server",
i18n: { i18n: {
defaultLocale: "nb", defaultLocale: "nb",
locales: ["nb", "en"] locales: ["nb", "en"]
}, },
integrations: [tailwind(), sitemap(), mdx(), svelte(), icon()] integrations: [
tailwind(),
sitemap(),
mdx(),
svelte(),
icon(),
paraglide({
// recommended settings
project: "./project.inlang",
outdir: "./src/paraglide" //where your files should be
})
],
adapter: node({
mode: "standalone"
})
}) })

8
docker-compose.yml Normal file
View File

@ -0,0 +1,8 @@
services:
web:
restart: always
build:
context: .
dockerfile: Dockerfile
ports:
- "4321:4321"

View File

@ -18,5 +18,8 @@
"subject": "Subject", "subject": "Subject",
"email": "Email", "email": "Email",
"message": "Message", "message": "Message",
"send": "Send" "send": "Send",
"auto": "Auto",
"norwegian": "Norwegian",
"english": "English"
} }

View File

@ -18,5 +18,8 @@
"subject": "Emne", "subject": "Emne",
"email": "E-post", "email": "E-post",
"message": "Melding", "message": "Melding",
"send": "Send" "send": "Send",
"auto": "Auto",
"norwegian": "Norsk",
"english": "Engelsk"
} }

View File

@ -10,26 +10,28 @@
"astro": "astro", "astro": "astro",
"postinstall": "paraglide-js compile --project ./project.inlang --outdir ./src/paraglide", "postinstall": "paraglide-js compile --project ./project.inlang --outdir ./src/paraglide",
"format": "prettier --write \"./src/**/*.{js,mjs,ts,astro,svelte,css,md,json}\"", "format": "prettier --write \"./src/**/*.{js,mjs,ts,astro,svelte,css,md,json}\"",
"watch-translations": "paraglide-js compile --watch --project ./project.inlang --outdir ./src/paraglide" "watch-messages": "paraglide-js compile --watch --project ./project.inlang --outdir ./src/paraglide"
}, },
"dependencies": { "dependencies": {
"@astrojs/check": "^0.9.3", "@astrojs/check": "^0.9.4",
"@astrojs/mdx": "^3.1.7", "@astrojs/mdx": "^3.1.8",
"@astrojs/sitemap": "^3.1.6", "@astrojs/node": "^8.3.4",
"@astrojs/svelte": "^5.7.1", "@astrojs/sitemap": "^3.2.0",
"@astrojs/tailwind": "^5.1.1", "@astrojs/svelte": "^5.7.2",
"@iconify-json/pajamas": "^1.2.2", "@astrojs/tailwind": "^5.1.2",
"@iconify-json/pajamas": "^1.2.3",
"@inlang/paraglide-astro": "^0.2.2",
"@inlang/paraglide-js": "1.11.2",
"@tailwindcss/typography": "^0.5.15", "@tailwindcss/typography": "^0.5.15",
"astro": "^4.15.9", "astro": "^4.16.2",
"astro-icon": "^1.1.1", "astro-icon": "^1.1.1",
"sharp": "^0.33.5", "sharp": "^0.33.5",
"svelte": "^4.2.19", "svelte": "^4.2.19",
"tailwindcss": "^3.4.13", "tailwindcss": "^3.4.13",
"typescript": "^5.6.2" "typescript": "^5.6.3"
}, },
"devDependencies": { "devDependencies": {
"@inlang/paraglide-js": "1.11.2", "daisyui": "^4.12.13",
"daisyui": "^4.12.10",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"prettier-plugin-astro": "^0.14.1", "prettier-plugin-astro": "^0.14.1",
"prettier-plugin-svelte": "^3.2.7", "prettier-plugin-svelte": "^3.2.7",

2165
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
Contact: mailto:security@martials.no
Expires: 2029-12-31T23:00:00.000Z
Preferred-Languages: no,en
Canonical: https://martials.no/.well-known/security.txt

View File

@ -1,18 +1,21 @@
--- ---
import GiteaLink from "./links/GiteaLink.astro" import GiteaLink from "./links/GiteaLink.astro"
import ExternalLink from "./links/ExternalLink.astro"
import PajamasIcon from "./icons/PajamasIcon.astro" import PajamasIcon from "./icons/PajamasIcon.astro"
import ExternalLink from "./links/ExternalLink.astro"
import LanguageButtonGroup from "./LanguageButtonGroup.astro"
import * as m from "@/paraglide/messages" import * as m from "@/paraglide/messages"
const gitUrl = import.meta.env.GIT_URL const { GIT_URL, STATUS_URL } = import.meta.env
const statusUrl = import.meta.env.STATUS_URL
--- ---
<div class="divider"></div> <div class="divider" />
<div class="mx-auto py-5 flex flex-col gap-1 items-center"> <div class="py-5 flex flex-row gap-1 justify-around w-full items-center">
<GiteaLink href={`${gitUrl}/martials/martials.no`} /> <div>
<ExternalLink href={statusUrl} class="flex items-center" title="Status"> <GiteaLink href={`${GIT_URL}/martials/martials.no`} />
<PajamasIcon name="pajamas:status-health" class="w-6 h-6 mr-2" /> <ExternalLink href={STATUS_URL} class="flex items-center" title="Status">
{m.status()} <PajamasIcon name="pajamas:status-health" class="w-6 h-6 mr-2" />
</ExternalLink> {m.status()}
</ExternalLink>
</div>
<LanguageButtonGroup />
</div> </div>

View File

@ -0,0 +1,11 @@
---
import LocaleLink from "./links/LocaleLink.astro"
import { type NavLink, resolvePathname } from "@/utils/linking"
const currentPath = resolvePathname(Astro.url.pathname)
---
<div class="join">
<LocaleLink to={currentPath as NavLink} lang="nb" class="btn join-item">Norsk</LocaleLink>
<LocaleLink to={currentPath as NavLink} lang="en" class="btn join-item">English</LocaleLink>
</div>

View File

@ -1,13 +1,14 @@
--- ---
import Links from "../links" import LocaleLink from "./links/LocaleLink.astro"
import Links from "@/links"
--- ---
<div class="flex justify-end"> <div class="flex justify-end">
{ {
Links.map(({ to, label }) => ( Links.map(({ to, label }) => (
<a href={to} class="m-2 hover:underline"> <LocaleLink to={to} class="m-2 hover:underline">
{label} {label()}
</a> </LocaleLink>
)) ))
} }
</div> </div>

View File

@ -0,0 +1,16 @@
---
import { languageTag, type AvailableLanguageTag } from "@/paraglide/runtime"
import { localizePathname, type NavLink } from "@/utils/linking"
import type { ComponentProps } from "@/types/props"
interface Props extends ComponentProps {
to: NavLink
lang?: AvailableLanguageTag
}
const { to, class: clazz, lang = languageTag() } = Astro.props
---
<a href={localizePathname(to, lang)} class={clazz}>
<slot />
</a>

1
src/env.d.ts vendored
View File

@ -2,6 +2,7 @@
/// <reference types="astro/client" /> /// <reference types="astro/client" />
interface ImportMetaEnv { interface ImportMetaEnv {
readonly DOMAIN: string
readonly URL: string readonly URL: string
readonly GIT_URL: string readonly GIT_URL: string
readonly STATUS_URL: string readonly STATUS_URL: string

View File

@ -1,23 +1,32 @@
import * as m from "./paraglide/messages.js" import * as m from "@/paraglide/messages.js"
import type { NavLink } from "@/utils/linking.ts"
interface Link { interface Link {
label: string label: () => string
to: string to: NavLink
} }
const Links: Link[] = [ const Links: Link[] = [
{ {
label: m.home(), label: m.home,
to: "/", to: "/"
}, },
{ {
label: m.myProjects(), label: m.myProjects,
to: "/project", to: "/projects"
}, },
{ {
label: m.contactMe(), label: m.myLinks,
to: "/contact-me", to: "/links"
}, },
{
label: m.hardware,
to: "/hardware"
},
{
label: m.contactMe,
to: "/contact"
}
] ]
export default Links export default Links

View File

@ -0,0 +1,15 @@
import type { APIRoute } from "astro"
function getSecurityTxt(site?: URL) {
const canonical = new URL("/.well-known/security.txt", site)
return `
Contact: mailto:security@martials.no
Expires: 2029-12-31T23:00:00.000Z
Preferred-Languages: no,en
Canonical: ${canonical.href}
`
}
export const GET: APIRoute = ({ site }) => {
return new Response(getSecurityTxt(site))
}

View File

@ -2,6 +2,8 @@
import ProjectPage from "../../../components/projects/ProjectPage.astro" import ProjectPage from "../../../components/projects/ProjectPage.astro"
import { type GetStaticPathsResult } from "astro" import { type GetStaticPathsResult } from "astro"
export const prerender = true
export function getStaticPaths(): GetStaticPathsResult { export function getStaticPaths(): GetStaticPathsResult {
return [ return [
{ params: { project: "hotelservice" } }, { params: { project: "hotelservice" } },

View File

@ -2,6 +2,8 @@
import ProjectPage from "../../components/projects/ProjectPage.astro" import ProjectPage from "../../components/projects/ProjectPage.astro"
import { type GetStaticPathsResult } from "astro" import { type GetStaticPathsResult } from "astro"
export const prerender = true
export function getStaticPaths(): GetStaticPathsResult { export function getStaticPaths(): GetStaticPathsResult {
return [ return [
{ params: { project: "hotelservice" } }, { params: { project: "hotelservice" } },

1
src/types/types.ts Normal file
View File

@ -0,0 +1 @@
export type AbsolutePathname = `/${string}`

49
src/utils/linking.ts Normal file
View File

@ -0,0 +1,49 @@
import type { AvailableLanguageTag } from "@/paraglide/runtime.js"
import type { AbsolutePathname } from "@/types/types.ts"
interface TranslatedPathnames {
nb: AbsolutePathname
en: `/en${string}`
}
export type NavLink = "/" | "/contact" | "/projects" | "/links" | "/hardware"
const paths: NavLink[] = [
"/",
"/contact",
"/projects",
"/links",
"/hardware"
]
/**
* Defines the localized pathnames for the site.
* The key must be used to navigate to the correct path.
* The value is the path that will be used for the given locale.
*
* @see https://inlang.com/m/iljlwzfs/paraglide-astro-i18n
*/
const pathnames: Record<AbsolutePathname, TranslatedPathnames> = {}
for (const path of paths) {
pathnames[path] = {
nb: path,
en: `/en${path}`
}
}
export function localizePathname(
pathname: NavLink,
locale: AvailableLanguageTag
) {
if (pathnames[pathname]) {
return pathnames[pathname][locale]
}
return pathname
}
export function resolvePathname(pathname: string): AbsolutePathname {
if (pathname.startsWith("/en")) {
return pathname.slice(3) as AbsolutePathname
}
return pathname as AbsolutePathname
}