Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aae537b312 | |||
| 19238edf57 | |||
| 0f8ad4a36a | |||
| 9fac4d0149 | |||
| c71a28fd98 | |||
| 3bced84749 | |||
| 2c47870e0f | |||
| 0de1cdfb8d | |||
| cd6ba6df9a | |||
| 6d86630b4a | |||
| 8558e88a71 | |||
| 8c192ce8ab | |||
| b064ccf5d6 | |||
| fdb8017087 | |||
| 641524218e | |||
| 89c86a2d32 | |||
| af02de06b1 | |||
| 15829e1b19 | |||
| 9a49b9a29a | |||
| d839e9f178 | |||
| 52ecbac1bd | |||
| 97e11a4de7 | |||
| d6f6125204 | |||
| beb790bed8 | |||
| 070638b2f1 | |||
| f58af022dc | |||
| c1a0a5de6c | |||
| 2140d06fb5 | |||
| 6e24b68a08 | |||
| b3087aa9d4 | |||
| c459d58a28 | |||
| 005dc22a2e | |||
| 239bf163e8 | |||
| 8483ab9e84 | |||
| 16248416e7 | |||
| 4475879330 | |||
| 7d9ff9ff2b | |||
| 7492680457 | |||
| 85edb99e64 | |||
| 2e16cf9d51 |
@@ -3,7 +3,9 @@ name: Commit
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
# only trigger on branches, not on tags
|
# only trigger on branches, not on tags
|
||||||
branches: '**'
|
branches:
|
||||||
|
- 'main'
|
||||||
|
- 'dev'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# Job 1: Lint and Test (Type Check)
|
# Job 1: Lint and Test (Type Check)
|
||||||
@@ -34,3 +36,6 @@ jobs:
|
|||||||
- name: Type Check (Svelte Check)
|
- name: Type Check (Svelte Check)
|
||||||
# Based on your package.json "check" script
|
# Based on your package.json "check" script
|
||||||
run: pnpm check
|
run: pnpm check
|
||||||
|
|
||||||
|
- name: Run Tests (Vitest)
|
||||||
|
run: pnpm run test
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ jobs:
|
|||||||
http = true
|
http = true
|
||||||
insecure = true
|
insecure = true
|
||||||
|
|
||||||
- name: login
|
- name: login gitea registry
|
||||||
run: docker login -u schreifuchs -p ${{ secrets.REGISTRY_TOKEN }} git.schreifuchs.ch
|
run: docker login -u schreifuchs -p ${{ secrets.REGISTRY_TOKEN }} git.schreifuchs.ch
|
||||||
|
- name: login dockerhub
|
||||||
|
run: docker login -u aktitiel -p ${{ secrets.DOCKER_HUB_TOKEN}}
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
uses: https://github.com/docker/build-push-action@v5
|
uses: https://github.com/docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ jobs:
|
|||||||
http = true
|
http = true
|
||||||
insecure = true
|
insecure = true
|
||||||
|
|
||||||
- name: login
|
- name: login gitea registry
|
||||||
run: docker login -u schreifuchs -p ${{ secrets.REGISTRY_TOKEN }} git.schreifuchs.ch
|
run: docker login -u schreifuchs -p ${{ secrets.REGISTRY_TOKEN }} git.schreifuchs.ch
|
||||||
|
- name: login dockerhub
|
||||||
|
run: docker login -u aktitiel -p ${{ secrets.DOCKER_HUB_TOKEN}}
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
uses: https://github.com/docker/build-push-action@v5
|
uses: https://github.com/docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
pnpm run format
|
pnpm run format
|
||||||
pnpm run lint
|
pnpm run lint
|
||||||
|
pnpm run test
|
||||||
|
|||||||
+8
-1
@@ -10,6 +10,8 @@
|
|||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:watch": "vitest",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"lint": "prettier --check . && eslint .",
|
"lint": "prettier --check . && eslint .",
|
||||||
"db:start": "docker compose up",
|
"db:start": "docker compose up",
|
||||||
@@ -28,8 +30,11 @@
|
|||||||
"@sveltejs/kit": "^2.49.0",
|
"@sveltejs/kit": "^2.49.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
|
"@testing-library/svelte": "^5.3.1",
|
||||||
"@tiptap/core": "3.7.2",
|
"@tiptap/core": "3.7.2",
|
||||||
"@types/node": "^20.19.25",
|
"@types/node": "^20.19.25",
|
||||||
|
"@types/sanitize-html": "^2.16.1",
|
||||||
"drizzle-kit": "^0.31.7",
|
"drizzle-kit": "^0.31.7",
|
||||||
"drizzle-orm": "^0.44.7",
|
"drizzle-orm": "^0.44.7",
|
||||||
"eslint": "^9.39.1",
|
"eslint": "^9.39.1",
|
||||||
@@ -39,6 +44,7 @@
|
|||||||
"flowbite-svelte-icons": "^3.0.0",
|
"flowbite-svelte-icons": "^3.0.0",
|
||||||
"globals": "^16.5.0",
|
"globals": "^16.5.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
|
"jsdom": "^29.0.1",
|
||||||
"lowlight": "^3.3.0",
|
"lowlight": "^3.3.0",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"prettier-plugin-svelte": "^3.4.0",
|
"prettier-plugin-svelte": "^3.4.0",
|
||||||
@@ -47,7 +53,8 @@
|
|||||||
"tailwindcss": "^4.1.17",
|
"tailwindcss": "^4.1.17",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"typescript-eslint": "^8.47.0",
|
"typescript-eslint": "^8.47.0",
|
||||||
"vite": "^7.2.4"
|
"vite": "^7.2.4",
|
||||||
|
"vitest": "^4.1.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@auth/drizzle-adapter": "^1.11.1",
|
"@auth/drizzle-adapter": "^1.11.1",
|
||||||
|
|||||||
Generated
+787
File diff suppressed because it is too large
Load Diff
Vendored
+11
@@ -1,3 +1,5 @@
|
|||||||
|
import { DefaultSession } from '@auth/sveltekit';
|
||||||
|
|
||||||
// See https://svelte.dev/docs/kit/types#app.d.ts
|
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||||
// for information about these interfaces
|
// for information about these interfaces
|
||||||
declare global {
|
declare global {
|
||||||
@@ -10,4 +12,13 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module '@auth/sveltekit' {
|
||||||
|
interface Session {
|
||||||
|
user: {
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
} & DefaultSession['user'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export {};
|
export {};
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { extractFormData } from './extractFormData';
|
||||||
|
import * as v from 'valibot';
|
||||||
|
|
||||||
|
describe('extractFormData', () => {
|
||||||
|
it('should successfully extract and validate correct form data', async () => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('name', 'John Doe');
|
||||||
|
formData.append('age', '30');
|
||||||
|
|
||||||
|
const request = new Request('http://localhost', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
const schema = v.object({
|
||||||
|
name: v.string(),
|
||||||
|
age: v.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await extractFormData(request, schema);
|
||||||
|
|
||||||
|
expect(result.error).toBeNull();
|
||||||
|
expect(result.data).toEqual({ name: 'John Doe', age: '30' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail validation with missing required fields', async () => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('age', '30');
|
||||||
|
|
||||||
|
const request = new Request('http://localhost', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
const schema = v.object({
|
||||||
|
name: v.string(),
|
||||||
|
age: v.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await extractFormData(request, schema);
|
||||||
|
|
||||||
|
expect(result.data).toBeUndefined();
|
||||||
|
expect(result.error).toBeTypeOf('string');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { db } from '$lib/server/db';
|
||||||
|
import { aktis, ratings } from '$lib/server/db/schema';
|
||||||
|
import { avg, eq } from 'drizzle-orm';
|
||||||
|
|
||||||
|
export async function getAktisWithAvgRating(limit = 20, offset = 0) {
|
||||||
|
return await db
|
||||||
|
.select({
|
||||||
|
id: aktis.id,
|
||||||
|
title: aktis.title,
|
||||||
|
summary: aktis.summary,
|
||||||
|
rating: avg(ratings.rating)
|
||||||
|
})
|
||||||
|
.from(aktis)
|
||||||
|
.leftJoin(ratings, eq(aktis.id, ratings.aktiId))
|
||||||
|
.groupBy(aktis.id, aktis.title, aktis.summary)
|
||||||
|
.limit(limit)
|
||||||
|
.offset(offset);
|
||||||
|
}
|
||||||
@@ -1,39 +1,36 @@
|
|||||||
import type { Session, User } from '@auth/sveltekit';
|
import type { Session } from '@auth/sveltekit';
|
||||||
import { error } from '@sveltejs/kit';
|
import { error } from '@sveltejs/kit';
|
||||||
import { db } from './server/db';
|
import { db } from './db';
|
||||||
import { users } from './server/db/schema';
|
import { users } from './db/schema';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
interface Event {
|
interface Event {
|
||||||
locals: {
|
locals: {
|
||||||
auth(): Promise<Session | null>;
|
auth(): Promise<Session | null>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
interface UserWithId extends User {
|
|
||||||
id: string;
|
|
||||||
email: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function ensureAuth(event: Event): Promise<UserWithId> {
|
export async function ensureAuth(event: Event): Promise<Session['user']> {
|
||||||
const session = await getSession(event);
|
const session = await getSession(event);
|
||||||
if (!session) error(401, { message: 'Du muesch di zersch iiloge' });
|
if (!session) error(401, { message: 'Du muesch di zersch iiloge' });
|
||||||
|
|
||||||
const user = session?.user;
|
const user = session.user;
|
||||||
if (!user || !user.email || !user.id) {
|
if (!user || !user.email || !user.id) {
|
||||||
error(401, { message: 'Du muesch di zersch iiloge' });
|
error(401, { message: 'Du muesch di zersch iiloge' });
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...user, id: user.id, email: user.email }; // weird thingamajig so that ts compiler is happy
|
return user;
|
||||||
}
|
}
|
||||||
export async function getSession(event: Event) {
|
export async function getSession(event: Event) {
|
||||||
const session = await event.locals.auth();
|
const session = await event.locals.auth();
|
||||||
if (!session) return null;
|
if (!session) return null;
|
||||||
if (!session.user) error(403, { message: 'Di gits garnid. Vilich nomau usloge u iiloge?' });
|
if (!session.user) error(403, { message: 'Di gits garnid. Vilich nomau usloge u iiloge?' });
|
||||||
|
if (!session?.user?.email) return null;
|
||||||
|
|
||||||
const res = await db
|
const res = await db
|
||||||
.select({ id: users.id })
|
.select({ id: users.id })
|
||||||
.from(users)
|
.from(users)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.where(eq(users.email, session.user.email ?? 'eaf9302d-9525-4f3e-8147-9620d2076f67')); //uuid as default to find nothing
|
.where(eq(users.email, session.user.email));
|
||||||
|
|
||||||
if (!res[0]?.id) {
|
if (!res[0]?.id) {
|
||||||
error(403, { message: 'Di gits garnid. Vilich nomau usloge u iiloge?' });
|
error(403, { message: 'Di gits garnid. Vilich nomau usloge u iiloge?' });
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getSession as getSession } from '$lib/auth';
|
import { getSession } from '$lib/server/session';
|
||||||
import type { LayoutServerLoad } from './$types';
|
import type { LayoutServerLoad } from './$types';
|
||||||
|
|
||||||
export const load: LayoutServerLoad = async (event) => {
|
export const load: LayoutServerLoad = async (event) => {
|
||||||
|
|||||||
@@ -1,19 +1,11 @@
|
|||||||
import { db } from '$lib/server/db';
|
import { getAktisWithAvgRating } from '$lib/server/db/queries';
|
||||||
import { aktis, ratings } from '$lib/server/db/schema';
|
|
||||||
import { avg, eq } from 'drizzle-orm';
|
|
||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
|
|
||||||
export const load: PageServerLoad = async () => {
|
export const load: PageServerLoad = async ({ url }) => {
|
||||||
const a = await db
|
const offset = Number(url.searchParams.get('offset')) || 0;
|
||||||
.select({
|
const limit = 20;
|
||||||
id: aktis.id,
|
|
||||||
title: aktis.title,
|
const a = await getAktisWithAvgRating(limit, offset);
|
||||||
summary: aktis.summary,
|
|
||||||
rating: avg(ratings.rating)
|
|
||||||
})
|
|
||||||
.from(aktis)
|
|
||||||
.leftJoin(ratings, eq(aktis.id, ratings.aktiId))
|
|
||||||
.groupBy(aktis.id, aktis.title, aktis.summary);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
aktis: a.map((a) => ({ ...a, rating: a.rating ? parseFloat(a.rating) : undefined }))
|
aktis: a.map((a) => ({ ...a, rating: a.rating ? parseFloat(a.rating) : undefined }))
|
||||||
|
|||||||
+44
-1
@@ -1,12 +1,55 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import AktiCard from '$lib/components/akti/AktiCard.svelte';
|
import AktiCard from '$lib/components/akti/AktiCard.svelte';
|
||||||
import type { PageProps } from './$types';
|
import type { PageProps } from './$types';
|
||||||
|
import { Spinner } from 'flowbite-svelte';
|
||||||
|
|
||||||
let { data }: PageProps = $props();
|
let { data }: PageProps = $props();
|
||||||
|
|
||||||
|
let aktis = $state(data.aktis);
|
||||||
|
let offset = $state(data.aktis.length);
|
||||||
|
let loading = $state(false);
|
||||||
|
let hasMore = $state(data.aktis.length >= 20);
|
||||||
|
|
||||||
|
async function loadMore() {
|
||||||
|
if (loading || !hasMore) return;
|
||||||
|
loading = true;
|
||||||
|
const res = await fetch(`/api/aktis?offset=${offset}`);
|
||||||
|
const newAktis = await res.json();
|
||||||
|
|
||||||
|
if (newAktis.length < 20) {
|
||||||
|
hasMore = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
aktis = [...aktis, ...newAktis];
|
||||||
|
offset += newAktis.length;
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function infiniteScroll(node: HTMLElement) {
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
if (entries[0].isIntersecting) {
|
||||||
|
loadMore();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
observer.observe(node);
|
||||||
|
return {
|
||||||
|
destroy() {
|
||||||
|
observer.disconnect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="grid gap-5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 3xl:grid-cols-5">
|
<div class="grid gap-5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 3xl:grid-cols-5">
|
||||||
{#each data.aktis as akti (akti.id)}
|
{#each aktis as akti (akti.id)}
|
||||||
<AktiCard {akti}></AktiCard>
|
<AktiCard {akti}></AktiCard>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-10 flex justify-center h-20">
|
||||||
|
{#if loading}
|
||||||
|
<Spinner />
|
||||||
|
{:else if hasMore}
|
||||||
|
<div use:infiniteScroll></div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import { extractFormData } from '$lib/extractFormData';
|
|||||||
import { resolve } from '$app/paths';
|
import { resolve } from '$app/paths';
|
||||||
|
|
||||||
import * as v from 'valibot';
|
import * as v from 'valibot';
|
||||||
import { ensureAuth } from '$lib/auth';
|
import { ensureAuth } from '$lib/server/session';
|
||||||
import { db } from '$lib/server/db';
|
import { db } from '$lib/server/db';
|
||||||
import { aktis } from '$lib/server/db/schema';
|
import { aktis } from '$lib/server/db/schema';
|
||||||
|
import sanitizeHtml from 'sanitize-html';
|
||||||
export const load: PageServerLoad = async (event) => {
|
export const load: PageServerLoad = async (event) => {
|
||||||
await ensureAuth(event);
|
await ensureAuth(event);
|
||||||
return {};
|
return {};
|
||||||
@@ -28,6 +29,8 @@ export const actions = {
|
|||||||
|
|
||||||
if (!akti) return {};
|
if (!akti) return {};
|
||||||
|
|
||||||
|
akti.body = sanitizeHtml(akti.body);
|
||||||
|
|
||||||
const res = await db
|
const res = await db
|
||||||
.insert(aktis)
|
.insert(aktis)
|
||||||
.values({ ...akti, author: user.id! })
|
.values({ ...akti, author: user.id! })
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
import { db } from '$lib/server/db';
|
import { db } from '$lib/server/db';
|
||||||
import { aktis, ratings } from '$lib/server/db/schema';
|
import { aktis, ratings } from '$lib/server/db/schema';
|
||||||
import { error, redirect, type Actions } from '@sveltejs/kit';
|
import { error, fail, redirect, type Actions } from '@sveltejs/kit';
|
||||||
import { and, eq } from 'drizzle-orm';
|
import { and, eq } from 'drizzle-orm';
|
||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
import { ensureAuth } from '$lib/auth';
|
import { ensureAuth } from '$lib/server/session';
|
||||||
import { extractFormData } from '$lib/extractFormData';
|
import { extractFormData } from '$lib/extractFormData';
|
||||||
import * as v from 'valibot';
|
import * as v from 'valibot';
|
||||||
import { resolve } from '$app/paths';
|
import { resolve } from '$app/paths';
|
||||||
|
import sanitizeHtml from 'sanitize-html';
|
||||||
|
|
||||||
export const load: PageServerLoad = async (event) => {
|
export const load: PageServerLoad = async (event) => {
|
||||||
const akti = await db.query.aktis.findFirst({
|
const [akti, r] = await Promise.all([
|
||||||
|
db.query.aktis.findFirst({
|
||||||
where: eq(aktis.id, event.params.aktiId),
|
where: eq(aktis.id, event.params.aktiId),
|
||||||
with: { author: true }
|
with: { author: true }
|
||||||
});
|
}),
|
||||||
|
db.query.ratings.findMany({
|
||||||
const r = await db.query.ratings.findMany({
|
|
||||||
with: { user: true },
|
with: { user: true },
|
||||||
where: eq(ratings.aktiId, event.params.aktiId)
|
where: eq(ratings.aktiId, event.params.aktiId)
|
||||||
});
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
if (!akti) {
|
if (!akti) {
|
||||||
error(404, { message: 'Die Akti gits garnid, sorry...' });
|
error(404, { message: 'Die Akti gits garnid, sorry...' });
|
||||||
@@ -54,7 +56,9 @@ export const actions = {
|
|||||||
)
|
)
|
||||||
).data;
|
).data;
|
||||||
|
|
||||||
if (!changeRequest) return error(400);
|
if (!changeRequest) return fail(400, { message: 'Invalid data' });
|
||||||
|
|
||||||
|
changeRequest.body = sanitizeHtml(changeRequest.body);
|
||||||
|
|
||||||
await db
|
await db
|
||||||
.update(aktis)
|
.update(aktis)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
import { ensureAuth } from '$lib/auth';
|
import { ensureAuth } from '$lib/server/session';
|
||||||
import { error, redirect, type Actions } from '@sveltejs/kit';
|
import { error, fail, redirect, type Actions } from '@sveltejs/kit';
|
||||||
import { extractFormData } from '$lib/extractFormData';
|
import { extractFormData } from '$lib/extractFormData';
|
||||||
import { aktis, ratings } from '$lib/server/db/schema';
|
import { aktis, ratings } from '$lib/server/db/schema';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
@@ -54,7 +54,7 @@ export const actions = {
|
|||||||
)
|
)
|
||||||
).data;
|
).data;
|
||||||
|
|
||||||
if (!rating) return error(400);
|
if (!rating) return fail(400, { message: 'Invalid data' });
|
||||||
|
|
||||||
await db.insert(ratings).values({
|
await db.insert(ratings).values({
|
||||||
...rating,
|
...rating,
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { getAktisWithAvgRating } from '$lib/server/db/queries';
|
||||||
|
import { json, type RequestHandler } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
export const GET: RequestHandler = async ({ url }) => {
|
||||||
|
const offset = Number(url.searchParams.get('offset')) || 0;
|
||||||
|
const limit = 20;
|
||||||
|
|
||||||
|
const a = await getAktisWithAvgRating(limit, offset);
|
||||||
|
|
||||||
|
return json(a.map((a) => ({ ...a, rating: a.rating ? parseFloat(a.rating) : undefined })));
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
import '@testing-library/jest-dom/vitest';
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
|
import { svelteTesting } from '@testing-library/svelte/vite';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [sveltekit(), svelteTesting()],
|
||||||
|
test: {
|
||||||
|
include: ['src/**/*.{test,spec}.{js,ts}'],
|
||||||
|
environment: 'jsdom',
|
||||||
|
setupFiles: ['./vitest-setup.ts']
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user