Skip to content

Commit

Permalink
Merge branch 'main' into vue3
Browse files Browse the repository at this point in the history
  • Loading branch information
cdauth committed Nov 15, 2023
2 parents 0efa96f + 9e2d259 commit e6d612f
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 47 deletions.
37 changes: 35 additions & 2 deletions docs/src/developers/server/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ This manual assumes that you have docker set up on your system.

The FacilMap server is available as [`facilmap/facilmap`](https://hub.docker.com/r/facilmap/facilmap/) on Docker Hub. The [configuration](./config.md) can be defined using environment variables. The container will expose a HTTP server on port 8080, which you should put behind a reverse proxy such as [nginx-proxy](https://hub.docker.com/r/jwilder/nginx-proxy) or [traefik](https://traefik.io/traefik/) for HTTPS support.

FacilMap needs a database supported by [Sequelize](https://sequelize.org/master/) to run, it is recommended to use MySQL/MariaDB. When creating a database for FacilMap, make sure to use the `utf8mb4` charset/collation to make sure that characters from all languages can be used on a map. By default, MySQL/MariaDB uses the `latin1` charset, which mostly supports only basic latin characters. When you start the FacilMap server for the first time, the necessary tables are created using the charset of the database.
FacilMap needs a database supported by [Sequelize](https://sequelize.org/master/) to run, it is recommended to use MySQL/MariaDB. When creating a MySQL/MariaDB database for FacilMap, make sure to use the `utf8mb4` charset/collation to make sure that characters from all languages can be used on a map. By default, MySQL/MariaDB uses the `latin1` charset, which mostly supports only basic latin characters. When you start the FacilMap server for the first time, the necessary tables are created using the charset of the database. When using PostgreSQL, the PostGIS extensions must be enabled.

## docker-compose

To run FacilMap using [docker-compose](https://docs.docker.com/compose/), here is an example `docker-compose.yml`:
To run FacilMap with MySQL using [docker-compose](https://docs.docker.com/compose/), here is an example `docker-compose.yml`:

```yaml
version: "2"
Expand Down Expand Up @@ -45,6 +45,39 @@ services:
restart: unless-stopped
```

Here is an example with Postgres:

```yaml
version: "2"
services:
facilmap:
image: facilmap/facilmap
ports:
- 8080
links:
- db
environment:
USER_AGENT: My FacilMap (https://facilmap.example.org/, [email protected])
DB_TYPE: postgres
DB_HOST: db
DB_NAME: facilmap
DB_USER: facilmap
DB_PASSWORD: password
ORS_TOKEN: # Get an API key on https://go.openrouteservice.org/ (needed for routing)
MAPBOX_TOKEN: # Get an API key on https://www.mapbox.com/signup/ (needed for routing)
MAPZEN_TOKEN: # Get an API key on https://mapzen.com/developers/sign_up (needed for elevation info)
MAXMIND_USER_ID: # Sign up here https://www.maxmind.com/en/geolite2/signup (needed for geoip lookup to show initial map state)
MAXMIND_LICENSE_KEY:
restart: unless-stopped
db:
image: postgis/postgis
environment:
POSTGRES_USER: facilmap
POSTGRES_PASSWORD: password
POSTGRES_DB: facilmap
restart: unless-stopped
```

To start FacilMap, run `docker-compose up -d` in the directory of the `docker-compose.yml` file. To upgrade FacilMap, run `docker-compose pull` and then restart it by running `docker-compose up -d`.

## docker create
Expand Down
4 changes: 3 additions & 1 deletion docs/src/developers/server/standalone.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

The FacilMap server is written in [node.js](https://nodejs.org/en/). To run the FacilMap server, the following dependencies are needed:
* You need to have a recent version of node.js and npm installed.
* You need to create a database on one of the systems supported by [Sequelize](https://sequelize.org/master/), it is recommended to use MySQL/MariaDB. When creating a database for FacilMap, make sure to use the `utf8mb4` charset/collation to make sure that characters from all languages can be used on a map. By default, MySQL/MariaDB uses the `latin1` charset, which mostly supports only basic latin characters. When you start the FacilMap server for the first time, the necessary tables are created using the charset of the database.
* You need to create a database on one of the systems supported by [Sequelize](https://sequelize.org/master/), it is recommended to use MySQL/MariaDB.
* When creating a MySQL/MariaDB database for FacilMap, make sure to use the `utf8mb4` charset/collation to make sure that characters from all languages can be used on a map. By default, MySQL/MariaDB uses the `latin1` charset, which mostly supports only basic latin characters. When you start the FacilMap server for the first time, the necessary tables are created using the charset of the database.
* When using PostgreSQL, the PostGIS extensions must be enabled.
* It is recommended to run FacilMap as an unprivileged user.

## Run the latest release
Expand Down
1 change: 1 addition & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"@types/string-similarity": "^4.0.2",
"cpy-cli": "^5.0.0",
"debug": "^4.3.4",
"pg": "^8.11.3",
"rimraf": "^5.0.5",
"ts-node": "^10.9.1",
"typescript": "^5.2.2",
Expand Down
76 changes: 48 additions & 28 deletions server/src/database/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,34 +110,6 @@ export interface BboxWithExcept extends Bbox {
except?: Bbox;
}

export function makeBboxCondition(bbox: BboxWithExcept | null | undefined, posField = "pos"): WhereOptions {
if(!bbox)
return { };

const conditions = [ ];
conditions.push(
Sequelize.fn(
"MBRContains",
Sequelize.fn("LINESTRING", Sequelize.fn("POINT", bbox.left, bbox.bottom), Sequelize.fn("POINT", bbox.right, bbox.top)),
Sequelize.col(posField)
)
);

if(bbox.except) {
conditions.push({
[Op.not]: Sequelize.fn(
"MBRContains",
Sequelize.fn("LINESTRING", Sequelize.fn("POINT", bbox.except.left, bbox.except.bottom), Sequelize.fn("POINT", bbox.except.right, bbox.except.top)),
Sequelize.col(posField)
)
});
}

return {
[Op.and]: conditions
};
}

export default class DatabaseHelpers {

_db: Database;
Expand Down Expand Up @@ -339,6 +311,54 @@ export default class DatabaseHelpers {
await model.bulkCreate(this._dataToArr(data, idObj));
}

makeBboxCondition(bbox: BboxWithExcept | null | undefined, posField = "pos"): WhereOptions {
const dbType = this._db._conn.getDialect()
if(!bbox)
return { };

const conditions = [ ];
if(dbType == 'postgres') {
conditions.push(
Sequelize.where(
Sequelize.fn("ST_MakeLine", Sequelize.fn("St_Point", bbox.left, bbox.bottom), Sequelize.fn("St_Point", bbox.right, bbox.top)),
"~",
Sequelize.col(posField))
);
} else {
conditions.push(
Sequelize.fn(
"MBRContains",
Sequelize.fn("LINESTRING", Sequelize.fn("POINT", bbox.left, bbox.bottom), Sequelize.fn("POINT", bbox.right, bbox.top)),
Sequelize.col(posField)
)
);
}

if(bbox.except) {
if(dbType == 'postgres') {
conditions.push({
[Op.not]: Sequelize.where(
Sequelize.fn("St_MakeLine", Sequelize.fn("St_Point", bbox.except.left, bbox.except.bottom), Sequelize.fn("St_Point", bbox.except.right, bbox.except.top)),
"~",
Sequelize.col(posField)
)
});
} else {
conditions.push({
[Op.not]: Sequelize.fn(
"MBRContains",
Sequelize.fn("LINESTRING", Sequelize.fn("POINT", bbox.except.left, bbox.except.bottom), Sequelize.fn("POINT", bbox.except.right, bbox.except.top)),
Sequelize.col(posField)
)
});
}
}

return {
[Op.and]: conditions
};
}

async renameObjectDataField(padId: PadId, typeId: ID, rename: Record<string, { name?: string; values?: Record<string, string> }>, isLine: boolean): Promise<void> {
const objectStream = (isLine ? this._db.lines.getPadLinesByType(padId, typeId) : this._db.markers.getPadMarkersByType(padId, typeId));

Expand Down
42 changes: 35 additions & 7 deletions server/src/database/line.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type CreationAttributes, type CreationOptional, DataTypes, type ForeignKey, type HasManyGetAssociationsMixin, type InferAttributes, type InferCreationAttributes, Model, Op } from "sequelize";
import type { BboxWithZoom, ID, Latitude, Line, ExtraInfo, Longitude, PadId, Point, Route, TrackPoint, CRU } from "facilmap-types";
import Database from "./database.js";
import { type BboxWithExcept, createModel, dataDefinition, type DataModel, getDefaultIdType, getLatType, getLonType, getPosType, getVirtualLatType, getVirtualLonType, makeBboxCondition, makeNotNullForeignKey } from "./helpers.js";
import { type BboxWithExcept, createModel, dataDefinition, type DataModel, getDefaultIdType, getLatType, getLonType, getPosType, getVirtualLatType, getVirtualLonType, makeNotNullForeignKey } from "./helpers.js";
import { chunk, groupBy, isEqual, mapValues, omit } from "lodash-es";
import { calculateRouteForLine } from "../routing/routing.js";
import type { PadModel } from "./pad";
Expand Down Expand Up @@ -89,9 +89,30 @@ export default class DatabaseLines {
width : { type: DataTypes.INTEGER.UNSIGNED, allowNull: false },
name : { type: DataTypes.TEXT, allowNull: false },
distance : { type: DataTypes.FLOAT(24, 2).UNSIGNED, allowNull: true },
time : { type: DataTypes.INTEGER.UNSIGNED, allowNull: true },
ascent : { type: DataTypes.INTEGER.UNSIGNED, allowNull: true },
descent : { type: DataTypes.INTEGER.UNSIGNED, allowNull: true },
time : {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: true,
set: function(this: LineModel, v: number | null) {
// Round number to avoid integer column error in Postgres
this.setDataValue("time", v != null ? Math.round(v) : v);
}
},
ascent : {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: true,
set: function(this: LineModel, v: number | null) {
// Round number to avoid integer column error in Postgres
this.setDataValue("ascent", v != null ? Math.round(v) : v);
}
},
descent : {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: true,
set: function(this: LineModel, v: number | null) {
// Round number to avoid integer column error in Postgres
this.setDataValue("descent", v != null ? Math.round(v) : v);
}
},
top: getLatType(),
bottom: getLatType(),
left: getLonType(),
Expand Down Expand Up @@ -119,7 +140,14 @@ export default class DatabaseLines {
pos: getPosType(),
zoom: { type: DataTypes.INTEGER.UNSIGNED, allowNull: false, validate: { min: 1, max: 20 } },
idx: { type: DataTypes.INTEGER.UNSIGNED, allowNull: false },
ele: { type: DataTypes.INTEGER, allowNull: true }
ele: {
type: DataTypes.INTEGER,
allowNull: true,
set: function(this: LinePointModel, v: number | null) {
// Round number to avoid integer column error in Postgres
this.setDataValue("ele", v != null ? Math.round(v) : v);
}
}
}, {
sequelize: this._db._conn,
indexes: [
Expand Down Expand Up @@ -273,7 +301,7 @@ export default class DatabaseLines {
zoom: { [Op.lte]: bboxWithZoom.zoom },
lineId: { [Op.in]: lineIds }
},
makeBboxCondition(bboxWithZoom)
this._db.helpers.makeBboxCondition(bboxWithZoom)
]
},
attributes: ["pos", "lat", "lon", "ele", "zoom", "idx", "lineId"]
Expand All @@ -296,4 +324,4 @@ export default class DatabaseLines {
return points.map((point) => omit(point.toJSON(), ["pos"]) as TrackPoint);
}

}
}
15 changes: 11 additions & 4 deletions server/src/database/marker.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type CreationOptional, DataTypes, type ForeignKey, type InferAttributes, type InferCreationAttributes, Model } from "sequelize";
import type { BboxWithZoom, CRU, ID, Latitude, Longitude, Marker, PadId } from "facilmap-types";
import { type BboxWithExcept, createModel, dataDefinition, type DataModel, getDefaultIdType, getPosType, getVirtualLatType, getVirtualLonType, makeBboxCondition, makeNotNullForeignKey } from "./helpers.js";
import { type BboxWithExcept, createModel, dataDefinition, type DataModel, getDefaultIdType, getPosType, getVirtualLatType, getVirtualLonType, makeNotNullForeignKey } from "./helpers.js";
import Database from "./database.js";
import { getElevationForPoint } from "../elevation.js";
import type { PadModel } from "./pad.js";
Expand Down Expand Up @@ -43,7 +43,14 @@ export default class DatabaseMarkers {
size : { type: DataTypes.INTEGER.UNSIGNED, allowNull: false },
symbol : { type: DataTypes.TEXT, allowNull: false },
shape : { type: DataTypes.TEXT, allowNull: false },
ele: { type: DataTypes.INTEGER, allowNull: true }
ele: {
type: DataTypes.INTEGER,
allowNull: true,
set: function(this: MarkerModel, v: number | null) {
// Round number to avoid integer column error in Postgres
this.setDataValue("ele", v != null ? Math.round(v) : v);
}
}
}, {
sequelize: this._db._conn,
// pos index is created in migration
Expand All @@ -69,7 +76,7 @@ export default class DatabaseMarkers {
}

getPadMarkers(padId: PadId, bbox?: BboxWithZoom & BboxWithExcept): AsyncGenerator<Marker, void, void> {
return this._db.helpers._getPadObjects<Marker>("Marker", padId, { where: makeBboxCondition(bbox) });
return this._db.helpers._getPadObjects<Marker>("Marker", padId, { where: this._db.helpers.makeBboxCondition(bbox) });
}

getPadMarkersByType(padId: PadId, typeId: ID): AsyncGenerator<Marker, void, void> {
Expand Down Expand Up @@ -120,4 +127,4 @@ export default class DatabaseMarkers {
return result;
}

}
}
15 changes: 11 additions & 4 deletions server/src/database/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { generateRandomId } from "../utils/utils.js";
import { DataTypes, type InferAttributes, type InferCreationAttributes, Model, Op, type WhereOptions } from "sequelize";
import Database from "./database.js";
import type { BboxWithZoom, ID, Latitude, Longitude, PadId, Point, Route, RouteMode, TrackPoint } from "facilmap-types";
import { type BboxWithExcept, createModel, getPosType, getVirtualLatType, getVirtualLonType, makeBboxCondition } from "./helpers.js";
import { type BboxWithExcept, createModel, getPosType, getVirtualLatType, getVirtualLonType } from "./helpers.js";
import { calculateRouteForLine } from "../routing/routing.js";
import { omit } from "lodash-es";
import type { Point as GeoJsonPoint } from "geojson";
Expand Down Expand Up @@ -40,7 +40,14 @@ export default class DatabaseRoutes {
pos: getPosType(),
zoom: { type: DataTypes.INTEGER.UNSIGNED, allowNull: false, validate: { min: 1, max: 20 } },
idx: { type: DataTypes.INTEGER.UNSIGNED, allowNull: false },
ele: { type: DataTypes.INTEGER, allowNull: true }
ele: {
type: DataTypes.INTEGER,
allowNull: true,
set: function(this: RoutePointModel, v: number | null) {
// Round number to avoid integer column error in Postgres
this.setDataValue("ele", v != null ? Math.round(v) : v);
}
}
}, {
sequelize: this._db._conn,
indexes: [
Expand All @@ -56,7 +63,7 @@ export default class DatabaseRoutes {
routeId,
...(!bboxWithZoom ? {} : {
[Op.or]: [
{ [Op.and]: [ makeBboxCondition(bboxWithZoom), { zoom: { [Op.lte]: bboxWithZoom.zoom } } ] },
{ [Op.and]: [ this._db.helpers.makeBboxCondition(bboxWithZoom), { zoom: { [Op.lte]: bboxWithZoom.zoom } } ] },
...(!getCompleteBasicRoute ? [] : [
{ zoom: { [Op.lte]: 5 } }
])
Expand Down Expand Up @@ -210,4 +217,4 @@ export default class DatabaseRoutes {
return data.map((d) => omit(d.toJSON(), ["pos"]) as TrackPoint);
}

}
}
Loading

0 comments on commit e6d612f

Please sign in to comment.