3 Commits

7 changed files with 173 additions and 53 deletions

7
app.go
View File

@@ -36,7 +36,7 @@ func (a *App) startup(ctx context.Context) {
}
// Add location read scope so we can fetch character locations
a.ssi = NewESISSO(clientID, redirectURI, []string{
ssi, err := NewESISSO(clientID, redirectURI, []string{
"esi-location.read_location.v1",
"esi-location.read_ship_type.v1",
"esi-mail.organize_mail.v1",
@@ -76,6 +76,11 @@ func (a *App) startup(ctx context.Context) {
"esi-characters.read_titles.v1",
"esi-characters.read_fw_stats.v1",
})
if err != nil {
fmt.Printf("ERROR: Failed to initialize ESI SSO: %v\n", err)
return
}
a.ssi = ssi
}
// Greet returns a greeting for the given name

View File

@@ -74,6 +74,10 @@ type ESIToken struct {
CreatedAt time.Time
}
func (ESIToken) TableName() string {
return "esi_tokens"
}
type CharacterInfo struct {
CharacterID int64 `json:"character_id"`
CharacterName string `json:"character_name"`
@@ -108,28 +112,42 @@ type SystemKills struct {
NpcKills int64 `json:"npc_kills"`
}
func NewESISSO(clientID string, redirectURI string, scopes []string) *ESISSO {
func NewESISSO(clientID string, redirectURI string, scopes []string) (*ESISSO, error) {
s := &ESISSO{
clientID: clientID,
redirectURI: redirectURI,
scopes: scopes,
}
_ = s.initDB()
return s
if err := s.initDB(); err != nil {
return nil, fmt.Errorf("failed to initialize database: %w", err)
}
return s, nil
}
func (s *ESISSO) initDB() error {
home, err := os.UserHomeDir()
if err != nil {
return err
return fmt.Errorf("failed to get user home directory: %w", err)
}
dbPath := filepath.Join(home, ".industrializer", "sqlite-latest.sqlite")
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
if err != nil {
return err
return fmt.Errorf("failed to open database at %s: %w", dbPath, err)
}
if err := db.AutoMigrate(&ESIToken{}); err != nil {
return err
sqlDB, err := db.DB()
if err != nil {
return fmt.Errorf("failed to get underlying sql.DB: %w", err)
}
if err := sqlDB.Ping(); err != nil {
return fmt.Errorf("failed to ping database: %w", err)
}
if !db.Migrator().HasTable(&ESIToken{}) {
if err := db.Migrator().CreateTable(&ESIToken{}); err != nil {
return fmt.Errorf("failed to create table esi_tokens: %w", err)
}
}
s.db = db
return nil

View File

@@ -14,6 +14,7 @@ interface MapNodeProps {
type: 'region' | 'system';
security?: number;
signatures?: number;
jove_observatory?: boolean;
isDraggable?: boolean;
disableNavigate?: boolean;
jumps?: number;
@@ -37,6 +38,7 @@ export const MapNode: React.FC<MapNodeProps> = ({
type,
security,
signatures,
jove_observatory,
isDraggable = false,
disableNavigate = false,
jumps,
@@ -238,6 +240,29 @@ export const MapNode: React.FC<MapNodeProps> = ({
currentY += 15;
}
// Add jove observatory icon if present
if (jove_observatory) {
textElements.push(
<text
key="jove_observatory"
x="0"
y={currentY}
textAnchor="middle"
fill="#fbbf24"
fontSize="12"
className="pointer-events-none select-none"
style={{
textShadow: '1px 1px 2px rgba(0,0,0,0.8)',
vectorEffect: 'non-scaling-stroke'
}}
transform={`scale(${(1 / (1200 / viewBoxWidth)) * labelScale})`}
>
🔭
</text>
);
currentY += 15;
}
// Add jumps if enabled and present
if (showJumps && jumps !== undefined) {
textElements.push(

View File

@@ -1107,6 +1107,7 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
type="system"
security={system.security}
signatures={system.signatures}
jove_observatory={system.jove_observatory}
isDraggable={isWormholeRegion}
disableNavigate={viaMode}
jumps={getSystemJumps(system.solarSystemName)}

View File

@@ -1,5 +1,7 @@
import { System } from '@/lib/types';
import { useQuery } from '@tanstack/react-query';
import pb from '@/lib/pocketbase';
import { SystemResponse } from '@/lib/pbtypes';
const pocketbaseUrl = `https://evebase.site.quack-lab.dev/api/collections/regionview/records`;
@@ -23,6 +25,26 @@ const fetchRegionData = async (regionName: string): Promise<Map<string, System>>
system.signatures = systemSigs.sigcount;
}
}
const systemNames = Array.from(systemsMap.keys());
if (systemNames.length > 0) {
const filter = systemNames.map(name => `name='${name}'`).join(' || ');
try {
const systemRecords = await pb.collection('system').getFullList<SystemResponse>({
filter: `(${filter})`,
batch: 1000
});
for (const record of systemRecords) {
const system = systemsMap.get(record.name);
if (system && record.jove_observatory) {
system.jove_observatory = record.jove_observatory;
}
}
} catch (error) {
console.warn('Failed to fetch jove_observatory data:', error);
}
}
return systemsMap;
};

View File

@@ -25,7 +25,9 @@ export enum Collections {
// Alias types for improved usability
export type IsoDateString = string
export type IsoAutoDateString = string & { readonly autodate: unique symbol }
export type RecordIdString = string
export type FileNameString = string & { readonly filename: unique symbol }
export type HTMLString = string
type ExpandType<T> = unknown extends T
@@ -52,66 +54,66 @@ export type AuthSystemFields<T = unknown> = {
export type AuthoriginsRecord = {
collectionRef: string
created?: IsoDateString
created: IsoAutoDateString
fingerprint: string
id: string
recordRef: string
updated?: IsoDateString
updated: IsoAutoDateString
}
export type ExternalauthsRecord = {
collectionRef: string
created?: IsoDateString
created: IsoAutoDateString
id: string
provider: string
providerId: string
recordRef: string
updated?: IsoDateString
updated: IsoAutoDateString
}
export type MfasRecord = {
collectionRef: string
created?: IsoDateString
created: IsoAutoDateString
id: string
method: string
recordRef: string
updated?: IsoDateString
updated: IsoAutoDateString
}
export type OtpsRecord = {
collectionRef: string
created?: IsoDateString
created: IsoAutoDateString
id: string
password: string
recordRef: string
sentTo?: string
updated?: IsoDateString
updated: IsoAutoDateString
}
export type SuperusersRecord = {
created?: IsoDateString
created: IsoAutoDateString
email: string
emailVisibility?: boolean
id: string
password: string
tokenKey: string
updated?: IsoDateString
updated: IsoAutoDateString
verified?: boolean
}
export type IndBillitemRecord = {
created?: IsoDateString
created: IsoAutoDateString
id: string
name: string
quantity: number
updated?: IsoDateString
updated: IsoAutoDateString
}
export type IndCharRecord = {
created?: IsoDateString
created: IsoAutoDateString
id: string
name: string
updated?: IsoDateString
updated: IsoAutoDateString
}
export enum IndJobStatusOptions {
@@ -132,7 +134,7 @@ export type IndJobRecord = {
billOfMaterials?: RecordIdString[]
character?: RecordIdString
consumedMaterials?: RecordIdString[]
created?: IsoDateString
created: IsoAutoDateString
expenditures?: RecordIdString[]
id: string
income?: RecordIdString[]
@@ -148,13 +150,13 @@ export type IndJobRecord = {
saleEnd?: IsoDateString
saleStart?: IsoDateString
status: IndJobStatusOptions
updated?: IsoDateString
updated: IsoAutoDateString
}
export type IndTransactionRecord = {
buyer?: string
corporation?: string
created?: IsoDateString
created: IsoAutoDateString
date: IsoDateString
id: string
itemName: string
@@ -163,7 +165,7 @@ export type IndTransactionRecord = {
quantity: number
totalPrice: number
unitPrice: number
updated?: IsoDateString
updated: IsoAutoDateString
wallet?: string
}
@@ -175,7 +177,7 @@ export type RegionviewRecord = {
}
export type SignatureRecord = {
created?: IsoDateString
created: IsoAutoDateString
dangerous?: boolean
id: string
identifier: string
@@ -184,20 +186,20 @@ export type SignatureRecord = {
scanned?: string
system: RecordIdString
type?: string
updated?: IsoDateString
updated: IsoAutoDateString
}
export type SignatureNoteRulesRecord = {
created?: IsoDateString
created: IsoAutoDateString
enabled?: boolean
id: string
note: string
regex: string
updated?: IsoDateString
updated: IsoAutoDateString
}
export type SigviewRecord = {
created?: IsoDateString
created: IsoAutoDateString
dangerous?: boolean
id: string
identifier: string
@@ -207,24 +209,25 @@ export type SigviewRecord = {
sysid?: RecordIdString
system: string
type?: string
updated?: IsoDateString
updated: IsoAutoDateString
}
export type SystemRecord = {
connectedTo?: string
created?: IsoDateString
created: IsoAutoDateString
id: string
jove_observatory?: boolean
name: string
region: string
updated?: IsoDateString
updated: IsoAutoDateString
}
export type WormholeSystemsRecord = {
connectedSystems?: string
created?: IsoDateString
created: IsoAutoDateString
id: string
solarSystemName: string
updated?: IsoDateString
updated: IsoAutoDateString
x: number
y: number
}
@@ -284,23 +287,68 @@ export type CollectionResponses = {
wormholeSystems: WormholeSystemsResponse
}
// Utility types for create/update operations
type ProcessCreateAndUpdateFields<T> = Omit<{
// Omit AutoDate fields
[K in keyof T as Extract<T[K], IsoAutoDateString> extends never ? K : never]:
// Convert FileNameString to File
T[K] extends infer U ?
U extends (FileNameString | FileNameString[]) ?
U extends any[] ? File[] : File
: U
: never
}, 'id'>
// Create type for Auth collections
export type CreateAuth<T> = {
id?: RecordIdString
email: string
emailVisibility?: boolean
password: string
passwordConfirm: string
verified?: boolean
} & ProcessCreateAndUpdateFields<T>
// Create type for Base collections
export type CreateBase<T> = {
id?: RecordIdString
} & ProcessCreateAndUpdateFields<T>
// Update type for Auth collections
export type UpdateAuth<T> = Partial<
Omit<ProcessCreateAndUpdateFields<T>, keyof AuthSystemFields>
> & {
email?: string
emailVisibility?: boolean
oldPassword?: string
password?: string
passwordConfirm?: string
verified?: boolean
}
// Update type for Base collections
export type UpdateBase<T> = Partial<
Omit<ProcessCreateAndUpdateFields<T>, keyof BaseSystemFields>
>
// Get the correct create type for any collection
export type Create<T extends keyof CollectionResponses> =
CollectionResponses[T] extends AuthSystemFields
? CreateAuth<CollectionRecords[T]>
: CreateBase<CollectionRecords[T]>
// Get the correct update type for any collection
export type Update<T extends keyof CollectionResponses> =
CollectionResponses[T] extends AuthSystemFields
? UpdateAuth<CollectionRecords[T]>
: UpdateBase<CollectionRecords[T]>
// Type for usage with type asserted PocketBase instance
// https://github.com/pocketbase/js-sdk#specify-typescript-definitions
export type TypedPocketBase = PocketBase & {
collection(idOrName: '_authOrigins'): RecordService<AuthoriginsResponse>
collection(idOrName: '_externalAuths'): RecordService<ExternalauthsResponse>
collection(idOrName: '_mfas'): RecordService<MfasResponse>
collection(idOrName: '_otps'): RecordService<OtpsResponse>
collection(idOrName: '_superusers'): RecordService<SuperusersResponse>
collection(idOrName: 'ind_billItem'): RecordService<IndBillitemResponse>
collection(idOrName: 'ind_char'): RecordService<IndCharResponse>
collection(idOrName: 'ind_job'): RecordService<IndJobResponse>
collection(idOrName: 'ind_transaction'): RecordService<IndTransactionResponse>
collection(idOrName: 'regionview'): RecordService<RegionviewResponse>
collection(idOrName: 'signature'): RecordService<SignatureResponse>
collection(idOrName: 'signature_note_rules'): RecordService<SignatureNoteRulesResponse>
collection(idOrName: 'sigview'): RecordService<SigviewResponse>
collection(idOrName: 'system'): RecordService<SystemResponse>
collection(idOrName: 'wormholeSystems'): RecordService<WormholeSystemsResponse>
}
export type TypedPocketBase = {
collection<T extends keyof CollectionResponses>(
idOrName: T
): RecordService<CollectionResponses[T]>
} & PocketBase

View File

@@ -10,6 +10,7 @@ export interface System {
y: number;
security?: number;
signatures?: number;
jove_observatory?: boolean;
connectedSystems: string;
}