From 6ee0a56f0ca2729225e52a37d2f51deba559e184 Mon Sep 17 00:00:00 2001 From: Bruno Bernardino Date: Wed, 1 May 2024 09:21:30 +0100 Subject: [PATCH] Respect `CONFIG_ENABLED_APPS` in `.env` --- .env.sample | 2 +- README.md | 21 +++++---------------- components/Header.tsx | 35 ++++++++++++++++++++++------------- crons/index.ts | 25 ++++++++++++++----------- lib/config.ts | 8 +++++++- routes/news.tsx | 5 +++++ routes/notes.tsx | 5 +++++ routes/photos.tsx | 5 +++++ 8 files changed, 64 insertions(+), 42 deletions(-) diff --git a/.env.sample b/.env.sample index cdc5713..40d41d2 100644 --- a/.env.sample +++ b/.env.sample @@ -14,7 +14,7 @@ PASSWORD_SALT="fake" BREVO_API_KEY="fake" CONFIG_ALLOW_SIGNUPS="false" -CONFIG_ENABLED_APPS="dashboard,news,files,notes,photos" +CONFIG_ENABLED_APPS="news,notes,photos" # dashboard and files cannot be disabled CONFIG_FILES_ROOT_PATH="data-files" CONFIG_ENABLE_EMAILS="false" # if true, email verification will be required for signups (using Brevo) CONFIG_ENABLE_FOREVER_SIGNUP="true" # if true, all signups become active for 100 years diff --git a/README.md b/README.md index a3ed4db..be59c9a 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ This is the [bewCloud app](https://bewcloud.com) built using [Fresh](https://fresh.deno.dev) and deployed using [docker compose](https://docs.docker.com/compose/). +If you're looking for the desktop sync app, it's at [`bewcloud-desktop`](https://github.com/bewcloud/bewcloud-desktop). + +If you're looking for the mobile app, it's at [`bewcloud-mobile`](https://github.com/bewcloud/bewcloud-mobile). + > [!CAUTION] > This is actively being built and should be considered pre-alpha. Bugs will exist. Code and models _can_ change without a good upgrade path (though I'll try to avoid that). **Don't use it as your only source of data!** @@ -60,26 +64,11 @@ $ make build # generates all static files for production deploy Just push to the `main` branch. -## Tentative Roadmap for a v1 beta: - -- [x] Dashboard with URLs and Notes -- [x] News -- [x] Files UI -- [x] WebDav Server -- [x] [Desktop app for selective file sync](https://github.com/bewcloud/bewcloud-desktop/releases) (`rclone` via WebDav) -- [x] [Mobile app for offline file view](https://github.com/bewcloud/bewcloud-mobile/releases) (API + WebDav client) -- [x] Add photo auto-upload support for mobile client -- [x] Notes UI -- [x] Photos UI -- [ ] Add notes view support for mobile app -- [ ] Add notes edit support for mobile app -- [ ] Respect `CONFIG_ENABLED_APPS` in `.env` for enabling apps - ## Where's Contacts/Calendar (CardDav/CalDav)?! Wasn't this supposed to be a core Nextcloud replacement? [Check this tag/release for more info and the code where/when that was being done](https://github.com/bewcloud/bewcloud/releases/tag/v0.0.1-self-made-carddav-caldav). Contacts/CardDav worked and Calendar/CalDav mostly worked as well at that point. -My focus is still to get me to replace Nextcloud for me and my family ASAP, but turns out it's not easy to do it all in a single, installable _thing_, so I'm focusing on the Files UI, sync, and sharing, since [Radicale](https://radicale.org/v3.html) solved my other issues better than my own solution (and it's already _very_ efficient). +My focus was to get me to replace Nextcloud for me and my family ASAP, and it turns out it's not easy to do it all in a single, installable _thing_, so I focused on the Files UI, sync, and sharing, since [Radicale](https://radicale.org/v3.html) solved my other issues better than my own solution (and it's already _very_ efficient). ## How does file sharing work? diff --git a/components/Header.tsx b/components/Header.tsx index 201e28f..753fe59 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -1,6 +1,7 @@ import { Head } from 'fresh/runtime.ts'; import { User } from '/lib/types.ts'; +import { isAppEnabled } from '/lib/config.ts'; interface Data { route: string; @@ -22,29 +23,37 @@ export default function Header({ route, user }: Data) { const iconWidthAndHeightInPixels = 20; - const menuItems: MenuItem[] = [ + const potentialMenuItems: (MenuItem | null)[] = [ { url: '/dashboard', label: 'Dashboard', }, - { - url: '/news', - label: 'News', - }, + isAppEnabled('news') + ? { + url: '/news', + label: 'News', + } + : null, { url: '/files', label: 'Files', }, - { - url: '/notes', - label: 'Notes', - }, - { - url: '/photos', - label: 'Photos', - }, + isAppEnabled('notes') + ? { + url: '/notes', + label: 'Notes', + } + : null, + isAppEnabled('photos') + ? { + url: '/photos', + label: 'Photos', + } + : null, ]; + const menuItems = potentialMenuItems.filter(Boolean) as MenuItem[]; + if (user) { const activeMenu = menuItems.find((menu) => route.startsWith(menu.url)); diff --git a/crons/index.ts b/crons/index.ts index 979d273..f380a6c 100644 --- a/crons/index.ts +++ b/crons/index.ts @@ -1,5 +1,6 @@ import { Cron } from 'https://deno.land/x/croner@8.0.1/dist/croner.js'; +import { isAppEnabled } from '/lib/config.ts'; import { cleanupSessions } from './cleanup.ts'; import { fetchNewArticles } from './news.ts'; @@ -16,17 +17,19 @@ export function startCrons() { }, ); - new Cron( - // Every 30 minutes. - '*/30 * * * *', - { - name: 'news', - protect: true, - }, - async () => { - await fetchNewArticles(); - }, - ); + if (isAppEnabled('news')) { + new Cron( + // Every 30 minutes. + '*/30 * * * *', + { + name: 'news', + protect: true, + }, + async () => { + await fetchNewArticles(); + }, + ); + } console.log('Crons starting...'); } diff --git a/lib/config.ts b/lib/config.ts index d2166bb..09c6d04 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -14,6 +14,12 @@ export async function isSignupAllowed() { return false; } +export function isAppEnabled(app: 'news' | 'notes' | 'photos') { + const enabledApps = (Deno.env.get('CONFIG_ENABLED_APPS') || '').split(',') as typeof app[]; + + return enabledApps.includes(app); +} + export function isEmailEnabled() { const areEmailsAllowed = Deno.env.get('CONFIG_ENABLE_EMAILS') === 'true'; @@ -27,7 +33,7 @@ export function isForeverSignupEnabled() { } export function getFilesRootPath() { - const configRootPath = Deno.env.get('CONFIG_FILES_ROOT_PATH'); + const configRootPath = Deno.env.get('CONFIG_FILES_ROOT_PATH') || ''; const filesRootPath = `${Deno.cwd()}/${configRootPath}`; diff --git a/routes/news.tsx b/routes/news.tsx index 9711580..babe418 100644 --- a/routes/news.tsx +++ b/routes/news.tsx @@ -1,6 +1,7 @@ import { Handlers, PageProps } from 'fresh/server.ts'; import { FreshContextState, NewsFeedArticle } from '/lib/types.ts'; +import { isAppEnabled } from '/lib/config.ts'; import { getNewsArticles } from '/lib/data/news.ts'; import Articles from '/islands/news/Articles.tsx'; @@ -14,6 +15,10 @@ export const handler: Handlers = { return new Response('Redirect', { status: 303, headers: { 'Location': `/login` } }); } + if (!isAppEnabled('news')) { + return new Response('Redirect', { status: 303, headers: { 'Location': `/dashboard` } }); + } + const userArticles = await getNewsArticles(context.state.user.id); return await context.render({ userArticles }); diff --git a/routes/notes.tsx b/routes/notes.tsx index 13f19c3..2520a16 100644 --- a/routes/notes.tsx +++ b/routes/notes.tsx @@ -1,6 +1,7 @@ import { Handlers, PageProps } from 'fresh/server.ts'; import { Directory, DirectoryFile, FreshContextState } from '/lib/types.ts'; +import { isAppEnabled } from '/lib/config.ts'; import { getDirectories, getFiles } from '/lib/data/files.ts'; import NotesWrapper from '/islands/notes/NotesWrapper.tsx'; @@ -16,6 +17,10 @@ export const handler: Handlers = { return new Response('Redirect', { status: 303, headers: { 'Location': `/login` } }); } + if (!isAppEnabled('notes')) { + return new Response('Redirect', { status: 303, headers: { 'Location': `/files` } }); + } + const searchParams = new URL(request.url).searchParams; let currentPath = searchParams.get('path') || '/Notes/'; diff --git a/routes/photos.tsx b/routes/photos.tsx index fb970dc..9e43f60 100644 --- a/routes/photos.tsx +++ b/routes/photos.tsx @@ -1,6 +1,7 @@ import { Handlers, PageProps } from 'fresh/server.ts'; import { Directory, DirectoryFile, FreshContextState } from '/lib/types.ts'; +import { isAppEnabled } from '/lib/config.ts'; import { getDirectories, getFiles } from '/lib/data/files.ts'; import { PHOTO_EXTENSIONS } from '/lib/utils/photos.ts'; import PhotosWrapper from '/islands/photos/PhotosWrapper.tsx'; @@ -17,6 +18,10 @@ export const handler: Handlers = { return new Response('Redirect', { status: 303, headers: { 'Location': `/login` } }); } + if (!isAppEnabled('photos')) { + return new Response('Redirect', { status: 303, headers: { 'Location': `/files` } }); + } + const searchParams = new URL(request.url).searchParams; let currentPath = searchParams.get('path') || '/Photos/';