├── api
├── .gitignore
├── config
│ └── default.json
├── Dockerfile
├── package.json
├── index.js
└── package-lock.json
├── web
├── .dockerignore
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── nginx.conf
├── src
│ ├── App.test.js
│ ├── App.css
│ ├── index.css
│ ├── index.js
│ ├── App.js
│ ├── serviceWorker.js
│ └── logo.svg
├── Dockerfile
├── .gitignore
├── package.json
└── README.md
├── .gitignore
├── start.sh
├── .env.sample
├── README.md
├── Caddyfile
├── docker-compose.yml
└── dashboard.json
/api/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/web/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/web/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | /influxdb-data
3 | /grafana-data
4 | /.caddy
5 | /log
6 | .idea
7 | *.iml
8 |
--------------------------------------------------------------------------------
/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | mkdir -p influxdb-data grafana-data .caddy log
6 | docker-compose up -d
--------------------------------------------------------------------------------
/web/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thecasualcoder/hacktoberfest-monitor/HEAD/web/public/favicon.ico
--------------------------------------------------------------------------------
/web/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thecasualcoder/hacktoberfest-monitor/HEAD/web/public/logo192.png
--------------------------------------------------------------------------------
/web/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thecasualcoder/hacktoberfest-monitor/HEAD/web/public/logo512.png
--------------------------------------------------------------------------------
/api/config/default.json:
--------------------------------------------------------------------------------
1 | {
2 | "app": {
3 | "port": 3000
4 | },
5 | "hosts":["github.com","gitlab.com"]
6 | }
7 |
--------------------------------------------------------------------------------
/web/nginx.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 |
4 | location / {
5 | root /opt/app/;
6 | index index.html index.htm;
7 | }
8 | }
--------------------------------------------------------------------------------
/.env.sample:
--------------------------------------------------------------------------------
1 | INFLUXDB_USER_PASSWORD=##put-a-secure-password##
2 | GF_SECURITY_ADMIN_PASSWORD=##put-a-secure-password##
3 | CADDY_EMAIL=email@domain.com
4 | GF_SERVER_ROOT_URL=##root-url-of-grafana##
--------------------------------------------------------------------------------
/api/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:10.16.3
2 |
3 | ENV NODE_ENV production
4 | ENV APP_DIR /opt/app
5 | WORKDIR ${APP_DIR}
6 |
7 | ADD package.json package-lock.json ./
8 | RUN npm install --production
9 | ADD . ./
10 |
11 | EXPOSE 3000
12 |
13 | CMD ["npm", "start"]
14 |
--------------------------------------------------------------------------------
/web/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/web/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:10.16.3 as source
2 | WORKDIR /opt/app
3 | COPY ./package.json ./
4 | RUN npm install --production
5 | COPY . ./
6 | RUN npm run build
7 |
8 | FROM nginx:1.17.4
9 | WORKDIR /opt/app
10 | COPY --from=source /opt/app/build .
11 | COPY nginx.conf /etc/nginx/conf.d/default.conf
12 | CMD ["nginx", "-g", "daemon off;"]
13 |
--------------------------------------------------------------------------------
/web/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | }
8 |
9 | .App-header {
10 | background-color: #282c34;
11 | min-height: 100vh;
12 | display: flex;
13 | flex-direction: column;
14 | align-items: center;
15 | justify-content: center;
16 | font-size: calc(10px + 2vmin);
17 | color: white;
18 | }
19 |
20 | .App-link {
21 | color: #09d3ac;
22 | }
23 |
--------------------------------------------------------------------------------
/web/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/web/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Hacktoberfest Monitor
2 |
3 | A dashboard for tracking hactoberfest PR contributions.
4 |
5 | ## Setup
6 |
7 | 1. Setup grafana and influxDB
8 |
9 | ```bash
10 | ## Setup necessary passwords in .env file
11 | $ cp .env.sample .env
12 |
13 | ## Startup docker-compose
14 | $ docker-compose up -d
15 | ```
16 |
17 | 2. Add influx as Grafana datasource. Visit http://localhost:3000 and add datasource with InfluxDB URL http://influxdb:8086
--------------------------------------------------------------------------------
/Caddyfile:
--------------------------------------------------------------------------------
1 | contributions.hacktoberfest.thecasualcoder.in
2 | tls {$CADDY_EMAIL}
3 |
4 | ext .html # Clean URLs
5 | errors error.log { # Error log
6 | 404 error-404.html # Custom error page
7 | }
8 |
9 | proxy /leaderboard http://grafana:3000 {
10 | fail_timeout 5s
11 | transparent
12 | without /leaderboard
13 | }
14 |
15 | proxy /api http://api:3000 {
16 | fail_timeout 5s
17 | transparent
18 | }
19 |
20 | proxy / http://web {
21 | fail_timeout 5s
22 | transparent
23 | }
24 |
--------------------------------------------------------------------------------
/api/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "api",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "axios": "^0.21.1",
14 | "body-parser": "^1.19.0",
15 | "config": "^3.3.2",
16 | "express": "^4.17.1",
17 | "express-rate-limit": "^5.0.0",
18 | "influx": "^5.6.3"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/web/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "axios": "^0.21.1",
7 | "react": "^16.10.2",
8 | "react-alert": "^5.5.0",
9 | "react-alert-template-basic": "^1.0.0",
10 | "react-dom": "^16.10.2",
11 | "react-jsonschema-form": "^1.8.0",
12 | "react-scripts": "3.2.0",
13 | "react-transition-group": "^4.3.0"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test",
19 | "eject": "react-scripts eject"
20 | },
21 | "eslintConfig": {
22 | "extends": "react-app"
23 | },
24 | "browserslist": {
25 | "production": [
26 | ">0.2%",
27 | "not dead",
28 | "not op_mini all"
29 | ],
30 | "development": [
31 | "last 1 chrome version",
32 | "last 1 firefox version",
33 | "last 1 safari version"
34 | ]
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/web/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import {transitions, positions, Provider as AlertProvider} from 'react-alert';
5 | import AlertTemplate from 'react-alert-template-basic';
6 | import App from './App';
7 | import * as serviceWorker from './serviceWorker';
8 |
9 | const alertOptions = {
10 | position: positions.TOP_CENTER,
11 | timeout: 5000,
12 | offset: '30px',
13 | transition: transitions.SCALE,
14 | width: '600px',
15 | };
16 |
17 | const Root = () => (
18 |
19 |
20 |
21 | );
22 |
23 | ReactDOM.render(Root(), document.getElementById('root'));
24 |
25 | // If you want your app to work offline and load faster, you can change
26 | // unregister() to register() below. Note this comes with some pitfalls.
27 | // Learn more about service workers: https://bit.ly/CRA-PWA
28 | serviceWorker.unregister();
29 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.7"
2 | services:
3 | caddy:
4 | image: "abiosoft/caddy"
5 | volumes:
6 | - ./log:/srv
7 | - ./Caddyfile:/etc/Caddyfile
8 | - ./.caddy:/root/.caddy
9 | ports:
10 | - "80:80"
11 | - "443:443"
12 | environment:
13 | ACME_AGREE: "true"
14 | CADDY_EMAIL: "${CADDY_EMAIL}"
15 | influxdb:
16 | image: influxdb
17 | volumes:
18 | - "./influxdb-data:/var/lib/influxdb"
19 | environment:
20 | INFLUXDB_USER: "hacktober_collector"
21 | INFLUXDB_DB: "hacktober_metrics"
22 | INFLUXDB_USER_PASSWORD: "${INFLUXDB_USER_PASSWORD}"
23 | grafana:
24 | image: grafana/grafana
25 | environment:
26 | GF_SECURITY_ADMIN_PASSWORD: "${GF_SECURITY_ADMIN_PASSWORD}"
27 | GF_SERVER_ROOT_URL: "${GF_SERVER_ROOT_URL}"
28 | GF_AUTH_ANONYMOUS_ENABLED: "true"
29 | ports:
30 | - "3000:3000"
31 | volumes:
32 | - "./grafana-data:/var/lib/grafana"
33 | web:
34 | build:
35 | context: web/
36 | dockerfile: Dockerfile
37 | api:
38 | build:
39 | context: api/
40 | dockerfile: Dockerfile
41 |
--------------------------------------------------------------------------------
/web/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useAlert} from 'react-alert';
3 | import Form from "react-jsonschema-form";
4 | import axios from 'axios';
5 | import './App.css';
6 |
7 | const languages = [
8 | "GoLang", "Java", "Python", "Kotlin", "Swift", "Objective C", "C#", "JavaScript", "Ruby", "Shell Script", "TypeScript", "CSS", "C++", "Rust", "Other"
9 | ].sort();
10 |
11 | const schema = {
12 | title: "Hacktoberfest - ThoughtWorks",
13 | type: "object",
14 | required: ["pr_link", "language"],
15 | properties: {
16 | pr_link: {type: "string", title: "Pull Request Link"},
17 | language: {type: "string", title: "Language", "enum": languages}
18 | }
19 | };
20 |
21 | const log = (type) => console.log.bind(console, type);
22 |
23 | function App() {
24 | const alert = useAlert();
25 | return (
26 |