Upgrade Fresh to v2

Also upgrade all dependencies, make some unnecessary, and some other small tweaks.

Because this has a big potential of unexpectedly breaking things, I'm doing a "breaking release", v3.
This commit is contained in:
Bruno Bernardino 2025-09-26 18:48:02 +01:00
parent ba2103afa9
commit 986573cfc9
No known key found for this signature in database
GPG key ID: D1B0A69ADD114ECE
138 changed files with 13638 additions and 844 deletions

View file

@ -12,3 +12,4 @@ node_modules
_fresh
data-files
bewcloud.config.sample.ts
.vite

2
.dvmrc
View file

@ -1 +1 @@
2.4.5
2.5.1

View file

@ -22,7 +22,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Log in to the Container registry
uses: docker/login-action@v3

View file

@ -10,7 +10,7 @@ jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Configure SSH
run: |
mkdir -p ~/.ssh/

View file

@ -6,7 +6,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: denoland/setup-deno@v2
with:
deno-version-file: .dvmrc

3
.gitignore vendored
View file

@ -18,3 +18,6 @@ bewcloud.config.ts
# Radicale files
data-radicale/
# Vite files
.vite

View file

@ -1,4 +1,4 @@
FROM denoland/deno:ubuntu-2.4.5
FROM denoland/deno:ubuntu-2.5.1
EXPOSE 8000
@ -22,4 +22,7 @@ USER deno
# Compile the main app so that it doesn't need to be compiled each startup/entry.
RUN deno cache --reload main.ts
# Allow the crons to start
ENV START_BEWCLOUD_CRONS=true
CMD ["run", "--allow-all", "main.ts"]

View file

@ -1,3 +1,5 @@
SHELL := /bin/bash
.PHONY: start
start:
deno task start
@ -19,10 +21,6 @@ build:
migrate-db:
deno run --allow-net --allow-read --allow-env migrate-db.ts
.PHONY: crons/cleanup
crons/cleanup:
deno run --allow-net --allow-read --allow-env crons/cleanup.ts
.PHONY: exec-db
exec-db:
docker exec -it -u postgres $(shell basename $(CURDIR))-postgresql-1 psql

View file

@ -3,7 +3,7 @@ import { Config, PartialDeep } from './lib/types.ts';
/** Check the Config type for all the possible options and instructions. */
const config: PartialDeep<Config> = {
auth: {
baseUrl: 'http://localhost:8000', // The base URL of the application you use to access the app, i.e. "http://localhost:8000" or "https://cloud.example.com" (SSO redirect, if enabled, will be this + /oidc/callback, so "https://cloud.example.com/oidc/callback")
baseUrl: 'http://localhost:8000', // The base URL of the application you use to access the app, i.e. "http://localhost:8000" or "https://cloud.example.com" (note authentication won't work without https:// except for localhost; SSO redirect, if enabled, will be this + /oidc/callback, so "https://cloud.example.com/oidc/callback")
allowSignups: false, // If true, anyone can sign up for an account. Note that it's always possible to sign up for the first user, and they will be an admin
enableEmailVerification: false, // If true, email verification will be required for signups (using SMTP settings below)
enableForeverSignup: true, // If true, all signups become active for 100 years

1
client.ts Normal file
View file

@ -0,0 +1 @@
import './static/styles.css';

View file

@ -1,4 +1,4 @@
import { Head } from 'fresh/runtime.ts';
import { Head } from 'fresh/runtime';
import { OptionalApp, User } from '/lib/types.ts';

View file

@ -191,7 +191,6 @@ export default function ExpenseModal(
onChange={(event) => {
newExpenseBudget.value = event.currentTarget.value;
}}
placeholder='Misc'
>
{sortedBudgetNames.map((budget) => (
<option value={budget} selected={newExpenseBudget.value === budget}>{budget}</option>

View file

@ -1,6 +1,6 @@
import { useSignal } from '@preact/signals';
import { useEffect, useRef } from 'preact/hooks';
import { Chart } from 'chart.js';
import { Chart } from 'chart.js/auto';
import { formatNumber } from '/lib/utils/misc.ts';
import { Budget, SupportedCurrencySymbol } from '/lib/types.ts';

View file

@ -1,4 +1,4 @@
import { join } from 'std/path/join.ts';
import { join } from '@std/path';
import { Directory, DirectoryFile } from '/lib/types.ts';
import { humanFileSize, TRASH_PATH } from '/lib/utils/files.ts';

View file

@ -1,7 +1,7 @@
import { Directory, DirectoryFile } from '/lib/types.ts';
import { humanFileSize, TRASH_PATH } from '/lib/utils/files.ts';
interface ListFilesProps {
interface ListPhotosProps {
directories: Directory[];
files: DirectoryFile[];
onClickOpenRenameDirectory?: (parentPath: string, name: string) => void;
@ -13,7 +13,7 @@ interface ListFilesProps {
isShowingNotes?: boolean;
}
export default function ListFiles(
export default function ListPhotos(
{
directories,
files,
@ -24,7 +24,7 @@ export default function ListFiles(
onClickDeleteDirectory,
onClickDeleteFile,
isShowingNotes,
}: ListFilesProps,
}: ListPhotosProps,
) {
const dateFormatOptions: Intl.DateTimeFormatOptions = {
year: 'numeric',

View file

@ -1,4 +1,4 @@
import { Cron } from 'https://deno.land/x/croner@8.1.2/dist/croner.js';
import { Cron } from '@hexagon/croner';
import { AppConfig } from '/lib/config.ts';
import { cleanupSessions } from './sessions.ts';

View file

@ -1,13 +1,10 @@
{
"lock": false,
"tasks": {
"check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx",
"cli": "echo \"import '\\$fresh/src/dev/cli.ts'\" | deno run --unstable -A -",
"manifest": "deno task cli manifest $(pwd)",
"start": "deno run -A --watch=static/,routes/,lib/,components/,islands/ dev.ts",
"build": "deno run -A dev.ts build",
"preview": "deno run -A main.ts",
"update": "deno run -A -r https://fresh.deno.dev/update .",
"check": "deno fmt --check && deno lint && deno check",
"start": "START_BEWCLOUD_CRONS=true vite --host localhost --port 8000",
"build": "vite build",
"preview": "START_BEWCLOUD_CRONS=true deno serve -A _fresh/server.js",
"update": "deno run -A -r jsr:@fresh/update .",
"test": "deno test -A --check"
},
"fmt": {
@ -16,13 +13,14 @@
"indentWidth": 2,
"singleQuote": true,
"proseWrap": "preserve",
"exclude": ["README.md"]
"exclude": ["README.md", "lib/models/dav.js"]
},
"lint": {
"rules": {
"tags": ["fresh", "recommended"],
"exclude": ["no-explicit-any", "no-empty-interface", "no-window", "no-unused-vars"]
}
},
"exclude": ["lib/models/dav.js"]
},
"exclude": ["./_fresh/*", "./node_modules/*", "**/_fresh/*"],
"compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "preact" },
@ -30,28 +28,33 @@
"imports": {
"/": "./",
"./": "./",
"xml": "https://deno.land/x/xml@2.1.3/mod.ts",
"mrmime": "https://deno.land/x/mrmime@v2.0.0/mod.ts",
"fresh/": "https://deno.land/x/fresh@1.7.3/",
"$fresh/": "https://deno.land/x/fresh@1.7.3/",
"std/": "https://deno.land/std@0.224.0/",
"$std/": "https://deno.land/std@0.224.0/",
"postgres": "https://deno.land/x/postgres@v0.19.3/mod.ts",
"preact": "https://esm.sh/preact@10.23.2",
"preact/": "https://esm.sh/preact@10.23.2/",
"@preact/signals": "https://esm.sh/*@preact/signals@1.3.0",
"@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.8.0",
"chart.js": "https://esm.sh/chart.js@4.4.9/auto",
"otpauth": "https://esm.sh/otpauth@9.4.0",
"qrcode": "https://esm.sh/qrcode@1.5.4",
"openid-client": "https://esm.sh/openid-client@6.6.3",
"@simplewebauthn/server": "jsr:@simplewebauthn/server@13.1.2",
"@simplewebauthn/server/helpers": "jsr:@simplewebauthn/server@13.1.2/helpers",
"@simplewebauthn/browser": "jsr:@simplewebauthn/browser@13.1.2",
"tailwindcss": "npm:tailwindcss@3.4.17",
"tailwindcss/": "npm:/tailwindcss@3.4.17/",
"tailwindcss/plugin": "npm:/tailwindcss@3.4.17/plugin.js",
"nodemailer": "npm:nodemailer@7.0.5",
"tsdav": "https://raw.githubusercontent.com/sunsama/tsdav/cc1c5a09b64c87bbee7e5f171cfcb6748e99469e/dist/tsdav.js"
"fresh": "jsr:@fresh/core@2.1.1",
"postgres": "jsr:@db/postgres@0.19.5",
"@b-fuze/deno-dom": "jsr:@b-fuze/deno-dom@0.1.56",
"@fresh/plugin-vite": "jsr:@fresh/plugin-vite@1.0.4",
"@hexagon/croner": "jsr:@hexagon/croner@9.1.0",
"@mikaelporttila/rss": "jsr:@mikaelporttila/rss@1.1.3",
"@libs/qrcode": "jsr:@libs/qrcode@3.0.0",
"@libs/xml": "jsr:@libs/xml@7.0.2",
"@simplewebauthn/server": "jsr:@simplewebauthn/server@13.2.1",
"@simplewebauthn/browser": "jsr:@simplewebauthn/browser@13.2.0",
"@std/assert": "jsr:@std/assert@1.0.14",
"@std/dotenv": "jsr:@std/dotenv@0.225.5",
"@std/encoding": "jsr:@std/encoding@1.0.10",
"@std/http": "jsr:@std/http@1.0.20",
"@std/path": "jsr:@std/path@1.1.2",
"chart.js": "npm:chart.js@4.5.0",
"mrmime": "npm:mrmime@2.0.1",
"nodemailer": "npm:nodemailer@7.0.6",
"openid-client": "npm:openid-client@6.8.0",
"otpauth": "npm:otpauth@9.4.1",
"preact": "npm:preact@10.27.2",
"sharp": "npm:sharp@0.34.4",
"tailwindcss": "npm:tailwindcss@4.1.13",
"vite": "npm:vite@7.1.7",
"@preact/signals": "npm:@preact/signals@2.3.1",
"@tailwindcss/vite": "npm:@tailwindcss/vite@4.1.13"
}
}

1833
deno.lock Normal file

File diff suppressed because it is too large Load diff

8
dev.ts
View file

@ -1,8 +0,0 @@
#!/usr/bin/env -S deno run -A --watch=static/,routes/,lib/,components/,islands/
import dev from 'fresh/dev.ts';
import config from './fresh.config.ts';
import 'std/dotenv/load.ts';
await dev(import.meta.url, './main.ts', config);

View file

@ -18,7 +18,7 @@ services:
# NOTE: If you don't want to use the CardDav/CalDav servers, you can comment/remove this service.
radicale:
image: tomsquest/docker-radicale:3.5.4.0
image: tomsquest/docker-radicale:3.5.6.0
ports:
- 5232:5232
init: true

View file

@ -1,6 +1,6 @@
services:
website:
image: ghcr.io/bewcloud/bewcloud:v2.5.3
image: ghcr.io/bewcloud/bewcloud:v3.0.0
restart: always
ports:
- 127.0.0.1:8000:8000
@ -32,7 +32,7 @@ services:
# NOTE: If you don't want to use the CardDav/CalDav servers, you can comment/remove this service.
radicale:
image: tomsquest/docker-radicale:3.5.4.0
image: tomsquest/docker-radicale:3.5.6.0
# NOTE: uncomment below only if you need to connect to the CardDav/CalDav servers from outside the container
# ports:
# - 127.0.0.1:5232:5232

View file

@ -1,14 +0,0 @@
import { defineConfig } from 'fresh/server.ts';
import tailwind from 'fresh/plugins/tailwind.ts';
import { startCrons } from '/crons/index.ts';
const isBuildMode = Deno.args.includes('build');
if (!isBuildMode) {
await startCrons();
}
export default defineConfig({
plugins: [tailwind()],
});

View file

@ -1,225 +0,0 @@
// DO NOT EDIT. This file is generated by Fresh.
// This file SHOULD be checked into source version control.
// This file is automatically updated during development when running `dev.ts`.
import * as $_well_known_caldav from './routes/.well-known/caldav.tsx';
import * as $_well_known_carddav from './routes/.well-known/carddav.tsx';
import * as $_404 from './routes/_404.tsx';
import * as $_app from './routes/_app.tsx';
import * as $_middleware from './routes/_middleware.tsx';
import * as $api_auth_multi_factor_disable from './routes/api/auth/multi-factor/disable.ts';
import * as $api_auth_multi_factor_email_setup from './routes/api/auth/multi-factor/email/setup.ts';
import * as $api_auth_multi_factor_enable from './routes/api/auth/multi-factor/enable.ts';
import * as $api_auth_multi_factor_passkey_begin from './routes/api/auth/multi-factor/passkey/begin.ts';
import * as $api_auth_multi_factor_passkey_setup_begin from './routes/api/auth/multi-factor/passkey/setup-begin.ts';
import * as $api_auth_multi_factor_passkey_setup_complete from './routes/api/auth/multi-factor/passkey/setup-complete.ts';
import * as $api_auth_multi_factor_passkey_verify from './routes/api/auth/multi-factor/passkey/verify.ts';
import * as $api_auth_multi_factor_totp_setup from './routes/api/auth/multi-factor/totp/setup.ts';
import * as $api_calendar_add_event from './routes/api/calendar/add-event.tsx';
import * as $api_calendar_add from './routes/api/calendar/add.tsx';
import * as $api_calendar_delete_event from './routes/api/calendar/delete-event.tsx';
import * as $api_calendar_delete from './routes/api/calendar/delete.tsx';
import * as $api_calendar_export_events from './routes/api/calendar/export-events.tsx';
import * as $api_calendar_import from './routes/api/calendar/import.tsx';
import * as $api_calendar_search_events from './routes/api/calendar/search-events.tsx';
import * as $api_calendar_update from './routes/api/calendar/update.tsx';
import * as $api_contacts_add_addressbook from './routes/api/contacts/add-addressbook.tsx';
import * as $api_contacts_add from './routes/api/contacts/add.tsx';
import * as $api_contacts_delete_addressbook from './routes/api/contacts/delete-addressbook.tsx';
import * as $api_contacts_delete from './routes/api/contacts/delete.tsx';
import * as $api_contacts_get_addressbooks from './routes/api/contacts/get-addressbooks.tsx';
import * as $api_contacts_get from './routes/api/contacts/get.tsx';
import * as $api_contacts_import from './routes/api/contacts/import.tsx';
import * as $api_dashboard_save_links from './routes/api/dashboard/save-links.tsx';
import * as $api_dashboard_save_notes from './routes/api/dashboard/save-notes.tsx';
import * as $api_expenses_add_budget from './routes/api/expenses/add-budget.tsx';
import * as $api_expenses_add_expense from './routes/api/expenses/add-expense.tsx';
import * as $api_expenses_auto_complete from './routes/api/expenses/auto-complete.tsx';
import * as $api_expenses_delete_budget from './routes/api/expenses/delete-budget.tsx';
import * as $api_expenses_delete_expense from './routes/api/expenses/delete-expense.tsx';
import * as $api_expenses_export_expenses from './routes/api/expenses/export-expenses.tsx';
import * as $api_expenses_import_expenses from './routes/api/expenses/import-expenses.tsx';
import * as $api_expenses_update_budget from './routes/api/expenses/update-budget.tsx';
import * as $api_expenses_update_expense from './routes/api/expenses/update-expense.tsx';
import * as $api_files_create_directory from './routes/api/files/create-directory.tsx';
import * as $api_files_create_share from './routes/api/files/create-share.tsx';
import * as $api_files_delete_directory from './routes/api/files/delete-directory.tsx';
import * as $api_files_delete_share from './routes/api/files/delete-share.tsx';
import * as $api_files_delete from './routes/api/files/delete.tsx';
import * as $api_files_get_directories from './routes/api/files/get-directories.tsx';
import * as $api_files_get_share from './routes/api/files/get-share.tsx';
import * as $api_files_get from './routes/api/files/get.tsx';
import * as $api_files_move_directory from './routes/api/files/move-directory.tsx';
import * as $api_files_move from './routes/api/files/move.tsx';
import * as $api_files_rename_directory from './routes/api/files/rename-directory.tsx';
import * as $api_files_rename from './routes/api/files/rename.tsx';
import * as $api_files_search from './routes/api/files/search.tsx';
import * as $api_files_update_share from './routes/api/files/update-share.tsx';
import * as $api_files_upload from './routes/api/files/upload.tsx';
import * as $api_news_add_feed from './routes/api/news/add-feed.tsx';
import * as $api_news_delete_feed from './routes/api/news/delete-feed.tsx';
import * as $api_news_import_feeds from './routes/api/news/import-feeds.tsx';
import * as $api_news_mark_read from './routes/api/news/mark-read.tsx';
import * as $api_news_refresh_articles from './routes/api/news/refresh-articles.tsx';
import * as $api_notes_save from './routes/api/notes/save.tsx';
import * as $caldav from './routes/caldav.tsx';
import * as $calendar from './routes/calendar.tsx';
import * as $calendar_calendarEventId_ from './routes/calendar/[calendarEventId].tsx';
import * as $calendars from './routes/calendars.tsx';
import * as $carddav from './routes/carddav.tsx';
import * as $contacts from './routes/contacts.tsx';
import * as $contacts_contactId_ from './routes/contacts/[contactId].tsx';
import * as $dashboard from './routes/dashboard.tsx';
import * as $dav from './routes/dav.tsx';
import * as $expenses from './routes/expenses.tsx';
import * as $file_share_fileShareId_ from './routes/file-share/[fileShareId].tsx';
import * as $file_share_fileShareId_open_fileName_ from './routes/file-share/[fileShareId]/open/[fileName].tsx';
import * as $file_share_fileShareId_verify from './routes/file-share/[fileShareId]/verify.tsx';
import * as $files from './routes/files.tsx';
import * as $files_open_fileName_ from './routes/files/open/[fileName].tsx';
import * as $index from './routes/index.tsx';
import * as $login from './routes/login.tsx';
import * as $logout from './routes/logout.tsx';
import * as $mfa_verify from './routes/mfa-verify.tsx';
import * as $news from './routes/news.tsx';
import * as $news_feeds from './routes/news/feeds.tsx';
import * as $notes from './routes/notes.tsx';
import * as $notes_open_fileName_ from './routes/notes/open/[fileName].tsx';
import * as $oidc_callback from './routes/oidc/callback.tsx';
import * as $photos from './routes/photos.tsx';
import * as $photos_thumbnail_fileName_ from './routes/photos/thumbnail/[fileName].tsx';
import * as $settings from './routes/settings.tsx';
import * as $signup from './routes/signup.tsx';
import * as $Settings from './islands/Settings.tsx';
import * as $auth_MultiFactorAuthSettings from './islands/auth/MultiFactorAuthSettings.tsx';
import * as $auth_PasswordlessPasskeyLogin from './islands/auth/PasswordlessPasskeyLogin.tsx';
import * as $calendar_CalendarWrapper from './islands/calendar/CalendarWrapper.tsx';
import * as $calendar_Calendars from './islands/calendar/Calendars.tsx';
import * as $calendar_ViewCalendarEvent from './islands/calendar/ViewCalendarEvent.tsx';
import * as $contacts_Contacts from './islands/contacts/Contacts.tsx';
import * as $contacts_ViewContact from './islands/contacts/ViewContact.tsx';
import * as $dashboard_Links from './islands/dashboard/Links.tsx';
import * as $dashboard_Notes from './islands/dashboard/Notes.tsx';
import * as $expenses_ExpensesWrapper from './islands/expenses/ExpensesWrapper.tsx';
import * as $files_FilesWrapper from './islands/files/FilesWrapper.tsx';
import * as $news_Articles from './islands/news/Articles.tsx';
import * as $news_Feeds from './islands/news/Feeds.tsx';
import * as $notes_Note from './islands/notes/Note.tsx';
import * as $notes_NotesWrapper from './islands/notes/NotesWrapper.tsx';
import * as $photos_PhotosWrapper from './islands/photos/PhotosWrapper.tsx';
import type { Manifest } from '$fresh/server.ts';
const manifest = {
routes: {
'./routes/.well-known/caldav.tsx': $_well_known_caldav,
'./routes/.well-known/carddav.tsx': $_well_known_carddav,
'./routes/_404.tsx': $_404,
'./routes/_app.tsx': $_app,
'./routes/_middleware.tsx': $_middleware,
'./routes/api/auth/multi-factor/disable.ts': $api_auth_multi_factor_disable,
'./routes/api/auth/multi-factor/email/setup.ts': $api_auth_multi_factor_email_setup,
'./routes/api/auth/multi-factor/enable.ts': $api_auth_multi_factor_enable,
'./routes/api/auth/multi-factor/passkey/begin.ts': $api_auth_multi_factor_passkey_begin,
'./routes/api/auth/multi-factor/passkey/setup-begin.ts': $api_auth_multi_factor_passkey_setup_begin,
'./routes/api/auth/multi-factor/passkey/setup-complete.ts': $api_auth_multi_factor_passkey_setup_complete,
'./routes/api/auth/multi-factor/passkey/verify.ts': $api_auth_multi_factor_passkey_verify,
'./routes/api/auth/multi-factor/totp/setup.ts': $api_auth_multi_factor_totp_setup,
'./routes/api/calendar/add-event.tsx': $api_calendar_add_event,
'./routes/api/calendar/add.tsx': $api_calendar_add,
'./routes/api/calendar/delete-event.tsx': $api_calendar_delete_event,
'./routes/api/calendar/delete.tsx': $api_calendar_delete,
'./routes/api/calendar/export-events.tsx': $api_calendar_export_events,
'./routes/api/calendar/import.tsx': $api_calendar_import,
'./routes/api/calendar/search-events.tsx': $api_calendar_search_events,
'./routes/api/calendar/update.tsx': $api_calendar_update,
'./routes/api/contacts/add-addressbook.tsx': $api_contacts_add_addressbook,
'./routes/api/contacts/add.tsx': $api_contacts_add,
'./routes/api/contacts/delete-addressbook.tsx': $api_contacts_delete_addressbook,
'./routes/api/contacts/delete.tsx': $api_contacts_delete,
'./routes/api/contacts/get-addressbooks.tsx': $api_contacts_get_addressbooks,
'./routes/api/contacts/get.tsx': $api_contacts_get,
'./routes/api/contacts/import.tsx': $api_contacts_import,
'./routes/api/dashboard/save-links.tsx': $api_dashboard_save_links,
'./routes/api/dashboard/save-notes.tsx': $api_dashboard_save_notes,
'./routes/api/expenses/add-budget.tsx': $api_expenses_add_budget,
'./routes/api/expenses/add-expense.tsx': $api_expenses_add_expense,
'./routes/api/expenses/auto-complete.tsx': $api_expenses_auto_complete,
'./routes/api/expenses/delete-budget.tsx': $api_expenses_delete_budget,
'./routes/api/expenses/delete-expense.tsx': $api_expenses_delete_expense,
'./routes/api/expenses/export-expenses.tsx': $api_expenses_export_expenses,
'./routes/api/expenses/import-expenses.tsx': $api_expenses_import_expenses,
'./routes/api/expenses/update-budget.tsx': $api_expenses_update_budget,
'./routes/api/expenses/update-expense.tsx': $api_expenses_update_expense,
'./routes/api/files/create-directory.tsx': $api_files_create_directory,
'./routes/api/files/create-share.tsx': $api_files_create_share,
'./routes/api/files/delete-directory.tsx': $api_files_delete_directory,
'./routes/api/files/delete-share.tsx': $api_files_delete_share,
'./routes/api/files/delete.tsx': $api_files_delete,
'./routes/api/files/get-directories.tsx': $api_files_get_directories,
'./routes/api/files/get-share.tsx': $api_files_get_share,
'./routes/api/files/get.tsx': $api_files_get,
'./routes/api/files/move-directory.tsx': $api_files_move_directory,
'./routes/api/files/move.tsx': $api_files_move,
'./routes/api/files/rename-directory.tsx': $api_files_rename_directory,
'./routes/api/files/rename.tsx': $api_files_rename,
'./routes/api/files/search.tsx': $api_files_search,
'./routes/api/files/update-share.tsx': $api_files_update_share,
'./routes/api/files/upload.tsx': $api_files_upload,
'./routes/api/news/add-feed.tsx': $api_news_add_feed,
'./routes/api/news/delete-feed.tsx': $api_news_delete_feed,
'./routes/api/news/import-feeds.tsx': $api_news_import_feeds,
'./routes/api/news/mark-read.tsx': $api_news_mark_read,
'./routes/api/news/refresh-articles.tsx': $api_news_refresh_articles,
'./routes/api/notes/save.tsx': $api_notes_save,
'./routes/caldav.tsx': $caldav,
'./routes/calendar.tsx': $calendar,
'./routes/calendar/[calendarEventId].tsx': $calendar_calendarEventId_,
'./routes/calendars.tsx': $calendars,
'./routes/carddav.tsx': $carddav,
'./routes/contacts.tsx': $contacts,
'./routes/contacts/[contactId].tsx': $contacts_contactId_,
'./routes/dashboard.tsx': $dashboard,
'./routes/dav.tsx': $dav,
'./routes/expenses.tsx': $expenses,
'./routes/file-share/[fileShareId].tsx': $file_share_fileShareId_,
'./routes/file-share/[fileShareId]/open/[fileName].tsx': $file_share_fileShareId_open_fileName_,
'./routes/file-share/[fileShareId]/verify.tsx': $file_share_fileShareId_verify,
'./routes/files.tsx': $files,
'./routes/files/open/[fileName].tsx': $files_open_fileName_,
'./routes/index.tsx': $index,
'./routes/login.tsx': $login,
'./routes/logout.tsx': $logout,
'./routes/mfa-verify.tsx': $mfa_verify,
'./routes/news.tsx': $news,
'./routes/news/feeds.tsx': $news_feeds,
'./routes/notes.tsx': $notes,
'./routes/notes/open/[fileName].tsx': $notes_open_fileName_,
'./routes/oidc/callback.tsx': $oidc_callback,
'./routes/photos.tsx': $photos,
'./routes/photos/thumbnail/[fileName].tsx': $photos_thumbnail_fileName_,
'./routes/settings.tsx': $settings,
'./routes/signup.tsx': $signup,
},
islands: {
'./islands/Settings.tsx': $Settings,
'./islands/auth/MultiFactorAuthSettings.tsx': $auth_MultiFactorAuthSettings,
'./islands/auth/PasswordlessPasskeyLogin.tsx': $auth_PasswordlessPasskeyLogin,
'./islands/calendar/CalendarWrapper.tsx': $calendar_CalendarWrapper,
'./islands/calendar/Calendars.tsx': $calendar_Calendars,
'./islands/calendar/ViewCalendarEvent.tsx': $calendar_ViewCalendarEvent,
'./islands/contacts/Contacts.tsx': $contacts_Contacts,
'./islands/contacts/ViewContact.tsx': $contacts_ViewContact,
'./islands/dashboard/Links.tsx': $dashboard_Links,
'./islands/dashboard/Notes.tsx': $dashboard_Notes,
'./islands/expenses/ExpensesWrapper.tsx': $expenses_ExpensesWrapper,
'./islands/files/FilesWrapper.tsx': $files_FilesWrapper,
'./islands/news/Articles.tsx': $news_Articles,
'./islands/news/Feeds.tsx': $news_Feeds,
'./islands/notes/Note.tsx': $notes_Note,
'./islands/notes/NotesWrapper.tsx': $notes_NotesWrapper,
'./islands/photos/PhotosWrapper.tsx': $photos_PhotosWrapper,
},
baseUrl: import.meta.url,
} satisfies Manifest;
export default manifest;

View file

@ -391,8 +391,8 @@ export default function MultiFactorAuthSettings({ methods }: MultiFactorAuthSett
<p class='mb-4'>
1. Scan this QR code with your authenticator app (Aegis Authenticator, Google Authenticator, etc.):
</p>
<section class='flex justify-center mb-4'>
<img src={setupData.value.qrCodeUrl} alt='TOTP QR Code' class='border' />
<section class='flex justify-center mb-4 max-w-sm mx-auto'>
<img src={setupData.value.qrCodeUrl} alt='TOTP QR Code' class='border-8 border-white' />
</section>
<p class='text-sm text-gray-400 mb-4'>
Or manually enter this secret:{' '}

View file

@ -1,7 +1,6 @@
import { decodeBase64Url, encodeBase64Url } from 'std/encoding/base64url.ts';
import { decodeBase64 } from 'std/encoding/base64.ts';
import { Cookie, getCookies, setCookie } from 'std/http/cookie.ts';
import 'std/dotenv/load.ts';
import { decodeBase64, decodeBase64Url, encodeBase64Url } from '@std/encoding';
import { Cookie, getCookies, setCookie } from '@std/http';
import '@std/dotenv/load';
import { generateHash, isRunningLocally } from './utils/misc.ts';
import { User, UserSession } from './types.ts';
@ -46,7 +45,10 @@ export async function verifyAuthJwt<T = JwtData>(key: CryptoKey, jwt: string): P
}
const data = textToData(jwtParts[0] + '.' + jwtParts[1]);
if (await crypto.subtle.verify({ name: 'HMAC' }, key, decodeBase64Url(jwtParts[2]), data) === true) {
if (
await crypto.subtle.verify({ name: 'HMAC' }, key, decodeBase64Url(jwtParts[2]) as unknown as ArrayBuffer, data) ===
true
) {
return JSON.parse(dataToText(decodeBase64Url(jwtParts[1]))) as T;
}

View file

@ -59,7 +59,7 @@ export class AppConfig {
};
try {
const configFromFile: Config = (await import(`${Deno.cwd()}/bewcloud.config.ts`)).default;
const configFromFile: Config = (await import(/* @vite-ignore */ `${Deno.cwd()}/bewcloud.config.ts`)).default;
this.config = {
...config,

View file

@ -1,5 +1,5 @@
import { DOMParser, initParser } from 'https://deno.land/x/deno_dom@v0.1.45/deno-dom-wasm-noinit.ts';
import { Feed, parseFeed } from 'https://deno.land/x/rss@1.0.0/mod.ts';
import { DOMParser, initParser } from '@b-fuze/deno-dom/wasm-noinit';
import { Feed, parseFeed } from '@mikaelporttila/rss';
import { fetchUrl, fetchUrlAsGooglebot, fetchUrlWithProxy, fetchUrlWithRetries } from './utils/misc.ts';
import { NewsFeed, NewsFeedCrawlType, NewsFeedType } from './types.ts';

View file

@ -124,7 +124,7 @@ function generateInputHtml(
if (type === 'select') {
return (
<select class='mt-1 input-field' id={`field_${name}`} name={name} type={type} {...additionalAttributes}>
<select class='mt-1 input-field' id={`field_${name}`} name={name} {...additionalAttributes}>
{options?.map((option) => (
<option
value={option.value}

View file

@ -1,5 +1,5 @@
import { Client } from 'postgres';
import 'std/dotenv/load.ts';
import '@std/dotenv/load';
const POSTGRESQL_HOST = Deno.env.get('POSTGRESQL_HOST') || '';
const POSTGRESQL_USER = Deno.env.get('POSTGRESQL_USER') || '';

View file

@ -1,5 +1,4 @@
import { createDAVClient } from 'tsdav';
import { createDAVClient } from '/lib/models/dav.js';
import { AppConfig } from '/lib/config.ts';
import { getColorAsHex, parseVCalendar } from '/lib/utils/calendar.ts';
import { concurrentPromises } from '/lib/utils/misc.ts';
@ -146,7 +145,7 @@ export class CalendarModel {
displayName: string,
color?: string,
): Promise<void> {
// Make "manual" request (https://www.rfc-editor.org/rfc/rfc4791.html#page-20) because tsdav doesn't have PROPPATCH
// Make "manual" request (https://www.rfc-editor.org/rfc/rfc4791.html#page-20) because the dav client doesn't have PROPPATCH
const xmlBody = `<?xml version="1.0" encoding="utf-8"?>
<d:proppatch xmlns:d="DAV:" xmlns:a="http://apple.com/ns/ical/">
<d:set>

View file

@ -1,5 +1,4 @@
import { createDAVClient } from 'tsdav';
import { createDAVClient } from '/lib/models/dav.js';
import { AppConfig } from '/lib/config.ts';
import { parseVCard } from '/lib/utils/contacts.ts';

10891
lib/models/dav.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
import nodemailer from 'nodemailer';
import 'std/dotenv/load.ts';
import '@std/dotenv/load';
import { escapeHtml } from '/lib/utils/misc.ts';
import { AppConfig } from '/lib/config.ts';

View file

@ -1,7 +1,6 @@
import { join } from 'std/path/join.ts';
import { resolve } from 'std/path/resolve.ts';
import { join, resolve } from '@std/path';
import { lookup } from 'mrmime';
import { Cookie, getCookies, setCookie } from 'std/http/cookie.ts';
import { Cookie, getCookies, setCookie } from '@std/http';
import { AppConfig } from '/lib/config.ts';
import { Directory, DirectoryFile, FileShare } from '/lib/types.ts';

View file

@ -1,4 +1,4 @@
import { Cookie, getCookies, setCookie } from 'std/http/cookie.ts';
import { Cookie, getCookies, setCookie } from '@std/http';
import { MultiFactorAuthMethod, User } from '/lib/types.ts';
import {

View file

@ -1,7 +1,6 @@
import { Secret, TOTP } from 'otpauth';
import QRCode from 'qrcode';
import { encodeBase32 } from 'std/encoding/base32.ts';
import { decodeBase64, encodeBase64 } from 'std/encoding/base64.ts';
import { qrcode } from '@libs/qrcode';
import { decodeBase64, encodeBase32, encodeBase64 } from '@std/encoding';
import { MultiFactorAuthMethod } from '/lib/types.ts';
import { MFA_KEY, MFA_SALT } from '/lib/auth.ts';
@ -133,7 +132,8 @@ export class TOTPModel {
private static async generateQRCodeDataURL(secret: string, issuer: string, accountName: string): Promise<string> {
const totp = this.createTOTP(secret, issuer, accountName);
const uri = totp.toString();
return await QRCode.toDataURL(uri);
const svgString = await qrcode(uri, { output: 'svg', border: 0 });
return `data:image/svg+xml;base64,${encodeBase64(svgString)}`;
}
private static verifyTOTPToken(secret: string, token: string, window = 1): boolean {

View file

@ -1,4 +1,4 @@
import { Feed } from 'https://deno.land/x/rss@1.0.0/mod.ts';
import { Feed } from '@mikaelporttila/rss';
import Database, { sql } from '/lib/interfaces/database.ts';
import Locker from '/lib/interfaces/locker.ts';
@ -136,7 +136,7 @@ export class FeedModel {
feedArticle.id;
// Fix relative URLs in the feeds
if (url.startsWith('/')) {
if (url!.startsWith('/')) {
const feedUrl = new URL(newsFeed.feed_url);
url = `${feedUrl.origin}${url}`;
}

View file

@ -1,6 +1,6 @@
import { decodeBase64Url } from 'std/encoding/base64url.ts';
import { decodeBase64Url } from '@std/encoding';
import * as openIdClient from 'openid-client';
import 'std/dotenv/load.ts';
import '@std/dotenv/load';
import { createSessionResponse, dataToText } from '/lib/auth.ts';
import { UserModel } from '/lib/models/user.ts';

View file

@ -156,7 +156,7 @@ export type OptionalApp = 'news' | 'notes' | 'photos' | 'expenses' | 'contacts'
export interface Config {
auth: {
/** The base URL of the application you use to access the app, i.e. "http://localhost:8000" or "https://cloud.example.com" */
/** The base URL of the application you use to access the app, i.e. "http://localhost:8000" or "https://cloud.example.com" (note authentication won't work without https:// except for localhost; SSO redirect, if enabled, will be this + /oidc/callback, so "https://cloud.example.com/oidc/callback") */
baseUrl: string;
/** If true, anyone can sign up for an account. Note that it's always possible to sign up for the first user, and they will be an admin */
allowSignups: boolean;
@ -238,6 +238,7 @@ export interface MultiFactorAuthMethod {
counter?: number;
device_type?: string;
backed_up?: boolean;
// @ts-ignore this fails in vite/tests
transports?: AuthenticatorTransport[];
};
};

View file

@ -1,5 +1,4 @@
import { assertEquals } from 'std/assert/assert_equals.ts';
import { assertMatch } from 'std/assert/assert_match.ts';
import { assertEquals, assertMatch } from '@std/assert';
import { Calendar, CalendarEvent } from '/lib/models/calendar.ts';
import {

View file

@ -1,5 +1,4 @@
import { assertEquals } from 'std/assert/assert_equals.ts';
import { assertMatch } from 'std/assert/assert_match.ts';
import { assertEquals, assertMatch } from '@std/assert';
import { generateVCard, getIdFromVCard, parseVCard, splitTextIntoVCards, updateVCard } from './contacts.ts';

View file

@ -1,4 +1,5 @@
import { assertEquals } from 'std/assert/assert_equals.ts';
import { assertEquals } from '@std/assert';
import { humanFileSize } from './files.ts';
Deno.test('that humanFileSize works', () => {

View file

@ -1,4 +1,4 @@
import { assertEquals } from 'std/assert/assert_equals.ts';
import { assertEquals } from '@std/assert';
import { SupportedCurrencySymbol } from '/lib/types.ts';
import {

View file

@ -1,4 +1,4 @@
import { join } from 'std/path/join.ts';
import { join } from '@std/path';
import { lookup } from 'mrmime';
export function getProperDestinationPath(url: string) {

22
main.ts
View file

@ -1,13 +1,15 @@
/// <reference no-default-lib="true" />
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />
/// <reference lib="dom.asynciterable" />
/// <reference lib="deno.ns" />
import { App, staticFiles } from 'fresh';
import 'std/dotenv/load.ts';
import { startCrons } from '/crons/index.ts';
import { start } from 'fresh/server.ts';
import manifest from './fresh.gen.ts';
import config from './fresh.config.ts';
const shouldStartCrons = Deno.env.get('START_BEWCLOUD_CRONS') === 'true';
await start(manifest, config);
if (shouldStartCrons) {
await startCrons();
}
export const app = new App()
// Add static file serving middleware
.use(staticFiles())
// Enable file-system based routing
.fsRoutes();

View file

@ -1,47 +1,80 @@
import { assert } from 'std/assert/assert.ts';
import { assertEquals } from 'std/assert/assert_equals.ts';
import { createHandler, ServeHandlerInfo } from 'fresh/server.ts';
import { join } from '@std/path';
import { assertEquals } from '@std/assert';
import { createBuilder, type InlineConfig } from 'vite';
import manifest from './fresh.gen.ts';
import config from './fresh.config.ts';
// @ts-ignore Deno's newer types seem to have messed this up
const CONN_INFO: ServeHandlerInfo = {
remoteAddr: { hostname: '127.0.0.1', port: 53496, transport: 'tcp' },
};
Deno.test('Basic routes', async (testContext) => {
const handler = await createHandler(manifest, config);
Deno.test('Basic routes', { sanitizeResources: false, sanitizeOps: false }, async (testContext) => {
const app = await buildFreshApp();
const { server, address } = startTestServer(app);
await testContext.step('#1 GET /', async () => {
const response = await handler(new Request('http://127.0.0.1/'), CONN_INFO);
const response = await fetch(`${address}/`, { redirect: 'manual' });
await response.body?.cancel();
assertEquals(response.status, 303);
assertEquals(response.headers.get('Location'), '/login');
});
await testContext.step('#2 GET /login', async () => {
const response = await handler(new Request('http://127.0.0.1/login'), CONN_INFO);
const response = await fetch(`${address}/login`);
const text = await response.text();
assert(text.includes('bewCloud'));
assertEquals(text.includes('bewCloud'), true);
assertEquals(response.status, 200);
});
await testContext.step('#3 GET /blah', async () => {
const response = await handler(new Request('http://127.0.0.1/blah'), CONN_INFO);
const response = await fetch(`${address}/blah`);
const text = await response.text();
assert(text.includes('404 - Page not found'));
assertEquals(text.includes('404 - Page not found'), true);
assertEquals(response.status, 404);
});
await testContext.step('#4 POST /login', async () => {
const formData = new FormData();
formData.append('email', 'user@example.com');
const request = new Request('http://127.0.0.1/login', {
const response = await fetch(`${address}/login`, {
method: 'POST',
body: formData,
});
const response = await handler(request, CONN_INFO);
const text = await response.text();
assert(text.includes('Error: Password is too short'));
assertEquals(text.includes('Error: Password is too short'), true);
assertEquals(response.status, 200);
});
await testContext.step('#5 shutdown', async () => {
await server.shutdown();
});
});
// Default Fresh build configuration
const FRESH_BUILD_CONFIG: InlineConfig = {
logLevel: 'error',
root: './',
build: { emptyOutDir: true },
environments: {
ssr: { build: { outDir: join('_fresh', 'server') } },
client: { build: { outDir: join('_fresh', 'client') } },
},
};
// Helper function to create and build the Fresh app
async function buildFreshApp(config: InlineConfig = FRESH_BUILD_CONFIG) {
const builder = await createBuilder(config);
await builder.buildApp();
return await import('./_fresh/server.js');
}
// Helper function to start a test server
function startTestServer(app: {
default: {
fetch: (req: Request) => Promise<Response>;
};
}) {
const server = Deno.serve({
port: 8000,
handler: app.default.fetch,
});
const { port } = server.addr as Deno.NetAddr;
const address = `http://localhost:${port}`;
return { server, address };
}

View file

@ -1,4 +1,4 @@
import 'std/dotenv/load.ts';
import '@std/dotenv/load';
import Database, { sql } from '/lib/interfaces/database.ts';

View file

@ -1,9 +1,9 @@
import { Handler } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
interface Data {}
export const handler: Handler<Data, FreshContextState> = () => {
export const handler: RouteHandler<Data, FreshContextState> = () => {
return new Response('Redirecting...', { status: 301, headers: { 'Location': '/caldav' } });
};

View file

@ -1,9 +1,9 @@
import { Handler } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
interface Data {}
export const handler: Handler<Data, FreshContextState> = () => {
export const handler: RouteHandler<Data, FreshContextState> = () => {
return new Response('Redirecting...', { status: 301, headers: { 'Location': '/carddav' } });
};

View file

@ -1,38 +0,0 @@
import { Head } from 'fresh/runtime.ts';
import { PageProps } from 'fresh/server.ts';
import { FreshContextState } from '/lib/types.ts';
interface Data {}
export default function Error404({ state }: PageProps<Data, FreshContextState>) {
return (
<>
<Head>
<title>404 - Page not found</title>
</Head>
<main>
<section class='max-w-screen-md mx-auto flex flex-col items-center justify-center'>
{!state.user
? (
<>
<img
class='my-6'
src='/images/logo-white.svg'
width='250'
height='50'
alt='the bewCloud logo: a stylized logo'
/>
<h1>404 - Page not found</h1>
</>
)
: null}
<p class='my-4'>
The page you were looking for doesn't exist.
</p>
<a href='/'>Go back home</a>
</section>
</main>
</>
);
}

View file

@ -1,4 +1,4 @@
import { PageProps } from 'fresh/server.ts';
import { PageProps } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { AppConfig } from '/lib/config.ts';
@ -7,7 +7,7 @@ import Header from '/components/Header.tsx';
interface Data {}
export default async function App(_request: Request, { route, Component, state }: PageProps<Data, FreshContextState>) {
export default async function App({ route, Component, state }: PageProps<Data, FreshContextState>) {
const config = await AppConfig.getConfig();
const defaultTitle = config.visuals.title || 'bewCloud is a modern and simpler alternative to Nextcloud and ownCloud';
@ -29,7 +29,7 @@ export default async function App(_request: Request, { route, Component, state }
<link rel='manifest' href='/manifest.json' />
</head>
<body class='h-full'>
<Header route={route} user={state.user} enabledApps={enabledApps} />
<Header route={route || '/'} user={state.user} enabledApps={enabledApps} />
<Component />
</body>
</html>

66
routes/_error.tsx Normal file
View file

@ -0,0 +1,66 @@
import { HttpError, PageProps } from 'fresh';
import { Head } from 'fresh/runtime';
import { FreshContextState } from '/lib/types.ts';
interface Data {}
export default function ErrorPage({ state, error }: PageProps<Data, FreshContextState>) {
if (error instanceof HttpError) {
const status = error.status;
// Render a 404 not found page
if (status === 404) {
return (
<>
<Head>
<title>404 - Page not found</title>
</Head>
<main>
<section class='max-w-screen-md mx-auto flex flex-col items-center justify-center'>
{!state.user
? (
<>
<img
class='my-6'
src='/images/logo-white.svg'
width='250'
height='50'
alt='the bewCloud logo: a stylized logo'
/>
<h1>404 - Page not found</h1>
</>
)
: null}
<p class='my-4'>
The page you were looking for doesn't exist.
</p>
<a href='/'>Go back home</a>
</section>
</main>
</>
);
}
}
console.error(error);
return (
<>
<Head>
<title>500 - Internal server error</title>
</Head>
<main>
<section class='max-w-screen-md mx-auto flex flex-col items-center justify-center'>
<p class='my-4'>
An internal server error occurred.
</p>
<pre>
{error?.toString() || JSON.stringify({ error }, null, 2)}
</pre>
<a href='/'>Go back home</a>
</section>
</main>
</>
);
}

View file

@ -1,10 +1,12 @@
import { FreshContext } from 'fresh/server.ts';
import { Middleware } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { getDataFromRequest } from '/lib/auth.ts';
export const handler = [
async function handleCors(request: Request, context: FreshContext<FreshContextState>) {
export const handler: Middleware<FreshContextState>[] = [
async function handleCors(context) {
const request = context.req;
const path = new URL(request.url).pathname;
if (
@ -51,7 +53,9 @@ export const handler = [
return response;
},
async function handleContextState(request: Request, context: FreshContext<FreshContextState>) {
async function handleContextState(context) {
const request = context.req;
const { user, session } = (await getDataFromRequest(request)) || {};
if (user) {
@ -67,7 +71,9 @@ export const handler = [
return response;
},
async function handleLogging(request: Request, context: FreshContext<FreshContextState>) {
async function handleLogging(context) {
const request = context.req;
const response = await context.next();
console.info(`${new Date().toISOString()} - [${response.status}] ${request.method} ${request.url}`);

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { PASSWORD_SALT } from '/lib/auth.ts';
@ -19,8 +19,10 @@ export interface ResponseBody {
error?: string;
}
export const handler: Handlers<unknown, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<unknown, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { UserModel } from '/lib/models/user.ts';
@ -16,8 +16,8 @@ export interface ResponseBody {
};
}
export const handler: Handlers<unknown, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<unknown, FreshContextState> = {
async POST(context) {
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { MultiFactorAuthModel } from '/lib/models/multi-factor-auth.ts';
@ -18,8 +18,10 @@ export interface ResponseBody {
error?: string;
}
export const handler: Handlers<unknown, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<unknown, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { PublicKeyCredentialCreationOptionsJSON } from '@simplewebauthn/server';
import { FreshContextState } from '/lib/types.ts';
@ -20,8 +20,10 @@ export interface ResponseBody {
};
}
export const handler: Handlers<unknown, FreshContextState> = {
async POST(request) {
export const handler: RouteHandler<unknown, FreshContextState> = {
async POST(context) {
const request = context.req;
const isMultiFactorAuthEnabled = await AppConfig.isMultiFactorAuthEnabled();
if (!isMultiFactorAuthEnabled) {

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { PublicKeyCredentialCreationOptionsJSON } from '@simplewebauthn/server';
import { FreshContextState } from '/lib/types.ts';
@ -19,8 +19,8 @@ export interface ResponseBody {
};
}
export const handler: Handlers<unknown, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<unknown, FreshContextState> = {
async POST(context) {
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { isoBase64URL } from '@simplewebauthn/server/helpers';
import { RegistrationResponseJSON } from '@simplewebauthn/server';
@ -18,8 +18,10 @@ export interface ResponseBody {
error?: string;
}
export const handler: Handlers<unknown, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<unknown, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { AuthenticationResponseJSON } from '@simplewebauthn/server';
import { FreshContextState } from '/lib/types.ts';
@ -19,8 +19,10 @@ export interface ResponseBody {
error?: string;
}
export const handler: Handlers<unknown, FreshContextState> = {
async POST(request) {
export const handler: RouteHandler<unknown, FreshContextState> = {
async POST(context) {
const request = context.req;
const isMultiFactorAuthEnabled = await AppConfig.isMultiFactorAuthEnabled();
if (!isMultiFactorAuthEnabled) {

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { UserModel } from '/lib/models/user.ts';
@ -19,8 +19,8 @@ export interface ResponseBody {
};
}
export const handler: Handlers<unknown, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<unknown, FreshContextState> = {
async POST(context) {
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { CalendarEvent, CalendarEventModel, CalendarModel } from '/lib/models/calendar.ts';
@ -22,8 +22,10 @@ export interface ResponseBody {
newCalendarEvents: CalendarEvent[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { Calendar, CalendarModel } from '/lib/models/calendar.ts';
@ -15,8 +15,10 @@ export interface ResponseBody {
newCalendars: Calendar[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { CalendarEvent, CalendarEventModel, CalendarModel } from '/lib/models/calendar.ts';
@ -19,8 +19,10 @@ export interface ResponseBody {
newCalendarEvents: CalendarEvent[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { Calendar, CalendarModel } from '/lib/models/calendar.ts';
@ -14,8 +14,10 @@ export interface ResponseBody {
newCalendars: Calendar[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { CalendarEvent, CalendarEventModel } from '/lib/models/calendar.ts';
@ -14,8 +14,10 @@ export interface ResponseBody {
calendarEvents: CalendarEvent[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { CalendarEvent, CalendarEventModel, CalendarModel } from '/lib/models/calendar.ts';
@ -20,8 +20,10 @@ export interface ResponseBody {
newCalendarEvents: CalendarEvent[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { CalendarEvent, CalendarEventModel } from '/lib/models/calendar.ts';
@ -15,8 +15,10 @@ export interface ResponseBody {
matchingCalendarEvents: CalendarEvent[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { Calendar, CalendarModel } from '/lib/models/calendar.ts';
@ -19,8 +19,10 @@ export interface ResponseBody {
newCalendars: Calendar[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { AddressBook, ContactModel } from '/lib/models/contacts.ts';
@ -14,8 +14,10 @@ export interface ResponseBody {
addressBooks: AddressBook[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { Contact, ContactModel } from '/lib/models/contacts.ts';
@ -17,8 +17,10 @@ export interface ResponseBody {
contacts: Contact[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { AddressBook, ContactModel } from '/lib/models/contacts.ts';
@ -14,8 +14,10 @@ export interface ResponseBody {
addressBooks: AddressBook[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { Contact, ContactModel } from '/lib/models/contacts.ts';
@ -15,8 +15,10 @@ export interface ResponseBody {
contacts: Contact[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { AddressBook, ContactModel } from '/lib/models/contacts.ts';
@ -12,8 +12,10 @@ export interface ResponseBody {
addressBooks: AddressBook[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { Contact, ContactModel } from '/lib/models/contacts.ts';
@ -14,8 +14,10 @@ export interface ResponseBody {
contacts: Contact[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { Contact, ContactModel } from '/lib/models/contacts.ts';
@ -17,8 +17,10 @@ export interface ResponseBody {
contacts: Contact[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { DashboardLink, FreshContextState } from '/lib/types.ts';
import { DashboardModel } from '/lib/models/dashboard.ts';
@ -13,8 +13,10 @@ export interface ResponseBody {
success: boolean;
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { DashboardModel } from '/lib/models/dashboard.ts';
@ -13,8 +13,10 @@ export interface ResponseBody {
success: boolean;
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Budget, FreshContextState } from '/lib/types.ts';
import { BudgetModel } from '/lib/models/expenses.ts';
@ -17,8 +17,10 @@ export interface ResponseBody {
newBudgets: Budget[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Budget, Expense, FreshContextState } from '/lib/types.ts';
import { BudgetModel, ExpenseModel } from '/lib/models/expenses.ts';
@ -20,8 +20,10 @@ export interface ResponseBody {
newBudgets: Budget[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FreshContextState } from '/lib/types.ts';
import { ExpenseModel } from '/lib/models/expenses.ts';
@ -14,8 +14,10 @@ export interface ResponseBody {
suggestions: string[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Budget, FreshContextState } from '/lib/types.ts';
import { BudgetModel } from '/lib/models/expenses.ts';
@ -15,8 +15,10 @@ export interface ResponseBody {
newBudgets: Budget[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Budget, Expense, FreshContextState } from '/lib/types.ts';
import { BudgetModel, ExpenseModel } from '/lib/models/expenses.ts';
@ -16,8 +16,10 @@ export interface ResponseBody {
newBudgets: Budget[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Budget, Expense, FreshContextState } from '/lib/types.ts';
import { BudgetModel, ExpenseModel } from '/lib/models/expenses.ts';
@ -15,8 +15,8 @@ export interface ResponseBody {
};
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Budget, Expense, FreshContextState } from '/lib/types.ts';
import { concurrentPromises } from '/lib/utils/misc.ts';
@ -19,8 +19,10 @@ export interface ResponseBody {
newExpenses: Expense[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Budget, FreshContextState } from '/lib/types.ts';
import { BudgetModel } from '/lib/models/expenses.ts';
@ -18,8 +18,10 @@ export interface ResponseBody {
newBudgets: Budget[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Budget, Expense, FreshContextState } from '/lib/types.ts';
import { BudgetModel, ExpenseModel } from '/lib/models/expenses.ts';
@ -21,8 +21,10 @@ export interface ResponseBody {
newBudgets: Budget[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Directory, FreshContextState } from '/lib/types.ts';
import { DirectoryModel } from '/lib/models/files.ts';
@ -15,8 +15,10 @@ export interface ResponseBody {
newDirectories: Directory[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Directory, DirectoryFile, FileShare, FreshContextState } from '/lib/types.ts';
import { DirectoryModel, FileModel, FileShareModel, getPathInfo } from '/lib/models/files.ts';
@ -21,8 +21,10 @@ export interface ResponseBody {
createdFileShareId: string;
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Directory, FreshContextState } from '/lib/types.ts';
import { DirectoryModel } from '/lib/models/files.ts';
@ -15,8 +15,10 @@ export interface ResponseBody {
newDirectories: Directory[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Directory, DirectoryFile, FreshContextState } from '/lib/types.ts';
import { DirectoryModel, FileModel, FileShareModel } from '/lib/models/files.ts';
@ -17,8 +17,10 @@ export interface ResponseBody {
newDirectories: Directory[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { DirectoryFile, FreshContextState } from '/lib/types.ts';
import { FileModel } from '/lib/models/files.ts';
@ -15,8 +15,10 @@ export interface ResponseBody {
newFiles: DirectoryFile[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Directory, FreshContextState } from '/lib/types.ts';
import { DirectoryModel } from '/lib/models/files.ts';
@ -15,8 +15,10 @@ export interface ResponseBody {
directories: Directory[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { FileShare, FreshContextState } from '/lib/types.ts';
import { FileShareModel } from '/lib/models/files.ts';
@ -15,8 +15,10 @@ export interface ResponseBody {
fileShare: FileShare;
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { DirectoryFile, FreshContextState } from '/lib/types.ts';
import { FileModel } from '/lib/models/files.ts';
@ -14,8 +14,10 @@ export interface ResponseBody {
files: DirectoryFile[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Directory, FreshContextState } from '/lib/types.ts';
import { DirectoryModel } from '/lib/models/files.ts';
@ -16,8 +16,10 @@ export interface ResponseBody {
newDirectories: Directory[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { DirectoryFile, FreshContextState } from '/lib/types.ts';
import { FileModel } from '/lib/models/files.ts';
@ -16,8 +16,10 @@ export interface ResponseBody {
newFiles: DirectoryFile[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Directory, FreshContextState } from '/lib/types.ts';
import { DirectoryModel } from '/lib/models/files.ts';
@ -16,8 +16,10 @@ export interface ResponseBody {
newDirectories: Directory[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { DirectoryFile, FreshContextState } from '/lib/types.ts';
import { FileModel } from '/lib/models/files.ts';
@ -16,8 +16,10 @@ export interface ResponseBody {
newFiles: DirectoryFile[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

View file

@ -1,4 +1,4 @@
import { Handlers } from 'fresh/server.ts';
import { RouteHandler } from 'fresh';
import { Directory, DirectoryFile, FreshContextState } from '/lib/types.ts';
import { searchFilesAndDirectories } from '/lib/models/files.ts';
@ -15,8 +15,10 @@ export interface ResponseBody {
files: DirectoryFile[];
}
export const handler: Handlers<Data, FreshContextState> = {
async POST(request, context) {
export const handler: RouteHandler<Data, FreshContextState> = {
async POST(context) {
const request = context.req;
if (!context.state.user) {
return new Response('Unauthorized', { status: 401 });
}

Some files were not shown because too many files have changed in this diff Show more