diff --git a/components/BlogBlocks.tsx b/components/BlogBlocks.tsx
index 9199c7e..328a574 100644
--- a/components/BlogBlocks.tsx
+++ b/components/BlogBlocks.tsx
@@ -69,9 +69,7 @@ export function BlogPost(
@@ -90,9 +88,7 @@ export function BlogPost(
function NewsTags({ tags }: Pick
) {
return (
- {tags
- ? tags.map((tag) => {tag} )
- : Aucun tag }
+ {tags ? tags.map((tag) => {tag} ) : Aucun tag }
)
}
diff --git a/components/Picture.tsx b/components/Picture.tsx
index e4e888b..1e4fa57 100644
--- a/components/Picture.tsx
+++ b/components/Picture.tsx
@@ -3,26 +3,26 @@ import { Ensure } from ':types'
import { JSX } from 'preact'
export type PictureProps =
- & { formats: string[] }
- & Ensure, 'src' | 'alt' | 'loading'>
+ & { formats: string[] }
+ & Ensure, 'src' | 'alt' | 'loading'>
export function Picture(
- { formats, src, ...props }: PictureProps,
+ { formats, src, ...props }: PictureProps,
) {
- const groups = unwrapSignalOrValue(src)?.match(/(?.*)(?\.\w+)/)
- ?.groups
- if (groups === undefined) {
- throw new SyntaxError(`unable to parse path of "${src.valueOf()}"`)
- }
+ const groups = unwrapSignalOrValue(src)?.match(/(?.*)(?\.\w+)/)
+ ?.groups
+ if (groups === undefined) {
+ throw new SyntaxError(`unable to parse path of "${src.valueOf()}"`)
+ }
- const { path } = groups
+ const { path } = groups
- return (
-
- {formats.map((format) => (
-
- ))}
-
-
- )
+ return (
+
+ {formats.map((format) => (
+
+ ))}
+
+
+ )
}
diff --git a/dev.ts b/dev.ts
index 3444176..2fbec6a 100644
--- a/dev.ts
+++ b/dev.ts
@@ -6,9 +6,9 @@ import config from './fresh.config.ts'
import '$std/dotenv/load.ts'
await dev(import.meta.url, './main.ts', {
- ...config,
- server: {
- cert: await Deno.readTextFile('./cert/localhost.pem'),
- key: await Deno.readTextFile('./cert/localhost-key.pem'),
- },
+ ...config,
+ server: {
+ cert: await Deno.readTextFile('./cert/localhost.pem'),
+ key: await Deno.readTextFile('./cert/localhost-key.pem'),
+ },
})
diff --git a/islands/BlogCardList.tsx b/islands/BlogCardList.tsx
index 9c69f04..d51022d 100644
--- a/islands/BlogCardList.tsx
+++ b/islands/BlogCardList.tsx
@@ -3,56 +3,56 @@ import CardList from ':islands/CardList.tsx'
import type { Ref } from 'preact'
export default function BlogCardList(
- { limit, usePlaceholder, useObserver }: {
- limit?: number
- usePlaceholder?: boolean
- useObserver?: boolean
- },
+ { limit, usePlaceholder, useObserver }: {
+ limit?: number
+ usePlaceholder?: boolean
+ useObserver?: boolean
+ },
) {
- if (usePlaceholder) {
- return (
-
- )
- }
+ if (usePlaceholder) {
+ return (
+
+ )
+ }
- return (
-
- )
+ return (
+
+ )
}
function builder(news: BlogProps) {
- return BlogCard({ ...news, lastUpdate: new Date(news.lastUpdate) })
+ return BlogCard({ ...news, lastUpdate: new Date(news.lastUpdate) })
}
function Placeholder({ ref }: { ref?: Ref | undefined }) {
- return (
-
-
Chargement ...
-
- )
+ return (
+
+
Chargement ...
+
+ )
}
function Fallback() {
- return (
-
-
Pas de news disponible
-
- )
+ return (
+
+
Pas de news disponible
+
+ )
}
diff --git a/islands/CardList.tsx b/islands/CardList.tsx
index 815fead..336e86e 100644
--- a/islands/CardList.tsx
+++ b/islands/CardList.tsx
@@ -7,97 +7,96 @@ import { useEffect, useRef } from 'preact/hooks'
export type Builder = (props: T) => JSX.Element
export type CardListProps = {
- apiRoute: string
- builder: Builder
- limit?: number
+ apiRoute: string
+ builder: Builder
+ limit?: number
} | {
- apiRoute: string
- builder: Builder
- limit?: number
- useObserver?: boolean
- placeholder: ({ ref }: { ref: Ref | undefined }) => JSX.Element
- fallback: Fallback
+ apiRoute: string
+ builder: Builder
+ limit?: number
+ useObserver?: boolean
+ placeholder: ({ ref }: { ref: Ref | undefined }) => JSX.Element
+ fallback: Fallback
}
export default function CardList(
- { limit, builder, apiRoute, ...props }: CardListProps,
+ { limit, builder, apiRoute, ...props }: CardListProps,
) {
- const list: Signal = useSignal([])
- const ac = new AbortController()
- const ref = useRef(null)
+ const list: Signal = useSignal([])
+ const ac = new AbortController()
+ const ref = useRef(null)
- const useObserver = 'useObserver' in props ? props.useObserver : false
- const placeholder = 'placeholder' in props ? props.placeholder : false
- const fallback = 'fallback' in props ? props.fallback : false
+ const useObserver = 'useObserver' in props ? props.useObserver : false
+ const placeholder = 'placeholder' in props ? props.placeholder : false
+ const fallback = 'fallback' in props ? props.fallback : false
- useEffect(() => {
- if (ref.current && useObserver) {
- const observer = new IntersectionObserver(([entry]) => {
- if (entry.isIntersecting) {
- fillList(list, builder, apiRoute, { limit, ac })
- observer.disconnect()
- }
- }, {
- rootMargin: '300px',
- })
- observer.observe(ref.current)
- } else {
- fillList(list, builder, apiRoute, { limit, ac })
- }
- })
+ useEffect(() => {
+ if (ref.current && useObserver) {
+ const observer = new IntersectionObserver(([entry]) => {
+ if (entry.isIntersecting) {
+ fillList(list, builder, apiRoute, { limit, ac })
+ observer.disconnect()
+ }
+ }, {
+ rootMargin: '300px',
+ })
+ observer.observe(ref.current)
+ } else {
+ fillList(list, builder, apiRoute, { limit, ac })
+ }
+ })
- if (limit && placeholder && fallback) {
- const placeholders = Array
- .from({ length: limit })
- .map((_, index) => (
-
- {updateFromList(list, index)}
-
- ))
- return <>{placeholders}>
- }
+ if (limit && placeholder && fallback) {
+ const placeholders = Array
+ .from({ length: limit })
+ .map((_, index) => (
+
+ ))
+ return <>{placeholders}>
+ }
- return <>{list}>
+ return <>{list}>
}
function fillList(
- list: Signal,
- builder: Builder,
- apiRoute: string,
- { limit, ac }: { limit?: number; ac?: AbortController },
+ list: Signal,
+ builder: Builder,
+ apiRoute: string,
+ { limit, ac }: { limit?: number; ac?: AbortController },
) {
- ;(async () => {
- const propsList = requestApiStream(
- apiRoute,
- 'GET',
- )
+ ;(async () => {
+ const propsList = requestApiStream(
+ apiRoute,
+ 'GET',
+ )
- for await (const props of propsList) {
- list.value = [
- ...list.value,
- builder(props),
- ]
- if (limit && list.value.length >= limit) break
- }
- ac?.abort()
- })()
+ for await (const props of propsList) {
+ list.value = [
+ ...list.value,
+ builder(props),
+ ]
+ if (limit && list.value.length >= limit) break
+ }
+ ac?.abort()
+ })()
}
function updateFromList(
- list: Signal,
- index: number,
+ list: Signal,
+ index: number,
): Promise {
- const { promise, resolve } = Promise.withResolvers()
- list.subscribe((value: JSX.Element[]) => {
- const selected = value.at(index)
- if (selected) {
- resolve(selected)
- }
- })
+ const { promise, resolve } = Promise.withResolvers()
+ list.subscribe((value: JSX.Element[]) => {
+ const selected = value.at(index)
+ if (selected) {
+ resolve(selected)
+ }
+ })
- return promise
+ return promise
}
diff --git a/islands/MemberCardList.tsx b/islands/MemberCardList.tsx
index 4db3ade..501ea77 100644
--- a/islands/MemberCardList.tsx
+++ b/islands/MemberCardList.tsx
@@ -3,58 +3,58 @@ import CardList from ':islands/CardList.tsx'
import type { Ref } from 'preact'
export default function MemberCardList(
- { limit, filters, usePlaceholder, useObserver }: {
- filters?: [string, string][]
- limit?: number
- usePlaceholder?: boolean
- useObserver?: boolean
- },
+ { limit, filters, usePlaceholder, useObserver }: {
+ filters?: [string, string][]
+ limit?: number
+ usePlaceholder?: boolean
+ useObserver?: boolean
+ },
) {
- const query = new URL('members/fetchAll', 'https://null/')
- filters?.forEach((filter) => query.searchParams.set(...filter))
+ const query = new URL('members/fetchAll', 'https://null/')
+ filters?.forEach((filter) => query.searchParams.set(...filter))
- const apiRoute = `${query.pathname}${query.search}`
+ const apiRoute = `${query.pathname}${query.search}`
- if (usePlaceholder) {
- return (
-
- )
- }
+ if (usePlaceholder) {
+ return (
+
+ )
+ }
- return (
-
- )
+ return (
+
+ )
}
function Placeholder({ ref }: { ref?: Ref | undefined }) {
- return (
-
-
Chargement ...
-
- )
+ return (
+
+
Chargement ...
+
+ )
}
function Fallback() {
- return (
-
-
Pas d'utilisateur
-
- )
+ return (
+
+
Pas d'utilisateur
+
+ )
}
diff --git a/islands/Suspens.tsx b/islands/Suspens.tsx
index 0054df4..b952709 100644
--- a/islands/Suspens.tsx
+++ b/islands/Suspens.tsx
@@ -18,13 +18,14 @@ function RenderError(
export type Fallback = ({ error }: { error: Error }) => JSX.Element
+export type SuspenseProps = {
+ loader: JSX.Element
+ fallback?: Fallback
+ signal?: AbortSignal
+} & ({ children: Promise } | { value: Promise })
+
export default function Suspense(
- { loader, fallback, signal, children }: {
- loader: JSX.Element
- children: Promise
- fallback?: Fallback
- signal?: AbortSignal
- },
+ { loader, fallback, signal, ...props }: SuspenseProps,
) {
const displayed = useSignal(loader)
let loaded = false
@@ -38,7 +39,9 @@ export default function Suspense(
}
})
- children
+ //Prevent transpilation error due to children expected to not be a promise
+ const inner = 'value' in props ? props.value : props.children
+ inner
.then((element) => {
if (signal?.aborted) return
displayed.value = element
diff --git a/routes/api/members/fetchAll.ts b/routes/api/members/fetchAll.ts
index c4e1af5..3535d92 100644
--- a/routes/api/members/fetchAll.ts
+++ b/routes/api/members/fetchAll.ts
@@ -5,24 +5,24 @@ import { SessionHandlers } from ':src/session/mod.ts'
import { respondApi, respondApiStream } from ':src/utils.ts'
export const handler: SessionHandlers = {
- GET(_req, ctx) {
- try {
- const memberList = dbToMemberCardProps(db)
+ GET(_req, ctx) {
+ try {
+ const memberList = dbToMemberCardProps(db)
- const params = ctx.url.searchParams
+ const params = ctx.url.searchParams
- const groupParam = params.get('group')
- if (groupParam) {
- const list = memberList.filter(
- (member) => member.groups.includes(groupParam),
- ) as AsyncIterableIterator
+ const groupParam = params.get('group')
+ if (groupParam) {
+ const list = memberList.filter(
+ (member) => member.groups.includes(groupParam),
+ ) as AsyncIterableIterator
- return respondApiStream(list)
- }
+ return respondApiStream(list)
+ }
- return respondApiStream(memberList)
- } catch (error) {
- return respondApi('error', error)
- }
- },
+ return respondApiStream(memberList)
+ } catch (error) {
+ return respondApi('error', error)
+ }
+ },
}
diff --git a/routes/api/news/fetchAll.ts b/routes/api/news/fetchAll.ts
index ff56fe0..b42f201 100644
--- a/routes/api/news/fetchAll.ts
+++ b/routes/api/news/fetchAll.ts
@@ -3,12 +3,12 @@ import { SessionHandlers } from ':src/session/mod.ts'
import { respondApi, respondApiStream } from ':src/utils.ts'
export const handler: SessionHandlers = {
- GET() {
- try {
- const newsList = fetchNewsList('cohabit')
- return respondApiStream(newsList)
- } catch (error) {
- return respondApi('error', error)
- }
- },
+ GET() {
+ try {
+ const newsList = fetchNewsList('cohabit')
+ return respondApiStream(newsList)
+ } catch (error) {
+ return respondApi('error', error)
+ }
+ },
}
diff --git a/routes/api/webauthn/login/[step].ts b/routes/api/webauthn/login/[step].ts
index 7a8d760..dca5f5a 100644
--- a/routes/api/webauthn/login/[step].ts
+++ b/routes/api/webauthn/login/[step].ts
@@ -2,7 +2,12 @@ import { db } from ':src/db/mod.ts'
import type { SessionHandlers } from ':src/session/mod.ts'
import { respondApi } from ':src/utils.ts'
import { getRelyingParty } from ':src/webauthn/mod.ts'
-import { Credential, Passkey, Ref, User } from '@cohabit/resources-manager/models'
+import {
+ Credential,
+ Passkey,
+ Ref,
+ User,
+} from '@cohabit/resources-manager/models'
import {
generateAuthenticationOptions,
verifyAuthenticationResponse,
diff --git a/routes/api/webauthn/register/[step].ts b/routes/api/webauthn/register/[step].ts
index ed6b6c4..a16fce9 100644
--- a/routes/api/webauthn/register/[step].ts
+++ b/routes/api/webauthn/register/[step].ts
@@ -12,7 +12,12 @@ import type {
//TODO improve workspace imports
import { db } from ':src/db/mod.ts'
import { getRelyingParty } from ':src/webauthn/mod.ts'
-import { Credential, Passkey, Ref, User } from '@cohabit/resources-manager/models'
+import {
+ Credential,
+ Passkey,
+ Ref,
+ User,
+} from '@cohabit/resources-manager/models'
import { encodeBase64 } from '@std/encoding'
type Params = { step: 'start' | 'finish' }
diff --git a/routes/index.tsx b/routes/index.tsx
index d6b77e7..5645c53 100644
--- a/routes/index.tsx
+++ b/routes/index.tsx
@@ -29,9 +29,8 @@ export default function Home() {
Nos machines
- Vous avez besoin d'aide pour concrétiser votre projet ? Le
- Fablab vous accompagnes dans vos projets, grâce à son parc
- de machine...
+ Vous avez besoin d'aide pour concrétiser votre projet ? Le Fablab vous
+ accompagnes dans vos projets, grâce à son parc de machine...
<>
@@ -63,29 +62,26 @@ export default function Home() {
Présentation
- Coh@bit est un fablab de l'université de Bordeaux ouvert à
- tous les publics depuis 2016. Du collégien à
- l'enseignant-chercheur, l'équipe du fablab accompagne les
- adhérents dans la réalisation de leurs projets de
- fabrication autour du numérique.
+ Coh@bit est un fablab de l'université de Bordeaux ouvert à tous les
+ publics depuis 2016. Du collégien à l'enseignant-chercheur, l'équipe
+ du fablab accompagne les adhérents dans la réalisation de leurs
+ projets de fabrication autour du numérique.
- Venez découvrir un tout nouvelle univers où vous pouvez
- concrétiser vos projet, découvrir des personnes avec les
- même affinités que vous, cultiver votre savoir et savoir
- faire, dans l'entraide et le partage.
+ Venez découvrir un tout nouvelle univers où vous pouvez concrétiser
+ vos projet, découvrir des personnes avec les même affinités que vous,
+ cultiver votre savoir et savoir faire, dans l'entraide et le partage.
- Créer par Frédéric Bos (Directeur de l'IUT de Bordeaux) en
- 2014, Coh@bit (Creative Open House at Bordeaux Institut of
- Technology) est une association réunissant deux entités : le
- Fablab et le Technoshop.
+ Créer par Frédéric Bos (Directeur de l'IUT de Bordeaux) en 2014,
+ Coh@bit (Creative Open House at Bordeaux Institut of Technology) est
+ une association réunissant deux entités : le Fablab et le Technoshop.
Ouvert à tous les publics depuis 2016, allant de
- l'enseignant-chercheur au collégien, l'équipe du fablab
- accompagne les adhérents dans la réalisation de leurs
- projets de fabrication numérique.
+ l'enseignant-chercheur au collégien, l'équipe du fablab accompagne les
+ adhérents dans la réalisation de leurs projets de fabrication
+ numérique.
diff --git a/src/blog/mod.ts b/src/blog/mod.ts
index 08f8c15..4fffdf5 100644
--- a/src/blog/mod.ts
+++ b/src/blog/mod.ts
@@ -4,101 +4,99 @@ import { base64ToString } from ':src/utils.ts'
import { extract } from '@std/front-matter/yaml'
export async function fetchNews(
- publisher: string,
- name: string,
+ publisher: string,
+ name: string,
): Promise {
- const apiUrl = 'https://git.cohabit.fr/api/v1/'
- const baseEndpoint = new URL(`repos/${publisher}/.news/`, apiUrl)
- const endpoint = new URL('contents/', baseEndpoint)
+ const apiUrl = 'https://git.cohabit.fr/api/v1/'
+ const baseEndpoint = new URL(`repos/${publisher}/.news/`, apiUrl)
+ const endpoint = new URL('contents/', baseEndpoint)
- // Get readme content api url
- const readmePath = encodeURIComponent(`${name}/README.md`)
- const contentUrl = new URL(readmePath, endpoint)
+ // Get readme content api url
+ const readmePath = encodeURIComponent(`${name}/README.md`)
+ const contentUrl = new URL(readmePath, endpoint)
- // Fetch readme content, commit hash and raw url for relative links
- const file = await getCommitAndContent(contentUrl)
- // Get commit infos (author + date) and get readme content from base64 source
- const { raw, url, lastUpdate, author } = await getAuthorAndParseContent(
- file,
- baseEndpoint,
- )
- // Extract frontmatter
- const { attrs, body } = extract(raw)
+ // Fetch readme content, commit hash and raw url for relative links
+ const file = await getCommitAndContent(contentUrl)
+ // Get commit infos (author + date) and get readme content from base64 source
+ const { raw, url, lastUpdate, author } = await getAuthorAndParseContent(
+ file,
+ baseEndpoint,
+ )
+ // Extract frontmatter
+ const { attrs, body } = extract(raw)
- // Transform API responses into BlogProps for BlogCard and BlogPost components
- return {
- author,
- publisher,
- lastUpdate,
- options: attrs['x-cohabit'],
- title: attrs.title,
- hash: file.sha,
- description: attrs.description,
- body,
- name,
- url,
- tags: attrs.tags,
- }
+ // Transform API responses into BlogProps for BlogCard and BlogPost components
+ return {
+ author,
+ publisher,
+ lastUpdate,
+ options: attrs['x-cohabit'],
+ title: attrs.title,
+ hash: file.sha,
+ description: attrs.description,
+ body,
+ name,
+ url,
+ tags: attrs.tags,
+ }
}
export async function* fetchNewsList(
- publisher: string,
+ publisher: string,
): AsyncGenerator {
- const apiUrl = 'https://git.cohabit.fr/api/v1/'
- const baseEndpoint = new URL(`repos/${publisher}/.news/`, apiUrl)
- const endpoint = new URL('contents/', baseEndpoint)
+ const apiUrl = 'https://git.cohabit.fr/api/v1/'
+ const baseEndpoint = new URL(`repos/${publisher}/.news/`, apiUrl)
+ const endpoint = new URL('contents/', baseEndpoint)
- // Fetch repo content
- const root = await fetch(endpoint).then((response) => response.json()) as {
- name: string
- type: string
- }[]
+ // Fetch repo content
+ const root = await fetch(endpoint).then((response) => response.json()) as {
+ name: string
+ type: string
+ }[]
- // Fetch `README.md` in sub directories
- const blogPropsList = root
- // Remove file and dir starting with "."
- .filter(isNewsDirectory)
- // Fetch single news and return BlogProps
- .map(({ name }) => fetchNews(publisher, name))
+ // Fetch `README.md` in sub directories
+ const blogPropsList = root
+ // Remove file and dir starting with "."
+ .filter(isNewsDirectory)
+ // Fetch single news and return BlogProps
+ .map(({ name }) => fetchNews(publisher, name))
- // Yield each news
- for (const blogProps of blogPropsList) {
- yield blogProps
- }
+ // Yield each news
+ for (const blogProps of blogPropsList) {
+ yield blogProps
+ }
}
async function getAuthorAndParseContent(
- file: { download_url: string; content: string; last_commit_sha: string },
- baseEndpoint: URL,
+ file: { download_url: string; content: string; last_commit_sha: string },
+ baseEndpoint: URL,
) {
- const commitUrl = new URL(
- `git/commits/${file.last_commit_sha}?stat=false&verification=false&files=false`,
- baseEndpoint,
- )
- const infos = await fetch(commitUrl).then((response) =>
- response.json()
- ) as {
- created: string
- author: { login: string }
- }
+ const commitUrl = new URL(
+ `git/commits/${file.last_commit_sha}?stat=false&verification=false&files=false`,
+ baseEndpoint,
+ )
+ const infos = await fetch(commitUrl).then((response) => response.json()) as {
+ created: string
+ author: { login: string }
+ }
- return {
- raw: base64ToString(file.content),
- url: file.download_url,
- lastUpdate: new Date(infos.created),
- author: infos.author.login,
- }
+ return {
+ raw: base64ToString(file.content),
+ url: file.download_url,
+ lastUpdate: new Date(infos.created),
+ author: infos.author.login,
+ }
}
async function getCommitAndContent(contentUrl: URL) {
- return await fetch(contentUrl).then((response) => response.json()) as {
- download_url: string
- content: string
- sha: string
- last_commit_sha: string
- }
+ return await fetch(contentUrl).then((response) => response.json()) as {
+ download_url: string
+ content: string
+ sha: string
+ last_commit_sha: string
+ }
}
function isNewsDirectory(entry: { name: string; type: string }): boolean {
- return entry.type === 'dir' && entry.name.startsWith('.') === false
+ return entry.type === 'dir' && entry.name.startsWith('.') === false
}
diff --git a/src/cache/middleware.ts b/src/cache/middleware.ts
index b714f66..aa94667 100644
--- a/src/cache/middleware.ts
+++ b/src/cache/middleware.ts
@@ -1,19 +1,19 @@
import { FreshContext } from '$fresh/server.ts'
export function useCache(
- _request: Request,
- response: Response,
- ctx: FreshContext,
+ _request: Request,
+ response: Response,
+ ctx: FreshContext,
) {
- if (ctx.config.dev) return
- if (
- ctx.url.pathname.match(
- /(.+\.|_)((css)|(ttf)|(woff2)|(png)|(svg)|(jpe?g)|(avif))/,
- )
- ) {
- response.headers.set(
- 'Cache-Control',
- 'public, max-age=31536000, immutable',
- )
- }
+ if (ctx.config.dev) return
+ if (
+ ctx.url.pathname.match(
+ /(.+\.|_)((css)|(ttf)|(woff2)|(png)|(svg)|(jpe?g)|(avif))/,
+ )
+ ) {
+ response.headers.set(
+ 'Cache-Control',
+ 'public, max-age=31536000, immutable',
+ )
+ }
}
diff --git a/src/csp/middleware.ts b/src/csp/middleware.ts
index cb8b02b..9eab075 100644
--- a/src/csp/middleware.ts
+++ b/src/csp/middleware.ts
@@ -2,31 +2,31 @@ import { FreshContext } from '$fresh/server.ts'
import { applyCspRulesWithNonce, CspRules } from ':src/csp/mod.ts'
export function useCsp(
- _request: Request,
- response: Response,
- ctx: FreshContext,
+ _request: Request,
+ response: Response,
+ ctx: FreshContext,
) {
//See https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/CSP
-
- const trustedDomains = ["'self'", 'https://git.cohabit.fr']
- const cspRules: CspRules = {
- defaultSrc: ["'none'"],
- frameAncestors: ["'none'"],
- upgradeInsecureRequests: true,
- styleSrc: [...trustedDomains, "'unsafe-inline'"], //set nonce to inline script
- manifestSrc: [`${ctx.url.origin.replace('http:', 'https:')}/manifest.json`],
- baseUri: ["'none'"],
- imgSrc: [
- ...trustedDomains,
- 'data:',
- 'https:',
- ],
- fontSrc: [...trustedDomains, 'https://cdn.jsdelivr.net'],
- scriptSrc: ["'self'", "'strict-dynamic'"],
- connectSrc: ["'self'"],
- formAction: ["'none'"],
- }
-
- return applyCspRulesWithNonce(response, cspRules)
+ const trustedDomains = ["'self'", 'https://git.cohabit.fr']
+
+ const cspRules: CspRules = {
+ defaultSrc: ["'none'"],
+ frameAncestors: ["'none'"],
+ upgradeInsecureRequests: true,
+ styleSrc: [...trustedDomains, "'unsafe-inline'"], //set nonce to inline script
+ manifestSrc: [`${ctx.url.origin.replace('http:', 'https:')}/manifest.json`],
+ baseUri: ["'none'"],
+ imgSrc: [
+ ...trustedDomains,
+ 'data:',
+ 'https:',
+ ],
+ fontSrc: [...trustedDomains, 'https://cdn.jsdelivr.net'],
+ scriptSrc: ["'self'", "'strict-dynamic'"],
+ connectSrc: ["'self'"],
+ formAction: ["'none'"],
+ }
+
+ return applyCspRulesWithNonce(response, cspRules)
}
diff --git a/src/csp/mod.ts b/src/csp/mod.ts
index e9639fd..7d5f61f 100644
--- a/src/csp/mod.ts
+++ b/src/csp/mod.ts
@@ -2,51 +2,51 @@ import type { ContentSecurityPolicyDirectives } from '$fresh/runtime.ts'
import { getFreshNonce, toSnakeCase } from ':src/utils.ts'
export type CspRules = ContentSecurityPolicyDirectives & {
- upgradeInsecureRequests: true
+ upgradeInsecureRequests: true
}
export function applyCspRules(
- { headers }: { headers: Headers },
- rules: CspRules,
+ { headers }: { headers: Headers },
+ rules: CspRules,
): void {
- const rulesString: string[] = []
+ const rulesString: string[] = []
- for (const rule in rules) {
- const value = rules[rule as unknown as keyof CspRules]
- const ruleName = toSnakeCase(rule)
+ for (const rule in rules) {
+ const value = rules[rule as unknown as keyof CspRules]
+ const ruleName = toSnakeCase(rule)
- if (typeof value === 'boolean') {
- rulesString.push(`${ruleName}`)
- continue
- }
+ if (typeof value === 'boolean') {
+ rulesString.push(`${ruleName}`)
+ continue
+ }
- if (Array.isArray(value)) {
- rulesString.push(`${ruleName} ${value.join(' ')}`)
- continue
- }
+ if (Array.isArray(value)) {
+ rulesString.push(`${ruleName} ${value.join(' ')}`)
+ continue
+ }
- if (typeof value === 'string') {
- rulesString.push(`${ruleName} ${value}`)
- continue
- }
+ if (typeof value === 'string') {
+ rulesString.push(`${ruleName} ${value}`)
+ continue
+ }
- throw TypeError(`unsupported csp rule "${rule}" with value (${value})`)
- }
+ throw TypeError(`unsupported csp rule "${rule}" with value (${value})`)
+ }
- headers.set('Content-Security-Policy', rulesString.join('; '))
+ headers.set('Content-Security-Policy', rulesString.join('; '))
}
export async function applyCspRulesWithNonce(
- response: Response,
- rules: CspRules,
+ response: Response,
+ rules: CspRules,
): Promise {
- // Get nonce from any html response
- const nonce = await getFreshNonce(response)
+ // Get nonce from any html response
+ const nonce = await getFreshNonce(response)
- // Add nonce to script src if defined
- if (nonce) {
- rules.scriptSrc?.push(`'nonce-${nonce}'`)
- }
+ // Add nonce to script src if defined
+ if (nonce) {
+ rules.scriptSrc?.push(`'nonce-${nonce}'`)
+ }
- return applyCspRules(response, rules)
+ return applyCspRules(response, rules)
}
diff --git a/src/db/mod.ts b/src/db/mod.ts
index f40a303..711590f 100644
--- a/src/db/mod.ts
+++ b/src/db/mod.ts
@@ -5,8 +5,8 @@ import type { MailAddress } from '@cohabit/resources-manager/types'
// Import Datas
import { exists } from '$std/fs/exists.ts'
import { ensureDir } from '$std/fs/mod.ts'
-import groups from ":src/db/mock/groups.json" with { type: 'json' }
-import users from ":src/db/mock/users.json" with { type: 'json' }
+import groups from ':src/db/mock/groups.json' with { type: 'json' }
+import users from ':src/db/mock/users.json' with { type: 'json' }
await ensureDir('./cache')
const dbPath = './cache/db.sqlite'
diff --git a/src/security_headers/middleware.ts b/src/security_headers/middleware.ts
index d53d61e..d90a6da 100644
--- a/src/security_headers/middleware.ts
+++ b/src/security_headers/middleware.ts
@@ -1,26 +1,26 @@
import { FreshContext } from '$fresh/server.ts'
export function useSecurityHeaders(
- _request: Request,
- response: Response,
- _ctx: FreshContext,
+ _request: Request,
+ response: Response,
+ _ctx: FreshContext,
) {
- // See https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/TLS#http_strict_transport_security
- response.headers.set(
- 'Strict-Transport-Security',
- 'max-age=63072000; includeSubDomains; preload',
- )
- //See https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/Referrer_policy
- response.headers.set(
- 'Referrer-Policy',
- 'no-referrer, strict-origin-when-cross-origin',
- )
- //See https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/MIME_types
- response.headers.set('X-Content-Type-Options', 'nosniff')
- //See https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/Clickjacking
- response.headers.set('X-Frame-Options', 'DENY')
- //See https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/CORP
- response.headers.set('Cross-Origin-Resource-Policy', 'same-origin')
- //See https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
- //? SRI plugin for non local resources only ?
+ // See https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/TLS#http_strict_transport_security
+ response.headers.set(
+ 'Strict-Transport-Security',
+ 'max-age=63072000; includeSubDomains; preload',
+ )
+ //See https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/Referrer_policy
+ response.headers.set(
+ 'Referrer-Policy',
+ 'no-referrer, strict-origin-when-cross-origin',
+ )
+ //See https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/MIME_types
+ response.headers.set('X-Content-Type-Options', 'nosniff')
+ //See https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/Clickjacking
+ response.headers.set('X-Frame-Options', 'DENY')
+ //See https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/CORP
+ response.headers.set('Cross-Origin-Resource-Policy', 'same-origin')
+ //See https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
+ //? SRI plugin for non local resources only ?
}
diff --git a/src/serviceworker/middleware.ts b/src/serviceworker/middleware.ts
index 1a62130..9f60d00 100644
--- a/src/serviceworker/middleware.ts
+++ b/src/serviceworker/middleware.ts
@@ -1,12 +1,12 @@
import { FreshContext } from '$fresh/server.ts'
export function useServiceworker(
- _request: Request,
- response: Response,
- ctx: FreshContext,
+ _request: Request,
+ response: Response,
+ ctx: FreshContext,
) {
- // Allow service worker to serve root scope
- if (ctx.url.pathname.endsWith('island-startserviceworker.js')) {
- response.headers.set('Service-Worker-Allowed', '/')
- }
+ // Allow service worker to serve root scope
+ if (ctx.url.pathname.endsWith('island-startserviceworker.js')) {
+ response.headers.set('Service-Worker-Allowed', '/')
+ }
}
diff --git a/src/session/middleware.ts b/src/session/middleware.ts
index 6b4ec3f..e979d1a 100644
--- a/src/session/middleware.ts
+++ b/src/session/middleware.ts
@@ -3,51 +3,51 @@ import { SessionStore } from ':src/session/mod.ts'
import { getCookies, setCookie } from 'jsr:@std/http@^0.224.4/cookie'
export function useSession(
- request: Request,
- response: Response,
- ctx: FreshContext,
+ request: Request,
+ response: Response,
+ ctx: FreshContext,
) {
- // Check if session already started
- if (SessionStore.getFromRequest(request) !== undefined) {
- return
- }
+ // Check if session already started
+ if (SessionStore.getFromRequest(request) !== undefined) {
+ return
+ }
- // Clear outdated cookies
- for (const cookie in getCookies(request.headers)) {
- setCookie(response.headers, {
- name: cookie,
- value: '',
- path: '/',
- expires: 0,
- })
- }
+ // Clear outdated cookies
+ for (const cookie in getCookies(request.headers)) {
+ setCookie(response.headers, {
+ name: cookie,
+ value: '',
+ path: '/',
+ expires: 0,
+ })
+ }
- // Create new session
- const session = SessionStore.createSession()
- ctx.state = { ...ctx.state, session }
+ // Create new session
+ const session = SessionStore.createSession()
+ ctx.state = { ...ctx.state, session }
- // Set session cookie
- setCookie(response.headers, {
- name: '__Secure-SESSION',
- value: session.uuid,
- httpOnly: true,
- sameSite: 'Strict',
- secure: true,
- path: '/',
- expires: SessionStore.maxAge,
- })
+ // Set session cookie
+ setCookie(response.headers, {
+ name: '__Secure-SESSION',
+ value: session.uuid,
+ httpOnly: true,
+ sameSite: 'Strict',
+ secure: true,
+ path: '/',
+ expires: SessionStore.maxAge,
+ })
- // Set csrf
- const csrf = crypto.randomUUID()
- session.set('_csrf', csrf)
+ // Set csrf
+ const csrf = crypto.randomUUID()
+ session.set('_csrf', csrf)
- setCookie(response.headers, {
- name: '__Host-CSRF',
- value: csrf,
- httpOnly: false,
- sameSite: 'Strict',
- secure: true,
- path: '/',
- expires: SessionStore.maxAge,
- })
+ setCookie(response.headers, {
+ name: '__Host-CSRF',
+ value: csrf,
+ httpOnly: false,
+ sameSite: 'Strict',
+ secure: true,
+ path: '/',
+ expires: SessionStore.maxAge,
+ })
}