Skip to content

Commit

Permalink
Add logic to logout
Browse files Browse the repository at this point in the history
  • Loading branch information
acelaya committed May 18, 2024
1 parent 3a661fb commit cc9c70b
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 44 deletions.
3 changes: 2 additions & 1 deletion app/auth/auth.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { Authenticator } from 'remix-auth';
import { FormStrategy } from 'remix-auth-form';
import type { UsersService } from '../users/UsersService.server';
import { credentialsSchema } from './credentials-schema.server';
import type { SessionData, SessionStorage } from './session.server';
import type { SessionStorage } from './session.server';
import type { SessionData } from './session-context';

export const CREDENTIALS_STRATEGY = 'credentials';

Expand Down
12 changes: 12 additions & 0 deletions app/auth/session-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createContext, useContext } from 'react';

export type SessionData = {
userId: number;
[key: string]: unknown;
};

const SessionContext = createContext<SessionData | null>(null);

export const { Provider: SessionProvider } = SessionContext;

export const useSession = () => useContext(SessionContext);
6 changes: 1 addition & 5 deletions app/auth/session.server.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { createCookieSessionStorage } from '@remix-run/node';
import { env, isProd } from '../utils/env.server';

export type SessionData = {
userId: number;
[key: string]: unknown;
};
import type { SessionData } from './session-context';

export const createSessionStorage = () => createCookieSessionStorage<SessionData>({
cookie: {
Expand Down
43 changes: 35 additions & 8 deletions app/common/MainHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,40 @@
import { faArrowRightFromBracket as faLogout, faChevronDown as arrowIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Link } from '@remix-run/react';
import { useToggle } from '@shlinkio/shlink-frontend-kit';
import type { FC } from 'react';
import React from 'react';
import { Navbar, NavbarBrand } from 'reactstrap';
import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap';
import { useSession } from '../auth/session-context';
import { ShlinkLogo } from './ShlinkLogo';

export const MainHeader: FC = () => (
<Navbar color="primary" dark fixed="top" className="main-header" expand="md">
<NavbarBrand tag={Link} to="/">
<ShlinkLogo className="tw-inline-block tw-mr-1 tw-w-[26px]" color="white" /> Shlink
</NavbarBrand>
</Navbar>
);
export const MainHeader: FC = () => {
const session = useSession();
const [isOpen, toggleCollapse] = useToggle();

return (
<Navbar color="primary" dark fixed="top" className="main-header" expand="md">
<NavbarBrand tag={Link} to="/">
<ShlinkLogo className="tw-inline-block tw-mr-1 tw-w-[26px]" color="white" /> Shlink
</NavbarBrand>

{session !== null && (
<>
<NavbarToggler onClick={toggleCollapse}>
<FontAwesomeIcon icon={arrowIcon} />
</NavbarToggler>

<Collapse navbar isOpen={isOpen}>
<Nav navbar className="tw-ml-auto">
<NavItem>
<NavLink tag={Link} to="/logout">
<FontAwesomeIcon icon={faLogout} className="tw-w-[26px] tw-inline-block" /> Logout
</NavLink>
</NavItem>
</Nav>
</Collapse>
</>
)}
</Navbar>
);
};
44 changes: 28 additions & 16 deletions app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,60 @@
import type { LoaderFunctionArgs } from '@remix-run/node';
import { Links, Meta, Outlet, Scripts } from '@remix-run/react';
import { Links, Meta, Outlet, Scripts, useLoaderData } from '@remix-run/react';
import { Authenticator } from 'remix-auth';
import type { SessionData } from './auth/session-context';
import { SessionProvider } from './auth/session-context';
import { MainHeader } from './common/MainHeader';
import { serverContainer } from './container/container.server';
import { appDataSource } from './db/data-source.server';
import './index.scss';

export async function loader(
{ request }: LoaderFunctionArgs,
authenticator: Authenticator = serverContainer[Authenticator.name],
authenticator: Authenticator<SessionData> = serverContainer[Authenticator.name],
) {
// FIXME This should be done during server start-up, not here
if (!appDataSource.isInitialized) {
console.log('Initializing database connection...');

Check warning on line 17 in app/root.tsx

View workflow job for this annotation

GitHub Actions / ci / lint (npm run lint)

Unexpected console statement
await appDataSource.initialize();
console.log('Database connection initialized');

Check warning on line 19 in app/root.tsx

View workflow job for this annotation

GitHub Actions / ci / lint (npm run lint)

Unexpected console statement
}

const { pathname } = new URL(request.url);
const isPublicRoute = ['/login'].includes(pathname);
if (!isPublicRoute) {
await authenticator.isAuthenticated(request, {
failureRedirect: `/login?redirect-to=${encodeURIComponent(pathname)}`,
});
}

return {};
const isPublicRoute = ['/login', '/logout'].includes(pathname);
const session = await (isPublicRoute
? authenticator.isAuthenticated(request) // For public routes, do not redirect
: authenticator.isAuthenticated(
request,
{ failureRedirect: `/login?redirect-to=${encodeURIComponent(pathname)}` },
));
return { session };
}

/* eslint-disable-next-line no-restricted-exports */
export default function App() {
const { session } = useLoaderData<typeof loader>();

return (
<html lang="en">
<head>
<title>Shlink dashboard</title>
<link rel="icon" href="data:image/x-icon;base64,AA" />

<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#4696e5" />
<Meta />

<link rel="icon" href="data:image/x-icon;base64,AA" />
<Links />
</head>
<body>
<MainHeader />
<div className="app">
<Outlet />
</div>
<Scripts />
<SessionProvider value={session}>
<MainHeader />
<div className="app">
<Outlet />
</div>
<Scripts />
</SessionProvider>
</body>
</html>
);
Expand Down
10 changes: 10 additions & 0 deletions app/routes/logout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { ActionFunctionArgs } from '@remix-run/node';
import { Authenticator } from 'remix-auth';
import { serverContainer } from '../container/container.server';

export function loader(
{ request }: ActionFunctionArgs,
authenticator: Authenticator = serverContainer[Authenticator.name],
) {
return authenticator.logout(request, { redirectTo: '/login' });
}
2 changes: 1 addition & 1 deletion app/routes/server.$serverId.$.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { ReactNode } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { Authenticator } from 'remix-auth';
import { ShlinkApiProxyClient } from '../api/ShlinkApiProxyClient.client';
import type { SessionData } from '../auth/session.server';
import type { SessionData } from '../auth/session-context';
import { serverContainer } from '../container/container.server';
import { SettingsService } from '../settings/SettingsService.server';
import { TagsService } from '../tags/TagsService.server';
Expand Down
2 changes: 1 addition & 1 deletion app/routes/server.$serverId.shlink-api.$method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { ShlinkApiClient } from '@shlinkio/shlink-js-sdk';
import { ErrorType } from '@shlinkio/shlink-js-sdk/api-contract';
import { Authenticator } from 'remix-auth';
import type { ApiClientBuilder } from '../api/apiClientBuilder.server';
import type { SessionData } from '../auth/session.server';
import type { SessionData } from '../auth/session-context';
import { serverContainer } from '../container/container.server';
import { ServersService } from '../servers/ServersService.server';
import { problemDetails } from '../utils/response.server';
Expand Down
2 changes: 1 addition & 1 deletion app/routes/server.$serverId.tags.colors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ActionFunctionArgs } from '@remix-run/node';
import { Authenticator } from 'remix-auth';
import type { SessionData } from '../auth/session.server';
import type { SessionData } from '../auth/session-context';
import { serverContainer } from '../container/container.server';
import { TagsService } from '../tags/TagsService.server';
import { empty } from '../utils/response.server';
Expand Down
17 changes: 8 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
"migration:create": "tsx node_modules/.bin/typeorm migration:create app/db/migrations/migration"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-brands-svg-icons": "^6.5.2",
"@fortawesome/free-regular-svg-icons": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/react-fontawesome": "^0.2.1",
"@remix-run/express": "^2.9.2",
"@remix-run/node": "^2.9.2",
"@remix-run/react": "^2.9.2",
Expand Down
2 changes: 1 addition & 1 deletion test/routes/server.$serverId.shlink-api.$method.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ErrorType } from '@shlinkio/shlink-js-sdk/api-contract';
import { fromPartial } from '@total-typescript/shoehorn';
import type { Authenticator } from 'remix-auth';
import { expect } from 'vitest';
import type { SessionData } from '../../app/auth/session.server';
import type { SessionData } from '../../app/auth/session-context';
import { action } from '../../app/routes/server.$serverId.shlink-api.$method';
import type { ServersService } from '../../app/servers/ServersService.server';

Expand Down
2 changes: 1 addition & 1 deletion test/routes/server.$serverId.tags.colors.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ActionFunctionArgs } from '@remix-run/node';
import { fromPartial } from '@total-typescript/shoehorn';
import type { Authenticator } from 'remix-auth';
import type { SessionData } from '../../app/auth/session.server';
import type { SessionData } from '../../app/auth/session-context';
import { action } from '../../app/routes/server.$serverId.tags.colors';
import type { TagsService } from '../../app/tags/TagsService.server';

Expand Down

0 comments on commit cc9c70b

Please sign in to comment.