96 switch from azure table storage to sqlite #97

Merged
noahspannbauer merged 7 commits from 96-switch-from-azure-table-storage-to-sqlite into main 2025-11-23 11:42:31 -05:00
15 changed files with 1211 additions and 117 deletions
Showing only changes of commit 25886c8498 - Show all commits

BIN
.DS_Store vendored

Binary file not shown.

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@ node_modules
.dapr
**/**/secrets.json
.prod
database

View File

@@ -22,7 +22,13 @@
FROM node:18-slim AS base
FROM node:22-slim AS base
FROM base AS migrate
WORKDIR migrations
COPY ./migrations .
RUN npm i -g pnpm
RUN pnpm install
FROM base AS api
WORKDIR api

View File

@@ -27,6 +27,7 @@
"@nestjs/core": "^10.0.0",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/typeorm": "^11.0.0",
"@noahspan/azure-database": "^3.1.2",
"@noahspan/noahspan-modules": "^1.1.5",
"@schematics/angular": "^17.3.7",
@@ -34,6 +35,8 @@
"dotenv": "^16.4.7",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"sqlite3": "^5.1.7",
"typeorm": "^0.3.25",
"uuid": "^10.0.0",
"uuidv4": "^6.2.13"
},

View File

@@ -7,6 +7,7 @@ import { HttpExceptionFilter } from './filters/http-exception.filter';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthGuard, AuthModule, UserModule } from '@noahspan/noahspan-modules';
import configuration from './config/configuration';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
@@ -27,6 +28,15 @@ import configuration from './config/configuration';
FeatureFlagModule,
LogModule,
PilotModule,
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
type: 'sqlite',
database: configService.get<string>('dbPath'),
entities: [__dirname + "/**/*.entity{.ts,.js}"],
synchronize: false
})
}),
UserModule.registerAsync({
inject: [ConfigService],
imports: [ConfigModule],

View File

@@ -0,0 +1,21 @@
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
import { PilotEntity } from '../pilot/pilot.entity';
@Entity({ name: 'certificates'})
export class CertificateEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
type: string;
@Column()
number: string;
@Column()
issueDate: Date
@ManyToOne(() => PilotEntity, (pilot: PilotEntity) => pilot.certificates)
@JoinColumn({ name: 'pilotId' })
pilot: PilotEntity;
}

View File

@@ -1,5 +1,6 @@
export default () => ({
azureStorageConnectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,
dbPath: process.env.DB_PATH,
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
tenantId: process.env.TENANT_ID

View File

@@ -0,0 +1,18 @@
import { PilotEntity } from 'src/pilot/pilot.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
@Entity({ name: 'endorsements' })
export class EndorsementEntity {
@PrimaryGeneratedColumn('uuid')
id: string
@Column()
type: string;
@Column()
issueDate: Date;
@ManyToOne(() => PilotEntity, (pilot: PilotEntity) => pilot.endorsements)
@JoinColumn({ name: 'pilotId' })
pilot: PilotEntity
}

View File

@@ -27,3 +27,82 @@ export class Log {
tracks?: string[];
notes?: string;
}
import { PilotEntity } from 'src/pilot/pilot.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
@Entity({ name: 'logs' })
export class LogEntity {
@PrimaryGeneratedColumn('uuid')
id: string
@Column()
date: Date;
@Column()
aircraftMakeModel: string;
@Column()
aircraftIdentity: string;
@Column()
routeFrom: string;
@Column()
routeTo: string;
@Column()
durationOfFlight: number;
@Column()
singleEngineLand: number;
@Column()
simulatorAtd: number;
@Column()
landingsDay: number;
@Column()
landingsNight: number;
@Column()
groundTrainingReceived: number;
@Column()
flightTrainingReceived: number;
@Column()
crossCountry: number;
@Column()
night: number;
@Column()
solo: number;
@Column()
pilotInCommand: number;
@Column()
instrumentActual: number;
@Column()
instrumentSimulated: number;
@Column()
instrumentApproaches: number;
@Column()
instrumentHolds: number;
@Column()
instrumentNavTrack: number;
@Column()
notes: string;
@ManyToOne(() => PilotEntity, (pilot: PilotEntity) => pilot.logs)
@JoinColumn({ name: 'pilotId' })
pilot: PilotEntity;
}

View File

@@ -0,0 +1,18 @@
import { PilotEntity } from 'src/pilot/pilot.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
@Entity({ name: 'medical' })
export class MedicalEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
class: string;
@Column()
expirationDate: Date;
@ManyToOne(() => PilotEntity, (pilot: PilotEntity) => pilot.medical)
@JoinColumn({ name: 'pilotId' })
pilot: PilotEntity;
}

View File

@@ -1,5 +0,0 @@
export class Certificate {
type: string;
issueDate: string;
number: string;
}

View File

@@ -1,4 +0,0 @@
export class Endorsement {
type: string;
issueDate: Date;
}

View File

@@ -1,18 +1,63 @@
import { EntityString } from '@noahspan/azure-database';
// import { EntityString } from '@noahspan/azure-database';
export class Pilot {
@EntityString() partitionKey: string;
@EntityString() rowKey: string;
@EntityString() id: string;
@EntityString() name: string;
@EntityString() address?: string;
@EntityString() city?: string;
@EntityString() state?: string;
@EntityString() postalCode?: string;
@EntityString() email?: string;
@EntityString() phone?: string;
@EntityString() medicalClass?: string;
@EntityString() medicalExpiration: string;
@EntityString() certificates: string;
@EntityString() endorsements: string;
// export class Pilot {
// @EntityString() partitionKey: string;
// @EntityString() rowKey: string;
// @EntityString() id: string;
// @EntityString() name: string;
// @EntityString() address?: string;
// @EntityString() city?: string;
// @EntityString() state?: string;
// @EntityString() postalCode?: string;
// @EntityString() email?: string;
// @EntityString() phone?: string;
// @EntityString() medicalClass?: string;
// @EntityString() medicalExpiration: string;
// @EntityString() certificates: string;
// @EntityString() endorsements: string;
// }
import { LogEntity } from 'src/log/log.entity';
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
import { CertificateEntity } from '../certificate/certificate.entity';
import { EndorsementEntity } from 'src/endorsement/endorsement.entity';
import { MedicalEntity } from 'src/medical/medical.entity';
@Entity({ name: 'pilots' })
export class PilotEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
name: string;
@Column()
address: string
@Column()
city: string;
@Column()
state: string;
@Column()
postalCode: string;
@Column()
email: string;
@Column()
phone: string;
@OneToMany(() => LogEntity, (log: LogEntity) => log.pilot, {onDelete: 'CASCADE', onUpdate: 'CASCADE'})
logs: LogEntity[];
@OneToMany(() => CertificateEntity, (certificate: CertificateEntity) => certificate.pilot, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
certificates: CertificateEntity[];
@OneToMany(() => EndorsementEntity, (endorsement: EndorsementEntity) => endorsement.pilot, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
endorsements: EndorsementEntity[];
@OneToMany(() => MedicalEntity, (medical: MedicalEntity) => medical.pilot, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
medical: MedicalEntity[];
}

View File

@@ -1,34 +1,63 @@
version: '3.8'
services:
azurite:
container_name: azurite-flying
image: mcr.microsoft.com/azure-storage/azurite
ports:
- '10000:10000'
- '10001:10001'
- '10002:10002'
command: 'azurite --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0 --loose --skipApiVersionCheck'
# azurite:
# container_name: azurite-flying
# image: mcr.microsoft.com/azure-storage/azurite
# ports:
# - '10000:10000'
# - '10001:10001'
# - '10002:10002'
# command: 'azurite --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0 --loose --skipApiVersionCheck'
# volumes:
# - ./azurite-flying:/data
restore:
container_name: restore
image: litestream/litestream:0.3.13
volumes:
- ./azurite-flying:/data
- ./database/flying.db:/var/lib/flying/flying.db
- ./database/litestream_dev.yml:/etc/litestream.yml
- backup:/var/lib/backup
- data:/var/lib/flying
command: restore -if-db-not-exists -if-replica-exists /var/lib/flying/flying.db
api:
container_name: flying-api
build:
context: .
target: api
ports:
- '3000:3000'
env_file:
- ./api/.env.compose
# api:
# container_name: flying-api
# build:
# context: .
# target: api
# ports:
# - '3000:3000'
# env_file:
# - ./api/.env.compose
# volumes:
# - data:/var/lib/flying
# depends_on:
# migrate:
# condition: service_completed_successfully
app:
container_name: flying-app
build:
context: .
target: app
ports:
- '8080:8080'
# replicate:
# container_name: replicate
# image: litestream/litestream:0.3.13
# volumes:
# - ./database/litestream_dev.yml:/etc/litestream.yml
# - backup:/var/lib/backup
# - data:/var/lib/flying
# command: replicate
# depends_on:
# migrate:
# condition: service_completed_successfully
# app:
# container_name: flying-app
# build:
# context: .
# target: app
# ports:
# - '8080:8080'
volumes:
azurite-flying:
# azurite-flying:
backup:
data:

998
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff