Bläddra i källkod

Merge branch 'frontend-boilerplate' into api-users-context

mergin
Alexander Belanger 5 år sedan
förälder
incheckning
a3b51d7a87

+ 94 - 0
dashboard/src/main/home/Toolbar.tsx

@@ -0,0 +1,94 @@
+import React, { Component } from 'react';
+import styled from 'styled-components';
+import ReactModal from 'react-modal';
+
+import { Context } from '../../shared/Context';
+
+import Sidebar from './sidebar/Sidebar';
+import ClusterConfigModal from './modals/ClusterConfigModal';
+
+type PropsType = {
+  logOut: () => void
+};
+
+type StateType = {
+};
+
+export default class Home extends Component<PropsType, StateType> {
+  render() {
+    return (
+      <StyledHome>
+        <ReactModal
+          isOpen={this.context.currentModal === 'ClusterConfigModal'}
+          onRequestClose={() => this.context.setCurrentModal(null)}
+          style={MediumModalStyles}
+          ariaHideApp={false}
+        >
+          <ClusterConfigModal />
+        </ReactModal>
+
+        <Sidebar logOut={this.props.logOut} />
+        <DummyDashboard>
+          🏗️🏗️🏗️🏗️🏗️
+        </DummyDashboard>
+      </StyledHome>
+    );
+  }
+}
+
+Home.contextType = Context;
+
+const MediumModalStyles = {
+  overlay: {
+    backgroundColor: 'rgba(0,0,0,0.6)',
+    zIndex: 2,
+  },
+  content: {
+    borderRadius: '7px',
+    border: 0,
+    width: '760px',
+    maxWidth: '80vw',
+    margin: '0 auto',
+    height: '575px',
+    top: 'calc(50% - 289px)',
+    backgroundColor: '#24272a',
+    animation: 'floatInModal 0.5s 0s',
+    overflow: 'visible',
+  },
+};
+
+const DummyDashboard = styled.div`
+  height: 100%;
+  width: 100vw;
+  font-family: 'Work Sans', sans-serif;
+  overflow-y: auto;
+  display: flex;
+  letter-spacing: 10px;
+  flex: 1;
+  justify-content: center;
+  padding-bottom: 30px;
+  align-items: center;
+  background: ${props => props.theme.bg};
+  position: relative;
+`;
+
+const StyledHome = styled.div`
+  width: 100vw;
+  height: 100vh;
+  position: fixed;
+  top: 0;
+  left: 0;
+  margin: 0;
+  user-select: none;
+  display: flex;
+  justify-content: center;
+
+  @keyframes floatInModal {
+    from {
+      opacity: 0; transform: translateY(30px);
+    }
+    to {
+      opacity: 1; transform: translateY(0px);
+    }
+  }
+`;

+ 23 - 39
dashboard/src/main/home/modals/ClusterConfigModal.tsx

@@ -4,7 +4,7 @@ import close from '../../../assets/close.png';
 
 import api from '../../../shared/api';
 import { Context } from '../../../shared/Context';
-import { ClusterConfig } from '../../../shared/types';
+import { KubeContextConfig } from '../../../shared/types';
 
 import YamlEditor from '../../../components/YamlEditor';
 import SaveButton from '../../../components/SaveButton';
@@ -14,8 +14,7 @@ type PropsType = {
 
 type StateType = {
   currentTab: string,
-  clusters: ClusterConfig[],
-  selected: boolean[],
+  kubeContexts: KubeContextConfig[],
   rawKubeconfig: string,
   saveKubeconfigStatus: string | null,
   saveSelectedStatus: string | null
@@ -24,8 +23,7 @@ type StateType = {
 export default class ClusterConfigModal extends Component<PropsType, StateType> {
   state = {
     currentTab: 'kubeconfig',
-    clusters: [] as ClusterConfig[],
-    selected: [] as boolean[],
+    kubeContexts: [] as KubeContextConfig[],
     rawKubeconfig: '# If you are using certificate files, include those explicitly',
     saveKubeconfigStatus: null as (string | null),
     saveSelectedStatus: null as (string | null),
@@ -35,26 +33,12 @@ export default class ClusterConfigModal extends Component<PropsType, StateType>
     let { setCurrentError, userId } = this.context;
 
     // Parse kubeconfig to retrieve all possible clusters
-    api.getAllClusters('<token>', {}, { id: userId }, (err: any, res: any) => {
+    api.getContexts('<token>', {}, { id: userId }, (err: any, res: any) => {
       if (err) {
         setCurrentError('getAllClusters: ' + JSON.stringify(err));
       } else {
-        let clusters = res.data;
-        this.setState({ clusters });
-
-        if (clusters && clusters.length > 0) {
-          
-          // Check against list of connected clusters
-          api.getClusters('<token>', {}, { id: userId }, (err: any, res: any) => {
-            if (err) {
-              setCurrentError('getClusters: ' + JSON.stringify(err));
-            } else {
-              console.log(res)
-              let selected = clusters.map((x: ClusterConfig) => res.data.includes(x));
-              this.setState({ selected });
-            }
-          });
-        }
+        console.log(res.data)
+        this.setState({ kubeContexts: res.data })
       }
     });
   }
@@ -64,7 +48,7 @@ export default class ClusterConfigModal extends Component<PropsType, StateType>
 
     api.getUser('<token>', {}, { id: userId }, (err: any, res: any) => {
       if (err) {
-        setCurrentError('getUser: ' + JSON.stringify(err));
+        setCurrentError(JSON.stringify(err));
       } else if (res.data.rawKubeConfig !== '') {
         this.setState({ rawKubeconfig: res.data.rawKubeConfig });
       }
@@ -80,22 +64,22 @@ export default class ClusterConfigModal extends Component<PropsType, StateType>
   };
 
   toggleCluster = (i: number): void => {
-    let newSelected = this.state.selected;
-    newSelected[i] = !this.state.selected[i];
-    this.setState({ selected: newSelected });
+    let newKubeContexts = this.state.kubeContexts;
+    newKubeContexts[i].selected = !newKubeContexts[i].selected;
+    this.setState({ kubeContexts: newKubeContexts });
   };
 
   renderClusterList = (): JSX.Element[] | JSX.Element => {
-    let { clusters, selected } = this.state;
+    let { kubeContexts } = this.state;
 
-    if (clusters && clusters.length > 0) {
-      return clusters.map((cluster: ClusterConfig, i) => {
+    if (kubeContexts && kubeContexts.length > 0) {
+      return kubeContexts.map((kubeContext: KubeContextConfig, i) => {
         return (
           <Row key={i} onClick={() => this.toggleCluster(i)}>
-            <Checkbox checked={selected[i]}>
+            <Checkbox checked={kubeContext.selected}>
               <i className="material-icons">done</i>
             </Checkbox>
-            {cluster.name}
+            {kubeContext.name}
           </Row>
         );
       })
@@ -137,22 +121,22 @@ export default class ClusterConfigModal extends Component<PropsType, StateType>
   }
 
   handleSaveSelected = () => {
-    let { clusters, selected } = this.state;
+    let { kubeContexts } = this.state;
     let { userId } = this.context;
 
     this.setState({ saveSelectedStatus: 'loading' });
-    let allowedClusters: string[] = [];
-    clusters.forEach((x, i) => {
-      if (selected[i]) {
-        allowedClusters.push(x.name);
+    let allowedContexts: string[] = [];
+    kubeContexts.forEach((x, i) => {
+      if (x.selected) {
+        allowedContexts.push(x.name);
       }
     });
 
-    console.log(allowedClusters);
+    console.log(allowedContexts);
     
     api.updateUser(
       '<token>',
-      { allowedClusters },
+      { allowedContexts },
       { id: userId },
       (err: any, res: any) => {
         if (err) {
@@ -190,7 +174,7 @@ export default class ClusterConfigModal extends Component<PropsType, StateType>
         </ClusterList>
         <SaveButton
           text='Save Selected'
-          disabled={this.state.clusters.length === 0}
+          disabled={this.state.kubeContexts.length === 0}
           onClick={this.handleSaveSelected}
           status={this.state.saveSelectedStatus}
         />

+ 8 - 30
dashboard/src/main/home/sidebar/ClusterSection.tsx

@@ -4,7 +4,6 @@ import drawerBg from '../../../assets/drawer-bg.png';
 
 import api from '../../../shared/api';
 import { Context } from '../../../shared/Context';
-import { ClusterConfig } from '../../../shared/types';
 
 import Drawer from './Drawer';
 
@@ -17,31 +16,10 @@ type StateType = {
   configExists: boolean,
   showDrawer: boolean,
   initializedDrawer: boolean,
-  clusters: any[],
+  kubeContexts: string[],
   activeIndex: number,
 };
 
-const dummyClusters: ClusterConfig[]  = [
-  { 
-    name: 'happy-ol-trees', 
-    server: 'idc',
-    context: 'idk',
-    user: 'jusrhee'
-  },
-  { 
-    name: 'joyous-petite-rocks', 
-    server: 'idc',
-    context: 'idk',
-    user: 'jusrhee'
-  },
-  { 
-    name: 'friendly-small-bush', 
-    server: 'idc',
-    context: 'idk',
-    user: 'jusrhee'
-  }
-];
-
 export default class ClusterSection extends Component<PropsType, StateType> {
 
   // Need to track initialized for animation mounting
@@ -49,18 +27,18 @@ export default class ClusterSection extends Component<PropsType, StateType> {
     configExists: true,
     showDrawer: false,
     initializedDrawer: false,
-    clusters: [] as ClusterConfig[],
+    kubeContexts: [] as string[],
     activeIndex: 0,
   };
 
   componentDidMount() {
     let { setCurrentError, userId } = this.context;
 
-    api.getClusters('<token>', {}, { id: userId }, (err: any, res: any) => {      
+    api.getContexts('<token>', {}, { id: userId }, (err: any, res: any) => {      
       if (err) {
         setCurrentError(JSON.stringify(err));
       } else {
-        this.setState({ clusters: res.data });
+        this.setState({ kubeContexts: res });
       }
     });
   }
@@ -88,7 +66,7 @@ export default class ClusterSection extends Component<PropsType, StateType> {
         <Drawer
           toggleDrawer={this.toggleDrawer}
           showDrawer={this.state.showDrawer}
-          clusters={this.state.clusters}
+          kubeContexts={this.state.kubeContexts}
           activeIndex={this.state.activeIndex}
           setActiveIndex={(i: number): void => this.setState({ activeIndex: i })}
         />
@@ -97,14 +75,14 @@ export default class ClusterSection extends Component<PropsType, StateType> {
   };
 
   renderContents = (): JSX.Element => {
-    let { clusters, activeIndex, showDrawer } = this.state;
+    let { kubeContexts, activeIndex, showDrawer } = this.state;
 
-    if (clusters.length > 0) {
+    if (kubeContexts.length > 0) {
       return (
         <ClusterSelector showDrawer={showDrawer}>
           <LinkWrapper>
             <ClusterIcon><i className="material-icons">polymer</i></ClusterIcon>
-            <ClusterName>{clusters[activeIndex].name}</ClusterName>
+            <ClusterName>{kubeContexts[activeIndex]}</ClusterName>
           </LinkWrapper>
           <DrawerButton onClick={this.toggleDrawer}>
             <BgAccent src={drawerBg} />

+ 3 - 4
dashboard/src/main/home/sidebar/Drawer.tsx

@@ -3,12 +3,11 @@ import styled from 'styled-components';
 import close from '../../../assets/close.png';
 
 import { Context } from '../../../shared/Context';
-import { ClusterConfig } from '../../../shared/types';
 
 type PropsType = {
   toggleDrawer: () => void,
   showDrawer: boolean,
-  clusters: ClusterConfig[],
+  kubeContexts: string[],
   activeIndex: number,
   setActiveIndex: (i: number) => void
 };
@@ -19,7 +18,7 @@ type StateType = {
 export default class Drawer extends Component<PropsType, StateType> {
 
   renderClusterList = (): JSX.Element[] => {
-    return this.props.clusters.map((cluster, i) => {
+    return this.props.kubeContexts.map((kubeContext: string, i: number) => {
       /*
       let active = this.context.activeProject &&
         this.context.activeProject.namespace == val.namespace; 
@@ -32,7 +31,7 @@ export default class Drawer extends Component<PropsType, StateType> {
           onClick={() => this.props.setActiveIndex(i)}
         >
           <ClusterIcon><i className="material-icons">polymer</i></ClusterIcon>
-          <ClusterName>{cluster.name}</ClusterName>
+          <ClusterName>{kubeContext}</ClusterName>
         </ClusterOption>
       );
     });

+ 11 - 5
dashboard/src/main/home/sidebar/Sidebar.tsx

@@ -117,9 +117,11 @@ export default class Sidebar extends Component<PropsType, StateType> {
             releaseDrawer={() => this.setState({ forceCloseDrawer: false })}
           />
 
-          <LogOutButton onClick={this.handleLogout}>
-            Log Out <i className="material-icons">keyboard_return</i>
-          </LogOutButton>
+          <BottomSection>
+            <LogOutButton onClick={this.handleLogout}>
+              Log Out <i className="material-icons">keyboard_return</i>
+            </LogOutButton>
+          </BottomSection>
         </StyledSidebar>
       </div>
     );
@@ -160,10 +162,14 @@ const NavButton = styled.div`
   }
 `;
 
-const LogOutButton = styled(NavButton)`
+const BottomSection = styled.div`
   position: absolute;
-  width: calc(100% - 55px); 
+  width: 100%;
   bottom: 12px;
+`;
+
+const LogOutButton = styled(NavButton)`
+  width: calc(100% - 55px); 
   border-top-right-radius: 3px;
   border-bottom-right-radius: 3px;
   margin-left: -1px;

+ 5 - 1
dashboard/src/shared/Context.tsx

@@ -41,10 +41,14 @@ class ContextProvider extends Component {
     setUserId: (userId: number): void => {
       this.setState({ userId });
     },
+    devOpsMode: true,
+    setDevOpsMode: (devOpsMode: boolean): void => {
+      this.setState({ devOpsMode });
+    }
   };
 
   componentDidMount() {
-    this.setState({ userId: 3 });
+    this.setState({ userId: 1 });
   }
 
   render() {

+ 4 - 9
dashboard/src/shared/api.tsx

@@ -29,17 +29,13 @@ const getUser = baseApi<{}, { id: number }>('GET', pathParams => {
 
 const updateUser = baseApi<{
   rawKubeConfig?: string,
-  allowedClusters?: string[]
+  allowedContexts?: string[]
 }, { id: number }>('PUT', pathParams => {
   return `/api/users/${pathParams.id}`;
 });
 
-const getClusters = baseApi<{}, { id: number }>('GET', pathParams => {
-  return `/api/users/${pathParams.id}/clusters`;
-});
-
-const getAllClusters = baseApi<{}, { id: number }>('GET', pathParams => {
-  return `/api/users/${pathParams.id}/clusters/all`;
+const getContexts = baseApi<{}, { id: number }>('GET', pathParams => {
+  return `/api/users/${pathParams.id}/contexts`;
 });
 
 // Bundle export to allow default api import (api.<method> is more readable)
@@ -50,6 +46,5 @@ export default {
   logOutUser,
   getUser,
   updateUser,
-  getClusters,
-  getAllClusters
+  getContexts,
 }

+ 3 - 2
dashboard/src/shared/types.tsx

@@ -1,6 +1,7 @@
-export interface ClusterConfig {
+export interface KubeContextConfig {
+  cluster: string,
   name: string,
+  selected?: boolean,
   server: string,
-  context: string,
   user: string
 }

+ 1 - 1
internal/repository/gorm/user.go

@@ -46,7 +46,7 @@ func (repo *UserRepository) ReadUserByEmail(email string) (*models.User, error)
 
 // UpdateUser modifies an existing User in the database
 func (repo *UserRepository) UpdateUser(user *models.User) (*models.User, error) {
-	if err := repo.db.First(&models.User{}, user.ID).Updates(user).Error; err != nil {
+	if err := repo.db.Save(user).Error; err != nil {
 		return nil, err
 	}
 

+ 1 - 3
server/api/user_handler.go

@@ -3,7 +3,6 @@ package api
 import (
 	"encoding/json"
 	"errors"
-	"fmt"
 	"net/http"
 	"strconv"
 	"strings"
@@ -57,8 +56,7 @@ func (app *App) HandleCreateUser(w http.ResponseWriter, r *http.Request) {
 // HandleAuthCheck checks whether current session is authenticated.
 func (app *App) HandleAuthCheck(w http.ResponseWriter, r *http.Request) {
 	session, err := app.store.Get(r, app.cookieName)
-	cook, _ := r.Cookie("porter")
-	fmt.Println("cooki", cook)
+
 	if err != nil {
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 	}