Skip to content

Commit

Permalink
feat: auth provider and login page
Browse files Browse the repository at this point in the history
  • Loading branch information
vcastellm committed Sep 29, 2024
1 parent e605de0 commit 81c989c
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 28 deletions.
32 changes: 8 additions & 24 deletions dkron/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"net/http"
"sort"
"strconv"
"time"

"github.com/distribworks/dkron/v4/types"
"github.com/gin-contrib/cors"
Expand Down Expand Up @@ -48,23 +47,21 @@ func NewTransport(a *Agent, log *logrus.Entry) *HTTPTransport {

func (h *HTTPTransport) ServeHTTP() {
h.Engine = gin.Default()
h.Engine.Use(h.Options)

rootPath := h.Engine.Group("/")

rootPath.Use(cors.New(cors.Config{
AllowOrigins: []string{"*"},
AllowMethods: []string{"*"},
AllowHeaders: []string{"*"},
ExposeHeaders: []string{"*"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}))
config := cors.DefaultConfig()
config.AllowAllOrigins = true
config.AllowMethods = []string{"*"}
config.AllowHeaders = []string{"*"}
config.ExposeHeaders = []string{"*"}

rootPath.Use(cors.New(config))
rootPath.Use(h.MetaMiddleware())

h.APIRoutes(rootPath)
if h.agent.config.UI {
h.UI(rootPath, uiDist)
h.UI(rootPath, &uiDist)
}

h.logger.WithFields(logrus.Fields{
Expand Down Expand Up @@ -131,19 +128,6 @@ func (h *HTTPTransport) MetaMiddleware() gin.HandlerFunc {
}
}

func (h *HTTPTransport) Options(c *gin.Context) {
if c.Request.Method != "OPTIONS" {
c.Next()
} else {
c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Content-Type", "application/json")
gh := cors.Default()
gh(c)

c.AbortWithStatus(http.StatusOK)
}
}

func renderJSON(c *gin.Context, status int, v interface{}) {
if _, ok := c.GetQuery(pretty); ok {
c.IndentedJSON(status, v)
Expand Down
4 changes: 3 additions & 1 deletion ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import jobs from './jobs';
import { BusyList } from './executions/BusyList';
import { Layout } from './layout';
import dataProvider from './dataProvider';
import authProvider from './authProvider';
import Dashboard from './dashboard';
import Settings from './settings/Settings';
import LoginPage from './LoginPage';

declare global {
interface Window {
Expand All @@ -21,11 +23,11 @@ declare global {
}
}

const authProvider = () => Promise.resolve();
const history = createHashHistory();

export const App = () => <Admin
dashboard={Dashboard}
loginPage={LoginPage}
authProvider={authProvider}
dataProvider={dataProvider}
layout={Layout}
Expand Down
29 changes: 29 additions & 0 deletions ui/src/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useState } from 'react';
import { useLogin, useNotify } from 'react-admin';

const LoginPage = ({ theme }) => {
const [token, setToken] = useState('');
const login = useLogin();
const notify = useNotify();

const handleSubmit = e => {
e.preventDefault();
login({ token }).catch(() =>
notify('Invalid token')
);
};

return (
<form onSubmit={handleSubmit}>
<input
name="token"
type="text"
value={token}
onChange={e => setToken(e.target.value)}
/>
</form>
);
};

export default LoginPage;

27 changes: 27 additions & 0 deletions ui/src/authProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { AuthProvider } from 'react-admin';

const authProvider: AuthProvider = {
login: ({ token }) => {
localStorage.setItem('token', token);
return Promise.resolve();
},
logout: () => {
localStorage.removeItem('token');
return Promise.resolve();
},
checkAuth: () =>
localStorage.getItem('token') ? Promise.resolve() : Promise.reject(),
checkError: (error) => {
const status = error.status;
if (status === 401 || status === 403) {
localStorage.removeItem('token');
return Promise.reject();
}
// other error code (404, 500, etc): no need to log out
return Promise.resolve();
},
getIdentity: () => Promise.resolve(),
getPermissions: () => Promise.resolve(),
};

export default authProvider;
20 changes: 17 additions & 3 deletions ui/src/dataProvider.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import { fetchUtils } from 'ra-core';
import jsonServerProvider from 'ra-data-json-server';
import { stringify } from 'query-string';
import { DataProvider } from 'react-admin';

export const apiUrl = window.DKRON_API_URL || 'http://localhost:8080/v1'
const dataProvider = jsonServerProvider(apiUrl);

const myDataProvider = {
const httpClient = (url: String, options: fetchUtils.Options = {}) => {
if (!options.headers) {
options.headers = fetchUtils.createHeadersFromOptions(options);
}

const token = localStorage.getItem('token');
if (token) {
options.headers.set('Authorization', `Bearer ${token}`);
}
return fetchUtils.fetchJson(url, options);
};

const dataProvider = jsonServerProvider(apiUrl, httpClient);

const dkronDataProvider: DataProvider = {
...dataProvider,
getManyReference: (resource: any, params: any) => {
const { page, perPage } = params.pagination;
Expand Down Expand Up @@ -39,4 +53,4 @@ const myDataProvider = {
}
}

export default myDataProvider;
export default dkronDataProvider;

0 comments on commit 81c989c

Please sign in to comment.