Field setups often relay through a phone/gateway that owns the MQTT connection, so the publishing credential and the instrument are now separate concepts: - Points messages carry a "serial" identifying the locator receiver; unknown serials are auto-registered as devices in the publisher's org (name "Locator <serial>"). Device.mqttUsername is now optional and serialNumber is unique per org. - LocatePoint gains standard receiver telemetry: frequencyHz, currentMa, signalDb, gainDb, locateMode (PEAK/NULL/BROAD_PEAK/SONDE), phaseDeg, compassDeg, distortionPct, plus GPS quality columns vAccuracy, satellites, hdop. All optional; raw payload still kept. - Migration hand-edited to preserve the generated geom column and its GIST index (Prisma diff wanted to drop both). - REST create/point DTOs, map tooltips, job-detail latest-point readout, devices settings form, seed, and the sample publisher all carry the new fields. - Added .dockerignore for backend/web: COPY . . was clobbering the image's freshly generated Prisma client with the host's stale node_modules, breaking image builds after schema changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
99 lines
2.8 KiB
TypeScript
99 lines
2.8 KiB
TypeScript
import { PrismaClient, GpsFixType, JobStatus, LocateMode, UtilityType } from '@prisma/client';
|
|
import * as bcrypt from 'bcryptjs';
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
const SEED_ADMIN_EMAIL = 'brent.perteet@gmail.com';
|
|
const SEED_ADMIN_PASSWORD = 'changeme123';
|
|
|
|
async function main() {
|
|
const org = await prisma.organization.upsert({
|
|
where: { slug: 'umagul' },
|
|
update: {},
|
|
create: { name: 'Umagul', slug: 'umagul' },
|
|
});
|
|
|
|
const admin = await prisma.user.upsert({
|
|
where: { email: SEED_ADMIN_EMAIL },
|
|
update: {},
|
|
create: {
|
|
email: SEED_ADMIN_EMAIL,
|
|
name: 'Brent Perteet',
|
|
passwordHash: await bcrypt.hash(SEED_ADMIN_PASSWORD, 12),
|
|
},
|
|
});
|
|
|
|
await prisma.orgMembership.upsert({
|
|
where: { orgId_userId: { orgId: org.id, userId: admin.id } },
|
|
update: { role: 'ORG_ADMIN' },
|
|
create: { orgId: org.id, userId: admin.id, role: 'ORG_ADMIN' },
|
|
});
|
|
|
|
const device = await prisma.device.upsert({
|
|
where: { mqttUsername: 'testuser' },
|
|
update: {},
|
|
create: {
|
|
orgId: org.id,
|
|
name: 'Demo locator',
|
|
mqttUsername: 'testuser',
|
|
serialNumber: 'DEMO-0001',
|
|
},
|
|
});
|
|
|
|
const job = await prisma.job.upsert({
|
|
where: { orgId_ticketNumber: { orgId: org.id, ticketNumber: 'TKT-2026-0001' } },
|
|
update: {},
|
|
create: {
|
|
orgId: org.id,
|
|
ticketNumber: 'TKT-2026-0001',
|
|
title: 'Gas line locate - Main St demo',
|
|
description: 'Seeded demo job with sample RTK points along a gas line.',
|
|
address: '100 Main St',
|
|
status: JobStatus.IN_PROGRESS,
|
|
createdById: admin.id,
|
|
assignedToId: admin.id,
|
|
},
|
|
});
|
|
|
|
const existing = await prisma.locatePoint.count({ where: { jobId: job.id } });
|
|
if (existing === 0) {
|
|
// A short run of points along a line (roughly northeast, ~1.5m spacing)
|
|
const startLat = 33.15012345;
|
|
const startLng = -96.83512345;
|
|
const points = Array.from({ length: 12 }, (_, i) => ({
|
|
jobId: job.id,
|
|
deviceId: device.id,
|
|
lat: startLat + i * 0.0000135,
|
|
lng: startLng + i * 0.0000042,
|
|
altitude: 187.4 + i * 0.02,
|
|
fixType: GpsFixType.FIXED_RTK,
|
|
hAccuracy: 0.014,
|
|
vAccuracy: 0.021,
|
|
satellites: 22,
|
|
hdop: 0.7,
|
|
depth: 1.2,
|
|
frequencyHz: 33000,
|
|
currentMa: 48.5 - i * 0.4,
|
|
signalDb: 62.1 - i * 0.3,
|
|
gainDb: 40,
|
|
locateMode: LocateMode.PEAK,
|
|
compassDeg: 17.5,
|
|
distortionPct: 4.2,
|
|
utilityType: UtilityType.GAS,
|
|
sequence: i + 1,
|
|
recordedAt: new Date(Date.now() - (12 - i) * 5000),
|
|
}));
|
|
await prisma.locatePoint.createMany({ data: points });
|
|
}
|
|
|
|
console.log(`Seeded org=${org.slug} admin=${admin.email} device=${device.mqttUsername} job=${job.ticketNumber}`);
|
|
console.log(`Admin password: ${SEED_ADMIN_PASSWORD}`);
|
|
}
|
|
|
|
main()
|
|
.catch((e) => {
|
|
console.error(e);
|
|
process.exit(1);
|
|
})
|
|
.finally(() => prisma.$disconnect());
|