diff --git a/.gitignore b/.gitignore index 8260b0d..459c23c 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,5 @@ public/dist /log.html /hint-report/ .vscode +/osrm/ +/nominatim/ diff --git a/README.md b/README.md index 4fa97d1..7d1bf64 100644 --- a/README.md +++ b/README.md @@ -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 ``` diff --git a/docker-compose.yml b/docker-compose.yml index 59e44d4..8e0328f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 diff --git a/esbuild.js b/esbuild.js index 99f1a17..354736b 100644 --- a/esbuild.js +++ b/esbuild.js @@ -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); } diff --git a/get-map.sh b/get-map.sh new file mode 100755 index 0000000..760226c --- /dev/null +++ b/get-map.sh @@ -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 + diff --git a/package-lock.json b/package-lock.json index be38939..79bf27e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 9dd0542..c362ef1 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/back/app.ts b/src/back/app.ts index 551faeb..d42b3d1 100644 --- a/src/back/app.ts +++ b/src/back/app.ts @@ -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 { diff --git a/src/back/collections/collections.ts b/src/back/collections/collections.ts index 558871c..69a1b34 100644 --- a/src/back/collections/collections.ts +++ b/src/back/collections/collections.ts @@ -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, diff --git a/src/back/collections/places.ts b/src/back/collections/places.ts new file mode 100644 index 0000000..d788171 --- /dev/null +++ b/src/back/collections/places.ts @@ -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(); +} diff --git a/src/back/collections/routes.ts b/src/back/collections/routes.ts new file mode 100644 index 0000000..78dc048 --- /dev/null +++ b/src/back/collections/routes.ts @@ -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(), + }; +} diff --git a/src/back/routes/common/navbar.ts b/src/back/routes/common/navbar.ts index 804d54d..f3e18df 100644 --- a/src/back/routes/common/navbar.ts +++ b/src/back/routes/common/navbar.ts @@ -1,4 +1,5 @@ import { BaseContext } from "koa"; +import { AddRouteURL, ListRoutesURL } from "../routes"; export default async function navbar(ctx: BaseContext) { return /* HTML */ ` `; } diff --git a/src/back/routes/route/[id].page.ts b/src/back/routes/route/[id].page.ts new file mode 100644 index 0000000..4e01cf3 --- /dev/null +++ b/src/back/routes/route/[id].page.ts @@ -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 */ `
`; + } +})(); diff --git a/src/back/routes/route/[id].test.ts b/src/back/routes/route/[id].test.ts new file mode 100644 index 0000000..11937b4 --- /dev/null +++ b/src/back/routes/route/[id].test.ts @@ -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); + }); + }); +}); diff --git a/src/back/routes/routes.list.ts b/src/back/routes/routes.list.ts new file mode 100644 index 0000000..9967992 --- /dev/null +++ b/src/back/routes/routes.list.ts @@ -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