Compare commits
4 Commits
87184c431d
...
b8e77b2a54
Author | SHA1 | Date | |
---|---|---|---|
b8e77b2a54 | |||
740cba625d | |||
1a2fec6a59 | |||
c701a510f7 |
5
.dockerignore
Normal file
5
.dockerignore
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.astro
|
||||||
|
.gitea
|
||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
dist
|
5
.env
5
.env
@ -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"
|
@ -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
|
|
19
.gitea/workflows/deploy.yaml
Normal file
19
.gitea/workflows/deploy.yaml
Normal 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
26
Dockerfile
Normal 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
|
@ -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
8
docker-compose.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
services:
|
||||||
|
web:
|
||||||
|
restart: always
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "4321:4321"
|
@ -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"
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
|
24
package.json
24
package.json
@ -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
2165
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
4
public/.well-known/security.txt
Normal file
4
public/.well-known/security.txt
Normal 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
|
@ -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`} />
|
||||||
|
<ExternalLink href={STATUS_URL} class="flex items-center" title="Status">
|
||||||
<PajamasIcon name="pajamas:status-health" class="w-6 h-6 mr-2" />
|
<PajamasIcon name="pajamas:status-health" class="w-6 h-6 mr-2" />
|
||||||
{m.status()}
|
{m.status()}
|
||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
</div>
|
</div>
|
||||||
|
<LanguageButtonGroup />
|
||||||
|
</div>
|
||||||
|
11
src/components/LanguageButtonGroup.astro
Normal file
11
src/components/LanguageButtonGroup.astro
Normal 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>
|
@ -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>
|
||||||
|
16
src/components/links/LocaleLink.astro
Normal file
16
src/components/links/LocaleLink.astro
Normal 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
1
src/env.d.ts
vendored
@ -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
|
||||||
|
27
src/links.ts
27
src/links.ts
@ -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
|
||||||
|
15
src/pages/.well-known/security.txt.ts
Normal file
15
src/pages/.well-known/security.txt.ts
Normal 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))
|
||||||
|
}
|
@ -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" } },
|
@ -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
1
src/types/types.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type AbsolutePathname = `/${string}`
|
49
src/utils/linking.ts
Normal file
49
src/utils/linking.ts
Normal 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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user