Respect CONFIG_ENABLED_APPS in .env

This commit is contained in:
Bruno Bernardino
2024-05-01 09:21:30 +01:00
parent 635ca90de0
commit 6ee0a56f0c
8 changed files with 64 additions and 42 deletions

View File

@@ -14,7 +14,7 @@ PASSWORD_SALT="fake"
BREVO_API_KEY="fake" BREVO_API_KEY="fake"
CONFIG_ALLOW_SIGNUPS="false" 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_FILES_ROOT_PATH="data-files"
CONFIG_ENABLE_EMAILS="false" # if true, email verification will be required for signups (using Brevo) 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 CONFIG_ENABLE_FOREVER_SIGNUP="true" # if true, all signups become active for 100 years

View File

@@ -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/). 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] > [!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!** > 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. 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? ## 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. [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? ## How does file sharing work?

View File

@@ -1,6 +1,7 @@
import { Head } from 'fresh/runtime.ts'; import { Head } from 'fresh/runtime.ts';
import { User } from '/lib/types.ts'; import { User } from '/lib/types.ts';
import { isAppEnabled } from '/lib/config.ts';
interface Data { interface Data {
route: string; route: string;
@@ -22,29 +23,37 @@ export default function Header({ route, user }: Data) {
const iconWidthAndHeightInPixels = 20; const iconWidthAndHeightInPixels = 20;
const menuItems: MenuItem[] = [ const potentialMenuItems: (MenuItem | null)[] = [
{ {
url: '/dashboard', url: '/dashboard',
label: 'Dashboard', label: 'Dashboard',
}, },
{ isAppEnabled('news')
url: '/news', ? {
label: 'News', url: '/news',
}, label: 'News',
}
: null,
{ {
url: '/files', url: '/files',
label: 'Files', label: 'Files',
}, },
{ isAppEnabled('notes')
url: '/notes', ? {
label: 'Notes', url: '/notes',
}, label: 'Notes',
{ }
url: '/photos', : null,
label: 'Photos', isAppEnabled('photos')
}, ? {
url: '/photos',
label: 'Photos',
}
: null,
]; ];
const menuItems = potentialMenuItems.filter(Boolean) as MenuItem[];
if (user) { if (user) {
const activeMenu = menuItems.find((menu) => route.startsWith(menu.url)); const activeMenu = menuItems.find((menu) => route.startsWith(menu.url));

View File

@@ -1,5 +1,6 @@
import { Cron } from 'https://deno.land/x/croner@8.0.1/dist/croner.js'; 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 { cleanupSessions } from './cleanup.ts';
import { fetchNewArticles } from './news.ts'; import { fetchNewArticles } from './news.ts';
@@ -16,17 +17,19 @@ export function startCrons() {
}, },
); );
new Cron( if (isAppEnabled('news')) {
// Every 30 minutes. new Cron(
'*/30 * * * *', // Every 30 minutes.
{ '*/30 * * * *',
name: 'news', {
protect: true, name: 'news',
}, protect: true,
async () => { },
await fetchNewArticles(); async () => {
}, await fetchNewArticles();
); },
);
}
console.log('Crons starting...'); console.log('Crons starting...');
} }

View File

@@ -14,6 +14,12 @@ export async function isSignupAllowed() {
return false; 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() { export function isEmailEnabled() {
const areEmailsAllowed = Deno.env.get('CONFIG_ENABLE_EMAILS') === 'true'; const areEmailsAllowed = Deno.env.get('CONFIG_ENABLE_EMAILS') === 'true';
@@ -27,7 +33,7 @@ export function isForeverSignupEnabled() {
} }
export function getFilesRootPath() { 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}`; const filesRootPath = `${Deno.cwd()}/${configRootPath}`;

View File

@@ -1,6 +1,7 @@
import { Handlers, PageProps } from 'fresh/server.ts'; import { Handlers, PageProps } from 'fresh/server.ts';
import { FreshContextState, NewsFeedArticle } from '/lib/types.ts'; import { FreshContextState, NewsFeedArticle } from '/lib/types.ts';
import { isAppEnabled } from '/lib/config.ts';
import { getNewsArticles } from '/lib/data/news.ts'; import { getNewsArticles } from '/lib/data/news.ts';
import Articles from '/islands/news/Articles.tsx'; import Articles from '/islands/news/Articles.tsx';
@@ -14,6 +15,10 @@ export const handler: Handlers<Data, FreshContextState> = {
return new Response('Redirect', { status: 303, headers: { 'Location': `/login` } }); 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); const userArticles = await getNewsArticles(context.state.user.id);
return await context.render({ userArticles }); return await context.render({ userArticles });

View File

@@ -1,6 +1,7 @@
import { Handlers, PageProps } from 'fresh/server.ts'; import { Handlers, PageProps } from 'fresh/server.ts';
import { Directory, DirectoryFile, FreshContextState } from '/lib/types.ts'; import { Directory, DirectoryFile, FreshContextState } from '/lib/types.ts';
import { isAppEnabled } from '/lib/config.ts';
import { getDirectories, getFiles } from '/lib/data/files.ts'; import { getDirectories, getFiles } from '/lib/data/files.ts';
import NotesWrapper from '/islands/notes/NotesWrapper.tsx'; import NotesWrapper from '/islands/notes/NotesWrapper.tsx';
@@ -16,6 +17,10 @@ export const handler: Handlers<Data, FreshContextState> = {
return new Response('Redirect', { status: 303, headers: { 'Location': `/login` } }); 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; const searchParams = new URL(request.url).searchParams;
let currentPath = searchParams.get('path') || '/Notes/'; let currentPath = searchParams.get('path') || '/Notes/';

View File

@@ -1,6 +1,7 @@
import { Handlers, PageProps } from 'fresh/server.ts'; import { Handlers, PageProps } from 'fresh/server.ts';
import { Directory, DirectoryFile, FreshContextState } from '/lib/types.ts'; import { Directory, DirectoryFile, FreshContextState } from '/lib/types.ts';
import { isAppEnabled } from '/lib/config.ts';
import { getDirectories, getFiles } from '/lib/data/files.ts'; import { getDirectories, getFiles } from '/lib/data/files.ts';
import { PHOTO_EXTENSIONS } from '/lib/utils/photos.ts'; import { PHOTO_EXTENSIONS } from '/lib/utils/photos.ts';
import PhotosWrapper from '/islands/photos/PhotosWrapper.tsx'; import PhotosWrapper from '/islands/photos/PhotosWrapper.tsx';
@@ -17,6 +18,10 @@ export const handler: Handlers<Data, FreshContextState> = {
return new Response('Redirect', { status: 303, headers: { 'Location': `/login` } }); 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; const searchParams = new URL(request.url).searchParams;
let currentPath = searchParams.get('path') || '/Photos/'; let currentPath = searchParams.get('path') || '/Photos/';