Checkpoint. Need to add an easy way to read route params to render a single sealious item

master
Kuba Orlik 2 years ago
parent 5261a3140d
commit dd9362f9e4

2
.gitignore vendored

@ -26,3 +26,5 @@ public/dist
/log.html
/hint-report/
.vscode
/osrm/
/nominatim/

@ -8,7 +8,9 @@
## Installation
```
./get-map.sh
./npm.sh install
docker-compose build
```
Always use ./npm.sh when installing dependencies.
@ -16,6 +18,7 @@ Always use ./npm.sh when installing dependencies.
## Running the app in development mode
```
docker-compose up -d
npm run watch
```

@ -4,6 +4,7 @@ services:
image: mongo:4.4-bionic
ports:
- "127.0.0.1:${PORT:-2074}7:27017"
test:
image: sealious-app:latest
build:
@ -13,8 +14,68 @@ services:
- ./:/opt/sealious-app/
- ~/.npm_cacache:/opt/sealious-app/.npm_cacache
user: ${UID:-1000}:${GID:-1000}
mailcatcher:
image: schickling/mailcatcher:latest
ports:
- "127.0.0.1:${PORT:-108}2:1080"
- "127.0.0.1:${PORT:-102}6:1025"
vroom:
image: vroomvrp/vroom-docker:v1.12.0
container_name: vroom
volumes:
- ./vroom-conf/:/conf
environment:
- VROOM_ROUTER=osrm # router to use, osrm, valhalla or ors
depends_on:
- osrm
ports:
- 3000:3000
logging:
driver: "json-file"
options:
max-size: "50m"
# EXAMPLE for OSRM, please consult the repo for details: https://hub.docker.com/r/osrm/osrm-backend/
osrm:
image: osrm/osrm-backend
container_name: osrm
restart: always
ports:
- 5000:5000
volumes:
- ./osrm:/data
command: "osrm-routed --max-matching-size 1000 --max-table-size 1000 --max-viaroute-size 1000 --algorithm mld /data/wielkopolskie-latest.osrm"
logging:
driver: "json-file"
options:
max-size: "50m"
vroom-frontend:
image: vroom-frontent:latest
build:
context: ./vroom-frontend
dockerfile: ./vroom-frontend.Dockerfile
user: ${UID:-1000}:${GID:-1000}
ports:
- 9966:9966
logging:
driver: "json-file"
options:
max-size: "50m"
nominatim:
container_name: nominatim
image: mediagis/nominatim:4.0
restart: always
ports:
- "7000:8080"
environment:
# see https://github.com/mediagis/nominatim-docker/tree/master/4.0#configuration for more options
PBF_PATH: /data/wielkopolskie-latest.osm.pbf
NOMINATIM_PASSWORD: very_secure_password
volumes:
- ./nominatim:/var/lib/postgresql/12/main
- ./osrm:/data
shm_size: 1gb

@ -1,16 +1,29 @@
const { build } = require("esbuild");
const { spawn } = require("child_process");
const { sassPlugin } = require("esbuild-sass-plugin");
const glob = require("tiny-glob");
const chokidar = require("chokidar");
const glob = require("tiny-glob");
const watch = process.argv.includes("--watch");
async function build_scss(watch) {
async function build_ts() {
const entryPoints = await glob("./src/back/**/*.ts");
await build({
entryPoints,
sourcemap: true,
outdir: "./dist/back",
logLevel: "info",
platform: "node",
target: "node16",
format: "cjs",
});
}
async function complex_build(watch) {
let scss_build;
if (watch) {
const scss_watcher = chokidar.watch("src", { ignoreInitial: true });
scss_watcher.on("all", (_, path) => {
scss_watcher.on("all", async (_, path) => {
if (!scss_build) return;
if (path.endsWith(".scss") && !path.endsWith("/includes.scss")) {
// refresh the list of all scss files in includes.scss
@ -18,6 +31,8 @@ async function build_scss(watch) {
"close",
() => {
try {
debugger;
scss_build.rebuild();
console.log(`Built main.scss [on ${path}]`);
} catch (e) {
@ -31,33 +46,30 @@ async function build_scss(watch) {
}
);
}
if (path.endsWith(".ts")) {
await build_ts();
console.log(`Finished TS biuld [on ${path}]`);
}
});
}
scss_build = await build({
entryPoints: ["./src/main.scss"],
sourcemap: true,
outfile: "./public/dist/style.css",
logLevel: "info",
incremental: watch,
plugins: [sassPlugin()],
});
scss_build.rebuild();
try {
scss_build = await build({
entryPoints: ["./src/main.scss"],
sourcemap: true,
outfile: "./public/dist/style.css",
logLevel: "info",
incremental: watch,
plugins: [sassPlugin()],
});
await build_ts();
} catch (e) {
console.error(e);
}
watch && scss_build.rebuild();
}
(async () => {
const entryPoints = await glob("./src/back/**/*.ts");
build({
entryPoints,
sourcemap: true,
outdir: "./dist/back",
logLevel: "info",
platform: "node",
watch,
target: "node16",
format: "cjs",
});
build({
entryPoints: ["./src/front/index.ts"],
sourcemap: true,
@ -68,7 +80,7 @@ async function build_scss(watch) {
});
try {
await build_scss(watch);
await complex_build(watch);
} catch (e) {
console.error(e.message);
}

@ -0,0 +1,13 @@
#!/bin/bash -x
mkdir -p osrm
cd osrm || exit 1
wget http://download.geofabrik.de/europe/poland/wielkopolskie-latest.osm.pbf &
wget_pid=$!
docker pull osrm/osrm-backend &
pull_pid=$!
wait $wget_pid $pull_pid
docker run -t -v "${PWD}:/data" osrm/osrm-backend osrm-extract -p /opt/car.lua /data/wielkopolskie-latest.osm.pbf
docker run -t -v "${PWD}:/data" osrm/osrm-backend osrm-partition /data/wielkopolskie-latest.osrm
docker run -t -v "${PWD}:/data" osrm/osrm-backend osrm-customize /data/wielkopolskie-latest.osrm

64
package-lock.json generated

@ -12,7 +12,7 @@
"@babel/core": "^7.12.10",
"@hotwired/turbo": "^7.1.0",
"@koa/router": "^10.1.1",
"@sealcode/sealgen": "^0.1.7",
"@sealcode/sealgen": "^0.2.1",
"@sealcode/ts-predicates": "^0.4.0",
"esbuild-node-tsc": "^1.8.2",
"hint": "^7.0.1",
@ -2443,12 +2443,13 @@
}
},
"node_modules/@sealcode/sealgen": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.1.7.tgz",
"integrity": "sha512-hnDY3KSUYtUnf1ybNgsg/PDnEmD5IMar3IWt3HoPS/EBuMtevNKozu1IxvtcI6fY4aNfBAGygo0pgsYuhYsdBA==",
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.2.1.tgz",
"integrity": "sha512-d9zccWf8yz9pcYbPbE1KgCUdfcwiVVPQRZWKmH62ZNxQsnJkYhrxMW0/q9BLZeZkL6T1QqW8kupLFIJvQ7MlDQ==",
"dependencies": {
"@sealcode/ts-predicates": "^0.2.1",
"locreq": "^2.0.2"
"locreq": "^2.0.2",
"prompts": "^2.4.2"
},
"bin": {
"sealgen": "lib/index.js"
@ -9489,6 +9490,14 @@
"node": ">=0.10.0"
}
},
"node_modules/kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
"engines": {
"node": ">=6"
}
},
"node_modules/koa": {
"version": "2.13.4",
"resolved": "https://registry.npmjs.org/koa/-/koa-2.13.4.tgz",
@ -12656,6 +12665,18 @@
"node": ">=10"
}
},
"node_modules/prompts": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
"integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
"dependencies": {
"kleur": "^3.0.3",
"sisteransi": "^1.0.5"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/proto-list": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
@ -13932,6 +13953,11 @@
"semver": "bin/semver.js"
}
},
"node_modules/sisteransi": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
},
"node_modules/slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@ -18202,12 +18228,13 @@
}
},
"@sealcode/sealgen": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.1.7.tgz",
"integrity": "sha512-hnDY3KSUYtUnf1ybNgsg/PDnEmD5IMar3IWt3HoPS/EBuMtevNKozu1IxvtcI6fY4aNfBAGygo0pgsYuhYsdBA==",
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.2.1.tgz",
"integrity": "sha512-d9zccWf8yz9pcYbPbE1KgCUdfcwiVVPQRZWKmH62ZNxQsnJkYhrxMW0/q9BLZeZkL6T1QqW8kupLFIJvQ7MlDQ==",
"requires": {
"@sealcode/ts-predicates": "^0.2.1",
"locreq": "^2.0.2"
"locreq": "^2.0.2",
"prompts": "^2.4.2"
},
"dependencies": {
"@sealcode/ts-predicates": {
@ -23419,6 +23446,11 @@
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
},
"kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="
},
"koa": {
"version": "2.13.4",
"resolved": "https://registry.npmjs.org/koa/-/koa-2.13.4.tgz",
@ -25896,6 +25928,15 @@
"retry": "^0.12.0"
}
},
"prompts": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
"integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
"requires": {
"kleur": "^3.0.3",
"sisteransi": "^1.0.5"
}
},
"proto-list": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
@ -26862,6 +26903,11 @@
}
}
},
"sisteransi": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
},
"slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",

@ -20,7 +20,7 @@
"@babel/core": "^7.12.10",
"@hotwired/turbo": "^7.1.0",
"@koa/router": "^10.1.1",
"@sealcode/sealgen": "^0.1.7",
"@sealcode/sealgen": "^0.2.1",
"@sealcode/ts-predicates": "^0.4.0",
"esbuild-node-tsc": "^1.8.2",
"hint": "^7.0.1",

@ -8,7 +8,7 @@ const PORT = process.env.SEALIOUS_PORT ? parseInt(process.env.SEALIOUS_PORT) : 8
const base_url = process.env.SEALIOUS_BASE_URL || `http://localhost:${PORT}`;
const MONGO_PORT = process.env.SEALIOUS_MONGO_PORT
? parseInt(process.env.SEALIOUS_MONGO_PORT)
: 20726;
: 20747;
const MONGO_HOST = process.env.SEALIOUS_MONGO_HOST || "127.0.0.1";
export default class TheApp extends App {

@ -4,6 +4,8 @@ import { App } from "sealious";
import _GroupsToUsers from "./groups-to-users";
import _Groups from "./groups";
import _PasswordResetIntents from "./password-reset-intents";
import _Places from "./places";
import _Routes from "./routes";
import _Secrets from "./secrets";
import _UserRoles from "./user-roles";
import _Users from "./users";
@ -11,6 +13,8 @@ import _Users from "./users";
export const GroupsToUsers = new _GroupsToUsers();
export const Groups = new _Groups();
export const PasswordResetIntents = new _PasswordResetIntents();
export const Places = new _Places();
export const Routes = new _Routes();
export const Secrets = new _Secrets();
export const UserRoles = new _UserRoles();
export const Users = new _Users();
@ -20,6 +24,8 @@ export const collections = {
"groups-to-users": GroupsToUsers,
groups: Groups,
"password-reset-intents": PasswordResetIntents,
places: Places,
routes: Routes,
secrets: Secrets,
"user-roles": UserRoles,
users: Users,

@ -0,0 +1,8 @@
import { Collection, FieldTypes, Policies } from "sealious";
export default class Places extends Collection {
fields = {
content: new FieldTypes.Text(),
};
defaultPolicy = new Policies.Public();
}

@ -0,0 +1,11 @@
import { Collection, FieldTypes, Policies } from "sealious";
export default class Routes extends Collection {
fields = {
name: FieldTypes.Required(new FieldTypes.Text()),
};
defaultPolicy = new Policies.Owner();
policies = {
create: new Policies.LoggedIn(),
};
}

@ -1,4 +1,5 @@
import { BaseContext } from "koa";
import { AddRouteURL, ListRoutesURL } from "../routes";
export default async function navbar(ctx: BaseContext) {
return /* HTML */ ` <nav>
@ -13,6 +14,8 @@ export default async function navbar(ctx: BaseContext) {
</a>
<ul>
<li><a href="/logowanie">Logowanie</a></li>
<li><a href="${ListRoutesURL}">Trasy</a></li>
<li><a href="${AddRouteURL}">Nowa trasa</a></li>
</ul>
</nav>`;
}

@ -0,0 +1,17 @@
import { BaseContext } from "koa";
import { tempstream } from "tempstream";
import { Page } from "../../page/page";
export const actionName = "ShowRoute";
export default new (class ShowRoutePage extends Page {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async canAccess(_: BaseContext) {
return { canAccess: true, message: "" };
}
async render(ctx: BaseContext) {
const id = ctx.params.id;
return tempstream/* HTML */ `<div></div>`;
}
})();

@ -0,0 +1,17 @@
import { withProdApp } from "../../test_utils/with-prod-app";
import { LONG_TEST_TIMEOUT, webhintURL } from "../../test_utils/webhint";
import { ShowRouteURL } from "../routes";
describe("ShowRoute", () => {
it("doesn't crash", async function () {
this.timeout(LONG_TEST_TIMEOUT);
return withProdApp(async ({ base_url, rest_api }) => {
await rest_api.get(ShowRouteURL);
await webhintURL(base_url + ShowRouteURL);
// alternatively you can use webhintHTML for faster but less precise scans
// or for scanning responses of requests that use some form of authorization:
// const response = await rest_api.get(ShowRouteURL);
// await webhintHTML(response);
});
});
});

@ -0,0 +1,38 @@
import { BaseContext } from "koa";
import { CollectionItem } from "sealious";
import { tempstream } from "tempstream";
import { Routes } from "../collections/collections";
import html from "../html";
import {
BaseListPageDefaultProps,
BaseListPageProps,
BaseListPagePropsShape,
SealiousItemListPage,
} from "../page/list";
import { AllQueryParams } from "../page/props-parser";
export const actionName = "ListRoutes";
export default new (class ListRoutesPage extends SealiousItemListPage<typeof Routes> {
propsParser = new AllQueryParams<BaseListPageProps>(
BaseListPagePropsShape,
BaseListPageDefaultProps
);
async render(ctx: BaseContext) {
return html(
ctx,
"Trasy",
tempstream/* HTML */ `<div>
<h2>Zapisane trasy</h2>
<ul>
${super.render(ctx)}
</ul>
</div>`
);
}
async renderItem(_: BaseContext, item: CollectionItem<typeof Routes>) {
return tempstream`<li><a href="">${item.get("name") as string}</a></li>`;
}
})(Routes);

@ -0,0 +1,17 @@
import { withProdApp } from "../test_utils/with-prod-app";
import { LONG_TEST_TIMEOUT, webhintURL } from "../test_utils/webhint";
import { ListRoutesURL } from "./routes";
describe("ListRoutes", () => {
it("doesn't crash", async function () {
this.timeout(LONG_TEST_TIMEOUT);
return withProdApp(async ({ base_url, rest_api }) => {
await rest_api.get(ListRoutesURL);
await webhintURL(base_url + ListRoutesURL);
// alternatively you can use webhintHTML for faster but less precise scans
// or for scanning responses of requests that use some form of authorization:
// const response = await rest_api.get(ListRoutesURL);
// await webhintHTML(response);
});
});
});

@ -3,13 +3,28 @@
import Router from "@koa/router";
import { Middlewares } from "sealious";
import { default as ShowRoute } from "./route/[id].page";
import { default as AddRoute } from "./routes/add.form";
import { default as ListRoutes } from "./routes.list";
import { default as TestComplex } from "./test-complex.form";
import { default as MyProfile } from "./users/me.page";
export const ShowRouteURL = "/route/:id/";
export const AddRouteURL = "/routes/add/";
export const ListRoutesURL = "/routes/";
export const TestComplexURL = "/test-complex/";
export const MyProfileURL = "/users/me/";
export default function mountAutoRoutes(router: Router) {
router.use(ShowRouteURL, Middlewares.extractContext(), Middlewares.parseBody());
ShowRoute.mount(router, ShowRouteURL);
router.use(AddRouteURL, Middlewares.extractContext(), Middlewares.parseBody());
AddRoute.mount(router, AddRouteURL);
router.use(ListRoutesURL, Middlewares.extractContext(), Middlewares.parseBody());
ListRoutes.mount(router, ListRoutesURL);
router.use(TestComplexURL, Middlewares.extractContext(), Middlewares.parseBody());
TestComplex.mount(router, TestComplexURL);

@ -0,0 +1,33 @@
import { BaseContext } from "koa";
import { Routes } from "../../collections/collections";
import { FormHeader, SimpleInput } from "../../forms/controls";
import { FormField } from "../../forms/field";
import Form, { FormData } from "../../forms/form";
import { collectionFieldValidator } from "../../forms/validator";
import html from "../../html";
export const actionName = "AddRoute";
export default new (class AddRouteForm extends Form {
defaultSuccessMessage = "Pomyślnie utworzono użytkownika";
fields = [new FormField("name", true, collectionFieldValidator(Routes.fields.name))];
controls = [
new FormHeader("Dodaj nową trasę"),
new SimpleInput("name", { label: "Nazwa trasy", type: "text" }),
];
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async canAccess(_: BaseContext) {
return { canAccess: true, message: "" };
}
async onSubmit(ctx: BaseContext, values: Record<"name", string>) {
await ctx.$app.collections.routes.create(ctx.$context, values);
}
async render(ctx: BaseContext, data: FormData, path: string) {
return html(ctx, "Nowa trasa", await super.render(ctx, data, path));
}
})();

@ -0,0 +1,17 @@
import { withProdApp } from "../../test_utils/with-prod-app";
import { LONG_TEST_TIMEOUT, webhintURL } from "../../test_utils/webhint";
import { AddRouteURL } from "../routes";
describe("AddRoute", () => {
it("doesn't crash", async function () {
this.timeout(LONG_TEST_TIMEOUT);
return withProdApp(async ({ base_url, rest_api }) => {
await rest_api.get(AddRouteURL);
await webhintURL(base_url + AddRouteURL);
// alternatively you can use webhintHTML for faster but less precise scans
// or for scanning responses of requests that use some form of authorization:
// const response = await rest_api.get(AddRouteURL);
// await webhintHTML(response);
});
});
});

@ -0,0 +1,80 @@
cliArgs:
geometry: false # retrieve geometry (-g)
planmode: false # run vroom in plan mode (-c) if set to true
threads: 4 # number of threads to use (-t)
explore: 5 # exploration level to use (0..5) (-x)
limit: '1mb' # max request size
logdir: '/..' # the path for the logs relative to ./src
logsize: '100M' # max log file size for rotation
maxlocations: 1000 # max number of jobs/shipments locations
maxvehicles: 200 # max number of vehicles
override: true # allow cli options override (-c, -g, -t and -x)
path: '' # VROOM path (if not in $PATH)
port: 3000 # expressjs port
router: 'osrm' # routing backend (osrm, libosrm or ors)
timeout: 300000 # milli-seconds
baseurl: '/' #base url for api
routingServers:
osrm:
car:
host: '0.0.0.0'
port: '5000'
bike:
host: '0.0.0.0'
port: '5001'
foot:
host: '0.0.0.0'
port: '5002'
ors:
driving-car:
host: '0.0.0.0'
port: '8080'
driving-hgv:
host: '0.0.0.0'
port: '8080'
cycling-regular:
host: '0.0.0.0'
port: '8080'
cycling-mountain:
host: '0.0.0.0'
port: '8080'
cycling-road:
host: '0.0.0.0'
port: '8080'
cycling-electric:
host: '0.0.0.0'
port: '8080'
foot-walking:
host: '0.0.0.0'
port: '8080'
foot-hiking:
host: '0.0.0.0'
port: '8080'
valhalla:
auto:
host: '0.0.0.0'
port: '8002'
bicycle:
host: '0.0.0.0'
port: '8002'
pedestrian:
host: '0.0.0.0'
port: '8002'
motorcycle:
host: '0.0.0.0'
port: '8002'
motor_scooter:
host: '0.0.0.0'
port: '8002'
taxi:
host: '0.0.0.0'
port: '8002'
hov:
host: '0.0.0.0'
port: '8002'
truck:
host: '0.0.0.0'
port: '8002'
bus:
host: '0.0.0.0'
port: '8002'

@ -0,0 +1,7 @@
FROM node:18
RUN git clone https://github.com/VROOM-Project/vroom-frontend.git /opt/vroom-frontend
WORKDIR /opt/vroom-frontend
RUN npm install
CMD npm run serve
Loading…
Cancel
Save