forked from seekseek/ui
Initial commit
commit
3f67beaaa6
@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
config.json
|
@ -0,0 +1,43 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const Promise = require("bluebird");
|
||||||
|
const budoExpress = require("budo-express");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
return Promise.try(() => {
|
||||||
|
return require("../src/app");
|
||||||
|
}).then((app) => {
|
||||||
|
budoExpress({
|
||||||
|
// port: 3000,
|
||||||
|
// port: 3100,
|
||||||
|
port: 3500,
|
||||||
|
allowUnsafeHost: true,
|
||||||
|
expressApp: app,
|
||||||
|
basePath: path.join(__dirname, ".."),
|
||||||
|
entryFiles: "src/frontend/index.jsx",
|
||||||
|
staticPath: "public",
|
||||||
|
bundlePath: "js/bundle.js",
|
||||||
|
livereloadPattern: "**/*.{css,html,js}",
|
||||||
|
browserify: {
|
||||||
|
extensions: [".jsx"],
|
||||||
|
plugin: [
|
||||||
|
[ "icssify", {
|
||||||
|
before: [
|
||||||
|
// require('postcss-import')(),
|
||||||
|
require('postcss-mixins')(),
|
||||||
|
require("postcss-nested")(),
|
||||||
|
require('postcss-simple-vars')(),
|
||||||
|
require('postcss-color-function')(),
|
||||||
|
require('autoprefixer')()
|
||||||
|
]
|
||||||
|
}] // FIXME: css-extract
|
||||||
|
],
|
||||||
|
transform: [
|
||||||
|
["babelify", {
|
||||||
|
presets: ["@babel/preset-env", "@babel/preset-react"],
|
||||||
|
}],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,11 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const config = require("./config.json");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
client: "pg",
|
||||||
|
connection: {
|
||||||
|
host: config.database.socketPath,
|
||||||
|
database: config.database.database
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,3 @@
|
|||||||
|
Note: text_pattern_ops index is needed for prefix LIKE to be guaranteed-fast
|
||||||
|
|
||||||
|
CREATE INDEX search_index ON items ((lower(data ->> 'name'::text)) text_pattern_ops);
|
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "ui",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"repository": "git@git.cryto.net:seekseek/ui.git",
|
||||||
|
"author": "Sven Slootweg <admin@cryto.net>",
|
||||||
|
"license": "WTFPL OR CC0-1.0",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "NODE_ENV=development nodemon --ext js,jsx,mjs,json,ftl --ignore node_modules --ignore public/js --ignore src/frontend bin/server.js",
|
||||||
|
"dev-css": "postcss --watch src/css/style.css -o public/css/style.css"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.12.9",
|
||||||
|
"@babel/preset-env": "^7.12.7",
|
||||||
|
"@babel/preset-react": "^7.12.7",
|
||||||
|
"@joepie91/eslint-config": "^1.1.0",
|
||||||
|
"autoprefixer": "^9.0.0",
|
||||||
|
"babelify": "^10.0.0",
|
||||||
|
"budo-express": "^1.0.6",
|
||||||
|
"eslint": "^7.15.0",
|
||||||
|
"eslint-plugin-react": "^7.21.5",
|
||||||
|
"eslint-plugin-react-hooks": "^4.2.0",
|
||||||
|
"icssify": "^1.2.1",
|
||||||
|
"nodemon": "^2.0.6",
|
||||||
|
"postcss": "^8.2.1",
|
||||||
|
"postcss-cli": "^8.3.1",
|
||||||
|
"postcss-color-function": "^4.1.0",
|
||||||
|
"postcss-mixins": "^6",
|
||||||
|
"postcss-nested": "^4",
|
||||||
|
"postcss-simple-vars": "^5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@joepie91/express-react-views": "^1.0.1",
|
||||||
|
"axios": "^0.21.1",
|
||||||
|
"bluebird": "^3.7.2",
|
||||||
|
"classnames": "^2.2.6",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"express-promise-router": "^4.1.0",
|
||||||
|
"knex": "^0.95.2",
|
||||||
|
"match-value": "^1.1.0",
|
||||||
|
"pg": "^8.5.1",
|
||||||
|
"react": "^17.0.1",
|
||||||
|
"react-dom": "^17.0.1",
|
||||||
|
"syncpipe": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
require('postcss-mixins')(),
|
||||||
|
require("postcss-nested")(),
|
||||||
|
require('postcss-simple-vars')(),
|
||||||
|
require('postcss-color-function')(),
|
||||||
|
require('autoprefixer')()
|
||||||
|
]
|
||||||
|
};
|
@ -0,0 +1,62 @@
|
|||||||
|
html, body {
|
||||||
|
/* background-color: rgb(248, 249, 236); */
|
||||||
|
background-color: #f9f9f9ff;
|
||||||
|
font-family: sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
max-width: 900px;
|
||||||
|
margin-left: 1em;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-left: 0.5em;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
padding-bottom: .7em;
|
||||||
|
border-bottom: 2px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contents {
|
||||||
|
margin-bottom: 2em;
|
||||||
|
padding: .7em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0.5em;
|
||||||
|
right: 0.5em;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-top: 1px solid black;
|
||||||
|
height: 2em;
|
||||||
|
padding: 0.2em;
|
||||||
|
background-color: #f9f9f9ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer .wrapper {
|
||||||
|
margin-left: calc(1em - 0.5em);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoContainer {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoContainer .logo {
|
||||||
|
height: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoContainer .siteTag {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
color: teal;
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,113 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="624.12909"
|
||||||
|
height="149.9104"
|
||||||
|
viewBox="0 0 165.13416 39.663792"
|
||||||
|
version="1.1"
|
||||||
|
id="svg851"
|
||||||
|
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
|
||||||
|
sodipodi:docname="logo.svg"
|
||||||
|
inkscape:export-filename="/home/sven/magnifier.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96">
|
||||||
|
<defs
|
||||||
|
id="defs845" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.4142136"
|
||||||
|
inkscape:cx="290.07278"
|
||||||
|
inkscape:cy="-52.014372"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1051"
|
||||||
|
inkscape:window-x="1920"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata848">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-137.20652,-103.9911)">
|
||||||
|
<g
|
||||||
|
aria-label="seekseek"
|
||||||
|
id="text905"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:25.4px;line-height:36.6346px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1">
|
||||||
|
<path
|
||||||
|
d="m 197.21016,126.62008 q 0,2.1082 -1.4986,3.2512 -1.4732,1.1176 -4.4196,1.1176 -1.4478,0 -2.4892,-0.2032 -1.0414,-0.1778 -2.0828,-0.6096 v -3.1242 q 1.1176,0.508 2.413,0.8382 1.2954,0.3302 2.286,0.3302 1.1176,0 1.5748,-0.3302 0.4826,-0.3302 0.4826,-0.8636 0,-0.3556 -0.2032,-0.635 -0.1778,-0.2794 -0.8128,-0.635 -0.635,-0.3556 -1.9812,-0.9144 -1.2954,-0.5588 -2.1336,-1.0922 -0.8382,-0.5588 -1.2446,-1.3208 -0.4064,-0.7874 -0.4064,-1.9558 0,-1.9304 1.4986,-2.8956 1.4986,-0.9652 3.9878,-0.9652 1.2954,0 2.4638,0.254 1.1684,0.254 2.413,0.8382 l -1.143,2.7178 q -1.016,-0.4318 -1.9304,-0.7112 -0.9144,-0.3048 -1.8542,-0.3048 -1.6764,0 -1.6764,0.9144 0,0.3302 0.2032,0.6096 0.2286,0.254 0.8382,0.5588 0.635,0.3048 1.8542,0.8128 1.1938,0.4826 2.0574,1.016 0.8636,0.508 1.3208,1.2954 0.4826,0.762 0.4826,2.0066 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:25.4px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.264583px"
|
||||||
|
id="path1512" />
|
||||||
|
<path
|
||||||
|
d="m 205.87155,116.61248 q 2.8702,0 4.5466,1.651 1.6764,1.6256 1.6764,4.6482 v 1.8288 h -8.9408 q 0.0508,1.6002 0.9398,2.5146 0.9144,0.9144 2.5146,0.9144 1.3462,0 2.4384,-0.254 1.0922,-0.2794 2.2606,-0.8382 v 2.921 q -1.016,0.508 -2.159,0.7366 -1.1176,0.254 -2.7178,0.254 -2.0828,0 -3.683,-0.762 -1.6002,-0.7874 -2.5146,-2.3622 -0.9144,-1.5748 -0.9144,-3.9624 0,-2.4384 0.8128,-4.0386 0.8382,-1.6256 2.3114,-2.4384 1.4732,-0.8128 3.429,-0.8128 z m 0.0254,2.6924 q -1.0922,0 -1.8288,0.7112 -0.7112,0.7112 -0.8382,2.2098 h 5.3086 q -0.0254,-1.27 -0.6604,-2.0828 -0.635,-0.8382 -1.9812,-0.8382 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:25.4px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.264583px"
|
||||||
|
id="path1514" />
|
||||||
|
<path
|
||||||
|
d="m 220.88294,116.61248 q 2.8702,0 4.5466,1.651 1.6764,1.6256 1.6764,4.6482 v 1.8288 h -8.9408 q 0.0508,1.6002 0.9398,2.5146 0.9144,0.9144 2.5146,0.9144 1.3462,0 2.4384,-0.254 1.0922,-0.2794 2.2606,-0.8382 v 2.921 q -1.016,0.508 -2.159,0.7366 -1.1176,0.254 -2.7178,0.254 -2.0828,0 -3.683,-0.762 -1.6002,-0.7874 -2.5146,-2.3622 -0.9144,-1.5748 -0.9144,-3.9624 0,-2.4384 0.8128,-4.0386 0.8382,-1.6256 2.3114,-2.4384 1.4732,-0.8128 3.429,-0.8128 z m 0.0254,2.6924 q -1.0922,0 -1.8288,0.7112 -0.7112,0.7112 -0.8382,2.2098 h 5.3086 q -0.0254,-1.27 -0.6604,-2.0828 -0.635,-0.8382 -1.9812,-0.8382 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:25.4px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.264583px"
|
||||||
|
id="path1516" />
|
||||||
|
<path
|
||||||
|
d="m 233.96393,111.43088 v 8.636 q 0,0.7874 -0.0762,1.5748 -0.0508,0.762 -0.127,1.5494 h 0.0508 q 0.381,-0.5334 0.7874,-1.0668 0.4064,-0.5588 0.8636,-1.0414 l 3.8862,-4.2164 h 4.2672 l -5.5118,6.0198 5.842,7.8486 h -4.3688 l -3.9878,-5.6134 -1.6256,1.2954 v 4.318 h -3.7846 v -19.304 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:25.4px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.264583px"
|
||||||
|
id="path1518" />
|
||||||
|
<path
|
||||||
|
d="m 255.60471,126.62008 q 0,2.1082 -1.4986,3.2512 -1.4732,1.1176 -4.4196,1.1176 -1.4478,0 -2.4892,-0.2032 -1.0414,-0.1778 -2.0828,-0.6096 v -3.1242 q 1.1176,0.508 2.413,0.8382 1.2954,0.3302 2.286,0.3302 1.1176,0 1.5748,-0.3302 0.4826,-0.3302 0.4826,-0.8636 0,-0.3556 -0.2032,-0.635 -0.1778,-0.2794 -0.8128,-0.635 -0.635,-0.3556 -1.9812,-0.9144 -1.2954,-0.5588 -2.1336,-1.0922 -0.8382,-0.5588 -1.2446,-1.3208 -0.4064,-0.7874 -0.4064,-1.9558 0,-1.9304 1.4986,-2.8956 1.4986,-0.9652 3.9878,-0.9652 1.2954,0 2.4638,0.254 1.1684,0.254 2.413,0.8382 l -1.143,2.7178 q -1.016,-0.4318 -1.9304,-0.7112 -0.9144,-0.3048 -1.8542,-0.3048 -1.6764,0 -1.6764,0.9144 0,0.3302 0.2032,0.6096 0.2286,0.254 0.8382,0.5588 0.635,0.3048 1.8542,0.8128 1.1938,0.4826 2.0574,1.016 0.8636,0.508 1.3208,1.2954 0.4826,0.762 0.4826,2.0066 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:25.4px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.264583px"
|
||||||
|
id="path1520" />
|
||||||
|
<path
|
||||||
|
d="m 264.2661,116.61248 q 2.8702,0 4.5466,1.651 1.6764,1.6256 1.6764,4.6482 v 1.8288 h -8.9408 q 0.0508,1.6002 0.9398,2.5146 0.9144,0.9144 2.5146,0.9144 1.3462,0 2.4384,-0.254 1.0922,-0.2794 2.2606,-0.8382 v 2.921 q -1.016,0.508 -2.159,0.7366 -1.1176,0.254 -2.7178,0.254 -2.0828,0 -3.683,-0.762 -1.6002,-0.7874 -2.5146,-2.3622 -0.9144,-1.5748 -0.9144,-3.9624 0,-2.4384 0.8128,-4.0386 0.8382,-1.6256 2.3114,-2.4384 1.4732,-0.8128 3.429,-0.8128 z m 0.0254,2.6924 q -1.0922,0 -1.8288,0.7112 -0.7112,0.7112 -0.8382,2.2098 h 5.3086 q -0.0254,-1.27 -0.6604,-2.0828 -0.635,-0.8382 -1.9812,-0.8382 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:25.4px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.264583px"
|
||||||
|
id="path1522" />
|
||||||
|
<path
|
||||||
|
d="m 279.27749,116.61248 q 2.8702,0 4.5466,1.651 1.6764,1.6256 1.6764,4.6482 v 1.8288 h -8.9408 q 0.0508,1.6002 0.9398,2.5146 0.9144,0.9144 2.5146,0.9144 1.3462,0 2.4384,-0.254 1.0922,-0.2794 2.2606,-0.8382 v 2.921 q -1.016,0.508 -2.159,0.7366 -1.1176,0.254 -2.7178,0.254 -2.0828,0 -3.683,-0.762 -1.6002,-0.7874 -2.5146,-2.3622 -0.9144,-1.5748 -0.9144,-3.9624 0,-2.4384 0.8128,-4.0386 0.8382,-1.6256 2.3114,-2.4384 1.4732,-0.8128 3.429,-0.8128 z m 0.0254,2.6924 q -1.0922,0 -1.8288,0.7112 -0.7112,0.7112 -0.8382,2.2098 h 5.3086 q -0.0254,-1.27 -0.6604,-2.0828 -0.635,-0.8382 -1.9812,-0.8382 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:25.4px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.264583px"
|
||||||
|
id="path1524" />
|
||||||
|
<path
|
||||||
|
d="m 292.35848,111.43088 v 8.636 q 0,0.7874 -0.0762,1.5748 -0.0508,0.762 -0.127,1.5494 h 0.0508 q 0.381,-0.5334 0.7874,-1.0668 0.4064,-0.5588 0.8636,-1.0414 l 3.8862,-4.2164 h 4.2672 l -5.5118,6.0198 5.842,7.8486 h -4.3688 l -3.9878,-5.6134 -1.6256,1.2954 v 4.318 h -3.7846 v -19.304 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:25.4px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.264583px"
|
||||||
|
id="path1526" />
|
||||||
|
</g>
|
||||||
|
<rect
|
||||||
|
style="opacity:0.99;fill:#333333;stroke-width:0.175183;paint-order:markers stroke fill"
|
||||||
|
id="rect1418"
|
||||||
|
width="5.357511"
|
||||||
|
height="22.925625"
|
||||||
|
x="195.92026"
|
||||||
|
y="-21.044689"
|
||||||
|
transform="rotate(45)" />
|
||||||
|
<path
|
||||||
|
id="path1414"
|
||||||
|
style="opacity:0.99;fill:#000000;stroke-width:0.175183;paint-order:markers stroke fill"
|
||||||
|
d="m 162.88237,103.99118 a 13.521487,13.521487 0 0 0 -9.51545,3.96032 13.521487,13.521487 0 0 0 0,19.12234 13.521487,13.521487 0 0 0 19.12235,0 13.521487,13.521487 0 0 0 0,-19.12234 13.521487,13.521487 0 0 0 -9.6069,-3.96032 z m 0.0151,4.51556 a 9.0058318,9.0058318 0 0 1 6.39856,2.6377 9.0058318,9.0058318 0 0 1 0,12.73615 9.0058318,9.0058318 0 0 1 -12.73616,0 9.0058318,9.0058318 0 0 1 0,-12.73615 9.0058318,9.0058318 0 0 1 6.3376,-2.6377 z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 9.5 KiB |
@ -0,0 +1,47 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const Promise = require("bluebird");
|
||||||
|
const express = require("express");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const knex = require("knex")(require("../knexfile"));
|
||||||
|
|
||||||
|
let app = express();
|
||||||
|
|
||||||
|
app.set("views", path.join(__dirname, "views"));
|
||||||
|
app.set("view engine", "jsx");
|
||||||
|
app.engine("jsx", require("@joepie91/express-react-views").createEngine());
|
||||||
|
|
||||||
|
app.use(express.static(path.join(__dirname, "../public")));
|
||||||
|
|
||||||
|
app.get("/", (req, res) => {
|
||||||
|
res.redirect("/datasheets");
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/datasheets", (req, res) => {
|
||||||
|
res.render("index");
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/search", (req, res) => {
|
||||||
|
return Promise.try(() => {
|
||||||
|
return knex.raw(`
|
||||||
|
SELECT
|
||||||
|
data->>'manufacturer' AS manufacturer,
|
||||||
|
data->>'name' AS name,
|
||||||
|
data->>'url' AS url,
|
||||||
|
data->>'description' AS description,
|
||||||
|
data->>'source' AS source
|
||||||
|
FROM items WHERE
|
||||||
|
id LIKE 'datasheet:%'
|
||||||
|
AND lower(data->>'name') LIKE :query
|
||||||
|
ORDER BY name
|
||||||
|
LIMIT 20
|
||||||
|
`, {
|
||||||
|
query: req.query.query.toLowerCase() + "%"
|
||||||
|
});
|
||||||
|
}).then((result) => {
|
||||||
|
res.json({ results: result.rows });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app;
|
@ -0,0 +1,65 @@
|
|||||||
|
$pagePadding: 0.5em;
|
||||||
|
$background: #f9f9f9ff;
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
/* background-color: rgb(248, 249, 236); */
|
||||||
|
background-color: $background;
|
||||||
|
font-family: sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
max-width: 900px;
|
||||||
|
margin-left: 1em;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-left: $pagePadding;
|
||||||
|
margin-right: $pagePadding;
|
||||||
|
padding-bottom: .7em;
|
||||||
|
border-bottom: 2px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contents {
|
||||||
|
margin-bottom: 2em;
|
||||||
|
padding: .7em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: $pagePadding;
|
||||||
|
right: $pagePadding;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-top: 1px solid black;
|
||||||
|
height: 2em;
|
||||||
|
padding: 0.2em;
|
||||||
|
background-color: $background;
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
margin-left: calc(1em - $pagePadding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoContainer {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
height: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.siteTag {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
color: teal;
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
|||||||
|
.query {
|
||||||
|
font-size: 1.3em;
|
||||||
|
background-color: white;
|
||||||
|
color: black;
|
||||||
|
border: 1px solid teal;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: .4em .8em;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noResults {
|
||||||
|
padding: .6em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.results {
|
||||||
|
border: 1px solid teal;
|
||||||
|
border-radius: 5px;
|
||||||
|
/* margin-top: 1em; */
|
||||||
|
|
||||||
|
.result {
|
||||||
|
border-top: 1px solid teal;
|
||||||
|
padding: .6em 1em;
|
||||||
|
font-size: .8em;
|
||||||
|
|
||||||
|
&.first {
|
||||||
|
border-top: 0;
|
||||||
|
font-size: 1em
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-size: 1.3em;
|
||||||
|
|
||||||
|
.manufacturer {
|
||||||
|
color: #4f4e4e;
|
||||||
|
margin-right: .3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
color: #4f4e4e;
|
||||||
|
margin-top: .2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download {
|
||||||
|
float: right;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: .3em .8em;
|
||||||
|
|
||||||
|
margin-top: -.3em;
|
||||||
|
margin-right: -.7em;
|
||||||
|
|
||||||
|
border: 1px solid #006262;
|
||||||
|
background-color: #039f9f;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sources {
|
||||||
|
color: gray;
|
||||||
|
font-size: 0.8em;
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: .3em;
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const React = require("react");
|
||||||
|
const classnames = require("classnames");
|
||||||
|
const axios = require("axios");
|
||||||
|
const syncpipe = require("syncpipe");
|
||||||
|
const matchValue = require("match-value");
|
||||||
|
|
||||||
|
const style = require("./datasheet-search.css");
|
||||||
|
|
||||||
|
function getSources(results) {
|
||||||
|
return syncpipe(results, [
|
||||||
|
(_) => _.map((result) => result.source),
|
||||||
|
// (_) => _.filter((source) => source != null),
|
||||||
|
(_) => _.map((source) => source != null ? source : "unknown"),
|
||||||
|
(_) => new Set(_),
|
||||||
|
(_) => Array.from(_),
|
||||||
|
(_) => _.map((source) => {
|
||||||
|
return matchValue(source, {
|
||||||
|
tme: "TME",
|
||||||
|
lcsc: "LCSC",
|
||||||
|
st: "STMicroelectronics",
|
||||||
|
unknown: "Unknown"
|
||||||
|
});
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SearchResult({ isFirst, name, description, url, manufacturer }) {
|
||||||
|
// FIXME: Handle 'no datasheet available'
|
||||||
|
return (
|
||||||
|
<div className={classnames(style.result, { [style.first]: isFirst })}>
|
||||||
|
<a href={url} className={style.download} target="_blank">
|
||||||
|
Download datasheet
|
||||||
|
</a>
|
||||||
|
<div className={style.name}>
|
||||||
|
<span className={style.manufacturer}>
|
||||||
|
{manufacturer}
|
||||||
|
</span>
|
||||||
|
<span className={style.model}>
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className={style.description}>
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function NoResults() {
|
||||||
|
return (
|
||||||
|
<div className={style.noResults}>
|
||||||
|
No results.
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function DatasheetSearch({}) {
|
||||||
|
let [ query, setQuery ] = React.useState(""); // FIXME
|
||||||
|
let [ results, setResults ] = React.useState([]);
|
||||||
|
let [ sources, setSources ] = React.useState([]);
|
||||||
|
let cancellationToken = React.useRef();
|
||||||
|
|
||||||
|
function updateQuery(event) {
|
||||||
|
setQuery(event.target.value.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
cancellationToken.current = axios.CancelToken.source();
|
||||||
|
|
||||||
|
if (query.length > 0) {
|
||||||
|
axios.post("/search", {}, {
|
||||||
|
params: { query: query }
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (!cancelled) {
|
||||||
|
setResults(response.data.results);
|
||||||
|
|
||||||
|
setSources(getSources(response.data.results));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (!axios.isCancel()) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setResults([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
cancelled = true;
|
||||||
|
cancellationToken.current.cancel("Query input changed");
|
||||||
|
};
|
||||||
|
}, [ query ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="search">
|
||||||
|
<input type="text" className={style.query} placeholder="Start typing..." onChange={updateQuery} />
|
||||||
|
<div className={style.sources}>
|
||||||
|
{(results.length > 0)
|
||||||
|
? <>
|
||||||
|
<strong>Sources:</strong>
|
||||||
|
|
||||||
|
{sources.join(", ")}
|
||||||
|
</>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className={style.results}>
|
||||||
|
{(results.length > 0)
|
||||||
|
? results.map((result, i) => {
|
||||||
|
return <SearchResult
|
||||||
|
isFirst={(i === 0)}
|
||||||
|
{...result}
|
||||||
|
/>;
|
||||||
|
})
|
||||||
|
: <NoResults />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,8 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const React = require("react");
|
||||||
|
const ReactDOM = require("react-dom");
|
||||||
|
|
||||||
|
const DatasheetSearch = require("./components/datasheet-search");
|
||||||
|
|
||||||
|
ReactDOM.render(<DatasheetSearch />, document.querySelector("#datasheetSearch"));
|
@ -0,0 +1,42 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const React = require("react");
|
||||||
|
|
||||||
|
module.exports = function Index() {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charSet="UTF-8"/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
|
<title>seekseek</title>
|
||||||
|
<link rel="stylesheet" href="/css/style.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div className="header">
|
||||||
|
<div className="wrapper">
|
||||||
|
<div className="logoContainer">
|
||||||
|
<img src="/images/logo.svg" alt="seekseek logo" className="logo"/>
|
||||||
|
<div className="siteTag">datasheets</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="contents">
|
||||||
|
<div className="wrapper">
|
||||||
|
<div id="datasheetSearch">
|
||||||
|
Loading, please wait...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="footer">
|
||||||
|
<div className="wrapper">
|
||||||
|
<a href="https://matrix.to/#/#seekseek:pixie.town?via=pixie.town&via=matrix.org&via=librepush.net" className="chat">
|
||||||
|
Come chat with us!
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="/js/bundle.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in New Issue