chore: move backend to hono + typescript (#558)

* feat: setup secure presigned uploads and downloads

* clean up

* update lock

* Get presigned url finally working

* 557: Migrate backend to hono + ts

* chore: update Dockerfile and package.json for backend configuration

* add dev command

* remove unused packages

* Enhance backend API and frontend components for course management

- Updated CORS configuration in the backend to allow specific methods and headers.
- Refactored lesson download endpoint to use a new route for PDF downloads.
- Removed unused certificate download functionality from the lesson router.
- Updated frontend components to reflect changes in API endpoints and improve video upload functionality.
- Introduced new video upload component and adjusted video handling in lesson materials.
- Cleaned up unnecessary imports and optimized component structure for better performance.

* PR cleanups

* Enhance email configuration by adding SMTP_SENDER environment variable

- Updated docker-compose.yaml to include SMTP_SENDER.
- Modified Dockerfile to accept SMTP_SENDER as an argument.
- Updated environment schema in env.ts to define SMTP_SENDER as optional.
- Adjusted mail service to use SMTP_SENDER for the email 'from' field.
- Updated documentation to reflect the new SMTP_SENDER variable.

* Remove debug log statement from sendEmail function

---------

Co-authored-by: Iniubong Obonguko <[email protected]>
This commit is contained in:
Rotimi Best
2025-06-09 01:38:57 +01:00
committed by GitHub
co-authored by Iniubong Obonguko
parent 9eb7b01587
commit 82be557a42
80 changed files with 3291 additions and 2302 deletions
@@ -1,7 +1,6 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { env } from '$env/dynamic/public';
import CourseContainer from '$lib/components/CourseContainer/index.svelte';
import {
checkExercisesComplete,
@@ -30,8 +29,6 @@
import IconButton from '$lib/components/IconButton/index.svelte';
import CourseIcon from '$lib/components/Icons/CourseIcon.svelte';
import { PageBody, PageNav } from '$lib/components/Page';
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import RoleBasedSecurity from '$lib/components/RoleBasedSecurity/index.svelte';
import { snackbar } from '$lib/components/Snackbar/store';
import MODES from '$lib/utils/constants/mode';
@@ -42,9 +39,9 @@
import { COURSE_VERSION, type Lesson, type LessonCompletion } from '$lib/utils/types';
import { Dropdown } from 'carbon-components-svelte';
import { ChevronLeft, ChevronRight, Edit, Save } from 'carbon-icons-svelte';
import Download from 'carbon-icons-svelte/lib/Download.svelte';
import OverflowMenuVertical from 'carbon-icons-svelte/lib/OverflowMenuVertical.svelte';
import ResultOld from 'carbon-icons-svelte/lib/ResultOld.svelte';
import { apiClient } from '$lib/utils/services/api';
export let data;
@@ -163,13 +160,9 @@
const lessonNumber = getLessonOrder(currentLesson.id);
const slideUrl = $lesson.materials.slide_url || '';
const response = await fetch(env.PUBLIC_SERVER_URL + '/downloadLesson', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
const response = await apiClient.post(
'/course/lesson/download/pdf',
{
title: currentLesson.title,
number: lessonNumber,
orgName: $currentOrg.name,
@@ -177,16 +170,13 @@
slideUrl: slideUrl,
video: lessonVideo,
courseTitle: $course.title
})
});
},
{
responseType: 'blob'
}
);
if (!response.ok) {
throw new Error(await response.text());
}
const data = await response.blob();
console.log(data);
const file = new Blob([data], { type: 'application/pdf' });
const file = new Blob([response.data], { type: 'application/pdf' });
const fileURL = URL.createObjectURL(file);
let a = document.createElement('a');
@@ -386,17 +376,6 @@
<Edit size={24} />
{/if}
</IconButton>
{#if $course.metadata.lessonDownload && !!env.PUBLIC_SERVER_URL}
<PrimaryButton
className="mr-"
variant={VARIANTS.OUTLINED}
onClick={downloadLesson}
{isLoading}
>
<Download size={16} />
</PrimaryButton>
{/if}
</div>
<Dropdown items={LANGUAGES} bind:selectedId={$lesson.locale} class="h-full" />