Skip to content

Commit

Permalink
solves #3
Browse files Browse the repository at this point in the history
  • Loading branch information
C4illin committed Mar 4, 2024
1 parent da2b2c0 commit a8e94c9
Show file tree
Hide file tree
Showing 10 changed files with 314 additions and 87 deletions.
3 changes: 2 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/db/
/node_modules/
/frontend/node_modules/
/backend/node_modules/
/vendor/
.env
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -228,4 +228,5 @@ $RECYCLE.BIN/
# .pnp.*

# End of https://www.toptal.com/developers/gitignore/api/node,macos,linux,windows,visualstudiocode,yarn,composer
/db/
/db/
docker-compose.yml
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ ENV NODE_ENV=production
WORKDIR /app/frontend

COPY frontend/package*.json ./
RUN npm install
RUN npm install --production

COPY frontend/ ./
RUN npm run production

WORKDIR /app/backend

COPY backend/package*.json ./
RUN npm install
RUN npm install --production

COPY backend/ ./

Expand Down
52 changes: 25 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,41 @@
# CHS MC Website

This is the CHS MC website that can be found here [mc.chs.se][1]
This is the CHS MC website that can be found here [mc.chs.se](https://mc.chs.se/).

## Requirement
The source code for the old CHS MC website can be found here on [github](https://github.com/gudchalmers/chs-mc-website/tree/aa622740b57cfc073a5d3f4b9321ecb184ad7804).

* Requires a database with a `mc_stats` table
* Webserver with php, [composer][2] and [nodejs][3]

## Setup
## Development
```shell script
cd frontend
npm install
npm run prod

Rename the `.env.example` to `.env` and modify it to the current environment.
cd ..
cd backend
npm install
npx nodemon index.js
```

The site is served out of the `public` folder.
For frontend development you probably want a better setup.

To setup the dev run:
## Deployment

```shell script
composer install
npm install
npm run dev
# or
npm run watch
```
Using docker-compose:

To setup the production site run:
```yml
version: '3'

```shell script
composer install --optimize-autoloader --no-dev
npm install --production
npm run prod
services:
chs-mc-website:
image: ghcr.io/gudchalmers/chs-mc-website:main
container_name: chs-mc-website
restart: unless-stopped
ports:
- "3000:3000"
```
Copy the latest version of the dynmap website files from the plugin folder to the `public/dynmap` folder.

## License

[MIT][4]

[1]: https://mc.chs.se/
[2]: https://getcomposer.org/
[3]: https://nodejs.org/
[4]: https://choosealicense.com/licenses/mit/
[MIT](https://choosealicense.com/licenses/mit/)
30 changes: 28 additions & 2 deletions backend/views/home.html → backend/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,33 @@ <h1 class="title">
<h2 class="title is-4">Getting started:</h2>
<p class="subtitle is-6">To join the server you need to register your account with your Chalmers student
email.</p>
<div class="tile notification is-info has-text-left">
<form id="register-form" action="/register" method="POST">
<div class="field">
<label class="label">Chalmers email</label>
<div class="control has-icons-left">
<input class="input" type="email" placeholder="[email protected]" required pattern=".*@chalmers.se"
name="email">
<span class="icon is-small is-left">
<i class="fas fa-envelope"></i>
</span>
</div>
</div>
<div class="field">
<label class="label">Minecraft username</label>
<div class="control has-icons-left">
<input class="input" type="text" placeholder="Your Minecraft username" required name="username">
<span class="icon is-small is-left">
<i class="fas fa-user"></i>
</span>
</div>
</div>
<div class="field">
<div class="control">
<input class="button is-primary" type="submit" value="Submit">
</div>
</div>
</form>
<!-- <div class="tile notification is-info has-text-left">
<ol>
<li>
<p>Login to <span class="has-text-warning">auth.mc.chs.se</span> in minecraft and follow the
Expand All @@ -70,7 +96,7 @@ <h2 class="title is-4">Getting started:</h2>
class="has-text-warning">mc.chs.se</span> and start playing.
</li>
</ol>
</div>
</div> -->
<hr>
<div>
<a target="_blank" href="https://gud.chs.chalmers.se">
Expand Down
137 changes: 126 additions & 11 deletions backend/index.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,138 @@
import express from 'express';
import express from "express";
import mc from "minecraftstatuspinger";
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import path from "path";
import { fileURLToPath } from "url";
import { dirname } from "path";
import crypto from "crypto";
import nodemailer from "nodemailer";
import mariadb from "mariadb";
import "dotenv/config";

const port = process.env.PORT || 3000;
const dbName = process.env.DB_NAME || "mc_stats";

const pool = mariadb.createPool({
host: "localhost",
user: process.env.DB_USER,
database: dbName,
password: process.env.DB_PASS,
connectionLimit: 5,
});

// seed db
const seed = async () => {
let conn;
try {
conn = await pool.getConnection();
await conn.query(`CREATE DATABASE IF NOT EXISTS ${dbName}`);
await conn.query(`USE ${dbName}`);
await conn.query(
`CREATE TABLE IF NOT EXISTS users (id INT AUTO_INCREMENT PRIMARY KEY, email VARCHAR(255), username VARCHAR(255), active BOOLEAN)`
);
await conn.query(
`CREATE TABLE IF NOT EXISTS confirmations (id INT AUTO_INCREMENT PRIMARY KEY, user_id INT, token VARCHAR(255))`
);
} catch (err) {
console.error(err);
} finally {
if (conn) conn.end();
}
};
seed();

const app = express();
const port = 3000;
const currentFilePath = fileURLToPath(import.meta.url);
const currentDirPath = dirname(currentFilePath);

// use public
app.use(express.static('../frontend/public'));
app.use(express.static("../frontend/public")); // use public
app.use(express.json()); // to support JSON-encoded bodies
app.use(express.urlencoded({ extended: true })); // to support URL-encoded bodies

app.get('/', (req, res) => {
const filePath = path.join(currentDirPath, 'views', 'home.html');
app.get("/", (req, res) => {
const filePath = path.join(currentDirPath, "home.html");
res.sendFile(filePath);
});

app.get('/ping', async (_, res) => {
const transporter = nodemailer.createTransport({
host: process.env.MAIL_HOST,
port: process.env.MAIL_PORT,
secure: true,
auth: {
user: process.env.MAIL_USER,
pass: process.env.MAIL_PASS,
},
});

app.post("/register", async (req, res) => {
let userEmail = req.body.email;
let username = req.body.username;

let token = crypto.randomBytes(32).toString("hex");

// Send email with the token
let mailOptions = {
from: `"${process.env.MAIL_NAME}>" <${process.env.MAIL_FROM}>`,
to: userEmail,
subject: "Registration Confirmation for mc.chs.se",
text:
"Please confirm your registration by clicking the following link: \nhttp://" +
req.headers.host +
"/confirm/" +
token +
"\n\n" +
"If you did not request this, please ignore this email.",
};

transporter.sendMail(mailOptions, function (err) {
if (err) {
console.error("There was an error: ", err);
} else {
console.log("Email sent");
}
});

let conn;
try {
conn = await pool.getConnection();
let sql = "INSERT INTO users (email, username, active) VALUES (?, ?, 0)";
await conn.query(sql, [userEmail, username]);
sql = "SELECT id FROM users WHERE email = ?";
const rows = await conn.query(sql, [userEmail]);
let userId = rows[0].id;
sql = "INSERT INTO confirmations (user_id, token) VALUES (?, ?)";
await conn.query(sql, [userId, token]);
} catch (err) {
console.error(err);
} finally {
if (conn) conn.end();
}
});

app.get("/confirm/:token", async (req, res) => {
let token = req.params.token;
let conn;
try {
conn = await pool.getConnection();
let sql = "SELECT user_id FROM confirmations WHERE token = ?";
const rows = await conn.query(sql, [token]);
if (rows.length) {
// If the token exists, delete it from the database and set the user to active
sql = "DELETE FROM confirmations WHERE token = ?";
await conn.query(sql, [token]);
sql = "UPDATE users SET active = 1 WHERE id = ?";
await conn.query(sql, [rows[0].user_id]);
res.send("Your account has been activated.");
} else {
res.send("Invalid token.");
}
} catch (err) {
console.error(err);
} finally {
if (conn) conn.end();
}
});

app.get("/ping", async (_, res) => {
try {
let result = await mc.lookup({ host: "mc.chs.se" });
res.send(result);
Expand All @@ -28,4 +143,4 @@ app.get('/ping', async (_, res) => {

app.listen(port, () => {
console.log(`App listening at http://localhost:${port}`);
});
});
Loading

0 comments on commit a8e94c9

Please sign in to comment.