Skip to content

Commit

Permalink
feat: add plausible and sentry (#76)
Browse files Browse the repository at this point in the history
Co-authored-by: Anton Lilleby <[email protected]>
  • Loading branch information
an2n and Anton Lilleby authored Jun 26, 2024
1 parent 103a4c1 commit fcfadb0
Show file tree
Hide file tree
Showing 18 changed files with 1,420 additions and 62 deletions.
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,21 @@ Vil du klikke deg rundt i browser for å se hva som skjer i testene, sleng på `

## Slack

Når et arrangement publiseres for første gang, vil det automatisk genereres en Slack-melding til kanalen #tmp\*arrangementer. For å bygge meldingen kan du benytte [Block Kit Builder](https://app.slack.com/block-kit-builder)
Når et arrangement publiseres for første gang, vil det automatisk genereres en Slack-melding til kanalen #tmp\*arrangementer For å bygge meldingen kan du benytte [Block Kit Builder](https://app.slack.com/block-kit-builder). Denne tjenesten tillater deg å visuelt designe layouten av dine Slack-meldinger med ulike blokker som knapper, tekstfelter og bilder for en mer engasjerende kommunikasjon.

## Epost
## Mandrillapp

Hvis du vil teste e-post lokalt, kan du legge til http://localhost:5173 i Access-Control-Allow-Origin i /app/src/lib/auth/cors.ts.
I tillegg må denne linjen kommenteres ut:

```
process.env.MODE !== "development"
```

# Sentry

Sentry brukes for å overvåke, logge og rapportere klientfeil.

# Plausible

Plausible tilbyr en g måte å analysere trafikk på nettstedet. Det er et open-source alternativ til tradisjonelle analyseverktøy som Google Analytics. Plausible er fritt for cookies og samler ingen personopplysninger. Vi trenger derfor ingen cookie consent. For å integrere Plausible er det lagt til et sporingsskriptet i HTML-headeren. Sporingen for å måle og analysere besøksstatistikk vises i Sanity studio.
3 changes: 3 additions & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ vite.config.ts.timestamp-*
/playwright-report/
/blob-report/
/playwright/.cache/

# Sentry Config File
.sentryclirc
1 change: 1 addition & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@sanity/image-url": "^1.0.2",
"@sanity/svelte-loader": "^1.11.1",
"@sanity/visual-editing": "^1.7.1",
"@sentry/sveltekit": "^8.12.0",
"@types/nodemailer": "^6.4.15",
"groq": "^3.33.0",
"ical-generator": "^7.1.0",
Expand Down
6 changes: 6 additions & 0 deletions app/src/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
<link rel="icon" href="%sveltekit.assets%/favicon.ico" />
<link rel="stylesheet" href="%sveltekit.assets%/global.css" />
<meta name="viewport" content="width=device-width" />
<!-- Plausible is cookieless, collects no personal data, and does not engage in cross-site or cross-device tracking -->
<script
defer
data-domain="capra-web.vercel.app"
src="https://plausible.io/js/script.js"
></script>
%sveltekit.head%
</head>
<body
Expand Down
4 changes: 2 additions & 2 deletions app/src/components/external/RegistrationFormExternal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@
{#each event.customOptions as customOption}
<RegistrationCustomOption
{form}
type={vercelStegaCleanAll(customOption.fieldType)}
option={vercelStegaCleanAll(customOption.fieldOption)}
inputType={vercelStegaCleanAll(customOption.fieldType)}
optionLabel={vercelStegaCleanAll(customOption.fieldOption)}
/>
{/each}
{/if}
Expand Down
4 changes: 2 additions & 2 deletions app/src/components/internal/RegistrationFormInternal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@
{#each event.customOptions as customOption}
<RegistrationCustomOption
{form}
option={vercelStegaCleanAll(customOption.fieldOption)}
type={vercelStegaCleanAll(customOption.fieldType)}
inputType={vercelStegaCleanAll(customOption.fieldType)}
optionLabel={vercelStegaCleanAll(customOption.fieldOption)}
/>
{/each}
{/if}
Expand Down
48 changes: 24 additions & 24 deletions app/src/components/shared/RegistrationCustomOption.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,50 @@
import { Checkbox, Input } from "flowbite-svelte";
import { writable } from "svelte/store";
export let form = writable<{
customOptions: {
value: string;
option: string;
}[];
}>();
export let option: string;
export let type: string;
interface CustomOption {
value: string;
option: string;
}
interface FormData {
customOptions: CustomOption[];
}
export let form = writable<FormData>({ customOptions: [] });
export let optionLabel: string;
export let inputType: string;
function handleCheckboxChange(event: Event) {
const target = event.target as HTMLInputElement;
if (target) {
handleValueChange(option, target.checked ? "Ja" : "");
}
const { checked } = event.target as HTMLInputElement;
updateFormValue(optionLabel, checked ? "Ja" : "");
}
function handleInputChange(event: Event) {
const target = event.target as HTMLInputElement;
if (target) {
handleValueChange(option, target.value);
}
const { value } = event.target as HTMLInputElement;
updateFormValue(optionLabel, value);
}
function handleValueChange(option: string, value: string) {
function updateFormValue(option: string, value: string) {
form.update((currentForm) => {
const updatedOptions = currentForm.customOptions.filter(
(customOption) => customOption.value && customOption.option !== option
(customOption) => customOption.option !== option
);
if (value) {
updatedOptions.push({ option, value });
}
currentForm.customOptions = updatedOptions;
return currentForm;
return { ...currentForm, customOptions: updatedOptions };
});
}
</script>

<div class="flex flex-col gap-1">
<span class="block text-sm font-bold text-gray-900 rtl:text-right dark:text-gray-300"
>{option}</span
>
{#if type === "checkbox"}
<span class="block text-sm font-bold text-gray-900 rtl:text-right dark:text-gray-300">
{optionLabel}
</span>
{#if inputType === "checkbox"}
<Checkbox name="customOptions" on:change={handleCheckboxChange}>Ja!</Checkbox>
{:else}
<Input name="customOptions" on:input={handleInputChange} />
Expand Down
21 changes: 21 additions & 0 deletions app/src/hooks.client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { handleErrorWithSentry, replayIntegration } from "@sentry/sveltekit";
import * as Sentry from '@sentry/sveltekit';

Sentry.init({
dsn: 'https://89a3c85a7dcebbb15812e9f467515450@o4507497115418624.ingest.de.sentry.io/4507497120923728',
tracesSampleRate: 1.0,

// This sets the sample rate to be 10%. You may want this to be 100% while
// in development and sample at a lower rate in production
replaysSessionSampleRate: 0.1,

// If the entire session is not sampled, use the below sample rate to sample
// sessions when an error occurs.
replaysOnErrorSampleRate: 1.0,

// If you don't want to use Session Replay, just remove the line below:
integrations: [replayIntegration()],
});

// If you have a custom error handler, pass it to `handleErrorWithSentry`
export const handleError = handleErrorWithSentry();
9 changes: 8 additions & 1 deletion app/src/hooks.server.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import * as Sentry from "@sentry/sveltekit";
import { createRequestHandler, setServerClient } from "@sanity/svelte-loader";
import { serverClient } from "$lib/server/sanity/client";
import { sequence } from "@sveltejs/kit/hooks";
import { createAuthHandler } from "$lib/auth";
import { createCorsHandler } from "$lib/auth/cors";

Sentry.init({
dsn: "https://89a3c85a7dcebbb15812e9f467515450@o4507497115418624.ingest.de.sentry.io/4507497120923728",
tracesSampleRate: 1
})

// Sets the client to be used by `loadQuery` when fetching data on the server.
// The loader will handle setting the correct fetch parameters, including
// perspective. This isn't a hook, but it's a good place to call this function
Expand All @@ -14,4 +20,5 @@ setServerClient(serverClient);
// helpers to the `event.locals` Svelte object, such as a preconfigured
// `loadQuery` function and `preview` state.

export const handle = sequence(createRequestHandler(), createAuthHandler, createCorsHandler);
export const handle = sequence(Sentry.sentryHandle(), sequence(createRequestHandler(), createAuthHandler, createCorsHandler));
export const handleError = Sentry.handleErrorWithSentry();
8 changes: 2 additions & 6 deletions app/src/lib/actions/external/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,9 @@ import { sendRegistrationConfirmed } from "$lib/email/event/registration";
import { sendConfirmUnregistration } from "$lib/email/event/unregistration";
import { RateLimiter } from "sveltekit-rate-limiter/server";

/**
** IP: Allows 20 requests per hour from the same IP address.
** IPUA (IP and User-Agent): Allows 10 requests per 5 minutes when both the IP address and the User-Agent of the requester are considered.
**/
const limiter = new RateLimiter({
IP: [20, "h"],
IPUA: [10, "m"],
IP: [20, "h"], // 20 rquests per hour from the same IP
IPUA: [10, "m"], // 10 requests per 5 minutes when same IP and User-Agent
});

export const submitRegistrationExternal: Actions["submitRegistrationExternal"] = async (
Expand Down
35 changes: 21 additions & 14 deletions app/src/lib/auth/cors.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
import { PUBLIC_SANITY_STUDIO_URL } from "$env/static/public";
import type { Handle } from "@sveltejs/kit";

const ALLOWED_ORIGIN = PUBLIC_SANITY_STUDIO_URL;
const ALLOWED_METHODS = "GET,OPTIONS,PATCH,DELETE,POST,PUT";
const ALLOWED_HEADERS =
"authorization, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version";

const corsHeaders = {
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Origin": PUBLIC_SANITY_STUDIO_URL,
"Access-Control-Allow-Methods": "GET,OPTIONS,PATCH,DELETE,POST,PUT",
"Access-Control-Allow-Headers":
"authorization, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version",
"Access-Control-Allow-Origin": ALLOWED_ORIGIN,
"Access-Control-Allow-Methods": ALLOWED_METHODS,
"Access-Control-Allow-Headers": ALLOWED_HEADERS,
};

export const createCorsHandler: Handle = async ({ event, resolve }) => {
if (event.request.method !== "OPTIONS") {
const response = await resolve(event);

const headers = new Headers(response.headers);
function applyCorsHeaders(headers: Headers): Headers {
for (const [key, value] of Object.entries(corsHeaders)) {
headers.set(key, value);
}
return headers;
}

for (const [key, value] of Object.entries(corsHeaders)) {
headers.set(key, value);
}
return response;
export const createCorsHandler: Handle = async ({ event, resolve }) => {
if (event.request.method === "OPTIONS") {
return new Response("OK", { headers: corsHeaders });
}

return new Response("OK", { headers: corsHeaders });
const response = await resolve(event);
const headers = applyCorsHeaders(new Headers(response.headers));

return new Response(response.body, { ...response, headers });
};
97 changes: 97 additions & 0 deletions app/src/routes/sentry-example/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<!--
This is just a very simple page with a button to throw an example error.
Feel free to delete this file and the entire sentry route.
-->

<script>
import * as Sentry from '@sentry/sveltekit';
function getSentryData() {
Sentry.startSpan({
name: 'Example Frontend Span',
op: 'test',
}, async () => {
const res = await fetch('/sentry-example');
if (!res.ok) {
throw new Error('Sentry Example Frontend Error');
}
});
}
</script>

<div>
<head>
<title>Sentry Onboarding</title>
<meta name="description" content="Test Sentry for your SvelteKit app!" />
</head>

<main>
<h1>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 44">
<path
fill="currentColor"
d="M124.32,28.28,109.56,9.22h-3.68V34.77h3.73V15.19l15.18,19.58h3.26V9.22h-3.73ZM87.15,23.54h13.23V20.22H87.14V12.53h14.93V9.21H83.34V34.77h18.92V31.45H87.14ZM71.59,20.3h0C66.44,19.06,65,18.08,65,15.7c0-2.14,1.89-3.59,4.71-3.59a12.06,12.06,0,0,1,7.07,2.55l2-2.83a14.1,14.1,0,0,0-9-3c-5.06,0-8.59,3-8.59,7.27,0,4.6,3,6.19,8.46,7.52C74.51,24.74,76,25.78,76,28.11s-2,3.77-5.09,3.77a12.34,12.34,0,0,1-8.3-3.26l-2.25,2.69a15.94,15.94,0,0,0,10.42,3.85c5.48,0,9-2.95,9-7.51C79.75,23.79,77.47,21.72,71.59,20.3ZM195.7,9.22l-7.69,12-7.64-12h-4.46L186,24.67V34.78h3.84V24.55L200,9.22Zm-64.63,3.46h8.37v22.1h3.84V12.68h8.37V9.22H131.08ZM169.41,24.8c3.86-1.07,6-3.77,6-7.63,0-4.91-3.59-8-9.38-8H154.67V34.76h3.8V25.58h6.45l6.48,9.2h4.44l-7-9.82Zm-10.95-2.5V12.6h7.17c3.74,0,5.88,1.77,5.88,4.84s-2.29,4.86-5.84,4.86Z M29,2.26a4.67,4.67,0,0,0-8,0L14.42,13.53A32.21,32.21,0,0,1,32.17,40.19H27.55A27.68,27.68,0,0,0,12.09,17.47L6,28a15.92,15.92,0,0,1,9.23,12.17H4.62A.76.76,0,0,1,4,39.06l2.94-5a10.74,10.74,0,0,0-3.36-1.9l-2.91,5a4.54,4.54,0,0,0,1.69,6.24A4.66,4.66,0,0,0,4.62,44H19.15a19.4,19.4,0,0,0-8-17.31l2.31-4A23.87,23.87,0,0,1,23.76,44H36.07a35.88,35.88,0,0,0-16.41-31.8l4.67-8a.77.77,0,0,1,1.05-.27c.53.29,20.29,34.77,20.66,35.17a.76.76,0,0,1-.68,1.13H40.6q.09,1.91,0,3.81h4.78A4.59,4.59,0,0,0,50,39.43a4.49,4.49,0,0,0-.62-2.28Z"
/>
</svg>
</h1>
<p>
Get Started with this <strong>simple Example:</strong>
</p>

<p>1. Send us a sample error:</p>
<button
type="button"
on:click={getSentryData}>
Throw error!
</button>

<p>
2. Look for the error on the
<a href="https://capra-consulting-y6.sentry.io/issues/?project=4507497120923728">Issues Page</a>.
</p>
<p style="margin-top: 24px;">
For more information, take a look at the
<a href="https://docs.sentry.io/platforms/javascript/guides/sveltekit/">
Sentry SvelteKit Documentation
</a>
</p>
</main>
</div>

<style>
main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
h1 {
font-size: 4rem;
margin: 14px 0;
}
svg {
height: 1em;
}
button {
padding: 12px;
cursor: pointer;
background-color: rgb(54, 45, 89);
border-radius: 4px;
border: none;
color: white;
font-size: 1em;
margin: 1em;
transition: all 0.25s ease-in-out;
}
button:hover {
background-color: #8c5393;
box-shadow: 4px;
box-shadow: 0px 0px 15px 2px rgba(140, 83, 147, 0.5);
}
button:active {
background-color: #c73852;
}
</style>
6 changes: 6 additions & 0 deletions app/src/routes/sentry-example/+server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// This is just a very simple API route that throws an example error.
// Feel free to delete this file and the entire sentry route.

export const GET = async () => {
throw new Error("Sentry Example API Route Error");
};
10 changes: 8 additions & 2 deletions app/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { sentrySvelteKit } from "@sentry/sveltekit";
import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vite";

export default defineConfig({
plugins: [sveltekit()],
});
plugins: [sentrySvelteKit({
sourceMapsUploadOptions: {
org: "capra-consulting-y6",
project: "javascript-sveltekit"
}
}), sveltekit()],
});
Loading

0 comments on commit fcfadb0

Please sign in to comment.