Merge pull request #100 from bewcloud/feature/upgrade-dependencies
Update all dependencies
This commit is contained in:
2
.github/workflows/build-docker-image.yml
vendored
2
.github/workflows/build-docker-image.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
- name: Log in to the Container registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
|
|||||||
2
.github/workflows/deploy.yml
vendored
2
.github/workflows/deploy.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- name: Configure SSH
|
- name: Configure SSH
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/.ssh/
|
mkdir -p ~/.ssh/
|
||||||
|
|||||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: denoland/setup-deno@v2
|
- uses: denoland/setup-deno@v2
|
||||||
with:
|
with:
|
||||||
deno-version-file: .dvmrc
|
deno-version-file: .dvmrc
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM denoland/deno:ubuntu-2.4.5
|
FROM denoland/deno:ubuntu-2.5.2
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
@@ -9,8 +9,6 @@ WORKDIR /app
|
|||||||
# These steps will be re-run upon each file change in your working directory:
|
# These steps will be re-run upon each file change in your working directory:
|
||||||
ADD . /app
|
ADD . /app
|
||||||
|
|
||||||
RUN rm -fr node_modules _fresh
|
|
||||||
|
|
||||||
# Build fresh
|
# Build fresh
|
||||||
RUN deno task build
|
RUN deno task build
|
||||||
|
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -1,3 +1,5 @@
|
|||||||
|
SHELL := /bin/bash
|
||||||
|
|
||||||
.PHONY: start
|
.PHONY: start
|
||||||
start:
|
start:
|
||||||
deno task start
|
deno task start
|
||||||
@@ -19,10 +21,6 @@ build:
|
|||||||
migrate-db:
|
migrate-db:
|
||||||
deno run --allow-net --allow-read --allow-env migrate-db.ts
|
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
|
.PHONY: exec-db
|
||||||
exec-db:
|
exec-db:
|
||||||
docker exec -it -u postgres $(shell basename $(CURDIR))-postgresql-1 psql
|
docker exec -it -u postgres $(shell basename $(CURDIR))-postgresql-1 psql
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Config, PartialDeep } from './lib/types.ts';
|
|||||||
/** Check the Config type for all the possible options and instructions. */
|
/** Check the Config type for all the possible options and instructions. */
|
||||||
const config: PartialDeep<Config> = {
|
const config: PartialDeep<Config> = {
|
||||||
auth: {
|
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
|
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)
|
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
|
enableForeverSignup: true, // If true, all signups become active for 100 years
|
||||||
|
|||||||
@@ -191,7 +191,6 @@ export default function ExpenseModal(
|
|||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
newExpenseBudget.value = event.currentTarget.value;
|
newExpenseBudget.value = event.currentTarget.value;
|
||||||
}}
|
}}
|
||||||
placeholder='Misc'
|
|
||||||
>
|
>
|
||||||
{sortedBudgetNames.map((budget) => (
|
{sortedBudgetNames.map((budget) => (
|
||||||
<option value={budget} selected={newExpenseBudget.value === budget}>{budget}</option>
|
<option value={budget} selected={newExpenseBudget.value === budget}>{budget}</option>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useSignal } from '@preact/signals';
|
import { useSignal } from '@preact/signals';
|
||||||
import { useEffect, useRef } from 'preact/hooks';
|
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 { formatNumber } from '/lib/utils/misc.ts';
|
||||||
import { Budget, SupportedCurrencySymbol } from '/lib/types.ts';
|
import { Budget, SupportedCurrencySymbol } from '/lib/types.ts';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { join } from 'std/path/join.ts';
|
import { join } from '@std/path';
|
||||||
|
|
||||||
import { Directory, DirectoryFile } from '/lib/types.ts';
|
import { Directory, DirectoryFile } from '/lib/types.ts';
|
||||||
import { humanFileSize, TRASH_PATH } from '/lib/utils/files.ts';
|
import { humanFileSize, TRASH_PATH } from '/lib/utils/files.ts';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Directory, DirectoryFile } from '/lib/types.ts';
|
import { Directory, DirectoryFile } from '/lib/types.ts';
|
||||||
import { humanFileSize, TRASH_PATH } from '/lib/utils/files.ts';
|
import { humanFileSize, TRASH_PATH } from '/lib/utils/files.ts';
|
||||||
|
|
||||||
interface ListFilesProps {
|
interface ListPhotosProps {
|
||||||
directories: Directory[];
|
directories: Directory[];
|
||||||
files: DirectoryFile[];
|
files: DirectoryFile[];
|
||||||
onClickOpenRenameDirectory?: (parentPath: string, name: string) => void;
|
onClickOpenRenameDirectory?: (parentPath: string, name: string) => void;
|
||||||
@@ -13,7 +13,7 @@ interface ListFilesProps {
|
|||||||
isShowingNotes?: boolean;
|
isShowingNotes?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ListFiles(
|
export default function ListPhotos(
|
||||||
{
|
{
|
||||||
directories,
|
directories,
|
||||||
files,
|
files,
|
||||||
@@ -24,7 +24,7 @@ export default function ListFiles(
|
|||||||
onClickDeleteDirectory,
|
onClickDeleteDirectory,
|
||||||
onClickDeleteFile,
|
onClickDeleteFile,
|
||||||
isShowingNotes,
|
isShowingNotes,
|
||||||
}: ListFilesProps,
|
}: ListPhotosProps,
|
||||||
) {
|
) {
|
||||||
const dateFormatOptions: Intl.DateTimeFormatOptions = {
|
const dateFormatOptions: Intl.DateTimeFormatOptions = {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
|
|||||||
@@ -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 { AppConfig } from '/lib/config.ts';
|
||||||
import { cleanupSessions } from './sessions.ts';
|
import { cleanupSessions } from './sessions.ts';
|
||||||
|
|||||||
52
deno.json
52
deno.json
@@ -1,13 +1,12 @@
|
|||||||
{
|
{
|
||||||
"lock": false,
|
"lock": false,
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx",
|
"check": "deno fmt --check && deno lint && deno check .",
|
||||||
"cli": "echo \"import '\\$fresh/src/dev/cli.ts'\" | deno run --unstable -A -",
|
"cli": "echo \"import '\\$fresh/src/dev/cli.ts'\" | deno run --unstable -A -",
|
||||||
"manifest": "deno task cli manifest $(pwd)",
|
"manifest": "deno task cli manifest $(pwd)",
|
||||||
"start": "deno run -A --watch=static/,routes/,lib/,components/,islands/ dev.ts",
|
"start": "deno run -A --watch=static/,routes/,lib/,components/,islands/ dev.ts",
|
||||||
"build": "deno run -A dev.ts build",
|
"build": "deno run -A dev.ts build",
|
||||||
"preview": "deno run -A main.ts",
|
"preview": "deno run -A main.ts",
|
||||||
"update": "deno run -A -r https://fresh.deno.dev/update .",
|
|
||||||
"test": "deno test -A --check"
|
"test": "deno test -A --check"
|
||||||
},
|
},
|
||||||
"fmt": {
|
"fmt": {
|
||||||
@@ -16,13 +15,14 @@
|
|||||||
"indentWidth": 2,
|
"indentWidth": 2,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"proseWrap": "preserve",
|
"proseWrap": "preserve",
|
||||||
"exclude": ["README.md"]
|
"exclude": ["README.md", "lib/models/dav.js"]
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
"rules": {
|
"rules": {
|
||||||
"tags": ["fresh", "recommended"],
|
"tags": ["fresh", "recommended"],
|
||||||
"exclude": ["no-explicit-any", "no-empty-interface", "no-window", "no-unused-vars"]
|
"exclude": ["no-explicit-any", "no-empty-interface", "no-window", "no-unused-vars"]
|
||||||
}
|
},
|
||||||
|
"exclude": ["lib/models/dav.js"]
|
||||||
},
|
},
|
||||||
"exclude": ["./_fresh/*", "./node_modules/*", "**/_fresh/*"],
|
"exclude": ["./_fresh/*", "./node_modules/*", "**/_fresh/*"],
|
||||||
"compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "preact" },
|
"compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "preact" },
|
||||||
@@ -30,28 +30,36 @@
|
|||||||
"imports": {
|
"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/",
|
||||||
"$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": "jsr:@db/postgres@0.19.5",
|
||||||
"postgres": "https://deno.land/x/postgres@v0.19.3/mod.ts",
|
"@b-fuze/deno-dom": "jsr:@b-fuze/deno-dom@0.1.56",
|
||||||
"preact": "https://esm.sh/preact@10.23.2",
|
"@fresh/plugin-vite": "jsr:@fresh/plugin-vite@1.0.4",
|
||||||
"preact/": "https://esm.sh/preact@10.23.2/",
|
"@hexagon/croner": "jsr:@hexagon/croner@9.1.0",
|
||||||
"@preact/signals": "https://esm.sh/*@preact/signals@1.3.0",
|
"@mikaelporttila/rss": "jsr:@mikaelporttila/rss@1.1.3",
|
||||||
"@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.8.0",
|
"@libs/qrcode": "jsr:@libs/qrcode@3.0.0",
|
||||||
"chart.js": "https://esm.sh/chart.js@4.4.9/auto",
|
"@libs/xml": "jsr:@libs/xml@7.0.2",
|
||||||
"otpauth": "https://esm.sh/otpauth@9.4.0",
|
"@simplewebauthn/server": "jsr:@simplewebauthn/server@13.2.1",
|
||||||
"qrcode": "https://esm.sh/qrcode@1.5.4",
|
"@simplewebauthn/browser": "jsr:@simplewebauthn/browser@13.2.0",
|
||||||
"openid-client": "https://esm.sh/openid-client@6.6.3",
|
"@std/assert": "jsr:@std/assert@1.0.14",
|
||||||
"@simplewebauthn/server": "jsr:@simplewebauthn/server@13.1.2",
|
"@std/dotenv": "jsr:@std/dotenv@0.225.5",
|
||||||
"@simplewebauthn/server/helpers": "jsr:@simplewebauthn/server@13.1.2/helpers",
|
"@std/encoding": "jsr:@std/encoding@1.0.10",
|
||||||
"@simplewebauthn/browser": "jsr:@simplewebauthn/browser@13.1.2",
|
"@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@3.4.17",
|
"tailwindcss": "npm:tailwindcss@3.4.17",
|
||||||
"tailwindcss/": "npm:/tailwindcss@3.4.17/",
|
"tailwindcss/": "npm:/tailwindcss@3.4.17/",
|
||||||
"tailwindcss/plugin": "npm:/tailwindcss@3.4.17/plugin.js",
|
"tailwindcss/plugin": "npm:/tailwindcss@3.4.17/plugin.js",
|
||||||
"nodemailer": "npm:nodemailer@7.0.5",
|
"vite": "npm:vite@7.1.7",
|
||||||
"tsdav": "https://raw.githubusercontent.com/sunsama/tsdav/cc1c5a09b64c87bbee7e5f171cfcb6748e99469e/dist/tsdav.js"
|
"@preact/signals": "npm:@preact/signals@2.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
dev.ts
2
dev.ts
@@ -3,6 +3,6 @@
|
|||||||
import dev from 'fresh/dev.ts';
|
import dev from 'fresh/dev.ts';
|
||||||
import config from './fresh.config.ts';
|
import config from './fresh.config.ts';
|
||||||
|
|
||||||
import 'std/dotenv/load.ts';
|
import '@std/dotenv/load';
|
||||||
|
|
||||||
await dev(import.meta.url, './main.ts', config);
|
await dev(import.meta.url, './main.ts', config);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ services:
|
|||||||
|
|
||||||
# NOTE: If you don't want to use the CardDav/CalDav servers, you can comment/remove this service.
|
# NOTE: If you don't want to use the CardDav/CalDav servers, you can comment/remove this service.
|
||||||
radicale:
|
radicale:
|
||||||
image: tomsquest/docker-radicale:3.5.4.0
|
image: tomsquest/docker-radicale:3.5.6.0
|
||||||
ports:
|
ports:
|
||||||
- 5232:5232
|
- 5232:5232
|
||||||
init: true
|
init: true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
website:
|
website:
|
||||||
image: ghcr.io/bewcloud/bewcloud:v2.5.3
|
image: ghcr.io/bewcloud/bewcloud:v2.6.0
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- 127.0.0.1:8000:8000
|
- 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.
|
# NOTE: If you don't want to use the CardDav/CalDav servers, you can comment/remove this service.
|
||||||
radicale:
|
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
|
# NOTE: uncomment below only if you need to connect to the CardDav/CalDav servers from outside the container
|
||||||
# ports:
|
# ports:
|
||||||
# - 127.0.0.1:5232:5232
|
# - 127.0.0.1:5232:5232
|
||||||
|
|||||||
@@ -391,8 +391,8 @@ export default function MultiFactorAuthSettings({ methods }: MultiFactorAuthSett
|
|||||||
<p class='mb-4'>
|
<p class='mb-4'>
|
||||||
1. Scan this QR code with your authenticator app (Aegis Authenticator, Google Authenticator, etc.):
|
1. Scan this QR code with your authenticator app (Aegis Authenticator, Google Authenticator, etc.):
|
||||||
</p>
|
</p>
|
||||||
<section class='flex justify-center mb-4'>
|
<section class='flex justify-center mb-4 max-w-sm mx-auto'>
|
||||||
<img src={setupData.value.qrCodeUrl} alt='TOTP QR Code' class='border' />
|
<img src={setupData.value.qrCodeUrl} alt='TOTP QR Code' class='border-8 border-white' />
|
||||||
</section>
|
</section>
|
||||||
<p class='text-sm text-gray-400 mb-4'>
|
<p class='text-sm text-gray-400 mb-4'>
|
||||||
Or manually enter this secret:{' '}
|
Or manually enter this secret:{' '}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { decodeBase64Url, encodeBase64Url } from 'std/encoding/base64url.ts';
|
import { decodeBase64, decodeBase64Url, encodeBase64Url } from '@std/encoding';
|
||||||
import { decodeBase64 } from 'std/encoding/base64.ts';
|
import { Cookie, getCookies, setCookie } from '@std/http';
|
||||||
import { Cookie, getCookies, setCookie } from 'std/http/cookie.ts';
|
import '@std/dotenv/load';
|
||||||
import 'std/dotenv/load.ts';
|
|
||||||
|
|
||||||
import { generateHash, isRunningLocally } from './utils/misc.ts';
|
import { generateHash, isRunningLocally } from './utils/misc.ts';
|
||||||
import { User, UserSession } from './types.ts';
|
import { User, UserSession } from './types.ts';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DOMParser, initParser } from 'https://deno.land/x/deno_dom@v0.1.45/deno-dom-wasm-noinit.ts';
|
import { DOMParser, initParser } from '@b-fuze/deno-dom/wasm-noinit';
|
||||||
import { Feed, parseFeed } from 'https://deno.land/x/rss@1.0.0/mod.ts';
|
import { Feed, parseFeed } from '@mikaelporttila/rss';
|
||||||
import { fetchUrl, fetchUrlAsGooglebot, fetchUrlWithProxy, fetchUrlWithRetries } from './utils/misc.ts';
|
import { fetchUrl, fetchUrlAsGooglebot, fetchUrlWithProxy, fetchUrlWithRetries } from './utils/misc.ts';
|
||||||
import { NewsFeed, NewsFeedCrawlType, NewsFeedType } from './types.ts';
|
import { NewsFeed, NewsFeedCrawlType, NewsFeedType } from './types.ts';
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ function generateInputHtml(
|
|||||||
|
|
||||||
if (type === 'select') {
|
if (type === 'select') {
|
||||||
return (
|
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) => (
|
{options?.map((option) => (
|
||||||
<option
|
<option
|
||||||
value={option.value}
|
value={option.value}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Client } from 'postgres';
|
import { Client } from 'postgres';
|
||||||
import 'std/dotenv/load.ts';
|
import '@std/dotenv/load';
|
||||||
|
|
||||||
const POSTGRESQL_HOST = Deno.env.get('POSTGRESQL_HOST') || '';
|
const POSTGRESQL_HOST = Deno.env.get('POSTGRESQL_HOST') || '';
|
||||||
const POSTGRESQL_USER = Deno.env.get('POSTGRESQL_USER') || '';
|
const POSTGRESQL_USER = Deno.env.get('POSTGRESQL_USER') || '';
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { createDAVClient } from 'tsdav';
|
import { createDAVClient } from '/lib/models/dav.js';
|
||||||
|
|
||||||
import { AppConfig } from '/lib/config.ts';
|
import { AppConfig } from '/lib/config.ts';
|
||||||
import { getColorAsHex, parseVCalendar } from '/lib/utils/calendar.ts';
|
import { getColorAsHex, parseVCalendar } from '/lib/utils/calendar.ts';
|
||||||
import { concurrentPromises } from '/lib/utils/misc.ts';
|
import { concurrentPromises } from '/lib/utils/misc.ts';
|
||||||
@@ -146,7 +145,7 @@ export class CalendarModel {
|
|||||||
displayName: string,
|
displayName: string,
|
||||||
color?: string,
|
color?: string,
|
||||||
): Promise<void> {
|
): 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"?>
|
const xmlBody = `<?xml version="1.0" encoding="utf-8"?>
|
||||||
<d:proppatch xmlns:d="DAV:" xmlns:a="http://apple.com/ns/ical/">
|
<d:proppatch xmlns:d="DAV:" xmlns:a="http://apple.com/ns/ical/">
|
||||||
<d:set>
|
<d:set>
|
||||||
@@ -195,15 +194,25 @@ export class CalendarEventModel {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const davCalendarEvents: DAVObject[] = await client.fetchCalendarObjects(fetchOptions);
|
||||||
|
|
||||||
if (dateRange) {
|
if (dateRange) {
|
||||||
fetchOptions.timeRange = {
|
fetchOptions.timeRange = {
|
||||||
start: dateRange.start.toISOString(),
|
start: dateRange.start.toISOString(),
|
||||||
end: dateRange.end.toISOString(),
|
end: dateRange.end.toISOString(),
|
||||||
};
|
};
|
||||||
fetchOptions.expand = true;
|
fetchOptions.expand = true;
|
||||||
}
|
|
||||||
|
|
||||||
const davCalendarEvents: DAVObject[] = await client.fetchCalendarObjects(fetchOptions);
|
// Sometimes the expand option doesn't return anything, so we we fetch with and without it, when queried for a date range
|
||||||
|
const davCalendarEventsWithExpansion = await client.fetchCalendarObjects(fetchOptions);
|
||||||
|
|
||||||
|
for (const davCalendarEvent of davCalendarEventsWithExpansion) {
|
||||||
|
// Only add the events that are not already in the list
|
||||||
|
if (!davCalendarEvents.some((davCalendarEvent) => davCalendarEvent.url === davCalendarEvent.url)) {
|
||||||
|
davCalendarEvents.push(davCalendarEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const calendarEvents: CalendarEvent[] = [];
|
const calendarEvents: CalendarEvent[] = [];
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { createDAVClient } from 'tsdav';
|
import { createDAVClient } from '/lib/models/dav.js';
|
||||||
|
|
||||||
import { AppConfig } from '/lib/config.ts';
|
import { AppConfig } from '/lib/config.ts';
|
||||||
import { parseVCard } from '/lib/utils/contacts.ts';
|
import { parseVCard } from '/lib/utils/contacts.ts';
|
||||||
|
|
||||||
|
|||||||
10891
lib/models/dav.js
Normal file
10891
lib/models/dav.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
import nodemailer from 'nodemailer';
|
import nodemailer from 'nodemailer';
|
||||||
import 'std/dotenv/load.ts';
|
import '@std/dotenv/load';
|
||||||
|
|
||||||
import { escapeHtml } from '/lib/utils/misc.ts';
|
import { escapeHtml } from '/lib/utils/misc.ts';
|
||||||
import { AppConfig } from '/lib/config.ts';
|
import { AppConfig } from '/lib/config.ts';
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { join } from 'std/path/join.ts';
|
import { join, resolve } from '@std/path';
|
||||||
import { resolve } from 'std/path/resolve.ts';
|
|
||||||
import { lookup } from 'mrmime';
|
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 { AppConfig } from '/lib/config.ts';
|
||||||
import { Directory, DirectoryFile, FileShare } from '/lib/types.ts';
|
import { Directory, DirectoryFile, FileShare } from '/lib/types.ts';
|
||||||
|
|||||||
@@ -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 { MultiFactorAuthMethod, User } from '/lib/types.ts';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Secret, TOTP } from 'otpauth';
|
import { Secret, TOTP } from 'otpauth';
|
||||||
import QRCode from 'qrcode';
|
import { qrcode } from '@libs/qrcode';
|
||||||
import { encodeBase32 } from 'std/encoding/base32.ts';
|
import { decodeBase64, encodeBase32, encodeBase64 } from '@std/encoding';
|
||||||
import { decodeBase64, encodeBase64 } from 'std/encoding/base64.ts';
|
|
||||||
|
|
||||||
import { MultiFactorAuthMethod } from '/lib/types.ts';
|
import { MultiFactorAuthMethod } from '/lib/types.ts';
|
||||||
import { MFA_KEY, MFA_SALT } from '/lib/auth.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> {
|
private static async generateQRCodeDataURL(secret: string, issuer: string, accountName: string): Promise<string> {
|
||||||
const totp = this.createTOTP(secret, issuer, accountName);
|
const totp = this.createTOTP(secret, issuer, accountName);
|
||||||
const uri = totp.toString();
|
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 {
|
private static verifyTOTPToken(secret: string, token: string, window = 1): boolean {
|
||||||
|
|||||||
@@ -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 Database, { sql } from '/lib/interfaces/database.ts';
|
||||||
import Locker from '/lib/interfaces/locker.ts';
|
import Locker from '/lib/interfaces/locker.ts';
|
||||||
@@ -136,7 +136,7 @@ export class FeedModel {
|
|||||||
feedArticle.id;
|
feedArticle.id;
|
||||||
|
|
||||||
// Fix relative URLs in the feeds
|
// Fix relative URLs in the feeds
|
||||||
if (url.startsWith('/')) {
|
if (url!.startsWith('/')) {
|
||||||
const feedUrl = new URL(newsFeed.feed_url);
|
const feedUrl = new URL(newsFeed.feed_url);
|
||||||
url = `${feedUrl.origin}${url}`;
|
url = `${feedUrl.origin}${url}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { decodeBase64Url } from 'std/encoding/base64url.ts';
|
import { decodeBase64Url } from '@std/encoding';
|
||||||
import * as openIdClient from 'openid-client';
|
import * as openIdClient from 'openid-client';
|
||||||
import 'std/dotenv/load.ts';
|
import '@std/dotenv/load';
|
||||||
|
|
||||||
import { createSessionResponse, dataToText } from '/lib/auth.ts';
|
import { createSessionResponse, dataToText } from '/lib/auth.ts';
|
||||||
import { UserModel } from '/lib/models/user.ts';
|
import { UserModel } from '/lib/models/user.ts';
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ export type OptionalApp = 'news' | 'notes' | 'photos' | 'expenses' | 'contacts'
|
|||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
auth: {
|
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;
|
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 */
|
/** 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;
|
allowSignups: boolean;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { assertEquals } from 'std/assert/assert_equals.ts';
|
import { assertEquals, assertMatch } from '@std/assert';
|
||||||
import { assertMatch } from 'std/assert/assert_match.ts';
|
|
||||||
|
|
||||||
import { Calendar, CalendarEvent } from '/lib/models/calendar.ts';
|
import { Calendar, CalendarEvent } from '/lib/models/calendar.ts';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { assertEquals } from 'std/assert/assert_equals.ts';
|
import { assertEquals, assertMatch } from '@std/assert';
|
||||||
import { assertMatch } from 'std/assert/assert_match.ts';
|
|
||||||
|
|
||||||
import { generateVCard, getIdFromVCard, parseVCard, splitTextIntoVCards, updateVCard } from './contacts.ts';
|
import { generateVCard, getIdFromVCard, parseVCard, splitTextIntoVCards, updateVCard } from './contacts.ts';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { assertEquals } from 'std/assert/assert_equals.ts';
|
import { assertEquals } from '@std/assert';
|
||||||
|
|
||||||
import { humanFileSize } from './files.ts';
|
import { humanFileSize } from './files.ts';
|
||||||
|
|
||||||
Deno.test('that humanFileSize works', () => {
|
Deno.test('that humanFileSize works', () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { assertEquals } from 'std/assert/assert_equals.ts';
|
import { assertEquals } from '@std/assert';
|
||||||
|
|
||||||
import { SupportedCurrencySymbol } from '/lib/types.ts';
|
import { SupportedCurrencySymbol } from '/lib/types.ts';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { join } from 'std/path/join.ts';
|
import { join } from '@std/path';
|
||||||
import { lookup } from 'mrmime';
|
import { lookup } from 'mrmime';
|
||||||
|
|
||||||
export function getProperDestinationPath(url: string) {
|
export function getProperDestinationPath(url: string) {
|
||||||
|
|||||||
2
main.ts
2
main.ts
@@ -4,7 +4,7 @@
|
|||||||
/// <reference lib="dom.asynciterable" />
|
/// <reference lib="dom.asynciterable" />
|
||||||
/// <reference lib="deno.ns" />
|
/// <reference lib="deno.ns" />
|
||||||
|
|
||||||
import 'std/dotenv/load.ts';
|
import '@std/dotenv/load';
|
||||||
|
|
||||||
import { start } from 'fresh/server.ts';
|
import { start } from 'fresh/server.ts';
|
||||||
import manifest from './fresh.gen.ts';
|
import manifest from './fresh.gen.ts';
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { assert } from 'std/assert/assert.ts';
|
import { assertEquals } from '@std/assert';
|
||||||
import { assertEquals } from 'std/assert/assert_equals.ts';
|
|
||||||
import { createHandler, ServeHandlerInfo } from 'fresh/server.ts';
|
import { createHandler, ServeHandlerInfo } from 'fresh/server.ts';
|
||||||
|
|
||||||
import manifest from './fresh.gen.ts';
|
import manifest from './fresh.gen.ts';
|
||||||
@@ -21,14 +20,14 @@ Deno.test('Basic routes', async (testContext) => {
|
|||||||
await testContext.step('#2 GET /login', async () => {
|
await testContext.step('#2 GET /login', async () => {
|
||||||
const response = await handler(new Request('http://127.0.0.1/login'), CONN_INFO);
|
const response = await handler(new Request('http://127.0.0.1/login'), CONN_INFO);
|
||||||
const text = await response.text();
|
const text = await response.text();
|
||||||
assert(text.includes('bewCloud'));
|
assertEquals(text.includes('bewCloud'), true);
|
||||||
assertEquals(response.status, 200);
|
assertEquals(response.status, 200);
|
||||||
});
|
});
|
||||||
|
|
||||||
await testContext.step('#3 GET /blah', async () => {
|
await testContext.step('#3 GET /blah', async () => {
|
||||||
const response = await handler(new Request('http://127.0.0.1/blah'), CONN_INFO);
|
const response = await handler(new Request('http://127.0.0.1/blah'), CONN_INFO);
|
||||||
const text = await response.text();
|
const text = await response.text();
|
||||||
assert(text.includes('404 - Page not found'));
|
assertEquals(text.includes('404 - Page not found'), true);
|
||||||
assertEquals(response.status, 404);
|
assertEquals(response.status, 404);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -41,7 +40,7 @@ Deno.test('Basic routes', async (testContext) => {
|
|||||||
});
|
});
|
||||||
const response = await handler(request, CONN_INFO);
|
const response = await handler(request, CONN_INFO);
|
||||||
const text = await response.text();
|
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);
|
assertEquals(response.status, 200);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'std/dotenv/load.ts';
|
import '@std/dotenv/load';
|
||||||
|
|
||||||
import Database, { sql } from '/lib/interfaces/database.ts';
|
import Database, { sql } from '/lib/interfaces/database.ts';
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
import { Head } from 'fresh/runtime.ts';
|
import { Head } from 'fresh/runtime.ts';
|
||||||
import { PageProps } from 'fresh/server.ts';
|
|
||||||
|
|
||||||
import { FreshContextState } from '/lib/types.ts';
|
export default function Error404() {
|
||||||
|
|
||||||
interface Data {}
|
|
||||||
|
|
||||||
export default function Error404({ state }: PageProps<Data, FreshContextState>) {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
@@ -13,20 +8,6 @@ export default function Error404({ state }: PageProps<Data, FreshContextState>)
|
|||||||
</Head>
|
</Head>
|
||||||
<main>
|
<main>
|
||||||
<section class='max-w-screen-md mx-auto flex flex-col items-center justify-center'>
|
<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'>
|
<p class='my-4'>
|
||||||
The page you were looking for doesn't exist.
|
The page you were looking for doesn't exist.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Handler, RouteConfig } from 'fresh/server.ts';
|
import { Handler, RouteConfig } from 'fresh/server.ts';
|
||||||
import { join } from 'std/path/join.ts';
|
import { join } from '@std/path';
|
||||||
import { parse, stringify } from 'xml';
|
import { parse, stringify } from '@libs/xml';
|
||||||
|
|
||||||
import { FreshContextState } from '/lib/types.ts';
|
import { FreshContextState } from '/lib/types.ts';
|
||||||
import { AppConfig } from '/lib/config.ts';
|
import { AppConfig } from '/lib/config.ts';
|
||||||
@@ -59,7 +59,7 @@ export const handler: Handler<Data, FreshContextState> = async (request, context
|
|||||||
return new Response('Not Found', { status: 404 });
|
return new Response('Not Found', { status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Response(fileResult.contents!, {
|
return new Response(fileResult.contents! as BodyInit, {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: {
|
headers: {
|
||||||
'cache-control': 'no-cache, no-store, must-revalidate',
|
'cache-control': 'no-cache, no-store, must-revalidate',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Handlers, PageProps } from 'fresh/server.ts';
|
import { Handlers, PageProps } from 'fresh/server.ts';
|
||||||
import { join } from 'std/path/join.ts';
|
import { join } from '@std/path';
|
||||||
|
|
||||||
import { Directory, DirectoryFile, FreshContextState } from '/lib/types.ts';
|
import { Directory, DirectoryFile, FreshContextState } from '/lib/types.ts';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Handlers } from 'fresh/server.ts';
|
import { Handlers } from 'fresh/server.ts';
|
||||||
import { join } from 'std/path/join.ts';
|
import { join } from '@std/path';
|
||||||
|
|
||||||
import { FreshContextState } from '/lib/types.ts';
|
import { FreshContextState } from '/lib/types.ts';
|
||||||
import { ensureFileSharePathIsValidAndSecurelyAccessible, FileModel, FileShareModel } from '/lib/models/files.ts';
|
import { ensureFileSharePathIsValidAndSecurelyAccessible, FileModel, FileShareModel } from '/lib/models/files.ts';
|
||||||
@@ -70,7 +70,7 @@ export const handler: Handlers<Data, FreshContextState> = {
|
|||||||
return new Response('Not Found', { status: 404 });
|
return new Response('Not Found', { status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Response(fileResult.contents!, {
|
return new Response(fileResult.contents! as BodyInit, {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: {
|
headers: {
|
||||||
'cache-control': 'no-cache, no-store, must-revalidate',
|
'cache-control': 'no-cache, no-store, must-revalidate',
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export const handler: Handlers<Data, FreshContextState> = {
|
|||||||
return new Response('Not Found', { status: 404 });
|
return new Response('Not Found', { status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Response(fileResult.contents!, {
|
return new Response(fileResult.contents! as BodyInit, {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: {
|
headers: {
|
||||||
'cache-control': 'no-cache, no-store, must-revalidate',
|
'cache-control': 'no-cache, no-store, must-revalidate',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Handlers } from 'fresh/server.ts';
|
import { Handlers } from 'fresh/server.ts';
|
||||||
import { resize } from 'https://deno.land/x/deno_image@0.0.4/mod.ts';
|
import sharp from 'sharp';
|
||||||
|
|
||||||
import { FreshContextState } from '/lib/types.ts';
|
import { FreshContextState } from '/lib/types.ts';
|
||||||
import { FileModel } from '/lib/models/files.ts';
|
import { FileModel } from '/lib/models/files.ts';
|
||||||
@@ -53,9 +53,17 @@ export const handler: Handlers<Data, FreshContextState> = {
|
|||||||
return new Response('Bad Request', { status: 400 });
|
return new Response('Bad Request', { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const resizedImageContents = await resize(fileResult.contents!, { width, height, aspectRatio: true });
|
try {
|
||||||
|
const image = sharp(fileResult.contents! as unknown as ArrayBuffer).resize({
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
fit: 'cover',
|
||||||
|
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
||||||
|
}).png();
|
||||||
|
|
||||||
return new Response(resizedImageContents, {
|
const resizedImageContents = await image.toBuffer();
|
||||||
|
|
||||||
|
return new Response(Uint8Array.from(resizedImageContents), {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: {
|
headers: {
|
||||||
'cache-control': `max-age=${604_800}`, // Tell browsers to cache for 1 week (60 * 60 * 24 * 7 = 604_800)
|
'cache-control': `max-age=${604_800}`, // Tell browsers to cache for 1 week (60 * 60 * 24 * 7 = 604_800)
|
||||||
@@ -63,5 +71,18 @@ export const handler: Handlers<Data, FreshContextState> = {
|
|||||||
'content-length': resizedImageContents.byteLength.toString(),
|
'content-length': resizedImageContents.byteLength.toString(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
// Serve original if we can't make a thumbnail
|
||||||
|
return new Response(fileResult.contents! as BodyInit, {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
'cache-control': `max-age=${604_800}`, // Tell browsers to cache for 1 week (60 * 60 * 24 * 7 = 604_800)
|
||||||
|
'content-type': fileResult.contentType!,
|
||||||
|
'content-length': fileResult.contents!.byteLength.toString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user