mirror of
https://github.com/bewcloud/bewcloud.git
synced 2026-03-11 08:54:49 +00:00
375 lines
15 KiB
TypeScript
375 lines
15 KiB
TypeScript
import { serveFile } from '@std/http/file-server';
|
|
|
|
import { isRunningLocally as isAppRunningLocally } from '/public/ts/utils/misc.ts';
|
|
import { serveFileWithSass, serveFileWithTs } from '/lib/utils/misc.ts';
|
|
import { getDataFromRequest } from '/lib/auth.ts';
|
|
import { Page } from '/lib/page.ts';
|
|
|
|
export interface Route {
|
|
pattern: URLPattern;
|
|
handler: (
|
|
request: Request,
|
|
match: URLPatternResult,
|
|
) => Response | Promise<Response>;
|
|
}
|
|
|
|
interface Routes {
|
|
[routeKey: string]: Route;
|
|
}
|
|
|
|
function createPageRouteHandler(id: string, pathname: string) {
|
|
return {
|
|
pattern: new URLPattern({ pathname }),
|
|
handler: async (request: Request, match: URLPatternResult) => {
|
|
try {
|
|
const page: Page = (await import(`/pages/${id}`)).default;
|
|
|
|
const isRunningLocally = isAppRunningLocally(request);
|
|
|
|
const {
|
|
get,
|
|
post,
|
|
put,
|
|
patch,
|
|
delete: deleteAction,
|
|
options,
|
|
catchAll,
|
|
} = page;
|
|
|
|
const { user, session, tokenData } = (await getDataFromRequest(request)) || {};
|
|
|
|
switch (request.method) {
|
|
case 'GET':
|
|
case 'HEAD':
|
|
case 'OPTIONS':
|
|
if (options) {
|
|
return await options({
|
|
request,
|
|
match,
|
|
user,
|
|
session: { userSession: session, tokenData },
|
|
isRunningLocally,
|
|
});
|
|
}
|
|
|
|
if (get) {
|
|
if (request.method === 'OPTIONS') {
|
|
const allowedMethods = ['GET', 'HEAD', 'OPTIONS'];
|
|
if (post) {
|
|
allowedMethods.push('POST');
|
|
}
|
|
if (put) {
|
|
allowedMethods.push('PUT');
|
|
}
|
|
if (patch) {
|
|
allowedMethods.push('PATCH');
|
|
}
|
|
if (deleteAction) {
|
|
allowedMethods.push('DELETE');
|
|
}
|
|
|
|
return new Response(null, {
|
|
status: 204,
|
|
headers: {
|
|
Allow: allowedMethods.join(', '),
|
|
},
|
|
});
|
|
}
|
|
|
|
const response = await get({
|
|
request,
|
|
match,
|
|
user,
|
|
session: {
|
|
userSession: session,
|
|
tokenData,
|
|
},
|
|
isRunningLocally,
|
|
});
|
|
|
|
if (request.method === 'HEAD') {
|
|
return new Response(null, {
|
|
status: response.status,
|
|
headers: response.headers,
|
|
});
|
|
}
|
|
|
|
return response;
|
|
}
|
|
break;
|
|
case 'POST':
|
|
if (post) {
|
|
return await post({
|
|
request,
|
|
match,
|
|
user,
|
|
session: { userSession: session, tokenData },
|
|
isRunningLocally,
|
|
});
|
|
}
|
|
break;
|
|
case 'PUT':
|
|
if (put) {
|
|
return await put({
|
|
request,
|
|
match,
|
|
user,
|
|
session: { userSession: session, tokenData },
|
|
isRunningLocally,
|
|
});
|
|
}
|
|
break;
|
|
case 'PATCH':
|
|
if (patch) {
|
|
return await patch({
|
|
request,
|
|
match,
|
|
user,
|
|
session: { userSession: session, tokenData },
|
|
isRunningLocally,
|
|
});
|
|
}
|
|
break;
|
|
case 'DELETE':
|
|
if (deleteAction) {
|
|
return await deleteAction({
|
|
request,
|
|
match,
|
|
user,
|
|
session: { userSession: session, tokenData },
|
|
isRunningLocally,
|
|
});
|
|
}
|
|
break;
|
|
default:
|
|
if (catchAll) {
|
|
return await catchAll({
|
|
request,
|
|
match,
|
|
user,
|
|
session: { userSession: session, tokenData },
|
|
isRunningLocally,
|
|
});
|
|
}
|
|
|
|
return new Response('Not Implemented', { status: 501 });
|
|
}
|
|
|
|
return new Response('Not Implemented', { status: 501 });
|
|
} catch (error) {
|
|
if ((error as Error).toString().includes('NotFound')) {
|
|
const notFoundPage: Page = (await import(`/pages/404.ts`)).default;
|
|
|
|
const response = await notFoundPage.get!({ request, match, isRunningLocally: false });
|
|
|
|
return new Response(response.body, {
|
|
status: 404,
|
|
headers: response.headers,
|
|
});
|
|
}
|
|
|
|
console.error(error);
|
|
|
|
return new Response('Internal Server Error', { status: 500 });
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
const oneDayInSeconds = 24 * 60 * 60;
|
|
|
|
const routes: Routes = {
|
|
robots: {
|
|
pattern: new URLPattern({ pathname: '/robots.txt' }),
|
|
handler: async (request) => {
|
|
const response = await serveFile(request, `public/robots.txt`);
|
|
response.headers.set('cache-control', `max-age=${oneDayInSeconds}, public`);
|
|
return response;
|
|
},
|
|
},
|
|
favicon: {
|
|
pattern: new URLPattern({ pathname: '/favicon.ico' }),
|
|
handler: async (request) => {
|
|
const response = await serveFile(request, `public/images/favicon.ico`);
|
|
response.headers.set('cache-control', `max-age=${oneDayInSeconds}, public`);
|
|
return response;
|
|
},
|
|
},
|
|
public: {
|
|
pattern: new URLPattern({ pathname: '/public/:filePath*' }),
|
|
handler: async (request, match) => {
|
|
const { filePath } = match.pathname.groups;
|
|
|
|
try {
|
|
const fullFilePath = `public/${decodeURIComponent(filePath!)}`;
|
|
|
|
const fileExtension = filePath!.split('.').pop()?.toLowerCase();
|
|
|
|
let response: Response;
|
|
|
|
if (fileExtension === 'ts') {
|
|
response = await serveFileWithTs(request, fullFilePath);
|
|
} else if (fileExtension === 'scss') {
|
|
response = await serveFileWithSass(request, fullFilePath);
|
|
} else {
|
|
response = await serveFile(request, fullFilePath);
|
|
|
|
if (filePath?.startsWith('js/')) {
|
|
response.headers.set('content-type', 'application/javascript; charset=utf-8');
|
|
}
|
|
}
|
|
|
|
response.headers.set('cache-control', `max-age=${oneDayInSeconds}, public`);
|
|
return response;
|
|
} catch (error) {
|
|
if ((error as Error).toString().includes('NotFound')) {
|
|
const notFoundPage: Page = (await import(`/pages/404.ts`)).default;
|
|
|
|
const response = await notFoundPage.get!({
|
|
request,
|
|
match,
|
|
isRunningLocally: false,
|
|
});
|
|
|
|
return new Response(response.body, {
|
|
status: 404,
|
|
headers: response.headers,
|
|
});
|
|
}
|
|
|
|
console.error(error);
|
|
|
|
return new Response('Internal Server Error', { status: 500 });
|
|
}
|
|
},
|
|
},
|
|
|
|
index: createPageRouteHandler('index.ts', '/'),
|
|
login: createPageRouteHandler('login.tsx', '/login'),
|
|
signup: createPageRouteHandler('signup.ts', '/signup'),
|
|
logout: createPageRouteHandler('logout.ts', '/logout'),
|
|
caldav: createPageRouteHandler('caldav.ts', '/caldav/:path*{/}?'),
|
|
calendar: createPageRouteHandler('calendar.ts', '/calendar'),
|
|
calendars: createPageRouteHandler('calendars.ts', '/calendars'),
|
|
calendarEvent: createPageRouteHandler('calendar/[calendarEventId].ts', '/calendar/:calendarEventId'),
|
|
carddav: createPageRouteHandler('carddav.ts', '/carddav/:path*{/}?'),
|
|
contacts: createPageRouteHandler('contacts.ts', '/contacts'),
|
|
contact: createPageRouteHandler('contacts/[contactId].ts', '/contacts/:contactId'),
|
|
dashboard: createPageRouteHandler('dashboard.ts', '/dashboard'),
|
|
dav: createPageRouteHandler('dav.ts', '/dav/:filePath*{/}?'),
|
|
expenses: createPageRouteHandler('expenses.ts', '/expenses'),
|
|
openFileShare: createPageRouteHandler(
|
|
'file-share/[fileShareId]/open/[fileName].ts',
|
|
'/file-share/:fileShareId/open/:fileName',
|
|
),
|
|
verifyFileShare: createPageRouteHandler('file-share/[fileShareId]/verify.tsx', '/file-share/:fileShareId/verify'),
|
|
fileShare: createPageRouteHandler('file-share/[fileShareId].ts', '/file-share/:fileShareId'),
|
|
files: createPageRouteHandler('files.ts', '/files'),
|
|
openFile: createPageRouteHandler('files/open/[fileName].ts', '/files/open/:fileName'),
|
|
mfaVerify: createPageRouteHandler('mfa-verify.tsx', '/mfa-verify'),
|
|
news: createPageRouteHandler('news.ts', '/news'),
|
|
newsFeeds: createPageRouteHandler('news/feeds.ts', '/news/feeds'),
|
|
notes: createPageRouteHandler('notes.ts', '/notes'),
|
|
openNote: createPageRouteHandler('notes/open/[fileName].ts', '/notes/open/:fileName'),
|
|
oidcCallback: createPageRouteHandler('oidc/callback.ts', '/oidc/callback'),
|
|
photos: createPageRouteHandler('photos.ts', '/photos'),
|
|
photoThumbnail: createPageRouteHandler('photos/thumbnail/[fileName].ts', '/photos/thumbnail/:fileName'),
|
|
settings: createPageRouteHandler('settings.ts', '/settings'),
|
|
|
|
wellKnownCalDav: createPageRouteHandler('.well-known/caldav.ts', '/.well-known/caldav'),
|
|
wellKnownCardDav: createPageRouteHandler('.well-known/carddav.ts', '/.well-known/carddav'),
|
|
|
|
// API routes
|
|
apiAuthMultiFactorEmailSetup: createPageRouteHandler(
|
|
'api/auth/multi-factor/email/setup.ts',
|
|
'/api/auth/multi-factor/email/setup',
|
|
),
|
|
apiAuthMultiFactorPasskeyBegin: createPageRouteHandler(
|
|
'api/auth/multi-factor/passkey/begin.ts',
|
|
'/api/auth/multi-factor/passkey/begin',
|
|
),
|
|
apiAuthMultiFactorPasskeySetupBegin: createPageRouteHandler(
|
|
'api/auth/multi-factor/passkey/setup-begin.ts',
|
|
'/api/auth/multi-factor/passkey/setup-begin',
|
|
),
|
|
apiAuthMultiFactorPasskeySetupComplete: createPageRouteHandler(
|
|
'api/auth/multi-factor/passkey/setup-complete.ts',
|
|
'/api/auth/multi-factor/passkey/setup-complete',
|
|
),
|
|
apiAuthMultiFactorPasskeyVerify: createPageRouteHandler(
|
|
'api/auth/multi-factor/passkey/verify.ts',
|
|
'/api/auth/multi-factor/passkey/verify',
|
|
),
|
|
apiAuthMultiFactorTotpSetup: createPageRouteHandler(
|
|
'api/auth/multi-factor/totp/setup.ts',
|
|
'/api/auth/multi-factor/totp/setup',
|
|
),
|
|
apiAuthMultiFactorDisable: createPageRouteHandler(
|
|
'api/auth/multi-factor/disable.ts',
|
|
'/api/auth/multi-factor/disable',
|
|
),
|
|
apiAuthMultiFactorEnable: createPageRouteHandler('api/auth/multi-factor/enable.ts', '/api/auth/multi-factor/enable'),
|
|
|
|
apiCalendarAddEvent: createPageRouteHandler('api/calendar/add-event.ts', '/api/calendar/add-event'),
|
|
apiCalendarAdd: createPageRouteHandler('api/calendar/add.ts', '/api/calendar/add'),
|
|
apiCalendarDeleteEvent: createPageRouteHandler('api/calendar/delete-event.ts', '/api/calendar/delete-event'),
|
|
apiCalendarDelete: createPageRouteHandler('api/calendar/delete.ts', '/api/calendar/delete'),
|
|
apiCalendarExportEvents: createPageRouteHandler('api/calendar/export-events.ts', '/api/calendar/export-events'),
|
|
apiCalendarImport: createPageRouteHandler('api/calendar/import.ts', '/api/calendar/import'),
|
|
apiCalendarSearchEvents: createPageRouteHandler('api/calendar/search-events.ts', '/api/calendar/search-events'),
|
|
apiCalendarUpdate: createPageRouteHandler('api/calendar/update.ts', '/api/calendar/update'),
|
|
|
|
apiContactsAddAddressBook: createPageRouteHandler('api/contacts/add-addressbook.ts', '/api/contacts/add-addressbook'),
|
|
apiContactsAdd: createPageRouteHandler('api/contacts/add.ts', '/api/contacts/add'),
|
|
apiContactsDeleteAddressBook: createPageRouteHandler(
|
|
'api/contacts/delete-addressbook.ts',
|
|
'/api/contacts/delete-addressbook',
|
|
),
|
|
apiContactsDelete: createPageRouteHandler('api/contacts/delete.ts', '/api/contacts/delete'),
|
|
apiContactsGetAddressBooks: createPageRouteHandler(
|
|
'api/contacts/get-addressbooks.ts',
|
|
'/api/contacts/get-addressbooks',
|
|
),
|
|
apiContactsGet: createPageRouteHandler('api/contacts/get.ts', '/api/contacts/get'),
|
|
apiContactsImport: createPageRouteHandler('api/contacts/import.ts', '/api/contacts/import'),
|
|
|
|
apiDashboardSaveLinks: createPageRouteHandler('api/dashboard/save-links.ts', '/api/dashboard/save-links'),
|
|
apiDashboardSaveNotes: createPageRouteHandler('api/dashboard/save-notes.ts', '/api/dashboard/save-notes'),
|
|
|
|
apiExpensesAddBudget: createPageRouteHandler('api/expenses/add-budget.ts', '/api/expenses/add-budget'),
|
|
apiExpensesAddExpense: createPageRouteHandler('api/expenses/add-expense.ts', '/api/expenses/add-expense'),
|
|
apiExpensesAutoComplete: createPageRouteHandler('api/expenses/auto-complete.ts', '/api/expenses/auto-complete'),
|
|
apiExpensesDeleteBudget: createPageRouteHandler('api/expenses/delete-budget.ts', '/api/expenses/delete-budget'),
|
|
apiExpensesDeleteExpense: createPageRouteHandler('api/expenses/delete-expense.ts', '/api/expenses/delete-expense'),
|
|
apiExpensesExportExpenses: createPageRouteHandler('api/expenses/export-expenses.ts', '/api/expenses/export-expenses'),
|
|
apiExpensesImportExpenses: createPageRouteHandler('api/expenses/import-expenses.ts', '/api/expenses/import-expenses'),
|
|
apiExpensesUpdateBudget: createPageRouteHandler('api/expenses/update-budget.ts', '/api/expenses/update-budget'),
|
|
apiExpensesUpdateExpense: createPageRouteHandler('api/expenses/update-expense.ts', '/api/expenses/update-expense'),
|
|
|
|
apiFilesCreateDirectory: createPageRouteHandler('api/files/create-directory.ts', '/api/files/create-directory'),
|
|
apiFilesCreateShare: createPageRouteHandler('api/files/create-share.ts', '/api/files/create-share'),
|
|
apiFilesDeleteDirectory: createPageRouteHandler('api/files/delete-directory.ts', '/api/files/delete-directory'),
|
|
apiFilesDeleteShare: createPageRouteHandler('api/files/delete-share.ts', '/api/files/delete-share'),
|
|
apiFilesDelete: createPageRouteHandler('api/files/delete.ts', '/api/files/delete'),
|
|
apiFilesDownloadDirectory: createPageRouteHandler('api/files/download-directory.ts', '/api/files/download-directory'),
|
|
apiFilesGetDirectories: createPageRouteHandler('api/files/get-directories.ts', '/api/files/get-directories'),
|
|
apiFilesGetShare: createPageRouteHandler('api/files/get-share.ts', '/api/files/get-share'),
|
|
apiFilesGet: createPageRouteHandler('api/files/get.ts', '/api/files/get'),
|
|
apiFilesMoveDirectory: createPageRouteHandler('api/files/move-directory.ts', '/api/files/move-directory'),
|
|
apiFilesMove: createPageRouteHandler('api/files/move.ts', '/api/files/move'),
|
|
apiFilesRenameDirectory: createPageRouteHandler('api/files/rename-directory.ts', '/api/files/rename-directory'),
|
|
apiFilesRename: createPageRouteHandler('api/files/rename.ts', '/api/files/rename'),
|
|
apiFilesSearch: createPageRouteHandler('api/files/search.ts', '/api/files/search'),
|
|
apiFilesUpdateShare: createPageRouteHandler('api/files/update-share.ts', '/api/files/update-share'),
|
|
apiFilesUpload: createPageRouteHandler('api/files/upload.ts', '/api/files/upload'),
|
|
|
|
apiNewsAddFeed: createPageRouteHandler('api/news/add-feed.ts', '/api/news/add-feed'),
|
|
apiNewsDeleteFeed: createPageRouteHandler('api/news/delete-feed.ts', '/api/news/delete-feed'),
|
|
apiNewsImportFeeds: createPageRouteHandler('api/news/import-feeds.ts', '/api/news/import-feeds'),
|
|
apiNewsMarkRead: createPageRouteHandler('api/news/mark-read.ts', '/api/news/mark-read'),
|
|
apiNewsRefreshArticles: createPageRouteHandler('api/news/refresh-articles.ts', '/api/news/refresh-articles'),
|
|
|
|
apiNotesSave: createPageRouteHandler('api/notes/save.ts', '/api/notes/save'),
|
|
};
|
|
|
|
export default routes;
|