Respect CONFIG_ENABLED_APPS in .env
This commit is contained in:
@@ -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
|
||||||
|
|||||||
21
README.md
21
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/).
|
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?
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|
||||||
|
|||||||
@@ -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...');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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}`;
|
||||||
|
|
||||||
|
|||||||
@@ -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 });
|
||||||
|
|||||||
@@ -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/';
|
||||||
|
|||||||
@@ -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/';
|
||||||
|
|||||||
Reference in New Issue
Block a user