diff --git a/bewcloud.config.sample.ts b/bewcloud.config.sample.ts index a22412e..6918864 100644 --- a/bewcloud.config.sample.ts +++ b/bewcloud.config.sample.ts @@ -21,7 +21,7 @@ const config: PartialDeep = { // allowDirectoryDownloads: false, // If true, directories can be downloaded as zip files // }, // core: { - // enabledApps: ['news', 'notes', 'photos', 'expenses', 'contacts', 'calendar'], // dashboard and files cannot be disabled + // enabledApps: ['dashboard', 'files', 'news', 'notes', 'photos', 'expenses', 'contacts', 'calendar'], // The apps to show, in order of appearance in the header. The first app will be the default one shown after logging in. At least one is required. // }, // visuals: { // title: 'My own cloud', diff --git a/components/Header.tsx b/components/Header.tsx index 53bb605..df146d8 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -1,6 +1,7 @@ import { Head } from 'fresh/runtime.ts'; import { OptionalApp, User } from '/lib/types.ts'; +import { capitalizeWord } from '/lib/utils/misc.ts'; interface Data { route: string; @@ -23,52 +24,10 @@ export default function Header({ route, user, enabledApps }: Data) { const iconWidthAndHeightInPixels = 20; - const potentialMenuItems: (MenuItem | null)[] = [ - { - url: '/dashboard', - label: 'Dashboard', - }, - enabledApps.includes('news') - ? { - url: '/news', - label: 'News', - } - : null, - { - url: '/files', - label: 'Files', - }, - enabledApps.includes('notes') - ? { - url: '/notes', - label: 'Notes', - } - : null, - enabledApps.includes('photos') - ? { - url: '/photos', - label: 'Photos', - } - : null, - enabledApps.includes('expenses') - ? { - url: '/expenses', - label: 'Expenses', - } - : null, - enabledApps.includes('contacts') - ? { - url: '/contacts', - label: 'Contacts', - } - : null, - enabledApps.includes('calendar') - ? { - url: '/calendar', - label: 'Calendar', - } - : null, - ]; + const potentialMenuItems: (MenuItem | null)[] = enabledApps.map((app) => ({ + url: `/${app}`, + label: capitalizeWord(app), + })); const menuItems = potentialMenuItems.filter(Boolean) as MenuItem[]; diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index e4fd493..9f337cb 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,13 +1,13 @@ services: postgresql: - image: postgres:17 + image: postgres:18.1 environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=fake - POSTGRES_DB=bewcloud restart: on-failure volumes: - - pgdata:/var/lib/postgresql/data + - pgdata:/var/lib/postgresql/18/docker ports: - 5432:5432 ulimits: @@ -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.7.0 + image: tomsquest/docker-radicale:3.5.8.2 ports: - 5232:5232 init: true diff --git a/docker-compose.yml b/docker-compose.yml index 0a97ea6..40df03f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: website: - image: ghcr.io/bewcloud/bewcloud:v2.8.1 + image: ghcr.io/bewcloud/bewcloud:v3.0.0 restart: always ports: - 127.0.0.1:8000:8000 @@ -13,14 +13,14 @@ services: - ./bewcloud.config.ts:/app/bewcloud.config.ts postgresql: - image: postgres:17 + image: postgres:18.1 environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=fake - POSTGRES_DB=bewcloud restart: always volumes: - - bewcloud-db:/var/lib/postgresql/data + - bewcloud-db:/var/lib/postgresql/18/docker # NOTE: uncomment below only if you need to connect to the database from outside the container # ports: # - 127.0.0.1:5432:5432 @@ -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.7.0 + image: tomsquest/docker-radicale:3.5.8.2 # 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 diff --git a/lib/config.ts b/lib/config.ts index a37ef87..69d4c59 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -25,7 +25,7 @@ export class AppConfig { allowDirectoryDownloads: false, }, core: { - enabledApps: ['news', 'notes', 'photos', 'expenses', 'contacts', 'calendar'], + enabledApps: ['dashboard', 'files', 'news', 'notes', 'photos', 'expenses', 'contacts', 'calendar'], }, visuals: { title: '', @@ -96,6 +96,10 @@ export class AppConfig { console.info('\nConfig loaded from bewcloud.config.ts', JSON.stringify(this.config, null, 2), '\n'); + if (this.config.core.enabledApps.length === 0) { + throw new Error('At least one app must be enabled. Please check the config.core.enabledApps array.'); + } + return; } catch (error) { console.error('Error loading config from bewcloud.config.ts. Using default config instead.', error); diff --git a/lib/models/oidc.ts b/lib/models/oidc.ts index 7e4f45a..d8456ae 100644 --- a/lib/models/oidc.ts +++ b/lib/models/oidc.ts @@ -184,7 +184,9 @@ export class OidcModel { throw new Error('There was a problem signing up or logging in!'); } - let urlToRedirectTo = '/dashboard'; + const firstEnabledApp = config.core.enabledApps[0]; + + let urlToRedirectTo = `/${firstEnabledApp}`; if (urlSearchParams.has('state')) { const state = this.parseState(urlSearchParams.get('state')!); diff --git a/lib/types.ts b/lib/types.ts index 2f1b481..04dd1a3 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -152,7 +152,7 @@ export const currencyMap = new Map([ export type PartialDeep = (T extends (infer U)[] ? PartialDeep[] : { [P in keyof T]?: PartialDeep }) | T; -export type OptionalApp = 'news' | 'notes' | 'photos' | 'expenses' | 'contacts' | 'calendar'; +export type OptionalApp = 'dashboard' | 'files' | 'news' | 'notes' | 'photos' | 'expenses' | 'contacts' | 'calendar'; export interface Config { auth: { @@ -188,7 +188,7 @@ export interface Config { allowDirectoryDownloads: boolean; }; core: { - /** dashboard and files cannot be disabled */ + /** The apps to show, in order of appearance in the header. The first app will be the default one shown after logging in. At least one is required. */ enabledApps: OptionalApp[]; }; visuals: { diff --git a/routes/api/calendar/add-event.tsx b/routes/api/calendar/add-event.tsx index 790a036..b7cd2f8 100644 --- a/routes/api/calendar/add-event.tsx +++ b/routes/api/calendar/add-event.tsx @@ -3,6 +3,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { CalendarEvent, CalendarEventModel, CalendarModel } from '/lib/models/calendar.ts'; import { generateVCalendar, getDateRangeForCalendarView } from '/lib/utils/calendar.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -28,6 +29,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('calendar'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/calendar/add.tsx b/routes/api/calendar/add.tsx index 08e8cfc..16ee672 100644 --- a/routes/api/calendar/add.tsx +++ b/routes/api/calendar/add.tsx @@ -3,6 +3,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { Calendar, CalendarModel } from '/lib/models/calendar.ts'; import { CALENDAR_COLOR_OPTIONS, getColorAsHex } from '/lib/utils/calendar.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -21,6 +22,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('calendar'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (requestBody.name) { diff --git a/routes/api/calendar/delete-event.tsx b/routes/api/calendar/delete-event.tsx index 1859bdc..be9155a 100644 --- a/routes/api/calendar/delete-event.tsx +++ b/routes/api/calendar/delete-event.tsx @@ -3,6 +3,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { CalendarEvent, CalendarEventModel, CalendarModel } from '/lib/models/calendar.ts'; import { getDateRangeForCalendarView } from '/lib/utils/calendar.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -25,6 +26,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('calendar'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/calendar/delete.tsx b/routes/api/calendar/delete.tsx index ae64db5..9de4f34 100644 --- a/routes/api/calendar/delete.tsx +++ b/routes/api/calendar/delete.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { Calendar, CalendarModel } from '/lib/models/calendar.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -20,6 +21,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('calendar'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (requestBody.calendarId) { diff --git a/routes/api/calendar/export-events.tsx b/routes/api/calendar/export-events.tsx index 39927f4..ff3facc 100644 --- a/routes/api/calendar/export-events.tsx +++ b/routes/api/calendar/export-events.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { CalendarEvent, CalendarEventModel } from '/lib/models/calendar.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -20,6 +21,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('calendar'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (!requestBody.calendarIds) { diff --git a/routes/api/calendar/import.tsx b/routes/api/calendar/import.tsx index df60736..3458557 100644 --- a/routes/api/calendar/import.tsx +++ b/routes/api/calendar/import.tsx @@ -4,6 +4,7 @@ import { FreshContextState } from '/lib/types.ts'; import { CalendarEvent, CalendarEventModel, CalendarModel } from '/lib/models/calendar.ts'; import { concurrentPromises } from '/lib/utils/misc.ts'; import { getDateRangeForCalendarView, getIdFromVEvent, splitTextIntoVEvents } from '/lib/utils/calendar.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -26,6 +27,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('calendar'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/calendar/search-events.tsx b/routes/api/calendar/search-events.tsx index 31bf660..2e57794 100644 --- a/routes/api/calendar/search-events.tsx +++ b/routes/api/calendar/search-events.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { CalendarEvent, CalendarEventModel } from '/lib/models/calendar.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -21,6 +22,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('calendar'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/calendar/update.tsx b/routes/api/calendar/update.tsx index b651e0c..6ab7350 100644 --- a/routes/api/calendar/update.tsx +++ b/routes/api/calendar/update.tsx @@ -4,6 +4,7 @@ import { FreshContextState } from '/lib/types.ts'; import { Calendar, CalendarModel } from '/lib/models/calendar.ts'; import { UserModel } from '/lib/models/user.ts'; import { CALENDAR_COLOR_OPTIONS, getColorAsHex } from '/lib/utils/calendar.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -25,6 +26,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('calendar'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (requestBody.id) { diff --git a/routes/api/contacts/add-addressbook.tsx b/routes/api/contacts/add-addressbook.tsx index d292ed0..987bf2c 100644 --- a/routes/api/contacts/add-addressbook.tsx +++ b/routes/api/contacts/add-addressbook.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { AddressBook, ContactModel } from '/lib/models/contacts.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -20,6 +21,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('contacts'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (!requestBody.name) { diff --git a/routes/api/contacts/add.tsx b/routes/api/contacts/add.tsx index 5544574..fd79eb4 100644 --- a/routes/api/contacts/add.tsx +++ b/routes/api/contacts/add.tsx @@ -3,6 +3,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { Contact, ContactModel } from '/lib/models/contacts.ts'; import { generateVCard } from '/lib/utils/contacts.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -23,6 +24,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('contacts'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (!requestBody.firstName || !requestBody.addressBookId) { diff --git a/routes/api/contacts/delete-addressbook.tsx b/routes/api/contacts/delete-addressbook.tsx index 053bb19..a694fe9 100644 --- a/routes/api/contacts/delete-addressbook.tsx +++ b/routes/api/contacts/delete-addressbook.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { AddressBook, ContactModel } from '/lib/models/contacts.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -20,6 +21,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('contacts'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (!requestBody.addressBookId) { diff --git a/routes/api/contacts/delete.tsx b/routes/api/contacts/delete.tsx index 438aed5..7b9d449 100644 --- a/routes/api/contacts/delete.tsx +++ b/routes/api/contacts/delete.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { Contact, ContactModel } from '/lib/models/contacts.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -21,6 +22,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('contacts'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (!requestBody.contactId || !requestBody.addressBookId) { diff --git a/routes/api/contacts/get-addressbooks.tsx b/routes/api/contacts/get-addressbooks.tsx index 07937da..b75fa17 100644 --- a/routes/api/contacts/get-addressbooks.tsx +++ b/routes/api/contacts/get-addressbooks.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { AddressBook, ContactModel } from '/lib/models/contacts.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -18,6 +19,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('contacts'))) { + return new Response('Forbidden', { status: 403 }); + } + const addressBooks = await ContactModel.listAddressBooks(context.state.user.id); const responseBody: ResponseBody = { success: true, addressBooks }; diff --git a/routes/api/contacts/get.tsx b/routes/api/contacts/get.tsx index d5d70ca..bbf6ceb 100644 --- a/routes/api/contacts/get.tsx +++ b/routes/api/contacts/get.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { Contact, ContactModel } from '/lib/models/contacts.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -20,6 +21,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('contacts'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (!requestBody.addressBookId) { diff --git a/routes/api/contacts/import.tsx b/routes/api/contacts/import.tsx index 24f2d28..f3179b7 100644 --- a/routes/api/contacts/import.tsx +++ b/routes/api/contacts/import.tsx @@ -4,6 +4,7 @@ import { FreshContextState } from '/lib/types.ts'; import { Contact, ContactModel } from '/lib/models/contacts.ts'; import { concurrentPromises } from '/lib/utils/misc.ts'; import { getIdFromVCard, splitTextIntoVCards } from '/lib/utils/contacts.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -23,6 +24,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('contacts'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (!requestBody.vCards || !requestBody.addressBookId) { diff --git a/routes/api/dashboard/save-links.tsx b/routes/api/dashboard/save-links.tsx index 531828b..2d3c15a 100644 --- a/routes/api/dashboard/save-links.tsx +++ b/routes/api/dashboard/save-links.tsx @@ -1,6 +1,7 @@ import { Handlers } from 'fresh/server.ts'; import { DashboardLink, FreshContextState } from '/lib/types.ts'; +import { AppConfig } from '/lib/config.ts'; import { DashboardModel } from '/lib/models/dashboard.ts'; interface Data {} @@ -19,6 +20,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('dashboard'))) { + return new Response('Forbidden', { status: 403 }); + } + const userDashboard = await DashboardModel.getByUserId(context.state.user.id); if (!userDashboard) { diff --git a/routes/api/dashboard/save-notes.tsx b/routes/api/dashboard/save-notes.tsx index 91ae900..9613935 100644 --- a/routes/api/dashboard/save-notes.tsx +++ b/routes/api/dashboard/save-notes.tsx @@ -1,6 +1,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; +import { AppConfig } from '/lib/config.ts'; import { DashboardModel } from '/lib/models/dashboard.ts'; interface Data {} @@ -19,6 +20,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('dashboard'))) { + return new Response('Forbidden', { status: 403 }); + } + const userDashboard = await DashboardModel.getByUserId(context.state.user.id); if (!userDashboard) { diff --git a/routes/api/expenses/add-budget.tsx b/routes/api/expenses/add-budget.tsx index abc0228..81f3c25 100644 --- a/routes/api/expenses/add-budget.tsx +++ b/routes/api/expenses/add-budget.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { Budget, FreshContextState } from '/lib/types.ts'; import { BudgetModel } from '/lib/models/expenses.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -23,6 +24,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('expenses'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/expenses/add-expense.tsx b/routes/api/expenses/add-expense.tsx index 36198cb..791292a 100644 --- a/routes/api/expenses/add-expense.tsx +++ b/routes/api/expenses/add-expense.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { Budget, Expense, FreshContextState } from '/lib/types.ts'; import { BudgetModel, ExpenseModel } from '/lib/models/expenses.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -26,6 +27,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('expenses'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/expenses/auto-complete.tsx b/routes/api/expenses/auto-complete.tsx index dbeb609..5efc2b9 100644 --- a/routes/api/expenses/auto-complete.tsx +++ b/routes/api/expenses/auto-complete.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { ExpenseModel } from '/lib/models/expenses.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -20,6 +21,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('expenses'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (!requestBody.name || requestBody.name.length < 2) { diff --git a/routes/api/expenses/delete-budget.tsx b/routes/api/expenses/delete-budget.tsx index ec9ad8b..e681a84 100644 --- a/routes/api/expenses/delete-budget.tsx +++ b/routes/api/expenses/delete-budget.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { Budget, FreshContextState } from '/lib/types.ts'; import { BudgetModel } from '/lib/models/expenses.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -21,6 +22,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('expenses'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/expenses/delete-expense.tsx b/routes/api/expenses/delete-expense.tsx index 928e800..ef9fa27 100644 --- a/routes/api/expenses/delete-expense.tsx +++ b/routes/api/expenses/delete-expense.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { Budget, Expense, FreshContextState } from '/lib/types.ts'; import { BudgetModel, ExpenseModel } from '/lib/models/expenses.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -22,6 +23,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('expenses'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/expenses/export-expenses.tsx b/routes/api/expenses/export-expenses.tsx index 6728c60..3d40fea 100644 --- a/routes/api/expenses/export-expenses.tsx +++ b/routes/api/expenses/export-expenses.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { Budget, Expense, FreshContextState } from '/lib/types.ts'; import { BudgetModel, ExpenseModel } from '/lib/models/expenses.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -21,6 +22,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('expenses'))) { + return new Response('Forbidden', { status: 403 }); + } + const newExpenses = await ExpenseModel.getAllForExport(context.state.user.id); const newBudgets = await BudgetModel.getAllForExport(context.state.user.id); diff --git a/routes/api/expenses/import-expenses.tsx b/routes/api/expenses/import-expenses.tsx index 90a23b7..9f5ea36 100644 --- a/routes/api/expenses/import-expenses.tsx +++ b/routes/api/expenses/import-expenses.tsx @@ -3,6 +3,7 @@ import { Handlers } from 'fresh/server.ts'; import { Budget, Expense, FreshContextState } from '/lib/types.ts'; import { concurrentPromises } from '/lib/utils/misc.ts'; import { BudgetModel, deleteAllBudgetsAndExpenses, ExpenseModel } from '/lib/models/expenses.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -25,6 +26,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('expenses'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/expenses/update-budget.tsx b/routes/api/expenses/update-budget.tsx index 5a6cdd0..b18ba4a 100644 --- a/routes/api/expenses/update-budget.tsx +++ b/routes/api/expenses/update-budget.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { Budget, FreshContextState } from '/lib/types.ts'; import { BudgetModel } from '/lib/models/expenses.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -24,6 +25,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('expenses'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/expenses/update-expense.tsx b/routes/api/expenses/update-expense.tsx index 6c43de7..d043435 100644 --- a/routes/api/expenses/update-expense.tsx +++ b/routes/api/expenses/update-expense.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { Budget, Expense, FreshContextState } from '/lib/types.ts'; import { BudgetModel, ExpenseModel } from '/lib/models/expenses.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -27,6 +28,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('expenses'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/files/create-directory.tsx b/routes/api/files/create-directory.tsx index 302c467..d25a178 100644 --- a/routes/api/files/create-directory.tsx +++ b/routes/api/files/create-directory.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { Directory, FreshContextState } from '/lib/types.ts'; import { DirectoryModel } from '/lib/models/files.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -21,6 +22,13 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if ( + !(await AppConfig.isAppEnabled('files')) && !(await AppConfig.isAppEnabled('photos')) && + !(await AppConfig.isAppEnabled('notes')) + ) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/files/create-share.tsx b/routes/api/files/create-share.tsx index 1fb2535..2aa82bf 100644 --- a/routes/api/files/create-share.tsx +++ b/routes/api/files/create-share.tsx @@ -27,6 +27,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('files'))) { + return new Response('Forbidden', { status: 403 }); + } + const isPublicFileSharingAllowed = await AppConfig.isPublicFileSharingAllowed(); if (!isPublicFileSharingAllowed) { diff --git a/routes/api/files/delete-directory.tsx b/routes/api/files/delete-directory.tsx index 414c3bb..3a894d3 100644 --- a/routes/api/files/delete-directory.tsx +++ b/routes/api/files/delete-directory.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { Directory, FreshContextState } from '/lib/types.ts'; import { DirectoryModel } from '/lib/models/files.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -21,6 +22,13 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if ( + !(await AppConfig.isAppEnabled('files')) && !(await AppConfig.isAppEnabled('photos')) && + !(await AppConfig.isAppEnabled('notes')) + ) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/files/delete-share.tsx b/routes/api/files/delete-share.tsx index c048d47..3e06dbc 100644 --- a/routes/api/files/delete-share.tsx +++ b/routes/api/files/delete-share.tsx @@ -29,6 +29,10 @@ export const handler: Handlers = { return new Response('Forbidden', { status: 403 }); } + if (!(await AppConfig.isAppEnabled('files'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/files/delete.tsx b/routes/api/files/delete.tsx index 6d86a1d..683de4f 100644 --- a/routes/api/files/delete.tsx +++ b/routes/api/files/delete.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { DirectoryFile, FreshContextState } from '/lib/types.ts'; import { FileModel } from '/lib/models/files.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -21,6 +22,13 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if ( + !(await AppConfig.isAppEnabled('files')) && !(await AppConfig.isAppEnabled('photos')) && + !(await AppConfig.isAppEnabled('notes')) + ) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/files/download-directory.tsx b/routes/api/files/download-directory.tsx index ea72065..b687fa0 100644 --- a/routes/api/files/download-directory.tsx +++ b/routes/api/files/download-directory.tsx @@ -20,6 +20,13 @@ export const handler: Handlers = { return new Response('Directory downloads are not enabled', { status: 403 }); } + if ( + !(await AppConfig.isAppEnabled('files')) && !(await AppConfig.isAppEnabled('photos')) && + !(await AppConfig.isAppEnabled('notes')) + ) { + return new Response('Forbidden', { status: 403 }); + } + const searchParams = new URL(request.url).searchParams; const parentPath = searchParams.get('parentPath') || '/'; const name = searchParams.get('name'); diff --git a/routes/api/files/get-directories.tsx b/routes/api/files/get-directories.tsx index 57fb679..4da4924 100644 --- a/routes/api/files/get-directories.tsx +++ b/routes/api/files/get-directories.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { Directory, FreshContextState } from '/lib/types.ts'; import { DirectoryModel } from '/lib/models/files.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -21,6 +22,13 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if ( + !(await AppConfig.isAppEnabled('files')) && !(await AppConfig.isAppEnabled('photos')) && + !(await AppConfig.isAppEnabled('notes')) + ) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/files/get-share.tsx b/routes/api/files/get-share.tsx index 2a6d3ff..164face 100644 --- a/routes/api/files/get-share.tsx +++ b/routes/api/files/get-share.tsx @@ -27,6 +27,10 @@ export const handler: Handlers = { return new Response('Forbidden', { status: 403 }); } + if (!(await AppConfig.isAppEnabled('files'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (!requestBody.fileShareId) { diff --git a/routes/api/files/get.tsx b/routes/api/files/get.tsx index 1d81ae6..ef32012 100644 --- a/routes/api/files/get.tsx +++ b/routes/api/files/get.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { DirectoryFile, FreshContextState } from '/lib/types.ts'; import { FileModel } from '/lib/models/files.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -20,6 +21,13 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if ( + !(await AppConfig.isAppEnabled('files')) && !(await AppConfig.isAppEnabled('photos')) && + !(await AppConfig.isAppEnabled('notes')) + ) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/files/move-directory.tsx b/routes/api/files/move-directory.tsx index 9b5da7a..28275e1 100644 --- a/routes/api/files/move-directory.tsx +++ b/routes/api/files/move-directory.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { Directory, FreshContextState } from '/lib/types.ts'; import { DirectoryModel } from '/lib/models/files.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -22,6 +23,13 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if ( + !(await AppConfig.isAppEnabled('files')) && !(await AppConfig.isAppEnabled('photos')) && + !(await AppConfig.isAppEnabled('notes')) + ) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/files/move.tsx b/routes/api/files/move.tsx index dd0156e..af40f42 100644 --- a/routes/api/files/move.tsx +++ b/routes/api/files/move.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { DirectoryFile, FreshContextState } from '/lib/types.ts'; import { FileModel } from '/lib/models/files.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -22,6 +23,13 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if ( + !(await AppConfig.isAppEnabled('files')) && !(await AppConfig.isAppEnabled('photos')) && + !(await AppConfig.isAppEnabled('notes')) + ) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/files/rename-directory.tsx b/routes/api/files/rename-directory.tsx index 3e10f14..9084239 100644 --- a/routes/api/files/rename-directory.tsx +++ b/routes/api/files/rename-directory.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { Directory, FreshContextState } from '/lib/types.ts'; import { DirectoryModel } from '/lib/models/files.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -22,6 +23,13 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if ( + !(await AppConfig.isAppEnabled('files')) && !(await AppConfig.isAppEnabled('photos')) && + !(await AppConfig.isAppEnabled('notes')) + ) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/files/rename.tsx b/routes/api/files/rename.tsx index 751bc0a..75c9f94 100644 --- a/routes/api/files/rename.tsx +++ b/routes/api/files/rename.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { DirectoryFile, FreshContextState } from '/lib/types.ts'; import { FileModel } from '/lib/models/files.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -22,6 +23,13 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if ( + !(await AppConfig.isAppEnabled('files')) && !(await AppConfig.isAppEnabled('photos')) && + !(await AppConfig.isAppEnabled('notes')) + ) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/files/search.tsx b/routes/api/files/search.tsx index 9229941..299a5c1 100644 --- a/routes/api/files/search.tsx +++ b/routes/api/files/search.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { Directory, DirectoryFile, FreshContextState } from '/lib/types.ts'; import { searchFilesAndDirectories } from '/lib/models/files.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -21,6 +22,13 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if ( + !(await AppConfig.isAppEnabled('files')) && !(await AppConfig.isAppEnabled('photos')) && + !(await AppConfig.isAppEnabled('notes')) + ) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (!requestBody.searchTerm?.trim()) { diff --git a/routes/api/files/update-share.tsx b/routes/api/files/update-share.tsx index 2941ebd..e55b622 100644 --- a/routes/api/files/update-share.tsx +++ b/routes/api/files/update-share.tsx @@ -32,6 +32,10 @@ export const handler: Handlers = { return new Response('Forbidden', { status: 403 }); } + if (!(await AppConfig.isAppEnabled('files'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/api/files/upload.tsx b/routes/api/files/upload.tsx index 790f50e..cceea74 100644 --- a/routes/api/files/upload.tsx +++ b/routes/api/files/upload.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { Directory, DirectoryFile, FreshContextState } from '/lib/types.ts'; import { DirectoryModel, FileModel } from '/lib/models/files.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -17,6 +18,13 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if ( + !(await AppConfig.isAppEnabled('files')) && !(await AppConfig.isAppEnabled('photos')) && + !(await AppConfig.isAppEnabled('notes')) + ) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().formData(); const pathInView = requestBody.get('path_in_view') as string; diff --git a/routes/api/news/add-feed.tsx b/routes/api/news/add-feed.tsx index cfe08f0..68fe432 100644 --- a/routes/api/news/add-feed.tsx +++ b/routes/api/news/add-feed.tsx @@ -3,6 +3,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState, NewsFeed } from '/lib/types.ts'; import { FeedModel } from '/lib/models/news.ts'; import { fetchNewArticles } from '/crons/news.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -21,6 +22,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('news'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (requestBody.feedUrl) { diff --git a/routes/api/news/delete-feed.tsx b/routes/api/news/delete-feed.tsx index 28f425a..2a469ae 100644 --- a/routes/api/news/delete-feed.tsx +++ b/routes/api/news/delete-feed.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState, NewsFeed } from '/lib/types.ts'; import { FeedModel } from '/lib/models/news.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -20,6 +21,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('news'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (requestBody.feedId) { diff --git a/routes/api/news/import-feeds.tsx b/routes/api/news/import-feeds.tsx index 2716962..cd04bfa 100644 --- a/routes/api/news/import-feeds.tsx +++ b/routes/api/news/import-feeds.tsx @@ -4,6 +4,7 @@ import { FreshContextState, NewsFeed } from '/lib/types.ts'; import { concurrentPromises } from '/lib/utils/misc.ts'; import { FeedModel } from '/lib/models/news.ts'; import { fetchNewArticles } from '/crons/news.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -22,6 +23,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('news'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (requestBody.feedUrls) { diff --git a/routes/api/news/mark-read.tsx b/routes/api/news/mark-read.tsx index 8c4a263..6ddc633 100644 --- a/routes/api/news/mark-read.tsx +++ b/routes/api/news/mark-read.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { ArticleModel } from '/lib/models/news.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -19,6 +20,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('news'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if (requestBody.articleId) { diff --git a/routes/api/news/refresh-articles.tsx b/routes/api/news/refresh-articles.tsx index 17ae173..600d8bf 100644 --- a/routes/api/news/refresh-articles.tsx +++ b/routes/api/news/refresh-articles.tsx @@ -3,6 +3,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState, NewsFeedArticle } from '/lib/types.ts'; import { ArticleModel, FeedModel } from '/lib/models/news.ts'; import { fetchNewArticles } from '/crons/news.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -19,6 +20,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('news'))) { + return new Response('Forbidden', { status: 403 }); + } + const newsFeeds = await FeedModel.list(context.state.user.id); if (!newsFeeds.length) { diff --git a/routes/api/notes/save.tsx b/routes/api/notes/save.tsx index ca38159..6c27de2 100644 --- a/routes/api/notes/save.tsx +++ b/routes/api/notes/save.tsx @@ -2,6 +2,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { FileModel } from '/lib/models/files.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -21,6 +22,10 @@ export const handler: Handlers = { return new Response('Unauthorized', { status: 401 }); } + if (!(await AppConfig.isAppEnabled('notes'))) { + return new Response('Forbidden', { status: 403 }); + } + const requestBody = await request.clone().json() as RequestBody; if ( diff --git a/routes/calendar.tsx b/routes/calendar.tsx index 2891082..f0a7d2a 100644 --- a/routes/calendar.tsx +++ b/routes/calendar.tsx @@ -29,6 +29,10 @@ export const handler: Handlers = { throw new Error('CalDAV server is not enabled'); } + if (!(await AppConfig.isAppEnabled('calendar'))) { + throw new Error('Calendar app is not enabled'); + } + const userId = context.state.user.id; const timezoneId = context.state.user.extra.timezone?.id || 'UTC'; const timezoneUtcOffset = context.state.user.extra.timezone?.utcOffset || 0; diff --git a/routes/calendar/[calendarEventId].tsx b/routes/calendar/[calendarEventId].tsx index 239eaa6..f38f431 100644 --- a/routes/calendar/[calendarEventId].tsx +++ b/routes/calendar/[calendarEventId].tsx @@ -28,6 +28,10 @@ export const handler: Handlers = { throw new Error('CalDAV server is not enabled'); } + if (!(await AppConfig.isAppEnabled('calendar'))) { + throw new Error('Calendar app is not enabled'); + } + let { calendarEventId } = context.params; const searchParams = new URL(request.url).searchParams; @@ -63,6 +67,10 @@ export const handler: Handlers = { throw new Error('CalDAV server is not enabled'); } + if (!(await AppConfig.isAppEnabled('calendar'))) { + throw new Error('Calendar app is not enabled'); + } + const { calendarEventId } = context.params; const searchParams = new URL(request.url).searchParams; diff --git a/routes/calendars.tsx b/routes/calendars.tsx index cf545ae..0e50c50 100644 --- a/routes/calendars.tsx +++ b/routes/calendars.tsx @@ -21,6 +21,10 @@ export const handler: Handlers = { throw new Error('CalDAV server is not enabled'); } + if (!(await AppConfig.isAppEnabled('calendar'))) { + throw new Error('Calendar app is not enabled'); + } + const userCalendars = await CalendarModel.list(context.state.user.id); return await context.render({ userCalendars }); diff --git a/routes/contacts.tsx b/routes/contacts.tsx index a469d5e..e6bdd15 100644 --- a/routes/contacts.tsx +++ b/routes/contacts.tsx @@ -28,6 +28,10 @@ export const handler: Handlers = { throw new Error('CardDAV server is not enabled'); } + if (!(await AppConfig.isAppEnabled('contacts'))) { + throw new Error('Contacts app is not enabled'); + } + const userId = context.state.user.id; const searchParams = new URL(request.url).searchParams; diff --git a/routes/contacts/[contactId].tsx b/routes/contacts/[contactId].tsx index b855412..72119a2 100644 --- a/routes/contacts/[contactId].tsx +++ b/routes/contacts/[contactId].tsx @@ -6,6 +6,7 @@ import { Contact, ContactModel } from '/lib/models/contacts.ts'; import { getFormDataField } from '/lib/form-utils.tsx'; import ViewContact, { formFields } from '/islands/contacts/ViewContact.tsx'; import { updateVCard } from '/lib/utils/contacts.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data { contact: Contact; @@ -21,6 +22,10 @@ export const handler: Handlers = { return new Response('Redirect', { status: 303, headers: { 'Location': `/login` } }); } + if (!(await AppConfig.isAppEnabled('contacts'))) { + throw new Error('Contacts app is not enabled'); + } + const { contactId } = context.params; const searchParams = new URL(request.url).searchParams; @@ -43,6 +48,10 @@ export const handler: Handlers = { return new Response('Redirect', { status: 303, headers: { 'Location': `/login` } }); } + if (!(await AppConfig.isAppEnabled('contacts'))) { + throw new Error('Contacts app is not enabled'); + } + const { contactId } = context.params; const searchParams = new URL(request.url).searchParams; diff --git a/routes/dashboard.tsx b/routes/dashboard.tsx index d56ca0a..0ef9ea3 100644 --- a/routes/dashboard.tsx +++ b/routes/dashboard.tsx @@ -2,6 +2,7 @@ import { Handlers, PageProps } from 'fresh/server.ts'; import { Dashboard, FreshContextState } from '/lib/types.ts'; import { DashboardModel } from '/lib/models/dashboard.ts'; +import { AppConfig } from '/lib/config.ts'; import Notes from '/islands/dashboard/Notes.tsx'; import Links from '/islands/dashboard/Links.tsx'; @@ -15,6 +16,10 @@ export const handler: Handlers = { return new Response('Redirect', { status: 303, headers: { 'Location': `/login` } }); } + if (!(await AppConfig.isAppEnabled('dashboard'))) { + return new Response('Redirect', { status: 303, headers: { 'Location': `/` } }); + } + let userDashboard = await DashboardModel.getByUserId(context.state.user.id); if (!userDashboard) { diff --git a/routes/dav.tsx b/routes/dav.tsx index 5192e89..1d3c88b 100644 --- a/routes/dav.tsx +++ b/routes/dav.tsx @@ -27,6 +27,13 @@ export const handler: Handler = async (request, context }); } + if ( + !(await AppConfig.isAppEnabled('files')) && !(await AppConfig.isAppEnabled('photos')) && + !(await AppConfig.isAppEnabled('notes')) + ) { + return new Response('Forbidden', { status: 403 }); + } + let { filePath } = context.params; if (!filePath) { diff --git a/routes/expenses.tsx b/routes/expenses.tsx index 4d5acba..9ffb62d 100644 --- a/routes/expenses.tsx +++ b/routes/expenses.tsx @@ -19,7 +19,7 @@ export const handler: Handlers = { } if (!(await AppConfig.isAppEnabled('expenses'))) { - return new Response('Redirect', { status: 303, headers: { 'Location': `/files` } }); + return new Response('Redirect', { status: 303, headers: { 'Location': `/` } }); } const searchParams = new URL(request.url).searchParams; diff --git a/routes/file-share/[fileShareId].tsx b/routes/file-share/[fileShareId].tsx index 0709597..f693f69 100644 --- a/routes/file-share/[fileShareId].tsx +++ b/routes/file-share/[fileShareId].tsx @@ -35,6 +35,10 @@ export const handler: Handlers = { const baseUrl = (await AppConfig.getConfig()).auth.baseUrl; + if (!(await AppConfig.isAppEnabled('files'))) { + return new Response('Not Found', { status: 404 }); + } + const fileShare = await FileShareModel.getById(fileShareId); if (!fileShare) { diff --git a/routes/file-share/[fileShareId]/open/[fileName].tsx b/routes/file-share/[fileShareId]/open/[fileName].tsx index ef213d1..37919e5 100644 --- a/routes/file-share/[fileShareId]/open/[fileName].tsx +++ b/routes/file-share/[fileShareId]/open/[fileName].tsx @@ -21,6 +21,10 @@ export const handler: Handlers = { return new Response('Not Found', { status: 404 }); } + if (!(await AppConfig.isAppEnabled('files'))) { + return new Response('Not Found', { status: 404 }); + } + const fileShare = await FileShareModel.getById(fileShareId); if (!fileShare) { diff --git a/routes/file-share/[fileShareId]/verify.tsx b/routes/file-share/[fileShareId]/verify.tsx index ffb851c..678266c 100644 --- a/routes/file-share/[fileShareId]/verify.tsx +++ b/routes/file-share/[fileShareId]/verify.tsx @@ -29,6 +29,10 @@ export const handler: Handlers = { return new Response('Not Found', { status: 404 }); } + if (!(await AppConfig.isAppEnabled('files'))) { + return new Response('Not Found', { status: 404 }); + } + const fileShare = await FileShareModel.getById(fileShareId); if (!fileShare) { @@ -54,6 +58,10 @@ export const handler: Handlers = { return new Response('Not Found', { status: 404 }); } + if (!(await AppConfig.isAppEnabled('files'))) { + return new Response('Not Found', { status: 404 }); + } + const fileShare = await FileShareModel.getById(fileShareId); if (!fileShare) { diff --git a/routes/files.tsx b/routes/files.tsx index abf4980..610844e 100644 --- a/routes/files.tsx +++ b/routes/files.tsx @@ -22,6 +22,10 @@ export const handler: Handlers = { const baseUrl = (await AppConfig.getConfig()).auth.baseUrl; + if (!(await AppConfig.isAppEnabled('files'))) { + return new Response('Redirect', { status: 303, headers: { 'Location': `/` } }); + } + const searchParams = new URL(request.url).searchParams; let currentPath = searchParams.get('path') || '/'; diff --git a/routes/files/open/[fileName].tsx b/routes/files/open/[fileName].tsx index 5732228..d38454f 100644 --- a/routes/files/open/[fileName].tsx +++ b/routes/files/open/[fileName].tsx @@ -1,6 +1,7 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; +import { AppConfig } from '/lib/config.ts'; import { FileModel } from '/lib/models/files.ts'; interface Data {} @@ -17,6 +18,10 @@ export const handler: Handlers = { return new Response('Not Found', { status: 404 }); } + if (!(await AppConfig.isAppEnabled('files'))) { + return new Response('Redirect', { status: 303, headers: { 'Location': `/` } }); + } + const searchParams = new URL(request.url).searchParams; let currentPath = searchParams.get('path') || '/'; diff --git a/routes/index.tsx b/routes/index.tsx index ca06a93..070a3f6 100644 --- a/routes/index.tsx +++ b/routes/index.tsx @@ -1,13 +1,18 @@ import { Handlers } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} export const handler: Handlers = { - GET(request, context) { + async GET(request, context) { + const config = await AppConfig.getConfig(); + if (context.state.user) { - return new Response('Redirect', { status: 303, headers: { 'Location': `/dashboard` } }); + const firstEnabledApp = config.core.enabledApps[0]; + + return new Response('Redirect', { status: 303, headers: { 'Location': `/${firstEnabledApp}` } }); } return new Response('Redirect', { status: 303, headers: { 'Location': '/login' } }); diff --git a/routes/news.tsx b/routes/news.tsx index f373396..5e5d39f 100644 --- a/routes/news.tsx +++ b/routes/news.tsx @@ -16,7 +16,7 @@ export const handler: Handlers = { } if (!(await AppConfig.isAppEnabled('news'))) { - return new Response('Redirect', { status: 303, headers: { 'Location': `/dashboard` } }); + return new Response('Redirect', { status: 303, headers: { 'Location': `/` } }); } const userArticles = await ArticleModel.listUnread(context.state.user.id); diff --git a/routes/news/feeds.tsx b/routes/news/feeds.tsx index f6edbed..a4c1333 100644 --- a/routes/news/feeds.tsx +++ b/routes/news/feeds.tsx @@ -2,6 +2,7 @@ import { Handlers, PageProps } from 'fresh/server.ts'; import { FreshContextState, NewsFeed } from '/lib/types.ts'; import { FeedModel } from '/lib/models/news.ts'; +import { AppConfig } from '/lib/config.ts'; import Feeds from '/islands/news/Feeds.tsx'; interface Data { @@ -14,6 +15,10 @@ export const handler: Handlers = { return new Response('Redirect', { status: 303, headers: { 'Location': `/login` } }); } + if (!(await AppConfig.isAppEnabled('news'))) { + return new Response('Redirect', { status: 303, headers: { 'Location': `/` } }); + } + const userFeeds = await FeedModel.list(context.state.user.id); return await context.render({ userFeeds }); diff --git a/routes/notes/open/[fileName].tsx b/routes/notes/open/[fileName].tsx index b409a5f..97c69c8 100644 --- a/routes/notes/open/[fileName].tsx +++ b/routes/notes/open/[fileName].tsx @@ -3,6 +3,7 @@ import { Handlers, PageProps } from 'fresh/server.ts'; import { FreshContextState } from '/lib/types.ts'; import { FileModel } from '/lib/models/files.ts'; import Note from '/islands/notes/Note.tsx'; +import { AppConfig } from '/lib/config.ts'; interface Data { fileName: string; @@ -22,6 +23,10 @@ export const handler: Handlers = { return new Response('Not Found', { status: 404 }); } + if (!(await AppConfig.isAppEnabled('notes'))) { + throw new Error('Notes app is not enabled'); + } + const searchParams = new URL(request.url).searchParams; let currentPath = searchParams.get('path') || '/'; diff --git a/routes/photos/thumbnail/[fileName].tsx b/routes/photos/thumbnail/[fileName].tsx index 58048f7..194848d 100644 --- a/routes/photos/thumbnail/[fileName].tsx +++ b/routes/photos/thumbnail/[fileName].tsx @@ -3,6 +3,7 @@ import sharp from 'sharp'; import { FreshContextState } from '/lib/types.ts'; import { FileModel } from '/lib/models/files.ts'; +import { AppConfig } from '/lib/config.ts'; interface Data {} @@ -23,6 +24,10 @@ export const handler: Handlers = { return new Response('Not Found', { status: 404 }); } + if (!(await AppConfig.isAppEnabled('photos'))) { + throw new Error('Photos app is not enabled'); + } + const searchParams = new URL(request.url).searchParams; let currentPath = searchParams.get('path') || '/';