Begin port to Node and TypeScript

parent b02cfb9a
VARNAME=value
# Created by https://www.gitignore.io/api/node
# Edit at https://www.gitignore.io/?templates=node
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# FuseBox cache
.fusebox/
# End of https://www.gitignore.io/api/node
{
"recommendations": [
"eg2.tslint",
"ms-azuretools.vscode-cosmosdb",
"streetsidesoftware.code-spell-checker"
]
}
{
// Use IntelliSense to learn about possible Node.js debug attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "attach",
"name": "Attach by Process ID",
"processId": "${command:PickProcess}",
"protocol": "inspector"
},
{
"type": "node",
"request": "launch",
"name": "Launch in Docker",
"preLaunchTask": "tsc-watch",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run",
"docker-debug"
],
"port": 9222,
"restart": true,
"timeout": 60000,
"localRoot": "${workspaceFolder}",
"remoteRoot": "/server",
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
],
"skipFiles": [
"<node_internals>/**/*.js"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}
// Place your settings in this file to overwrite default and user settings.
{
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/dist": true,
"**/coverge": true
},
"typescript.referencesCodeLens.enabled": true,
"tslint.ignoreDefinitionFiles": false,
"tslint.autoFixOnSave": true,
"tslint.exclude": "**/node_modules/**/*",
"cSpell.words": [
"csrf",
"definitelytyped",
"promisified"
]
}
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "build",
"group": {
"kind": "build",
"isDefault": true
}
},
{
"version": "0.1.0",
"tasks": [
{
"taskName": "tsc-watch",
"command": "npm",
"args": [
"run",
"watch"
],
"isShellCommand": true,
"isBackground": true,
"isBuildCommand": true,
"problemMatcher": "$tsc-watch",
"showOutput": "always"
}
]
}
FROM node:10-slim
WORKDIR /server
COPY . /server
RUN npm install
RUN npm run build
EXPOSE 3000
CMD [ "npm", "start" ]
This diff is collapsed.
......@@ -5,3 +5,14 @@ Map out points of interest at Mason, like water fountains, bathrooms, and study
Ideas/Planning: [https://go.gmu.edu/where-ideas](https://go.gmu.edu/where-ideas)
For updates, join #where on Slack.
# Running
First run `npm install` or `yarn`. Then run `npm run build` to build then project, and then `npm run start`. You should then be able to view the site [in your browser](http://localhost:3000/)
Alternatively, you can use Docker Compose by running `docker-compose up`. You can build and run the Docker container on it's own by executing the following
```
docker build -t where .
docker run -p 3000:3000 where
```
import * as shell from "shelljs";
shell.cp("-R", "src/public/js/lib", "dist/public/js/");
shell.cp("-R", "src/public/fonts", "dist/public/");
shell.cp("-R", "src/public/images", "dist/public/");
version: "2"
services:
web:
build: .
command: npm run debug
volumes:
- ./dist:/server/dist
ports:
- "3000:3000"
- "9222:9222"
module.exports = {
globals: {
'ts-jest': {
tsConfigFile: 'tsconfig.json'
}
},
moduleFileExtensions: [
'ts',
'js'
],
transform: {
'^.+\\.(ts|tsx)$': './node_modules/ts-jest/preprocessor.js'
},
testMatch: [
'**/test/**/*.test.(ts|js)'
],
testEnvironment: 'node'
};
\ No newline at end of file
venv/
*.pyc
*.egg-info/
dist/
settings.cfg
*.log*
recursive-include map_mason/templates *
recursive-include map_mason/static *
all: run
clean:
rm -rf venv && rm -rf *.egg-info && rm -rf dist && rm -rf *.log*
venv:
virtualenv --python=python3 venv && venv/bin/python setup.py develop
run: venv
FLASK_APP=map_mason MAP_MASON_SETTINGS=../settings.cfg venv/bin/flask run
test: venv
MAP_MASON_SETTINGS=../settings.cfg venv/bin/python -m unittest discover -s tests
sdist: venv test
venv/bin/python setup.py sdist
# MapMason
MapMason description
## Quick Start
Run the application:
make run
And open it in the browser at [http://127.0.0.1:5000/](http://127.0.0.1:5000/)
## Prerequisites
This is built to be used with Python 3. Update `Makefile` to switch to Python 2 if needed.
Some Flask dependencies are compiled during installation, so `gcc` and Python header files need to be present.
For example, on Ubuntu:
apt install build-essential python3-dev
## Development environment and release process
- create virtualenv with Flask and MapMason installed into it (latter is installed in
[develop mode](http://setuptools.readthedocs.io/en/latest/setuptools.html#development-mode) which allows
modifying source code directly without a need to re-install the app): `make venv`
- run development server in debug mode: `make run`; Flask will restart if source code is modified
- run tests: `make test` (see also: [Testing Flask Applications](http://flask.pocoo.org/docs/0.12/testing/))
- create source distribution: `make sdist` (will run tests first)
- to remove virtualenv and built distributions: `make clean`
- to add more python dependencies: add to `install_requires` in `setup.py`
- to modify configuration in development environment: edit file `settings.cfg`; this is a local configuration file
and it is *ignored* by Git - make sure to put a proper configuration file to a production environment when
deploying
## Deployment
If you are interested in an out-of-the-box deployment automation, check out accompanying
[`cookiecutter-flask-ansible`](https://github.com/candidtim/cookiecutter-flask-ansible).
Or, check out [Deploying with Fabric](http://flask.pocoo.org/docs/0.12/patterns/fabric/#fabric-deployment) on one of the
possible ways to automate the deployment.
In either case, generally the idea is to build a package (`make sdist`), deliver it to a server (`scp ...`),
install it (`pip install map_mason.tar.gz`), ensure that configuration file exists and
`MAP_MASON_SETTINGS` environment variable points to it, ensure that user has access to the
working directory to create and write log files in it, and finally run a
[WSGI container](http://flask.pocoo.org/docs/0.12/deploying/wsgi-standalone/) with the application.
And, most likely, it will also run behind a
[reverse proxy](http://flask.pocoo.org/docs/0.12/deploying/wsgi-standalone/#proxy-setups).
import os
from flask import Flask
app = Flask(__name__)
app.config.from_object('map_mason.default_settings')
app.config.from_envvar('MAP_MASON_SETTINGS')
if not app.debug:
import logging
from logging.handlers import TimedRotatingFileHandler
# https://docs.python.org/3.6/library/logging.handlers.html#timedrotatingfilehandler
file_handler = TimedRotatingFileHandler(os.path.join(app.config['LOG_DIR'], 'map_mason.log'), 'midnight')
file_handler.setLevel(logging.WARNING)
file_handler.setFormatter(logging.Formatter('<%(asctime)s> <%(levelname)s> %(message)s'))
app.logger.addHandler(file_handler)
import map_mason.views
DEBUG = False # make sure DEBUG is off unless enabled explicitly otherwise
LOG_DIR = '.' # create log files in current working directory
This diff is collapsed.
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link href="https://srct.gmu.io/masonstrap/css/masonstrap.min.css" rel="stylesheet">
{% block extra_head %}
{% endblock %}
<title>{% block title %}{% endblock %} - MapMason</title>
</head>
<body>
{% block content %}{% endblock %}
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://srct.gmu.io/masonstrap/js/masonstrap.min.js"></script>
{% block extra_js %}{% endblock %}
</body>
</html>
\ No newline at end of file
{% extends 'base.html' %}
{% block extra_head %}
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css"
integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ=="
crossorigin=""/>
<link rel="stylesheet" href="{{ url_for('static', filename='titatoggle-dist-min.css') }}">
<style>
#map {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
html, body {
height: 100%;
width: 100%;
}
#page-content {
height: 100%;
}
#map-container {
padding: 0;
margin: 0;
}
</style>
{% endblock %}
{% block extra_js %}
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js"
integrity="sha512-/Nsx9X4HebavoBvEBuyp3I7od5tA0UzAxs+j83KgC8PU0kgB4XiK4Lfe4y4cgBtaRJQEIFCW+oC506aPT2L1zw=="
crossorigin=""></script>
<script type="text/javascript">
// GMU Decimal Latitude and Decimal Longitude
const gmuLatitude = 38.8322871;
const gmuLongitude = -77.3080912;
const defaultZoom = 16;
var map = L.map('map', {
minZoom: 0,
maxZoom: 20,
zoomSnap: 0,
zoomDelta: 0.25
});
var cartodbAttribution = '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://cartodb.com/attributions">CartoDB</a>';
var tileUrl = 'http://{s}.tile.osm.org/{z}/{x}/{y}.png';
// 'http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png'
var positron = L.tileLayer(tileUrl, {
attribution: cartodbAttribution
}).addTo(map);
map.setView([gmuLatitude, gmuLongitude], defaultZoom);
</script>
{% endblock %}
{% block content %}
<div class="container-fluid" id="page-content">
<div class="row" style="height: 100%;">
<nav class="col-sm-3 col-md-2 d-none d-sm-block navbar-inverse navbar-dark bg-dark sidebar">
<h1 class="navbar-brand navbar-dark bg-dark">MapMason</h1>
<p>
<h3 class="text-primary">Search</h3>
<form class="form-inline mt-2 mt-md-0">
<small class="form-text text-light">Search via Name or Keyword</small>
<input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Name or Keyword">
</form>
</p>
<p>
<h3 class="text-primary">Filter Locations</h3>
<div class="nav flex-column text-light">
<div class="form-check checkbox-slider--b-flat">
<label>
<input type="checkbox"><span>Parking</span>
</label>
</div>
<div class="form-check checkbox-slider--b-flat">
<label>
<input type="checkbox"><span>Residence Halls</span>
</label>
</div>
<div class="form-check checkbox-slider--b-flat">
<label>
<input type="checkbox"><span>Dining Halls</span>
</label>
</div>
</div>
</p>
<p class="text-light">Having problems? <a href="mailto:srct@gmu.edu">Submit feedback</a></p>
<p class="text-muted">A service of <a href="http://srct.gmu.edu">Mason SRCT</a>. Licensed under the <a href="http://opensource.org/licenses/Apache-2.0">Apache License, Version 2.0</a>.</p>
</nav>
<main role="main" class="col-sm-9 ml-sm-auto col-md-10" id="map-container">
<div id="map"></div>
</main>
</div>
</div>
{% endblock %}
from flask import render_template
from map_mason import app
@app.route('/')
def index():
# app.logger.warning('Hit index')
return render_template('index.html')
from setuptools import setup, find_packages
setup(
name='map_mason',
version='1.0',
long_description=__doc__,
packages=find_packages(),
include_package_data=True,
zip_safe=False,
install_requires=[
'flask',
],
)
import unittest
import map_mason
class Map_masonTestCase(unittest.TestCase):
def setUp(self):
self.app = map_mason.app.test_client()
def test_index(self):
rv = self.app.get('/')
self.assertIn('Welcome to MapMason', rv.data.decode())
if __name__ == '__main__':
unittest.main()
{
"name": "map_mason",
"version": "0.1.0",
"description": "Map mason",
"repository": {
"type": "git",
"url": "https://git.gmu.edu/srct/where/"
},
"author": "Kunal Sarkhel",
"license": "Apache-2.0",
"scripts": {
"start": "npm run serve",
"build": "npm run build-sass && npm run build-ts && npm run tslint && npm run copy-static-assets",
"serve": "node dist/server.js",
"watch-node": "nodemon dist/server.js",
"watch": "concurrently -k -p \"[{name}]\" -n \"Sass,TypeScript,Node\" -c \"yellow.bold,cyan.bold,green.bold\" \"npm run watch-sass\" \"npm run watch-ts\" \"npm run watch-node\"",
"test": "jest --forceExit --coverage --verbose",
"watch-test": "npm run test -- --watchAll",
"build-ts": "tsc",
"watch-ts": "tsc -w",
"build-sass": "node-sass src/public/css/main.scss dist/public/css/main.css",
"watch-sass": "node-sass -w src/public/css/main.scss dist/public/css/main.css",
"tslint": "tslint -c tslint.json -p tsconfig.json",
"copy-static-assets": "ts-node copyStaticAssets.ts",
"debug": "npm run build && npm run watch-debug",
"serve-debug": "nodemon --inspect dist/server.js",
"watch-debug": "concurrently -k -p \"[{name}]\" -n \"Sass,TypeScript,Node\" -c \"yellow.bold,cyan.bold,green.bold\" \"npm run watch-sass\" \"npm run watch-ts\" \"npm run serve-debug\""
},
"dependencies": {
"async": "^2.6.0",
"bcrypt-nodejs": "^0.0.3",
"bluebird": "^3.5.1",
"body-parser": "^1.18.2",
"compression": "^1.7.1",
"dotenv": "^4.0.0",
"errorhandler": "^1.5.0",
"express": "^4.16.2",
"express-validator": "^4.3.0",
"fbgraph": "^1.4.1",
"lodash": "^4.17.5",
"lusca": "^1.5.2",
"nodemailer": "^4.4.1",
"pug": "^2.0.0-rc.4",
"request": "^2.83.0",
"request-promise": "^4.2.2",
"titatoggle": "^2.1.2",
"winston": "^2.4.0"
},
"devDependencies": {
"@types/async": "^2.0.45",
"@types/bcrypt-nodejs": "^0.0.30",
"@types/bluebird": "^3.5.20",
"@types/body-parser": "^1.16.8",
"@types/compression": "^0.0.35",
"@types/dotenv": "^4.0.3",
"@types/errorhandler": "^0.0.32",
"@types/express": "^4.11.1",
"@types/jest": "^22.1.3",
"@types/jquery": "^3.2.17",
"@types/lodash": "^4.14.91",
"@types/lusca": "^1.5.0",
"@types/morgan": "^1.7.35",
"@types/node": "^9.4.6",
"@types/nodemailer": "^4.3.4",
"@types/request": "^2.47.0",
"@types/shelljs": "^0.7.8",
"@types/supertest": "^2.0.4",
"chai": "^4.1.2",
"concurrently": "^3.5.1",
"jest": "^22.0.4",
"node-sass": "^4.7.2",
"nodemon": "^1.13.0",
"shelljs": "^0.8.1",
"supertest": "^3.0.0",
"ts-jest": "^22.0.4",
"ts-node": "^5.0.0",
"tslint": "^5.9.1",
"typescript": "^2.7.2"
}
}
import express from "express";
import compression from "compression"; // compresses requests
import bodyParser from "body-parser";
import logger from "./util/logger";
import lusca from "lusca";
import dotenv from "dotenv";
import path from "path";
import expressValidator from "express-validator";
import bluebird from "bluebird";
// Load environment variables from .env file, where API keys and passwords are configured
dotenv.config({path: ".env.example"});
// Controllers (route handlers)
import * as homeController from "./controllers/home";
import * as apiController from "./controllers/api";
// Create Express server
const app = express();
// Express configuration
app.set("port", process.env.PORT || 3000);
app.set("views", path.join(__dirname, "../views"));
app.set("view engine", "pug");
app.use(compression());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(expressValidator());
app.use(lusca.xframe("SAMEORIGIN"));
app.use(lusca.xssProtection(true));
app.use(
express.static(path.join(__dirname, "public"), {maxAge: 31557600000})
);
/**
* Primary app routes.
*/
app.get("/", homeController.index);
/**
* API examples routes.
*/
app.get("/api", apiController.getApi);
export default app;
"use strict";
import async from "async";
import request from "request";
import { Response, Request, NextFunction } from "express";
/**
* GET /api
* List of API examples.
*/
export let getApi = (req: Request, res: Response) => {
res.render("api/index", {
title: "API Examples"
});
};
import { Request, Response } from "express";
/**
* GET /
* Home page.
*/
export let index = (req: Request, res: Response) => {
res.render("home", {
title: "Home"
});
};
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.