Merge pull request #2 from ieeeucsd/ray-dev
code clean up, initiate eslint action
This commit is contained in:
commit
281d36fc2e
21 changed files with 1814 additions and 2667 deletions
20
.eslintrc.json
Normal file
20
.eslintrc.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended-requiring-type-checking"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"project": "./tsconfig.json"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-unsafe-argument": "warn",
|
||||||
|
"@typescript-eslint/no-unsafe-assignment": "warn",
|
||||||
|
"@typescript-eslint/unbound-method": "warn"
|
||||||
|
},
|
||||||
|
"overrides": [],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"plugins": ["@typescript-eslint"],
|
||||||
|
"ignorePatterns": ["node_modules", "build", "webpack**", "src/public/"]
|
||||||
|
}
|
3483
package-lock.json
generated
3483
package-lock.json
generated
File diff suppressed because it is too large
Load diff
59
package.json
59
package.json
|
@ -4,53 +4,50 @@
|
||||||
"description": "Website for IEEE of UC San Diego",
|
"description": "Website for IEEE of UC San Diego",
|
||||||
"main": "build/index.js",
|
"main": "build/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack & tsc",
|
"build": "webpack & tsc --build --verbose & node build/index.js generate",
|
||||||
"watch": "npm-watch build"
|
"start": "node build/index.js",
|
||||||
},
|
"watch": "webpack watch",
|
||||||
"watch": {
|
"generate": "node build/index.js generate",
|
||||||
"build": {
|
"lint": "eslint ."
|
||||||
"patterns": [
|
|
||||||
"src"
|
|
||||||
],
|
|
||||||
"quiet": true,
|
|
||||||
"silent": true,
|
|
||||||
"extensions": "js,jsx,ts,tsx,html,css"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.14",
|
||||||
"@types/node": "^16.11.7",
|
"@types/mongoose": "^5.11.97",
|
||||||
"@types/react-dom": "^16.9.14",
|
"@types/node": "^18.11.4",
|
||||||
"@types/ws": "^7.4.7",
|
"@types/react-dom": "^18.0.6",
|
||||||
"express": "^4.17.1",
|
"@types/ws": "^8.5.3",
|
||||||
"mongoose": "^6.1.7",
|
"express": "^4.18.2",
|
||||||
"react": "^16.14.0",
|
"mongoose": "^6.6.7",
|
||||||
"react-dom": "^16.14.0"
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.15.8",
|
"@babel/core": "^7.19.6",
|
||||||
"@babel/preset-env": "^7.15.8",
|
"@babel/preset-env": "^7.19.4",
|
||||||
"@babel/preset-react": "^7.14.5",
|
"@babel/preset-react": "^7.14.5",
|
||||||
"@types/react": "^16.14.20",
|
"@types/react": "^18.0.21",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.40.1",
|
"@typescript-eslint/eslint-plugin": "^5.40.1",
|
||||||
|
"@typescript-eslint/parser": "^5.40.1",
|
||||||
"babel-loader": "^8.2.3",
|
"babel-loader": "^8.2.3",
|
||||||
"copy-webpack-plugin": "^6.4.1",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"css-loader": "^5.2.7",
|
"css-loader": "^6.7.1",
|
||||||
"eslint": "^8.26.0",
|
"eslint": "^8.26.0",
|
||||||
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-config-standard-with-typescript": "^23.0.0",
|
"eslint-config-standard-with-typescript": "^23.0.0",
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-n": "^15.3.0",
|
"eslint-plugin-n": "^15.3.0",
|
||||||
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"eslint-plugin-promise": "^6.1.1",
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
"eslint-plugin-react": "^7.31.10",
|
"eslint-plugin-react": "^7.31.10",
|
||||||
"html-webpack-plugin": "^4.5.2",
|
"html-webpack-plugin": "^5.5.0",
|
||||||
"license-webpack-plugin": "^2.3.21",
|
"license-webpack-plugin": "^4.0.2",
|
||||||
"style-loader": "^2.0.0",
|
"style-loader": "^3.3.1",
|
||||||
"ts-loader": "^8.3.0",
|
"ts-loader": "^9.4.1",
|
||||||
"typescript": "^4.8.4",
|
"typescript": "^4.8.4",
|
||||||
"webpack": "^5.60.0",
|
"webpack": "^5.74.0",
|
||||||
"webpack-cli": "^4.9.1"
|
"webpack-cli": "^4.10.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,7 +25,7 @@ export const ACTIVE_PAGES = [
|
||||||
// Urls of team photos that will go in the "About" slideshow
|
// Urls of team photos that will go in the "About" slideshow
|
||||||
export const TEAM_PHOTOS: string[] = [];
|
export const TEAM_PHOTOS: string[] = [];
|
||||||
export const PROJECT_SPACE: string[] = [];
|
export const PROJECT_SPACE: string[] = [];
|
||||||
export const EVENTS: any[] = [];
|
export const EVENTS: unknown[] = [];
|
||||||
|
|
||||||
export const SOCIALS = [
|
export const SOCIALS = [
|
||||||
{
|
{
|
||||||
|
@ -44,6 +44,7 @@ export const SOCIALS = [
|
||||||
message: "@ieeeucsd",
|
message: "@ieeeucsd",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const EMAIL = [
|
export const EMAIL = [
|
||||||
{
|
{
|
||||||
icon: "img/email.svg",
|
icon: "img/email.svg",
|
||||||
|
@ -51,6 +52,7 @@ export const EMAIL = [
|
||||||
message: "ieee@eng.ucsd.edu",
|
message: "ieee@eng.ucsd.edu",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const EMAIL_WHITE = [
|
export const EMAIL_WHITE = [
|
||||||
{
|
{
|
||||||
icon: "img/email_white.svg",
|
icon: "img/email_white.svg",
|
||||||
|
|
|
@ -9,14 +9,12 @@ import SocialCard from "./components/SocialCard";
|
||||||
import Carousel from "./components/Carousel";
|
import Carousel from "./components/Carousel";
|
||||||
import Footer from "./components/Footer";
|
import Footer from "./components/Footer";
|
||||||
|
|
||||||
interface MainProps {}
|
class Main extends React.Component {
|
||||||
interface MainState {}
|
constructor() {
|
||||||
|
super({});
|
||||||
class Main extends React.Component<MainProps, MainState> {
|
|
||||||
constructor(props: MainProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
this.state = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -67,6 +65,7 @@ class Main extends React.Component<MainProps, MainState> {
|
||||||
<div className="join-scls">
|
<div className="join-scls">
|
||||||
{[...EMAIL, ...SOCIALS].map((n) => (
|
{[...EMAIL, ...SOCIALS].map((n) => (
|
||||||
<SocialCard
|
<SocialCard
|
||||||
|
key={n.url} // Hacky fix: use url as key
|
||||||
url={n.url}
|
url={n.url}
|
||||||
image={n.icon}
|
image={n.icon}
|
||||||
message={n.message}
|
message={n.message}
|
||||||
|
|
|
@ -38,7 +38,7 @@ export default class Carousel extends Component<CarouselProps, CarouselState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
let arr = this.chunkArray(this.props.items);
|
const arr = this.chunkArray(this.props.items);
|
||||||
return (
|
return (
|
||||||
<div className="carousel">
|
<div className="carousel">
|
||||||
<img
|
<img
|
||||||
|
@ -52,6 +52,7 @@ export default class Carousel extends Component<CarouselProps, CarouselState> {
|
||||||
<div className="carousel-items">
|
<div className="carousel-items">
|
||||||
{arr.map((items, i) => (
|
{arr.map((items, i) => (
|
||||||
<CarouselPage
|
<CarouselPage
|
||||||
|
key={i}
|
||||||
items={items}
|
items={items}
|
||||||
visible={i === this.state.page}
|
visible={i === this.state.page}
|
||||||
></CarouselPage>
|
></CarouselPage>
|
||||||
|
@ -72,7 +73,7 @@ export default class Carousel extends Component<CarouselProps, CarouselState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private chunkArray(array: CarouselItemProps[]): CarouselItemProps[][] {
|
private chunkArray(array: CarouselItemProps[]): CarouselItemProps[][] {
|
||||||
let returnArr = [] as CarouselItemProps[][];
|
const returnArr = [] as CarouselItemProps[][];
|
||||||
for (let i = 0; i < array.length; i += this.props.itemsPerPage) {
|
for (let i = 0; i < array.length; i += this.props.itemsPerPage) {
|
||||||
returnArr.push(array.slice(i, i + this.props.itemsPerPage));
|
returnArr.push(array.slice(i, i + this.props.itemsPerPage));
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,8 @@ export interface CarouselItemProps {
|
||||||
email: string;
|
email: string;
|
||||||
photo: string;
|
photo: string;
|
||||||
}
|
}
|
||||||
interface CarouselItemState {}
|
|
||||||
|
|
||||||
export default class CarouselItem extends Component<
|
export default class CarouselItem extends Component<CarouselItemProps> {
|
||||||
CarouselItemProps,
|
|
||||||
CarouselItemState
|
|
||||||
> {
|
|
||||||
constructor(props: CarouselItemProps) {
|
constructor(props: CarouselItemProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {};
|
this.state = {};
|
||||||
|
|
|
@ -6,12 +6,8 @@ interface CarouselPageProps {
|
||||||
items: CarouselItemProps[];
|
items: CarouselItemProps[];
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
}
|
}
|
||||||
interface CarouselPageState {}
|
|
||||||
|
|
||||||
export default class CarouselPage extends Component<
|
export default class CarouselPage extends Component<CarouselPageProps> {
|
||||||
CarouselPageProps,
|
|
||||||
CarouselPageState
|
|
||||||
> {
|
|
||||||
constructor(props: CarouselPageProps) {
|
constructor(props: CarouselPageProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {};
|
this.state = {};
|
||||||
|
@ -24,7 +20,7 @@ export default class CarouselPage extends Component<
|
||||||
style={this.props.visible ? {} : { display: "none" }}
|
style={this.props.visible ? {} : { display: "none" }}
|
||||||
>
|
>
|
||||||
{this.props.items.map((n) => (
|
{this.props.items.map((n) => (
|
||||||
<CarouselItem {...n}></CarouselItem>
|
<CarouselItem {...n} key={n.name}></CarouselItem>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,13 +5,10 @@ interface DefaultSectionProps {
|
||||||
title: string;
|
title: string;
|
||||||
paragraphs?: string[];
|
paragraphs?: string[];
|
||||||
className?: string;
|
className?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
interface DefaultSectionState {}
|
|
||||||
|
|
||||||
export default class DefaultSection extends Component<
|
export default class DefaultSection extends Component<DefaultSectionProps> {
|
||||||
DefaultSectionProps,
|
|
||||||
DefaultSectionState
|
|
||||||
> {
|
|
||||||
constructor(props: DefaultSectionProps) {
|
constructor(props: DefaultSectionProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {};
|
this.state = {};
|
||||||
|
@ -27,7 +24,7 @@ export default class DefaultSection extends Component<
|
||||||
<div className="section-title">{this.props.title}</div>
|
<div className="section-title">{this.props.title}</div>
|
||||||
{(this.props.paragraphs ? this.props.paragraphs : []).map(
|
{(this.props.paragraphs ? this.props.paragraphs : []).map(
|
||||||
(n) => (
|
(n) => (
|
||||||
<p>{n}</p>
|
<p key={n}>{n}</p>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
|
|
@ -3,12 +3,9 @@ import { Component } from "react";
|
||||||
import SocialCard from "./SocialCard";
|
import SocialCard from "./SocialCard";
|
||||||
import { EMAIL_WHITE, SOCIALS_WHITE } from "../Config";
|
import { EMAIL_WHITE, SOCIALS_WHITE } from "../Config";
|
||||||
|
|
||||||
interface FooterProps {}
|
export default class Footer extends Component {
|
||||||
interface FooterState {}
|
constructor() {
|
||||||
|
super({});
|
||||||
export default class Footer extends Component<FooterProps, FooterState> {
|
|
||||||
constructor(props: FooterProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
this.state = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +16,7 @@ export default class Footer extends Component<FooterProps, FooterState> {
|
||||||
<div className="footer-scls">
|
<div className="footer-scls">
|
||||||
{[...EMAIL_WHITE, ...SOCIALS_WHITE].map((n) => (
|
{[...EMAIL_WHITE, ...SOCIALS_WHITE].map((n) => (
|
||||||
<SocialCard
|
<SocialCard
|
||||||
|
key={n.url}
|
||||||
url={n.url}
|
url={n.url}
|
||||||
image={n.icon}
|
image={n.icon}
|
||||||
message={n.message}
|
message={n.message}
|
||||||
|
|
|
@ -6,12 +6,8 @@ interface InvolveBoxProps {
|
||||||
image: string;
|
image: string;
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
interface InvolveBoxState {}
|
|
||||||
|
|
||||||
export default class InvolveBox extends Component<
|
export default class InvolveBox extends Component<InvolveBoxProps> {
|
||||||
InvolveBoxProps,
|
|
||||||
InvolveBoxState
|
|
||||||
> {
|
|
||||||
constructor(props: InvolveBoxProps) {
|
constructor(props: InvolveBoxProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {};
|
this.state = {};
|
||||||
|
|
|
@ -6,12 +6,8 @@ interface SocialCardProps {
|
||||||
image: string;
|
image: string;
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
interface SocialCardState {}
|
|
||||||
|
|
||||||
export default class SocialCard extends Component<
|
export default class SocialCard extends Component<SocialCardProps> {
|
||||||
SocialCardProps,
|
|
||||||
SocialCardState
|
|
||||||
> {
|
|
||||||
constructor(props: SocialCardProps) {
|
constructor(props: SocialCardProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {};
|
this.state = {};
|
||||||
|
|
|
@ -21,7 +21,7 @@ export default class Splash extends Component<SplashProps, SplashState> {
|
||||||
this.interval = setInterval(
|
this.interval = setInterval(
|
||||||
this.changeImage.bind(this),
|
this.changeImage.bind(this),
|
||||||
this.props.delay
|
this.props.delay
|
||||||
) as any as number;
|
) as unknown as number;
|
||||||
}
|
}
|
||||||
|
|
||||||
private changeImage(): void {
|
private changeImage(): void {
|
||||||
|
|
|
@ -20,14 +20,18 @@ export default class TopBar extends Component<TopBarProps, TopBarState> {
|
||||||
showBurger: this.shouldShowBurger(),
|
showBurger: this.shouldShowBurger(),
|
||||||
menuVisible: false,
|
menuVisible: false,
|
||||||
};
|
};
|
||||||
|
this.toggleMenu.bind(this);
|
||||||
window.addEventListener("resize", this.checkResize.bind(this));
|
window.addEventListener("resize", this.checkResize.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private toggleMenu() {
|
private toggleMenu() {
|
||||||
this.setState({ menuVisible: !this.state.menuVisible });
|
this.setState({ menuVisible: !this.state.menuVisible });
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkResize() {
|
private checkResize() {
|
||||||
this.setState({ showBurger: this.shouldShowBurger() });
|
this.setState({ showBurger: this.shouldShowBurger() });
|
||||||
}
|
}
|
||||||
|
|
||||||
private shouldShowBurger(): boolean {
|
private shouldShowBurger(): boolean {
|
||||||
return window.innerWidth < TopBar.HAMBURGER_POINT;
|
return window.innerWidth < TopBar.HAMBURGER_POINT;
|
||||||
}
|
}
|
||||||
|
@ -48,7 +52,7 @@ export default class TopBar extends Component<TopBarProps, TopBarState> {
|
||||||
style={{
|
style={{
|
||||||
display: this.state.showBurger ? "initial" : "none",
|
display: this.state.showBurger ? "initial" : "none",
|
||||||
}}
|
}}
|
||||||
onClick={this.toggleMenu.bind(this)}
|
onClick={this.toggleMenu}
|
||||||
role="menubar"
|
role="menubar"
|
||||||
>
|
>
|
||||||
≡
|
≡
|
||||||
|
@ -69,7 +73,7 @@ export default class TopBar extends Component<TopBarProps, TopBarState> {
|
||||||
>
|
>
|
||||||
{this.props.links.map((l) => {
|
{this.props.links.map((l) => {
|
||||||
return (
|
return (
|
||||||
<a className="navlink" href={l.url}>
|
<a className="navlink" href={l.url} key={l.url}>
|
||||||
{l.name}
|
{l.name}
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,22 +1,18 @@
|
||||||
import * as ReactDom from "react-dom";
|
import * as ReactDom from "react-dom";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import TopBar from "./components/TopBar";
|
import TopBar from "./components/TopBar";
|
||||||
import { ACTIVE_PAGES, SOCIALS, EMAIL, OFFICERS } from "./Config";
|
import { ACTIVE_PAGES, SOCIALS, EMAIL } from "./Config";
|
||||||
import Splash from "./components/Splash";
|
import Splash from "./components/Splash";
|
||||||
import DefaultSection from "./components/DefaultSection";
|
import DefaultSection from "./components/DefaultSection";
|
||||||
import InvolveBox from "./components/InvolveBox";
|
|
||||||
import SocialCard from "./components/SocialCard";
|
import SocialCard from "./components/SocialCard";
|
||||||
import Carousel from "./components/Carousel";
|
|
||||||
import Footer from "./components/Footer";
|
import Footer from "./components/Footer";
|
||||||
|
|
||||||
interface MainProps {}
|
class Main extends React.Component {
|
||||||
interface MainState {}
|
constructor() {
|
||||||
|
super({});
|
||||||
class Main extends React.Component<MainProps, MainState> {
|
|
||||||
constructor(props: MainProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
this.state = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -51,6 +47,7 @@ class Main extends React.Component<MainProps, MainState> {
|
||||||
<div className="join-scls">
|
<div className="join-scls">
|
||||||
{[...EMAIL, ...SOCIALS].map((n) => (
|
{[...EMAIL, ...SOCIALS].map((n) => (
|
||||||
<SocialCard
|
<SocialCard
|
||||||
|
key={n.url}
|
||||||
url={n.url}
|
url={n.url}
|
||||||
image={n.icon}
|
image={n.icon}
|
||||||
message={n.message}
|
message={n.message}
|
||||||
|
|
|
@ -9,14 +9,12 @@ import SocialCard from "./components/SocialCard";
|
||||||
import Carousel from "./components/Carousel";
|
import Carousel from "./components/Carousel";
|
||||||
import Footer from "./components/Footer";
|
import Footer from "./components/Footer";
|
||||||
|
|
||||||
interface MainProps {}
|
class Main extends React.Component {
|
||||||
interface MainState {}
|
constructor() {
|
||||||
|
super({});
|
||||||
class Main extends React.Component<MainProps, MainState> {
|
|
||||||
constructor(props: MainProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
this.state = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -84,6 +82,7 @@ class Main extends React.Component<MainProps, MainState> {
|
||||||
<div className="join-scls">
|
<div className="join-scls">
|
||||||
{[...EMAIL, ...SOCIALS].map((n) => (
|
{[...EMAIL, ...SOCIALS].map((n) => (
|
||||||
<SocialCard
|
<SocialCard
|
||||||
|
key={n.url}
|
||||||
url={n.url}
|
url={n.url}
|
||||||
image={n.icon}
|
image={n.icon}
|
||||||
message={n.message}
|
message={n.message}
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
import * as ReactDom from "react-dom";
|
import * as ReactDom from "react-dom";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import TopBar from "./components/TopBar";
|
import TopBar from "./components/TopBar";
|
||||||
import { ACTIVE_PAGES, SOCIALS, EMAIL, OFFICERS } from "./Config";
|
import { ACTIVE_PAGES, SOCIALS, EMAIL } from "./Config";
|
||||||
import Splash from "./components/Splash";
|
import Splash from "./components/Splash";
|
||||||
import DefaultSection from "./components/DefaultSection";
|
import DefaultSection from "./components/DefaultSection";
|
||||||
import InvolveBox from "./components/InvolveBox";
|
import InvolveBox from "./components/InvolveBox";
|
||||||
import SocialCard from "./components/SocialCard";
|
import SocialCard from "./components/SocialCard";
|
||||||
import Carousel from "./components/Carousel";
|
|
||||||
import Footer from "./components/Footer";
|
import Footer from "./components/Footer";
|
||||||
|
|
||||||
interface MainProps {}
|
class Main extends React.Component {
|
||||||
interface MainState {}
|
constructor() {
|
||||||
|
super({});
|
||||||
class Main extends React.Component<MainProps, MainState> {
|
|
||||||
constructor(props: MainProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
this.state = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -78,6 +75,7 @@ class Main extends React.Component<MainProps, MainState> {
|
||||||
<div className="join-scls">
|
<div className="join-scls">
|
||||||
{[...EMAIL, ...SOCIALS].map((n) => (
|
{[...EMAIL, ...SOCIALS].map((n) => (
|
||||||
<SocialCard
|
<SocialCard
|
||||||
|
key={n.url} // Hacky fix: use url as key
|
||||||
url={n.url}
|
url={n.url}
|
||||||
image={n.icon}
|
image={n.icon}
|
||||||
message={n.message}
|
message={n.message}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Schema, Document, LeanDocument } from "mongoose";
|
import { Schema } from "mongoose";
|
||||||
import * as mongoose from "mongoose";
|
import * as mongoose from "mongoose";
|
||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
import { randomBytes, pbkdf2 } from "crypto";
|
import { randomBytes, pbkdf2 } from "crypto";
|
||||||
|
@ -25,19 +25,23 @@ const User = mongoose.model(
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface DBUser extends Document {
|
interface HashSalt {
|
||||||
|
hash: Buffer;
|
||||||
|
salt: Buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DBUser {
|
||||||
display: string;
|
display: string;
|
||||||
email: string;
|
email: {
|
||||||
|
public: boolean;
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
picture: string;
|
picture: string;
|
||||||
password: HashSalt;
|
password: HashSalt;
|
||||||
isOfficer: boolean;
|
isOfficer: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
uuid: string;
|
uuid: string;
|
||||||
}
|
}
|
||||||
interface HashSalt {
|
|
||||||
hash: Buffer;
|
|
||||||
salt: Buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class UserDatabase {
|
export default class UserDatabase {
|
||||||
// How many iterations pbkdf2 will iterate the password hashing algorithm
|
// How many iterations pbkdf2 will iterate the password hashing algorithm
|
||||||
|
@ -52,8 +56,14 @@ export default class UserDatabase {
|
||||||
* @param url the mongodb database url to connect to
|
* @param url the mongodb database url to connect to
|
||||||
*/
|
*/
|
||||||
constructor(url: string) {
|
constructor(url: string) {
|
||||||
mongoose.connect(url);
|
mongoose
|
||||||
mongoose.set("returnOriginal", false);
|
.connect(url)
|
||||||
|
.then(() => {
|
||||||
|
mongoose.set("returnOriginal", false);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions to edit current users
|
// Functions to edit current users
|
||||||
|
@ -71,8 +81,8 @@ export default class UserDatabase {
|
||||||
* @returns the user's display name
|
* @returns the user's display name
|
||||||
*/
|
*/
|
||||||
public async getName(uuid: string): Promise<string> {
|
public async getName(uuid: string): Promise<string> {
|
||||||
return ((await User.findOne({ uuid: uuid }).lean().exec()) as DBUser)
|
const user = await User.findOne({ uuid: uuid }).lean().exec();
|
||||||
.display;
|
return user.display;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,8 +99,8 @@ export default class UserDatabase {
|
||||||
* @returns the user's email
|
* @returns the user's email
|
||||||
*/
|
*/
|
||||||
public async getEmail(uuid: string): Promise<string> {
|
public async getEmail(uuid: string): Promise<string> {
|
||||||
return ((await User.findOne({ uuid: uuid }).lean().exec()) as DBUser)
|
const user = await User.findOne({ uuid: uuid }).lean().exec();
|
||||||
.email;
|
return user.email.text;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -99,7 +109,7 @@ export default class UserDatabase {
|
||||||
* @param newPassword the user's new password
|
* @param newPassword the user's new password
|
||||||
*/
|
*/
|
||||||
public async setPassword(uuid: string, newPassword: string) {
|
public async setPassword(uuid: string, newPassword: string) {
|
||||||
let hashsalt = await UserDatabase.hash(newPassword);
|
const hashsalt = await UserDatabase.hash(newPassword);
|
||||||
await User.findOneAndUpdate(
|
await User.findOneAndUpdate(
|
||||||
{ uuid: uuid },
|
{ uuid: uuid },
|
||||||
{ password: hashsalt }
|
{ password: hashsalt }
|
||||||
|
@ -111,8 +121,8 @@ export default class UserDatabase {
|
||||||
* @returns the user's password
|
* @returns the user's password
|
||||||
*/
|
*/
|
||||||
public async getPasswordHash(uuid: string): Promise<Buffer> {
|
public async getPasswordHash(uuid: string): Promise<Buffer> {
|
||||||
return ((await User.findOne({ uuid: uuid }).lean().exec()) as DBUser)
|
const user = await User.findOne({ uuid: uuid }).lean().exec();
|
||||||
.password.hash;
|
return user.password.hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,8 +142,8 @@ export default class UserDatabase {
|
||||||
* @returns the user's title
|
* @returns the user's title
|
||||||
*/
|
*/
|
||||||
public async getRank(uuid: string): Promise<string> {
|
public async getRank(uuid: string): Promise<string> {
|
||||||
return ((await User.findOne({ uuid: uuid }).lean().exec()) as DBUser)
|
const user = await User.findOne({ uuid: uuid }).lean().exec();
|
||||||
.title;
|
return user.title;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Gets whether of a user with a given UUID is an officer
|
* Gets whether of a user with a given UUID is an officer
|
||||||
|
@ -141,8 +151,8 @@ export default class UserDatabase {
|
||||||
* @returns true if the user is an officer
|
* @returns true if the user is an officer
|
||||||
*/
|
*/
|
||||||
public async isOfficer(uuid: string): Promise<boolean> {
|
public async isOfficer(uuid: string): Promise<boolean> {
|
||||||
return ((await User.findOne({ uuid: uuid }).lean().exec()) as DBUser)
|
const user = await User.findOne({ uuid: uuid }).lean().exec();
|
||||||
.isOfficer;
|
return user.isOfficer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -159,12 +169,13 @@ export default class UserDatabase {
|
||||||
* @returns the user's profile picture
|
* @returns the user's profile picture
|
||||||
*/
|
*/
|
||||||
public async getPicture(uuid: string): Promise<string> {
|
public async getPicture(uuid: string): Promise<string> {
|
||||||
return ((await User.findOne({ uuid: uuid }).lean().exec()) as DBUser)
|
const user = await User.findOne({ uuid: uuid }).lean().exec();
|
||||||
.picture;
|
return user.picture;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getUser(uuid: string): Promise<DBUser> {
|
public async getUser(uuid: string): Promise<DBUser> {
|
||||||
return User.findOne({ uuid: uuid }).exec() as Promise<DBUser>;
|
const user = await User.findOne({ uuid: uuid }).lean().exec();
|
||||||
|
return user as DBUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
// User creation and deletion functions
|
// User creation and deletion functions
|
||||||
|
@ -191,7 +202,7 @@ export default class UserDatabase {
|
||||||
title?: string,
|
title?: string,
|
||||||
isOfficer?: boolean
|
isOfficer?: boolean
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
let encrypted = await UserDatabase.hash(password);
|
const encrypted = await UserDatabase.hash(password);
|
||||||
await new User({
|
await new User({
|
||||||
display: display,
|
display: display,
|
||||||
email: email,
|
email: email,
|
||||||
|
@ -210,8 +221,8 @@ export default class UserDatabase {
|
||||||
* @returns the hashed password
|
* @returns the hashed password
|
||||||
*/
|
*/
|
||||||
public static async hash(password: string): Promise<HashSalt> {
|
public static async hash(password: string): Promise<HashSalt> {
|
||||||
let salt = randomBytes(UserDatabase.SALT_LENGTH);
|
const salt = randomBytes(UserDatabase.SALT_LENGTH);
|
||||||
let pass = Buffer.from(String.prototype.normalize(password));
|
const pass = Buffer.from(String.prototype.normalize(password));
|
||||||
return {
|
return {
|
||||||
hash: await pbkdf(
|
hash: await pbkdf(
|
||||||
pass,
|
pass,
|
|
@ -1,8 +1,7 @@
|
||||||
import express from "express";
|
import express, { NextFunction } from "express";
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import UserDatabase from "./Database";
|
|
||||||
|
|
||||||
interface Website {
|
interface Website {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
|
@ -66,7 +65,7 @@ const PORT = 9000;
|
||||||
// Make the public directory traversible to people online
|
// Make the public directory traversible to people online
|
||||||
APP.use(express.static(path.join(__dirname, "public")));
|
APP.use(express.static(path.join(__dirname, "public")));
|
||||||
// Put the cookies as a variable in the request
|
// Put the cookies as a variable in the request
|
||||||
APP.use((req: Request, res: Response, next: any) => {
|
APP.use((req: Request, res: Response, next: NextFunction) => {
|
||||||
req.cookies = req.headers.cookie;
|
req.cookies = req.headers.cookie;
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
@ -78,15 +77,19 @@ APP.use(express.urlencoded({ extended: true }));
|
||||||
APP.get("/", (req: Request, res: Response) => {
|
APP.get("/", (req: Request, res: Response) => {
|
||||||
respond(res, "index");
|
respond(res, "index");
|
||||||
});
|
});
|
||||||
|
|
||||||
APP.get("/events", (req: Request, res: Response) => {
|
APP.get("/events", (req: Request, res: Response) => {
|
||||||
respond(res, "events");
|
respond(res, "events");
|
||||||
});
|
});
|
||||||
|
|
||||||
APP.get("/projects", (req: Request, res: Response) => {
|
APP.get("/projects", (req: Request, res: Response) => {
|
||||||
respond(res, "projects");
|
respond(res, "projects");
|
||||||
});
|
});
|
||||||
|
|
||||||
APP.get("/committees", (req: Request, res: Response) => {
|
APP.get("/committees", (req: Request, res: Response) => {
|
||||||
respond(res, "committees");
|
respond(res, "committees");
|
||||||
});
|
});
|
||||||
|
|
||||||
APP.get("/contact", (req: Request, res: Response) => {
|
APP.get("/contact", (req: Request, res: Response) => {
|
||||||
respond(res, "contact");
|
respond(res, "contact");
|
||||||
});
|
});
|
||||||
|
@ -94,7 +97,7 @@ APP.get("/contact", (req: Request, res: Response) => {
|
||||||
/**
|
/**
|
||||||
* Utility functions for above methods
|
* Utility functions for above methods
|
||||||
*/
|
*/
|
||||||
function respond(res: any, filename: string) {
|
function respond(res: Response, filename: string) {
|
||||||
res.set({
|
res.set({
|
||||||
"Content-Type": "text/html",
|
"Content-Type": "text/html",
|
||||||
});
|
});
|
||||||
|
@ -102,19 +105,21 @@ function respond(res: any, filename: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePage(name: string): string {
|
function generatePage(name: string): string {
|
||||||
let site = WEBSITES.find((e) => e.sitename === name);
|
const site = WEBSITES.find((e) => e.sitename === name);
|
||||||
let html = TEMPLATE;
|
let html = TEMPLATE;
|
||||||
for (let key of Object.keys(site)) {
|
let key: string;
|
||||||
|
for (key of Object.keys(site)) {
|
||||||
html = html.replace(new RegExp("\\$" + key.toUpperCase()), site[key]);
|
html = html.replace(new RegExp("\\$" + key.toUpperCase()), site[key]);
|
||||||
}
|
}
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateFilePages() {
|
function generateFilePages() {
|
||||||
for (let site of WEBSITES) {
|
let site: Website;
|
||||||
let html = generatePage(site.sitename);
|
for (site of WEBSITES) {
|
||||||
|
const html = generatePage(site.sitename);
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
require("path").join(__dirname, "public/", `${site.sitename}.html`),
|
path.join(__dirname, "public/", `${site.sitename}.html`),
|
||||||
html
|
html
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -1,12 +1,11 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./build/",
|
"outDir": "./build/",
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": false,
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"target": "es6",
|
"target": "es6",
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"esModuleInterop": true
|
"esModuleInterop": true
|
||||||
},
|
},
|
||||||
"include": ["./src"],
|
"include": ["./src/util"]
|
||||||
"exclude": ["./src/public"]
|
|
||||||
}
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const CopyPlugin = require("copy-webpack-plugin");
|
const CopyPlugin = require("copy-webpack-plugin");
|
||||||
const LicensePlugin = require("license-webpack-plugin").LicenseWebpackPlugin;
|
|
||||||
const TerserPlugin = require("terser-webpack-plugin");
|
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -10,6 +8,7 @@ module.exports = {
|
||||||
path: path.resolve(__dirname, "build/public"),
|
path: path.resolve(__dirname, "build/public"),
|
||||||
filename: "./js/[name].js",
|
filename: "./js/[name].js",
|
||||||
},
|
},
|
||||||
|
mode: "production",
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
|
@ -29,44 +28,24 @@ module.exports = {
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: [".tsx", ".ts", ".js"],
|
extensions: [".tsx", ".ts", ".js"],
|
||||||
},
|
},
|
||||||
mode: "production",
|
|
||||||
plugins: [
|
plugins: [
|
||||||
new CopyPlugin({
|
new CopyPlugin({
|
||||||
patterns: [
|
patterns: [
|
||||||
{
|
{
|
||||||
from: "./src/public",
|
from: "./src/util",
|
||||||
to: ".",
|
|
||||||
globOptions: {
|
|
||||||
ignore: ["**/*.tsx", "**/*.ts"],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
new LicensePlugin({
|
|
||||||
outputFilename: "third-party-notice.txt",
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
optimization: {
|
|
||||||
minimize: true,
|
|
||||||
minimizer: [
|
|
||||||
new TerserPlugin({
|
|
||||||
terserOptions: {
|
|
||||||
output: {
|
|
||||||
comments: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
extractComments: false,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
//devtool: "source-map"
|
|
||||||
devServer: {
|
|
||||||
static: "./build/public",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all entries from a directory
|
||||||
|
* @param {*} dir Directory to load entries from
|
||||||
|
* @returns Object with entries
|
||||||
|
*/
|
||||||
function loadEntries(dir) {
|
function loadEntries(dir) {
|
||||||
let files = fs.readdirSync(path.join(__dirname, dir));
|
const files = fs.readdirSync(path.join(__dirname, dir));
|
||||||
let entries = {};
|
let entries = {};
|
||||||
files.forEach((file) => {
|
files.forEach((file) => {
|
||||||
let name = file.match(/^(.*)\.tsx$/);
|
let name = file.match(/^(.*)\.tsx$/);
|
||||||
|
|
Loading…
Reference in a new issue