Skip to content

Commit ca57775

Browse files
committed
Merge branch 'develop'
2 parents 5a8b768 + da8beca commit ca57775

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+3054
-6318
lines changed

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@
2222
*.tar.gz
2323
*.rar
2424

25+
# Intellij
26+
src/spring/.classpath
27+
src/spring/.project
28+
src/spring/.settings
29+
src/spring/.idea
30+
31+
2532
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
2633
hs_err_pid*
2734

@@ -270,5 +277,10 @@ Temporary Items
270277
.vagrant/
271278
*.box
272279

280+
# Build files
281+
src/client/build
282+
src/client/yarn.lock
283+
src/spring/target
284+
273285

274286
# End of https://www.gitignore.io/api/node,java,vagrant,java-web,reactnative

Vagrantfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ Vagrant.configure("2") do |config|
1414
# boxes at https://vagrantcloud.com/search.
1515
config.vm.box = "ubuntu/xenial64"
1616
config.vm.hostname = "CodeChill"
17+
18+
config.vm.provider "virtualbox" do |v|
19+
v.memory = 2048
20+
end
1721

1822
# Disable automatic box update checking. If you disable this, then
1923
# boxes will only be checked for updates when the user runs

src/client/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6+
"@types/lodash": "^4.14.104",
67
"@types/react-redux": "^5.0.14",
8+
"jwt-decode": "^2.2.0",
79
"react": "^16.2.0",
810
"react-bash": "^1.6.0",
911
"react-dom": "^16.2.0",
1012
"react-redux": "^5.0.6",
13+
"react-router-dom": "^4.2.2",
1114
"react-scripts-ts": "2.12.0",
15+
"semantic-ui-css": "^2.2.14",
16+
"semantic-ui-react": "^0.78.2",
1217
"text-encoding": "^0.6.4"
1318
},
1419
"scripts": {

src/client/src/App.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
font-size: large;
2323
}
2424

25+
.menu .item a {
26+
color: rgba(0,0,0,.87) !important;
27+
}
28+
2529
@keyframes App-logo-spin {
2630
from { transform: rotate(0deg); }
2731
to { transform: rotate(360deg); }

src/client/src/App.test.tsx

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/client/src/App.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
import * as React from "react";
2-
import Term, { Terminal } from "./components/Term";
2+
import AuthService from "./AuthService";
3+
import withAuth from "./components/withAuth";
4+
5+
class App extends React.Component<any, any> {
6+
Auth: AuthService;
7+
8+
constructor(props?: any, context?: any) {
9+
super(props, context);
10+
this.Auth = new AuthService();
11+
}
312

4-
export default class App extends React.Component<any, any> {
513
render() {
614
return (
715
<div>
8-
<Term
9-
prefix="code@chill"
10-
theme={Terminal.Themes.DARK}
11-
/>
16+
Welcome {this.props.user.sub}
1217
</div>
1318
);
1419
}
20+
1521
}
22+
export default withAuth(App);

src/client/src/AuthService.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import decode from "jwt-decode";
2+
export default class AuthService {
3+
// Initializing important variables
4+
5+
domain: string;
6+
7+
constructor(domain?: string) {
8+
this.domain = domain || "http://localhost:8080"; // API server domain
9+
this.fetch = this.fetch.bind(this); // React binding stuff
10+
this.login = this.login.bind(this);
11+
this.getProfile = this.getProfile.bind(this);
12+
}
13+
14+
login(username: string, password: string) {
15+
// Get a token from api server using the fetch api
16+
return this.fetch(`${this.domain}/auth`, {
17+
method: "POST",
18+
body: JSON.stringify({
19+
username,
20+
password
21+
})
22+
}).then((res) => {
23+
this.setToken(res.token); // Setting the token in localStorage
24+
return Promise.resolve(res);
25+
});
26+
}
27+
28+
loggedIn() {
29+
// Checks if there is a saved token and it"s still valid
30+
const token = this.getToken(); // GEtting token from localstorage
31+
return !!token && !this.isTokenExpired(token); // handwaiving here
32+
}
33+
34+
isTokenExpired(token: any) {
35+
try {
36+
const decoded = decode(token);
37+
if (decoded.exp < Date.now() / 1000) { // Checking if token is expired. N
38+
return true;
39+
} else {
40+
return false;
41+
}
42+
} catch (err) {
43+
return false;
44+
}
45+
}
46+
47+
setToken(idToken: any) {
48+
// Saves user token to localStorage
49+
localStorage.setItem("id_token", idToken);
50+
}
51+
52+
getToken() {
53+
// Retrieves the user token from localStorage
54+
return localStorage.getItem("id_token");
55+
}
56+
57+
logout() {
58+
// Clear user token and profile data from localStorage
59+
localStorage.removeItem("id_token");
60+
}
61+
62+
getProfile() {
63+
// Using jwt-decode npm package to decode the token
64+
return decode(this.getToken());
65+
}
66+
67+
fetch(url: any, options: any) {
68+
// performs api calls sending the required authentication headers
69+
const headers = {
70+
"Accept": "application/json",
71+
"Content-Type": "application/json"
72+
};
73+
74+
// Setting Authorization header
75+
// Authorization: Bearer xxxxxxx.xxxxxxxx.xxxxxx
76+
if (this.loggedIn()) {
77+
headers["Authorization"] = "Bearer " + this.getToken();
78+
}
79+
80+
return fetch(url, {
81+
headers,
82+
...options
83+
})
84+
.then(this._checkStatus)
85+
.then((response) => response.json());
86+
}
87+
88+
_checkStatus(response: any) {
89+
// raises an error in case response status is not a success
90+
if (response.status >= 200 && response.status < 300) { // Success status lies between 200 to 300
91+
return response;
92+
} else {
93+
var error = new Error(response.statusText);
94+
// error.response = response
95+
throw error;
96+
}
97+
}
98+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import * as React from "react";
2+
// import { BrowserRouter as Router, Route } from "react-router-dom";
3+
import { Route, Switch } from "react-router-dom";
4+
import App from "../App";
5+
import Term, { Terminal } from "./Term";
6+
import UserConnection from "./user/UserConnection";
7+
import Presentation from "./Presentation";
8+
import NotFound from "./NotFound";
9+
import withAuth from "./withAuth";
10+
11+
export default class CodeChillRouter extends React.Component<any, any> {
12+
13+
Term = withAuth(Term);
14+
15+
render() {
16+
return (
17+
<main>
18+
<Switch>
19+
<Route
20+
exact={true}
21+
path="/"
22+
component={Presentation}
23+
/>
24+
<Route
25+
exact={true}
26+
path="/home"
27+
component={App}
28+
/>
29+
<Route
30+
exact={true}
31+
path="/term"
32+
render={(props) => <Term prefix="code@chill" theme={Terminal.Themes.DARK} />}
33+
/>
34+
<Route
35+
exact={true}
36+
path="/login"
37+
// render={(props) => <UserConnection props={...props} />}
38+
component={UserConnection}
39+
/>
40+
<Route
41+
path="*"
42+
component={NotFound}
43+
/>
44+
</Switch>
45+
</main>
46+
);
47+
}
48+
49+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import * as React from "react";
2+
import * as _ from "lodash";
3+
import AuthService from "../AuthService";
4+
import {
5+
Container,
6+
Dropdown,
7+
Icon,
8+
Image,
9+
Menu,
10+
Sidebar,
11+
Responsive
12+
} from "semantic-ui-react";
13+
14+
const NavBarChildren = ({ children }) => (
15+
<Container style={{ marginTop: "5em" }}>{children}</Container>
16+
);
17+
18+
export default class NavBar extends React.Component<any, any> {
19+
20+
Auth: AuthService;
21+
22+
leftItems = [
23+
{ as: "a", content: "Terminal", key: "terminal", href: "/term" }
24+
];
25+
26+
constructor(props?: any, context?: any) {
27+
super(props, context);
28+
this.Auth = new AuthService();
29+
this.handleLogout = this.handleLogout.bind(this);
30+
this.handlePusher = this.handlePusher.bind(this);
31+
this.handleToggle = this.handleToggle.bind(this);
32+
this.state = {
33+
visible: false
34+
};
35+
}
36+
37+
render() {
38+
const { children } = this.props;
39+
return (
40+
<div>
41+
<Responsive {...Responsive.onlyMobile}>
42+
<Sidebar.Pushable>
43+
<Sidebar
44+
as={Menu}
45+
animation="overlay"
46+
icon="labeled"
47+
inverted={true}
48+
items={this.leftItems}
49+
vertical={true}
50+
visible={this.state.visible}
51+
/>
52+
<Sidebar.Pusher
53+
dimmed={this.state.visible}
54+
onClick={this.handlePusher}
55+
style={{ minHeight: "100vh" }}
56+
>
57+
<Menu fixed="top" inverted={true}>
58+
<Menu.Item>
59+
<Image size="mini" src="https://react.semantic-ui.com/logo.png" />
60+
</Menu.Item>
61+
<Menu.Item onClick={this.handleToggle}>
62+
<Icon name="sidebar" />
63+
</Menu.Item>
64+
<Menu.Menu position="right">
65+
<Dropdown item={true} text={this.props.user.sub}>
66+
<Dropdown.Menu>
67+
<Dropdown.Item><a href="/profile">Profile</a></Dropdown.Item>
68+
<Dropdown.Item
69+
onClick={this.handleLogout}
70+
>Log out
71+
</Dropdown.Item>
72+
</Dropdown.Menu>
73+
</Dropdown>
74+
</Menu.Menu>
75+
</Menu>
76+
<NavBarChildren>{children}</NavBarChildren>
77+
</Sidebar.Pusher>
78+
</Sidebar.Pushable>
79+
</Responsive>
80+
<Responsive minWidth={Responsive.onlyTablet.minWidth}>
81+
<Menu fixed="top" inverted={true}>
82+
<Menu.Item>
83+
<Image size="mini" src="https://react.semantic-ui.com/logo.png" />
84+
</Menu.Item>
85+
{_.map(this.leftItems, (item) => <Menu.Item {...item} />)}
86+
<Menu.Menu position="right">
87+
<Dropdown item={true} text={this.props.user.sub}>
88+
<Dropdown.Menu>
89+
<Dropdown.Item><a href="/profile">Profile</a></Dropdown.Item>
90+
<Dropdown.Item
91+
onClick={this.handleLogout}
92+
>Log out
93+
</Dropdown.Item>
94+
</Dropdown.Menu>
95+
</Dropdown>
96+
</Menu.Menu>
97+
</Menu>
98+
<NavBarChildren>{children}</NavBarChildren>
99+
</Responsive>
100+
</div>
101+
);
102+
}
103+
104+
private handleLogout() {
105+
this.Auth.logout();
106+
this.props.history.replace("/");
107+
}
108+
109+
private handlePusher() {
110+
const { visible } = this.state;
111+
112+
if (visible) {
113+
this.setState(
114+
{ visible: false }
115+
);
116+
}
117+
}
118+
119+
private handleToggle() {
120+
this.setState({ visible: !this.state.visible });
121+
}
122+
123+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as React from "react";
2+
3+
export default class NotFound extends React.Component {
4+
render() {
5+
return (
6+
<h1>
7+
Sorry, the ressource was not found on the server.
8+
</h1>
9+
);
10+
}
11+
}

0 commit comments

Comments
 (0)