Skip to content

Commit

Permalink
Fix Merge Conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
ingalls committed Oct 8, 2024
2 parents 2cf972b + 22a958d commit d50ee9c
Show file tree
Hide file tree
Showing 51 changed files with 6,505 additions and 6,947 deletions.
2 changes: 1 addition & 1 deletion .deploy
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"artifacts": {
"docker": [
"coe-ecr-etl:{{gitsha}}",
"coe-ecr-etl:task-{{gitsha}}",
"coe-ecr-etl:data-{{gitsha}}",
"coe-ecr-etl:pmtiles-{{gitsha}}",
"coe-ecr-etl:hooks-{{gitsha}}",
"coe-ecr-etl:events-{{gitsha}}"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ecr_task.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
if: github.event.pull_request.draft == false
strategy:
matrix:
task: [task, pmtiles, hooks, events]
task: [data, pmtiles, hooks, events]
steps:
- uses: actions/checkout@v4
with:
Expand All @@ -35,7 +35,7 @@ jobs:
aws-region: ${{secrets.AWS_REGION}}

- name: Docker Build Task
run: docker compose build ${{ matrix.task }}
run: docker buildx build ./tasks/${{ matrix.task }} -t cloudtak-${{ matrix.task }}

- name: Login to Amazon ECR
id: login-ecr
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ jobs:
ref: ${{github.event.pull_request.head.sha || github.sha}}

- name: Docker Data Task Build
run: docker compose build task
run: docker buildx build ./tasks/data -t cloudtak-data

- name: Docker Data Task Lint
run: docker run cloudtak-task:latest npm run lint
run: docker run cloudtak-data:latest npm run lint

- name: Docker Data Task Test
run: docker run cloudtak-task:latest npm test
run: docker run cloudtak-data:latest npm test

pmtiles:
runs-on: ubuntu-latest
Expand Down
40 changes: 40 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,46 @@

### Pending Release

### v4.7.0 - 2024-10-07

- :rocket: `UI/API` Add initial configuration mode
- :rocket: Rename `task-{{sha}}` to `data-{{sha}}` to match folder names

### v4.6.0 - 2024-10-03

- :rocket: `UI` Add Sector Drawing Mode

### v4.5.1 - 2024-10-02

- :rocket: `UI` Use colour to show terrain enablement state
- :rocket: `UI` Allow toggling 3D terrain on and off

### v4.5.0 - 2024-10-02

- :tada: `UI` Show a 3D Terrain button if 3D Terrain is enabled

### v4.4.2 - 2024-10-02

- :rocket: `API` Start to make improvements to caching behavior on initial load

### v4.4.1 - 2024-10-01

- :bug: `API` Set `useCache=true` when making Group List calls

### v4.4.0 - 2024-10-01

- :rocket: `UI` Use geolocation.watchPosition API instead of individual getLocation calls

### v4.3.0 - 2024-10-01

- :tada: Add the ability to see what channels a user has active (limited to channels you have access to)

### v4.2.0 - 2024-09-30

- :white_check_mark: Add initial Basemap Tests
- :white_check_mark: Add initial Server Config Tests
- :rocket: Add initial Certificate expiry API for admin cert

### v4.1.1 - 2024-09-26

- :bug: `API` Fix const assignment
Expand Down
683 changes: 662 additions & 21 deletions LICENSE

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ docker-compose up --build
Once the database and API service have built, the server will start on port 5000.
In your webbrowser visit `http://localhost:5000` to view the ETL UI

For non-aws environments, the default username & password is `admin`, `admin`.
This value can be customized via the `TAK_USERNAME` & `TAK_PASSWORD` env vars

Installation outside of the docker environment is also fairly straightforward.
In the `./api`, perform the following

Expand Down
6 changes: 5 additions & 1 deletion api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ try {

process.env = Object.assign(JSON.parse(String(fs.readFileSync(dotfile))), process.env);
} catch (err) {
console.log('ok - no .env file loaded', err);
if (err instanceof Error && err.message.startsWith('ENOENT')) {
console.log('ok - no .env file loaded - none found');
} else {
console.log('ok - no .env file loaded', err);
}
}

const pkg = JSON.parse(String(fs.readFileSync(new URL('./package.json', import.meta.url))));
Expand Down
6 changes: 5 additions & 1 deletion api/lib/api/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export const Content = Type.Object({
Name: Type.String()
});

export const Config = Type.Object({
uploadSizeLimit: Type.Integer()
})

export default class File {
api: TAKAPI;

Expand Down Expand Up @@ -130,7 +134,7 @@ export default class File {
});
}

async config() {
async config(): Promise<Static<typeof Config>> {
const url = new URL('/files/api/config', this.api.url);

return await this.api.fetch(url, {
Expand Down
81 changes: 81 additions & 0 deletions api/lib/api/subscriptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import TAKAPI from '../tak-api.js';
import { Type, Static } from '@sinclair/typebox';
import { Group } from './groups.js';
import { TAKList } from './types.js';

export const Subscription = Type.Object({
dn: Type.Union([Type.String(), Type.Null()]),
callsign: Type.String(),
clientUid: Type.String(),
lastReportMilliseconds: Type.Integer(),
takClient: Type.String(),
takVersion: Type.String(),
username: Type.String(),
groups: Type.Array(Group),
role: Type.String(),
team: Type.String(),
ipAddress: Type.String(),
port: Type.String(),
pendingWrites: Type.Integer(),
numProcessed: Type.Integer(),
protocol: Type.String(),
xpath: Type.Union([Type.String(), Type.Null()]),
subscriptionUid: Type.String(),
appFramerate: Type.String(),
battery: Type.String(),
batteryStatus: Type.String(),
batteryTemp: Type.String(),
deviceDataRx: Type.String(),
deviceDataTx: Type.String(),
heapCurrentSize: Type.String(),
heapFreeSize: Type.String(),
heapMaxSize: Type.String(),
deviceIPAddress: Type.String(),
storageAvailable: Type.String(),
storageTotal: Type.String(),
incognito: Type.Boolean(),
handlerType: Type.String(),
lastReportDiffMilliseconds: Type.Integer()
});

export const ListSubscriptionInput = Type.Object({
sortBy: Type.String({
default: 'CALLSIGN',
enum: ['CALLSIGN', 'UID']
}),
direction: Type.String({
default: 'ASCENDING',
enum: ['ASCENDING', 'DESCENDING']
}),
page: Type.Integer({
default: -1
}),
limit: Type.Integer({
default: -1
})
})

export default class {
api: TAKAPI;

constructor(api: TAKAPI) {
this.api = api;
}

async list(
query: Static<typeof ListSubscriptionInput>
): Promise<TAKList<Static<typeof Subscription>>> {
const url = new URL(`/Marti/api/subscriptions/all`, this.api.url);

let q: keyof Static<typeof ListSubscriptionInput>;
for (q in query) {
if (query[q] !== undefined) {
url.searchParams.append(q, String(query[q]));
}
}

return await this.api.fetch(url, {
method: 'GET'
});
}
}
26 changes: 25 additions & 1 deletion api/lib/control/tilejson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,30 @@ export default class TileJSON {
if (errors.length) throw new Err(400, null, JSON.stringify(errors));
}

static isValidURL(str: string): void {
let url: URL;

try {
url = new URL(str);
} catch (err) {
throw new Err(400, err instanceof Error ? err : new Error(String(err)), 'Invalid URL provided');
}

if (!['http:', 'https:'].includes(url.protocol)) {
throw new Err(400, null, 'Only HTTP and HTTPS Protocols are supported');
}

// Consistent Mapbox Style ZXY Endpoints: {z} vs TAK: {$z}
const pathname = decodeURIComponent(url.pathname).replace(/\{\$/g, '{');

if (
!(pathname.includes('{z}') && pathname.includes('{x}') && pathname.includes('{y}'))
&& !pathname.includes('{q}')
) {
throw new Err(400, null, 'Either ZXY or Quadkey variables must be used');
}
}

static json(config: TileJSONInterface): Static<typeof TileJSONType> {
const bounds = config.bounds || [-180, -90, 180, 90];
const center = config.center || pointOnFeature(bboxPolygon(bounds as BBox)).geometry.coordinates;
Expand All @@ -75,7 +99,7 @@ export default class TileJSON {
static quadkey(z: number, x: number, y: number): string {
const quadKey = [];
for (let i = z; i > 0; i--) {
let digit = '0';
let digit = 0;
const mask = 1 << (i - 1);
if ((x & mask) != 0) {
digit++;
Expand Down
2 changes: 1 addition & 1 deletion api/lib/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export enum Basemap_Style {

export enum Basemap_Type {
RASTER = 'raster',
TERRAIN = 'terrain',
TERRAIN = 'raster-dem',
VECTOR = 'vector',
}

Expand Down
5 changes: 3 additions & 2 deletions api/lib/schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { sql } from 'drizzle-orm';
import { Static } from '@sinclair/typebox'
import type { StyleContainer } from './style.js';
import { Polygon, Point } from 'geojson';
import { geometry, GeometryType } from '@openaddresses/batch-generic';
import { ConnectionAuth } from './connection-config.js';
import { TAKGroup, TAKRole } from './api/types.js';
Expand Down Expand Up @@ -90,8 +91,8 @@ export const Basemap = pgTable('basemaps', {
url: text('url').notNull(),
overlay: boolean('overlay').notNull().default(false),
username: text('username').references(() => Profile.username),
bounds: geometry('bounds', { type: GeometryType.Polygon, srid: 4326 }),
center: geometry('center', { type: GeometryType.Point, srid: 4326 }),
bounds: geometry('bounds', { type: GeometryType.Polygon, srid: 4326 }).$type<Polygon>(),
center: geometry('center', { type: GeometryType.Point, srid: 4326 }).$type<Point>(),
minzoom: integer('minzoom').notNull().default(0),
maxzoom: integer('maxzoom').notNull().default(16),
format: text('format').$type<Basemap_Format>().notNull().default(Basemap_Format.PNG),
Expand Down
3 changes: 3 additions & 0 deletions api/lib/tak-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Credentials from './api/credentials.js';
import Contacts from './api/contacts.js';
import Files from './api/files.js';
import Group from './api/groups.js';
import Subscription from './api/subscriptions.js';
import Video from './api/video.js';
import Export from './api/export.js';
import Err from '@openaddresses/batch-error';
Expand All @@ -28,6 +29,7 @@ export default class TAKAPI {
MissionLayer: MissionLayer;
Credentials: Credentials;
Contacts: Contacts;
Subscription: Subscription;
Group: Group;
Video: Video;
Export: Export;
Expand All @@ -45,6 +47,7 @@ export default class TAKAPI {
this.MissionLayer = new MissionLayer(this);
this.Credentials = new Credentials(this);
this.Contacts = new Contacts(this);
this.Subscription = new Subscription(this);
this.Group = new Group(this);
this.Video = new Video(this);
this.Files = new Files(this);
Expand Down
7 changes: 6 additions & 1 deletion api/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ export const ServerResponse = Type.Object({
name: Type.String(),
url: Type.String(),
api: Type.String(),
auth: Type.Boolean({ "description": "Once an admin certificate is configured it is not retrivable. This boolean refers to if a certificate is currently loaded" })
auth: Type.Boolean({ "description": "Once an admin certificate is configured it is not retrivable. This boolean refers to if a certificate is currently loaded" }),
certificate: Type.Optional(Type.Object({
subject: Type.String(),
validFrom: Type.String(),
validTo: Type.String()
})),
})

export const ProfileResponse = Type.Object({
Expand Down
19 changes: 16 additions & 3 deletions api/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,29 @@ http {
set $CONNECT "connect-src 'self' *.API_URL:* *.ROOT_URL:*";
add_header 'Content-Security-Policy' "default-src 'self' *.API_URL; ${IMG}; ${WORKER}; ${CONNECT}; ${STYLE_SRC_ATTR}; ${STYLE_SRC_ELEM}; ${FONT}; upgrade-insecure-requests;" always;

location = / {
add_header 'X-Content-Type-Options' 'nosniff' always;
add_header 'X-Frame-Options' 'DENY' always;
add_header 'Referrer-Policy' 'strict-origin-when-cross-origin' always;
add_header 'Strict-Transport-Security' 'max-age=31536000; includeSubDomains; preload' always;
add_header 'Permissions-Policy' 'fullscreen=(self), geolocation=(self), clipboard-read=(self), clipboard-write=(self)' always;
add_header 'Content-Security-Policy' "default-src 'self' *.API_URL; ${IMG}; ${WORKER}; ${CONNECT}; ${STYLE_SRC_ATTR}; ${STYLE_SRC_ELEM}; ${FONT}; upgrade-insecure-requests;" always;

add_header 'Cache-Control' 'no-store, no-cache, must-revalidate' always;
add_header 'Expires' 0 always;
add_header 'Pragma' 'no-cache' always;

alias /home/etl/api/web/dist/;
try_files /index.html =404;
}

location / {
if ($request_uri ~ ^/(.*)\.html) {
return 302 /$1;
}

alias /home/etl/api/web/dist/;

try_files $uri $uri.html $uri/ /index.html;

autoindex on;
}

location /docs/ {
Expand Down
Loading

0 comments on commit d50ee9c

Please sign in to comment.