Added react for photos face and identity editor

Signed-off-by: James Ketrenos <james_gitlab@ketrenos.com>
This commit is contained in:
James Ketrenos 2020-02-04 20:45:51 -08:00
parent 2037a3f636
commit ae4022f657
12 changed files with 605 additions and 22 deletions

4
.babelrc Normal file
View File

@ -0,0 +1,4 @@
{
"presets": [ "@babel/env", "@babel/preset-react" ],
"plugins": [ "@babel/plugin-proposal-class-properties" ]
}

View File

@ -1,20 +1,18 @@
{ {
"db": { "db": {
"photos": { "photos": {
"host": "sqlite:db/photos.db", "dialect": "sqlite",
"options": { "host": "db/photos.db",
"logging" : false, "logging" : false,
"timezone": "+00:00", "timezone": "+00:00",
"operatorsAliases": false "operatorsAliases": 0
}
}, },
"users": { "users": {
"host": "sqlite:db/users.db", "dialect": "sqlite",
"options": { "host": "db/users.db",
"logging" : false, "logging" : false,
"timezone": "+00:00", "timezone": "+00:00",
"operatorsAliases": false "operatorsAliases": 0
}
} }
}, },
"ldap": { "ldap": {

View File

@ -5,7 +5,13 @@
"main": "server/app.js", "main": "server/app.js",
"scripts": { "scripts": {
"start": "node ./server/app.js", "start": "node ./server/app.js",
"faces": "node ./server/face-recognizer.js" "faces": "node ./server/face-recognizer.js",
"dev": "webpack-dev-server --mode development --host 0.0.0.0 --config webpack.dev.js",
"build": "webpack --config webpack.prod.js",
"commit-build": "./commit-build.sh",
"watch": "webpack --config webpack.prod.js --watch",
"update": "./update.sh",
"backend": "NODE_CONFIG_ENV='production' node server/app.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -17,20 +23,25 @@
"face-recognition": "^0.9.4" "face-recognition": "^0.9.4"
}, },
"dependencies": { "dependencies": {
"@tensorflow/tfjs-core": "^1.5.1", "@tensorflow/tfjs-core": "^1.5.2",
"@tensorflow/tfjs-node": "^1.5.1", "@tensorflow/tfjs-node": "^1.5.2",
"animakit-expander": "^2.1.4",
"bluebird": "^3.7.2", "bluebird": "^3.7.2",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"bootstrap": "^4.4.1",
"canvas": "^2.6.1", "canvas": "^2.6.1",
"config": "^1.31.0", "config": "^3.1.0",
"connect-sqlite3": "^0.9.11", "connect-sqlite3": "^0.9.11",
"cookie-parser": "^1.4.4", "cookie-parser": "^1.4.4",
"core-js": "^3.2.1",
"exif-reader": "github:paras20xx/exif-reader", "exif-reader": "github:paras20xx/exif-reader",
"express": "^4.17.1", "express": "^4.17.1",
"express-session": "^1.17.0", "express-session": "^1.17.0",
"face-api.js": "^0.22.0", "face-api.js": "^0.22.0",
"handlebars": "^4.5.3", "googleapis": "^40.0.0",
"ldapauth-fork": "^4.2.0", "handlebars": "^4.7.2",
"jira-connector": "^2.10.0",
"ldapauth-fork": "^4.3.0",
"ldapjs": "^1.0.2", "ldapjs": "^1.0.2",
"mariasql": "^0.2.6", "mariasql": "^0.2.6",
"moment": "^2.24.0", "moment": "^2.24.0",
@ -38,13 +49,38 @@
"morgan": "^1.9.1", "morgan": "^1.9.1",
"mustache": "^3.2.1", "mustache": "^3.2.1",
"node-fetch": "^2.6.0", "node-fetch": "^2.6.0",
"nodemailer": "^4.7.0", "node-gzip": "^1.1.2",
"nodemailer": "^6.3.0",
"qs": "^6.9.1", "qs": "^6.9.1",
"sequelize": "^4.44.3", "react-app-polyfill": "^1.0.2",
"react-bootstrap": "^1.0.0-beta.16",
"react-date-range": "^1.0.0-beta",
"react-markdown": "^4.2.2",
"react-router-dom": "^5.0.1",
"react-scroll": "^1.7.14",
"react-syntax-highlighter": "^11.0.2",
"sequelize": "^5.21.3",
"sequelize-mysql": "^1.7.0", "sequelize-mysql": "^1.7.0",
"sharp": "^0.20.8", "sharp": "^0.20.8",
"sqlite3": "^4.1.1" "sqlite3": "^4.1.1"
}, },
"devDependencies": {
"@babel/cli": "^7.1.0",
"@babel/core": "^7.1.0",
"@babel/plugin-proposal-class-properties": "^7.4.4",
"@babel/preset-env": "^7.1.0",
"@babel/preset-react": "^7.0.0",
"babel-loader": "^8.0.2",
"css-loader": "^1.0.0",
"file-loader": "^4.1.0",
"react": "^16.8",
"react-dom": "^16.8",
"style-loader": "^0.23.0",
"webpack": "^4.19.1",
"webpack-cli": "^3.1.1",
"webpack-dev-server": "^3.3.1",
"webpack-merge": "^4.2.1"
},
"jshintConfig": { "jshintConfig": {
"undef": true, "undef": true,
"unused": true, "unused": true,

View File

@ -20,7 +20,7 @@ const fs = require('fs'),
function init() { function init() {
const db = { const db = {
sequelize: new Sequelize(config.get("db.photos.host"), config.get("db.photos.options")), sequelize: new Sequelize(config.get("db.photos")),
Sequelize: Sequelize Sequelize: Sequelize
}; };

View File

@ -18,7 +18,7 @@ const Sequelize = require('sequelize'),
function init() { function init() {
const db = { const db = {
sequelize: new Sequelize(config.get("db.users.host"), config.get("db.users.options")), sequelize: new Sequelize(config.get("db.users")),
Sequelize: Sequelize Sequelize: Sequelize
}; };

180
src/App.css Executable file
View File

@ -0,0 +1,180 @@
body {
font-family: 'Droid Sans', 'Arial Narrow', Arial, sans-serif;
}
#spinner {
display: none;
position: fixed;
left: 50%;
top: 50%;
width: 60px;
height: 60px;
margin-left: -30px;
margin-top: -30px;
z-index: 100;
background-image: url(../frontend/icons-512.png);
background-color: #0071C5;
border-radius: 50%;
}
#spinner.spin {
display: inline-block;
-webkit-animation:spin 1s linear infinite;
-moz-animation:spin 1s linear infinite;
animation:spin 1s linear infinite;
}
@-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } }
@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }
.Header {
background-color: #252525;
color: #ffffff;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 20;
display: flex;
box-sizing: border-box;
flex-direction: column;
user-select: none;
}
.Header .Group {
height: 60px;
display: flex;
padding-left: 1em;
padding-right: calc(1em + 52px);
background-color: #0071C5;
align-items: center;
}
.Header .Group .Subtitle {
font-size: 0.7em;
line-height: 1.5em;
}
.Header .Group .Heading {
margin: 0 auto;
width: calc(100% - 8em);
max-width: 90em;
}
.Header .Pages {
height: 60px;
display: flex;
margin: 0 auto;
width: calc(100% - 8em);
max-width: 90em;
/* justify-content: space-between;*/
font-size: 1.2em;
align-items: center;
}
.Header .Pages .Link {
display: inline-flex;
align-items: center;
height: 100%;
box-sizing: border-box;
padding: 0.5em 1em;
border-bottom: 3px solid transparent;
cursor: pointer;
}
.Header .Pages .Link.Highlight {
border-bottom-color: #0071C5 !important;
}
.Header .Pages .Link.Active,
.Header .Pages .Link:hover {
border-bottom-color: #003663;
/*border-bottom-color: #00A7FF;*/
}
a.Link {
font-family: Oswald;
text-decoration: none;
color: white;
}
.Footer {
position: fixed;
background-color: #252525;
color: #ffffff;
height: 64px;
bottom: 0;
left: 0;
right: 0;
font-size: 0.9em;
font-family: Droid Sans;
z-index: 20;
}
.Footer .Copyright {
position: absolute;
line-height: 64px;
text-align: center;
left: 64px;
right: 64px;
}
.Body {
position: absolute;
margin-top: 120px; /* .Header's two 60px chunks */
margin-bottom: 64px;
top: 0;
bottom: 0;
left: 0;
right: 0;
box-sizing: border-box;
background-image: linear-gradient(#090B1A, #131524);
color: #ffffff;
z-index: 0;
}
.Body > * {
box-sizing: border-box;
}
.Main {
display: inline-flex;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-image: url(../frontend/icons-512.png);
background-repeat: no-repeat;
background-position: center;
}
.Content {
position: relative;
box-sizing: border-box;
margin: 1em auto;
width: calc(100% - 8em);
max-width: 90em;
color: black;
z-index: 10;
background-color: #fff;
overflow-y: scroll;
scroll-behavior: smooth;
padding: 1.5em 1.5em 0 1.5em;
box-sizing: border-box;
transition: top 1s ease-in-out;
top: -100%;
}
.Content #MinBox {
position: relative;
box-sizing: border-box;
min-height: calc(100% - 2.5em);
padding-bottom: 1.3em;
}
.OnScreen {
top: 0px;
}

91
src/App.js Executable file
View File

@ -0,0 +1,91 @@
/* Polyfills for IE */
import 'react-app-polyfill/ie11';
import 'core-js/features/array/find';
import 'core-js/features/array/includes';
import 'core-js/features/number/is-nan';
/* App starts here */
import React from "react";
import { withRouter, NavLink, Route, Switch } from "react-router-dom";
import Modal from "react-bootstrap/Modal";
import Button from "react-bootstrap/Button";
import "bootstrap/dist/css/bootstrap.min.css";
/* Custom components */
import './modest.css';
import "./App.css";
class Header extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
}
render() {
return (
<div className="Header">
<Logo/>
<div>Header</div>
</div>
);
}
}
class Logo extends React.Component {
render() {
return <NavLink to="/"><div className="Logo"></div></NavLink>
}
}
class Footer extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="Footer">
<div className="Copyright">Copyright 2020 James Ketrenos</div>
</div>
);
}
}
function noChange() {};
class App extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
const location = this.props.history.location.pathname;
console.log(`App.mounted at ${location}`);
}
render() {
console.log("App");
return (
<div className="App" ref={ ref => (this.app = ref) }>
<Header/>
<div className="Body">
<div className="Main">
<Switch>
<Route path="/identities" render={ (props) => <div {...props}/> }/>
<Route path="/faces" render={ props => <div {...props}/> }/>
<Route path="/photos" render={ props => <div {...props}/> }/>
</Switch>
</div>
</div>
<Footer/>
</div>
);
}
}
export default withRouter(App);

15
src/index.js Normal file
View File

@ -0,0 +1,15 @@
import React from "react";
import { render } from "react-dom";
import { BrowserRouter } from "react-router-dom";
import App from "./App.js";
if (process.env.NODE_ENV !== 'production') {
console.log('DEVELOPMENT mode!');
}
render(
<BrowserRouter basename="/">
<App/>
</BrowserRouter>,
document.getElementById("root")
)

200
src/modest.css Normal file
View File

@ -0,0 +1,200 @@
@media print {
*,
*:before,
*:after {
background: transparent !important;
color: #000 !important;
box-shadow: none !important;
text-shadow: none !important;
}
a,
a:visited {
text-decoration: underline;
}
a[href]:after {
content: " (" attr(href) ")";
}
abbr[title]:after {
content: " (" attr(title) ")";
}
a[href^="#"]:after,
a[href^="javascript:"]:after {
content: "";
}
pre,
blockquote {
border: 1px solid #999;
page-break-inside: avoid;
}
thead {
display: table-header-group;
}
tr,
img {
page-break-inside: avoid;
}
img {
max-width: 100% !important;
}
p,
h2,
h3 {
orphans: 3;
widows: 3;
}
h2,
h3 {
page-break-after: avoid;
}
}
pre,
code {
font-family: Menlo, Monaco, "Courier New", monospace;
}
pre {
padding: .5rem;
line-height: 1.25;
overflow-x: scroll;
}
a,
a:visited {
color: #3498db;
}
a:hover,
a:focus,
a:active {
color: #2980b9;
}
.modest-no-decoration {
text-decoration: none;
}
html {
font-size: 12px;
}
@media screen and (min-width: 32rem) and (max-width: 48rem) {
html {
font-size: 15px;
}
}
@media screen and (min-width: 48rem) {
html {
font-size: 16px;
}
}
p,
.modest-p {
font-size: 1rem;
margin-bottom: 1.3rem;
}
h1,
.modest-h1,
h2,
.modest-h2,
h3,
.modest-h3,
h4,
.modest-h4 {
margin: 1.414rem 0 .5rem;
font-weight: inherit;
line-height: 1.42;
}
h1,
.modest-h1 {
margin-top: 0;
font-size: 3.998rem;
}
h2,
.modest-h2 {
font-size: 2.827rem;
}
h3,
.modest-h3 {
font-size: 1.999rem;
}
h4,
.modest-h4 {
font-size: 1.414rem;
}
h5,
.modest-h5 {
font-size: 1.121rem;
}
h6,
.modest-h6 {
font-size: .88rem;
}
small,
.modest-small {
font-size: .707em;
}
/* https://github.com/mrmrs/fluidity */
img,
canvas,
iframe,
video,
svg,
select,
textarea {
max-width: 100%;
}
@import url(https://fonts.googleapis.com/css?family=Open+Sans+Condensed:300,300italic,700);
@import url(https://fonts.googleapis.com/css?family=Arimo:700,700italic);
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: Arimo, Helvetica, sans-serif;
}
h1,
h2,
h3 {
border-bottom: 2px solid #fafafa;
margin-bottom: 1.15rem;
padding-bottom: .5rem;
text-align: left;
}
blockquote {
border-left: 8px solid #fafafa;
padding: 1rem;
}
pre,
code {
background-color: #fafafa;
}

33
webpack.common.js Normal file
View File

@ -0,0 +1,33 @@
const path = require("path");
const webpack = require("webpack");
module.exports = {
entry: "./src/index.js",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
options: { presets: ["@babel/env"] }
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
test: /\.(png|svg|jpg|md)$/,
use: [ "file-loader" ]
}
]
},
resolve: {
extensions: ["*", ".js", ".jsx"]
},
output: {
path: path.resolve(__dirname, "dist/"),
publicPath: "./dist/",
filename: "bundle.js"
}
};

18
webpack.dev.js Normal file
View File

@ -0,0 +1,18 @@
const path = require("path");
const merge = require('webpack-merge')
const common = require('./webpack.common.js');
const webpack = require('webpack');
module.exports = merge(common, {
mode: "development",
devServer: {
contentBase: path.join(__dirname, "/"),
port: 8765,
publicPath: "http://localhost:8765/dist/",
hotOnly: true,
disableHostCheck: true,
historyApiFallback: true
},
plugins: [new webpack.HotModuleReplacementPlugin()]
});

8
webpack.prod.js Normal file
View File

@ -0,0 +1,8 @@
const merge = require('webpack-merge')
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: "production",
devtool: 'source-map'
});