Commit 03a2daa9 authored by Sébastien Feugère's avatar Sébastien Feugère

Merge branch '84-react-refactoring' into 'master'

Resolve "Mojolicious Refactoring"

Closes #84

See merge request !1
parents 56615cbd 7af6c4b7
bower_components
node_modules
_archives
client/node_modules
server/data/*.db*
server/data/backups
server/local
image: node:7.10.0
before_script:
- npm install -g bower@1.8.0
- npm install
- bower install --allow-root
app_tests:
script:
- make tests
-pbp # Start with Perl Best Practices
-w # Show all warnings
-bom # Breaks before method call arrow -> are preserved
-l=80 # 80 characters per line
-mbl=2 # No more than 2 blank lines
-i=2 # Indentation is 2 columns
-ci=2 # Continuation indentation is 2 columns
-vt=0 # Less vertical tightness
-pt=1 # Always spaces in parentheses except if there is only one argument
-bt=0 # Low brace tightness
-sbt=0 # High square bracket tightness
-wn # Weld nested containers
-isbc # Don't indent comments without leading space
-nst # Don't output to STDOUT
-cab=2
[![pipeline status](https://gitlab.com/sfeug/hi.balik.network/badges/master/pipeline.svg)](https://gitlab.com/sfeug/hi.balik.network/commits/master) [![coverage report](https://gitlab.com/sfeug/hi.balik.network/badges/master/coverage.svg)](https://gitlab.com/sfeug/hi.balik.network/commits/master)
# Description
This is a personal
branding [website homepage](https://hi.balik.network).
This is a personal branding [website homepage](https://hi.balik.network). It's aim is too keep it simple.
I use it as a convenient way to give informations about my different
projects and as a playground for various technologies
as [Node.js](http://gulpjs.com/),
[Express](https://expressjs.com), [Gulp](http://gulpjs.com/) and all
the rich modern
*[MEAN](https://en.wikipedia.org/wiki/MEAN_(software_bundle)) JS ecosytem*.
I use it as a convenient way to give informations about different projects. It use a minimal dependencies stack, especially on the frontend
with mostly [jQuery](https://jquery.com/) and [KNACSS](https://www.knacss.com/): I choosed to limit myself to those since this project is so simple, I didn't want to download 30 000 dependencies by installing React and up dealing with dependency hell when upgrading my CV every year.
The backend is made with Mojolicious. Simple to install and deploy, full toolkit including a powerful template engine. I extended it with an I18N plugin that makes very easy to deal with the different languages.
Historically I used this projet to experiment and train myself on the *[MEAN](https://en.wikipedia.org/wiki/MEAN_(software_bundle)) JavaScript ecosytem* but this project don''t reflect this anymore. If you look at the pre-2019 git history, you can see how it was before ([Node.js](http://nodejs.org/), [Express](https://expressjs.comExpress.js), Bootstrap, etc.)
## Installation
You can fork it
on [Gitlab](https://gitlab.com/sfeug/balik.shurf.pl).
on [Gitlab](https://gitlab.com/smonff/balik.shurf.pl).
A minimum installation on Arch Linux would require:
A minimum installation on Some Linux would require:
$ pacman -S node npm gulp bower browserify
$ sudo npm install -g watchify
$ cd hi.balik.network
$ make dependencies-install
$ make tests
On a dev environment, you should do:
$ npm install -g supervisor
$ make supervisor
While working on SASS stylesheets or on browser-side javascripts
bundles, it can be useful to run these watchers:
$ make sass:watch
$ make watchify
$ apt install libmojolicious-perl carton
$ carton install
`sass:watch` is a Gulp task that takes care of compiling SCSS to plain
CSS, `watchify` is a watcher for [Browserify](http://browserify.org/).
All CSS and JavaScript dependencies are pre-packaged, no need to fetch them from NPM. Will maybe change in the future.
Deployment strategy with SystemD:
......
/* Copyright (C) 2017 Sébastien Feugère
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* <http://www.gnu.org/licenses/>.
*
* This program is a very simple web application that allows to create
* a homepage for various activities.
*
*/
'use strict';
let express = require('express'),
path = require('path'),
favicon = require('serve-favicon'),
logger = require('morgan'),
cookieParser = require('cookie-parser'),
bodyParser = require('body-parser'),
i18n = require('i18n'),
session = require('cookie-session'),
inspect = require('eyes').inspector({ maxLength: 10000 });
let app = express();
// Local modules
let chevronsMenu = require('./modules/chevrons-menu');
// Dynamic routes
// You need to stick having one route per file in
// target directory
const ROUTES = require('require-all')(__dirname + '/routes');
// Configuration
const CONFIG = require(__dirname + '/config/constants');
i18n.configure(CONFIG.I18N);
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(cookieParser(CONFIG.COOKIE_PARSER));
// CSS, client JS, fonts and images
app.use(express.static(path.join(__dirname, 'public')));
// For Certbot
app.use(express.static(path.join(__dirname, 'public/letsencrypt')));
app.use(session(CONFIG.SESSION));
app.use(i18n.init);
// Bower
app.use('/components', express.static(path.join(__dirname, 'bower_components')));
// Side navigation menu
app.use(chevronsMenu);
/**
* ALL MIDDLEWARES MUST BE INITIALIZED BEFORE ^
*/
// Helper that 'require' and 'use' the routes
// Save a lot of repetitive typing and makes possible to use our
// navigation 'chevrons-menu'. Could be "middlewared".
for (let route in ROUTES) {
if (route === 'index') {
app.use('/', ROUTES[route]);
} else {
app.use('/' + route, ROUTES[route]);
}
}
// Middleware for error handler
app.use(function(err, req, res, next) {
console.error(err.stack);
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
// Middleware for 404 handle
app.use(function (req, res, next) {
res.status(404);
res.render('404', {
url: req.url.replace('/', ''),
menu: {
fourzerofour: true
},
chevrons: false
});
return;
});
module.exports = app;
#!/usr/bin/env node
/**
* Module dependencies.
*/
'use strict';
let app = require('../app');
let debug = require('debug')('myapp:server');
let http = require('http');
const CONFIG = require('../config/constants');
/**
* Get port from environment and store in Express.
*/
const PORT = normalizePort(process.env.PORT || CONFIG.PORT);
app.set('port', PORT);
/**
* Create HTTP server.
*/
let server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(PORT);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
let port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
let bind = typeof port === 'string' ?
'Pipe ' + port :
'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
let addr = server.address();
let bind = typeof addr === 'string' ?
'pipe ' + addr :
'port ' + addr.port;
debug('Listening on ' + bind);
}
{
"name": "balik.shurf.pl",
"dependencies": {
"bootstrap": "^3.3.7",
"font-awesome": "^4.7.0",
"jquery": "^3.2.1"
}
}
REACT_APP_PROTOCOL=http
REACT_APP_HOST=padi
REACT_APP_PORT=3456
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
{
"localeDir": "src/locales/",
"srcPathDirs": ["src/"],
"format": "po",
"sourceLocale": "en"
}
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.<br>
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br>
You will also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.<br>
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.<br>
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br>
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
### Analyzing the Bundle Size
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
### Making a Progressive Web App
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
### Advanced Configuration
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
### Deployment
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
### `npm run build` fails to minify
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
{
"name": "hi.balik.network",
"version": "2.05",
"private": true,
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.17",
"@fortawesome/free-brands-svg-icons": "^5.8.1",
"@fortawesome/free-regular-svg-icons": "^5.8.1",
"@fortawesome/free-solid-svg-icons": "^5.8.1",
"@fortawesome/react-fontawesome": "^0.1.4",
"@lingui/react": "^2.7.4",
"axios": "^0.18.0",
"bootstrap": "^4.3.1",
"moment": "^2.24.0",
"node-sass": "^4.11.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-popper": "^1.3.3",
"react-router-dom": "^5.0.0",
"react-scripts": "2.1.8",
"react-transition-group": "^2.9.0",
"reactstrap": "^8.0.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"pug-convert": "pug-musings --in ../views/layout.pug --out ./src/components/layout.jsx",
"add-locale": "lingui add-locale",
"extract": "lingui extract",
"compile": "lingui compile"
},
"devDependencies": {
"@babel/core": "^7.4.3",
"@lingui/cli": "^2.7.4",
"@lingui/macro": "^2.7.4",
"babel-core": "^7.0.0-bridge.0",
"create-react-app": "^2.1.8"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>balık</title>
</head>
<body class="text-center">
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root" class="site-wrapper"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from "react-router-dom";
import './style/sass/style.scss';
import Header from './components/Header';
// import Chevrons from './components/Chevrons';
import Home from './components/Home';
import Projects from './components/Projects';
import CV from './components/CV';
import Contact from './components/Contact';
import Footer from './components/Footer';
class App extends Component {
render() {
return (
<Router>
<div className='cover-container d-flex w-100 h-100 p-3 mx-auto flex-column'>
<Header />
{/* <Chevrons /> */}
<main role='main' className='inner cover' id='content'>
<Route path="/" exact component={Home} />
<Route path="/projects/" component={Projects} />
<Route path="/cv/" component={CV} />
<Route path="/contact/" component={Contact} />
</main>
<Footer />
</div>
</Router>
);
}
}
export default App;
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});
const buildApiUrl = (page, lang) => {
const api_conf = `${process.env.REACT_APP_PROTOCOL}://` +
`${process.env.REACT_APP_HOST}` +
getPort()
+
`/i18n/${page}/${lang}`;
return api_conf;
};
const getPort = () => {
return process.env.REACT_APP_PORT !== '' ?
`:${process.env.REACT_APP_PORT}` : '';
};
export { buildApiUrl }
import React, { Component } from 'react';
//import { Trans } from '@lingui/react';
class CV extends Component {
render() {
return (
<div className='inner cover'>
<div className='row'>
<h1 className='col-sm-12 cover-heading'>Career</h1>
{/* Here, iterate on data available from the API*/}
{/* {this.getCVData(data)} */}
Pellentesque dapibus suscipit ligula. Donec posuere augue in quam. Etiam vel tortor sodales tellus ultricies commodo. Suspendisse potenti. Aenean in sem ac leo mollis blandit. Donec neque quam, dignissim in, mollis nec, sagittis eu, wisi. Phasellus lacus. Etiam laoreet quam sed arcu. Phasellus at dui in ligula mollis ultricies. Integer placerat tristique nisl. Praesent augue. Fusce commodo. Vestibulum convallis, lorem a tempus semper, dui dui euismod elit, vitae placerat urna tortor vitae lacus. Nullam libero mauris, consequat quis, varius et, dictum id, arcu. Mauris mollis tincidunt felis. Aliquam feugiat tellus ut neque. Nulla facilisis, risus a rhoncus fermentum, tellus tellus lacinia purus, et dictum nunc justo sit amet elit.
</div>
<div className='row'>
<h1 className='col-sm-12 cover-heading'>Training and education</h1>
{/* Here, iterate on data available from the API*/}
{/* {this.getCVData(data)} */}
Pellentesque dapibus suscipit ligula. Donec posuere augue in quam. Etiam vel tortor sodales tellus ultricies commodo. Suspendisse potenti. Aenean in sem ac leo mollis blandit. Donec neque quam, dignissim in, mollis nec, sagittis eu, wisi. Phasellus lacus. Etiam laoreet quam sed arcu. Phasellus at dui in ligula mollis ultricies. Integer placerat tristique nisl. Praesent augue. Fusce commodo. Vestibulum convallis, lorem a tempus semper, dui dui euismod elit, vitae placerat urna tortor vitae lacus. Nullam libero mauris, consequat quis, varius et, dictum id, arcu. Mauris mollis tincidunt felis. Aliquam feugiat tellus ut neque. Nulla facilisis, risus a rhoncus fermentum, tellus tellus lacinia purus, et dictum nunc justo sit amet elit.
</div>
</div>
);
}
}
export default CV;
import React, { Component } from 'react';
//import { Trans } from '@lingui/macro';
class Chevrons extends Component {
render() {
const chevron = {};
return (
<div id='chevrons'>
{/* each chevron in chevrons
if chevron.activated
if chevron.left
if chevron.right
*/}
<a
className='chevron-container'
href={chevron.link} >{chevron.label}
</a>
<i className='fa-chevron-left.fa.fa-4x.chevrons'></i>
<i className='i.fa-chevron-right.fa.fa-4x.chevrons'></i>
</div>
);
}
}
export default Chevrons;
import React, { Component } from 'react';
//import { Trans } from '@lingui/react';
class Contact extends Component {
render() {
return (
<div>Contact</div>
);
}
}
export default Contact;
import React, { Component } from 'react';