New eveal #32

Merged
Sirttas merged 115 commits from new-eveal into main 2026-07-14 17:15:11 +02:00
11 changed files with 1263 additions and 26 deletions
Showing only changes of commit a0cc120fec - Show all commits
+350 -3
View File
@@ -325,6 +325,25 @@ paths:
type: array type: array
items: items:
$ref: "#/components/schemas/ReprocessingResultResponse" $ref: "#/components/schemas/ReprocessingResultResponse"
/refresh:
post:
tags:
- auth
summary: Rotate the refresh session and mint a fresh access token
operationId: refresh
responses:
"200":
description: A fresh access token; the refresh cookie is rotated
content:
'*/*':
schema:
$ref: "#/components/schemas/RefreshResponse"
"401":
description: "Missing, expired, or reused refresh token"
content:
'*/*':
schema:
$ref: "#/components/schemas/RefreshResponse"
/market/types/parse: /market/types/parse:
post: post:
tags: tags:
@@ -353,6 +372,15 @@ paths:
"400": "400":
description: Returned when a line carries a quantity that is not a positive description: Returned when a line carries a quantity that is not a positive
whole number whole number
/logout:
post:
tags:
- auth
summary: Revoke the refresh session and clear the refresh cookie
operationId: logout
responses:
"204":
description: The refresh session is revoked and the cookie cleared
/ledgers/main: /ledgers/main:
post: post:
tags: tags:
@@ -412,6 +440,18 @@ paths:
"400": "400":
description: "Invalid request (e.g. blank name, a member ledger missing\ description: "Invalid request (e.g. blank name, a member ledger missing\
\ or already contained)" \ or already contained)"
/characters/add:
post:
tags:
- auth
summary: Seed the current user in the session so the next SSO links the new
character to them
operationId: addCharacter
responses:
"204":
description: The link is seeded; follow with a full-page nav to /oauth2/authorization/esi
"401":
description: Missing or expired access token
/activity/fetch: /activity/fetch:
post: post:
tags: tags:
@@ -462,6 +502,25 @@ paths:
text/plain: text/plain:
schema: schema:
type: string type: string
/me:
get:
tags:
- auth
summary: Return the authenticated user and the characters they own
operationId: me
responses:
"200":
description: The authenticated user and their characters
content:
'*/*':
schema:
$ref: "#/components/schemas/MeResponse"
"401":
description: Missing or expired access token
content:
'*/*':
schema:
$ref: "#/components/schemas/MeResponse"
/market/{marketTypeId}/scan: /market/{marketTypeId}/scan:
get: get:
tags: tags:
@@ -923,18 +982,28 @@ components:
schemas: schemas:
UpdateRuleBookRequest: UpdateRuleBookRequest:
type: object type: object
description: Request to update an existing rule book; replaces its mutable fields.
properties: properties:
name: name:
type: string type: string
description: Rule book name.
example: Trading rules
usedForAcquisitions: usedForAcquisitions:
type: boolean type: boolean
description: Whether this rule book is used to derive acquisitions.
example: true
ledgerRefs: ledgerRefs:
type: array type: array
description: Symbolic ledger references the script writes to; each must
be bound to a ledger per character.
items: items:
type: string type: string
example: sales
pattern: "[a-z][a-zA-Z0-9]*" pattern: "[a-z][a-zA-Z0-9]*"
script: script:
type: string type: string
description: The classification script source.
example: transfer.to('sales')
required: required:
- ledgerRefs - ledgerRefs
- name - name
@@ -942,21 +1011,34 @@ components:
- usedForAcquisitions - usedForAcquisitions
RuleBookResponse: RuleBookResponse:
type: object type: object
description: "A rule book: a named script that classifies transactions, together\
\ with the ledger references it expects."
properties: properties:
ruleBookId: ruleBookId:
type: string type: string
format: uuid format: uuid
description: Unique rule book identifier.
example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
name: name:
type: string type: string
description: Rule book name.
example: Trading rules
usedForAcquisitions: usedForAcquisitions:
type: boolean type: boolean
description: Whether this rule book is used to derive acquisitions.
example: true
ledgerRefs: ledgerRefs:
type: array type: array
description: Symbolic ledger references the script writes to; each must
be bound to a ledger per character.
items: items:
type: string type: string
example: sales
pattern: "[a-z][a-zA-Z0-9]*" pattern: "[a-z][a-zA-Z0-9]*"
script: script:
type: string type: string
description: The classification script source.
example: transfer.to('sales')
required: required:
- ledgerRefs - ledgerRefs
- name - name
@@ -965,9 +1047,12 @@ components:
- usedForAcquisitions - usedForAcquisitions
UpdateMainLedgerRequest: UpdateMainLedgerRequest:
type: object type: object
description: Request to update an existing main ledger.
properties: properties:
name: name:
type: string type: string
description: New name for the ledger.
example: Main wallet
required: required:
- name - name
MainLedgerResponse: MainLedgerResponse:
@@ -978,21 +1063,33 @@ components:
ledgerId: ledgerId:
type: string type: string
format: uuid format: uuid
description: Unique ledger identifier.
example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
name: name:
type: string type: string
description: Ledger name.
example: Main wallet
balance: balance:
type: number type: number
description: Current ISK balance of the ledger.
example: 1500000.0
description: "A main ledger: a standalone ledger with its own balance."
required: required:
- balance - balance
- ledgerId - ledgerId
- name - name
UpdateCombinedLedgerRequest: UpdateCombinedLedgerRequest:
type: object type: object
description: Request to update an existing combined ledger; replaces its name
and member set.
properties: properties:
name: name:
type: string type: string
description: New name for the combined ledger.
example: All wallets
memberLedgerIds: memberLedgerIds:
type: array type: array
description: Ids of the ledgers to aggregate.
items: items:
type: string type: string
format: uuid format: uuid
@@ -1007,15 +1104,24 @@ components:
ledgerId: ledgerId:
type: string type: string
format: uuid format: uuid
description: Unique ledger identifier.
example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
name: name:
type: string type: string
description: Ledger name.
example: All wallets
balance: balance:
type: number type: number
description: Aggregate ISK balance across all member ledgers.
example: 1500000.0
memberLedgerIds: memberLedgerIds:
type: array type: array
description: Ids of the ledgers aggregated by this combined ledger.
items: items:
type: string type: string
format: uuid format: uuid
description: "A combined ledger: an aggregate whose balance is the sum of its\
\ member ledgers."
required: required:
- balance - balance
- ledgerId - ledgerId
@@ -1023,70 +1129,106 @@ components:
- name - name
SetCharacterRuleBookRequest: SetCharacterRuleBookRequest:
type: object type: object
description: "Request to assign a rule book to a character, binding each of\
\ its ledger references to a concrete ledger."
properties: properties:
ruleBookId: ruleBookId:
type: string type: string
format: uuid format: uuid
description: Id of the rule book to assign.
example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
bindings: bindings:
type: object type: object
additionalProperties: additionalProperties:
type: string type: string
format: uuid format: uuid
description: Bindings from each ledger reference name to the ledger id it
resolves to for this character.
example:
sales: 5c1e6f2a-1234-4562-b3fc-2c963f66afa6
required: required:
- bindings - bindings
- ruleBookId - ruleBookId
CharacterResponse: CharacterResponse:
type: object type: object
description: An EVE Online character.
properties: properties:
characterId: characterId:
type: integer type: integer
format: int64 format: int64
description: EVE character id.
example: 2112625428
name: name:
type: string type: string
description: Character name.
example: CCP Falcon
required: required:
- characterId - characterId
- name - name
CharacterRuleBookResponse: CharacterRuleBookResponse:
type: object type: object
description: "A rule book assigned to a character, with the ledger bindings\
\ that resolve its references."
properties: properties:
character: character:
$ref: "#/components/schemas/CharacterResponse" $ref: "#/components/schemas/CharacterResponse"
description: The character the rule book is assigned to.
ruleBook: ruleBook:
$ref: "#/components/schemas/RuleBookSummaryResponse" $ref: "#/components/schemas/RuleBookSummaryResponse"
description: Summary of the assigned rule book.
bindings: bindings:
type: object type: object
additionalProperties: additionalProperties:
type: string type: string
format: uuid format: uuid
description: Bindings from each ledger reference name to the ledger id it
resolves to for this character.
example:
sales: 5c1e6f2a-1234-4562-b3fc-2c963f66afa6
required: required:
- bindings - bindings
- character - character
- ruleBook - ruleBook
RuleBookSummaryResponse: RuleBookSummaryResponse:
type: object type: object
description: "Lightweight reference to a rule book: its id and name only."
properties: properties:
ruleBookId: ruleBookId:
type: string type: string
format: uuid format: uuid
description: Unique rule book identifier.
example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
name: name:
type: string type: string
description: Rule book name.
example: Trading rules
required: required:
- name - name
- ruleBookId - ruleBookId
CreateRuleBookRequest: CreateRuleBookRequest:
type: object type: object
description: Request to create a new rule book.
properties: properties:
name: name:
type: string type: string
description: Rule book name.
example: Trading rules
usedForAcquisitions: usedForAcquisitions:
type: boolean type: boolean
description: Whether this rule book is used to derive acquisitions.
example: true
ledgerRefs: ledgerRefs:
type: array type: array
description: Symbolic ledger references the script writes to; each must
be bound to a ledger per character.
items: items:
type: string type: string
example: sales
pattern: "[a-z][a-zA-Z0-9]*" pattern: "[a-z][a-zA-Z0-9]*"
script: script:
type: string type: string
description: The classification script source.
example: transfer.to('sales')
required: required:
- ledgerRefs - ledgerRefs
- name - name
@@ -1094,24 +1236,34 @@ components:
- usedForAcquisitions - usedForAcquisitions
MarketTypeStackRequest: MarketTypeStackRequest:
type: object type: object
description: A quantity of a single market type to reprocess.
properties: properties:
marketTypeId: marketTypeId:
type: integer type: integer
format: int64 format: int64
description: EVE market type (item) id to reprocess.
example: 1230
quantity: quantity:
type: integer type: integer
format: int64 format: int64
description: Number of units to reprocess.
example: 1000
required: required:
- marketTypeId - marketTypeId
- quantity - quantity
ReprocessItemsRequest: ReprocessItemsRequest:
type: object type: object
description: Request to reprocess a set of item stacks on behalf of a character.
properties: properties:
characterId: characterId:
type: integer type: integer
format: int64 format: int64
description: EVE character id whose reprocessing skills and facility standings
are applied.
example: 2112625428
items: items:
type: array type: array
description: Item stacks to reprocess.
items: items:
$ref: "#/components/schemas/MarketTypeStackRequest" $ref: "#/components/schemas/MarketTypeStackRequest"
required: required:
@@ -1119,20 +1271,31 @@ components:
- items - items
MarketTypeStackAppraisalResponse: MarketTypeStackAppraisalResponse:
type: object type: object
description: A market type stack valued at current buy and sell prices.
properties: properties:
marketTypeId: marketTypeId:
type: integer type: integer
format: int64 format: int64
description: EVE market type (item) id.
example: 34
quantity: quantity:
type: integer type: integer
format: int64 format: int64
description: Number of units in the stack.
example: 1000
buy: buy:
type: number type: number
description: "Total buy value of the stack, in ISK."
example: 5420.0
sell: sell:
type: number type: number
description: "Total sell value of the stack, in ISK."
example: 6100.0
orderCount: orderCount:
type: integer type: integer
format: int64 format: int64
description: Number of market orders considered for the valuation.
example: 128
required: required:
- buy - buy
- marketTypeId - marketTypeId
@@ -1141,77 +1304,142 @@ components:
- sell - sell
ReprocessingResultResponse: ReprocessingResultResponse:
type: object type: object
description: "Result of reprocessing an item stack: the input stack and the\
\ resulting material outputs, each valued at market prices."
properties: properties:
input: input:
$ref: "#/components/schemas/MarketTypeStackAppraisalResponse" $ref: "#/components/schemas/MarketTypeStackAppraisalResponse"
description: The input stack that was reprocessed.
output: output:
type: array type: array
description: Material stacks yielded by reprocessing the input.
items: items:
$ref: "#/components/schemas/MarketTypeStackAppraisalResponse" $ref: "#/components/schemas/MarketTypeStackAppraisalResponse"
required: required:
- input - input
- output - output
RefreshResponse:
type: object
description: A freshly minted access token.
properties:
accessToken:
type: string
description: Short-lived bearer access token (JWT) for the Authorization
header.
required:
- accessToken
MarketTypeStackResponse: MarketTypeStackResponse:
type: object type: object
description: A quantity of a single market type.
properties: properties:
marketTypeId: marketTypeId:
type: integer type: integer
format: int64 format: int64
description: EVE market type (item) id.
example: 34
quantity: quantity:
type: integer type: integer
format: int64 format: int64
description: Number of units in the stack.
example: 1000
required: required:
- marketTypeId - marketTypeId
- quantity - quantity
CreateMainLedgerRequest: CreateMainLedgerRequest:
type: object type: object
description: Request to create a new main ledger.
properties: properties:
name: name:
type: string type: string
description: Name for the new ledger.
example: Main wallet
required: required:
- name - name
CreateCombinedLedgerRequest: CreateCombinedLedgerRequest:
type: object type: object
description: Request to create a new combined ledger aggregating the given member
ledgers.
properties: properties:
name: name:
type: string type: string
description: Name for the new combined ledger.
example: All wallets
memberLedgerIds: memberLedgerIds:
type: array type: array
description: Ids of the ledgers to aggregate.
items: items:
type: string type: string
format: uuid format: uuid
required: required:
- memberLedgerIds - memberLedgerIds
- name - name
MeResponse:
type: object
description: The authenticated user and the characters they own.
properties:
userId:
type: string
format: uuid
description: Unique user identifier.
example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
characters:
type: array
description: Characters owned by the user.
items:
$ref: "#/components/schemas/CharacterResponse"
required:
- characters
- userId
MarketScanResponse: MarketScanResponse:
type: object type: object
description: "Result of a market scan combining current prices, history quartiles\
\ and a computed profitability score for a market type."
properties: properties:
marketTypeId: marketTypeId:
type: integer type: integer
format: int64 format: int64
description: EVE market type (item) id.
example: 34
buy: buy:
type: number type: number
description: "Current best buy price, in ISK."
example: 5.42
sell: sell:
type: number type: number
description: "Current best sell price, in ISK."
example: 6.1
q1: q1:
type: number type: number
description: "First quartile (25th percentile) historical price, in ISK."
example: 4.8
median: median:
type: number type: number
description: "Median (50th percentile) historical price, in ISK."
example: 5.42
q3: q3:
type: number type: number
description: "Third quartile (75th percentile) historical price, in ISK."
example: 6.05
totalVolume: totalVolume:
type: integer type: integer
format: int64 format: int64
description: Total traded volume over the scanned period.
example: 15000000
trend: trend:
type: string type: string
description: Overall price trend over the scanned period.
enum: enum:
- UP - UP
- DOWN - DOWN
- FLAT - FLAT
profit: profit:
type: number type: number
description: "Estimated profit per unit after fees and taxes, in ISK."
example: 0.45
score: score:
type: number type: number
description: Computed profitability score used to rank scan results.
example: 0.82
required: required:
- buy - buy
- marketTypeId - marketTypeId
@@ -1225,25 +1453,40 @@ components:
- trend - trend
MarketHistoryResponse: MarketHistoryResponse:
type: object type: object
description: A single day of market history for a market type.
properties: properties:
marketTypeId: marketTypeId:
type: integer type: integer
format: int64 format: int64
description: EVE market type (item) id.
example: 34
date: date:
type: string type: string
format: date format: date
description: Date the history entry covers.
example: 2026-06-22
average: average:
type: number type: number
description: "Average price for the day, in ISK."
example: 5.42
highest: highest:
type: number type: number
description: "Highest price for the day, in ISK."
example: 6.1
lowest: lowest:
type: number type: number
description: "Lowest price for the day, in ISK."
example: 4.8
orderCount: orderCount:
type: integer type: integer
format: int64 format: int64
description: Number of orders for the day.
example: 320
volume: volume:
type: integer type: integer
format: int64 format: int64
description: Traded volume for the day.
example: 12500000
required: required:
- average - average
- date - date
@@ -1254,21 +1497,33 @@ components:
- volume - volume
HistoryQuartilesResponse: HistoryQuartilesResponse:
type: object type: object
description: Quartile statistics computed over a market type's price history.
properties: properties:
marketTypeId: marketTypeId:
type: integer type: integer
format: int64 format: int64
description: EVE market type (item) id.
example: 34
q1: q1:
type: number type: number
description: "First quartile (25th percentile) price, in ISK."
example: 4.8
median: median:
type: number type: number
description: "Median (50th percentile) price, in ISK."
example: 5.42
q3: q3:
type: number type: number
description: "Third quartile (75th percentile) price, in ISK."
example: 6.05
totalVolume: totalVolume:
type: integer type: integer
format: int64 format: int64
description: Total traded volume over the period.
example: 15000000
trend: trend:
type: string type: string
description: Overall price trend over the period.
enum: enum:
- UP - UP
- DOWN - DOWN
@@ -1282,33 +1537,58 @@ components:
- trend - trend
MarketTypeResponse: MarketTypeResponse:
type: object type: object
description: An EVE Online market type (item) and its static attributes.
properties: properties:
id: id:
type: integer type: integer
format: int64 format: int64
description: EVE market type (item) id.
example: 34
name: name:
type: string type: string
description: Item name.
example: Tritanium
groupId: groupId:
type: integer type: integer
format: int64 format: int64
description: Group id the item belongs to.
example: 18
marketGroupId: marketGroupId:
type: integer type: integer
format: int64 format: int64
description: Market group id the item is listed under.
example: 1857
description: description:
type: string type: string
description: Item description.
example: The main building block in space structures.
published: published:
type: boolean type: boolean
description: Whether the item is published and visible in game.
example: true
basePrice: basePrice:
type: number type:
- number
- "null"
description: "Base price of the item, in ISK, if defined."
example: 2.99
volume: volume:
type: number type: number
format: double format: double
description: "Packaged volume of a single unit, in m³."
example: 0.01
portionSize: portionSize:
type: integer type: integer
format: int32 format: int32
description: Number of units produced or reprocessed per portion.
example: 1
iconId: iconId:
type: integer type:
- integer
- "null"
format: int64 format: int64
description: "Icon id for the item, if defined."
example: 1
required: required:
- basePrice - basePrice
- description - description
@@ -1322,23 +1602,34 @@ components:
- volume - volume
MarketPriceResponse: MarketPriceResponse:
type: object type: object
description: Current best buy and sell prices for a market type.
properties: properties:
marketTypeId: marketTypeId:
type: integer type: integer
format: int64 format: int64
description: EVE market type (item) id.
example: 34
buy: buy:
type: number type: number
description: "Best buy price, in ISK."
example: 5.42
sell: sell:
type: number type: number
description: "Best sell price, in ISK."
example: 6.1
orderCount: orderCount:
type: integer type: integer
format: int64 format: int64
description: Number of market orders considered.
example: 128
required: required:
- buy - buy
- marketTypeId - marketTypeId
- orderCount - orderCount
- sell - sell
LedgerResponse: LedgerResponse:
description: "A ledger, either a standalone main ledger or a combined ledger\
\ aggregating others."
discriminator: discriminator:
propertyName: type propertyName: type
mapping: mapping:
@@ -1360,11 +1651,18 @@ components:
fromLedgerId: fromLedgerId:
type: string type: string
format: uuid format: uuid
description: Id of the ledger the ISK is debited from.
example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
toLedgerId: toLedgerId:
type: string type: string
format: uuid format: uuid
description: Id of the ledger the ISK is credited to.
example: 5c1e6f2a-1234-4562-b3fc-2c963f66afa6
amount: amount:
type: number type: number
description: Amount of ISK transferred.
example: 1500000.0
description: A transfer of ISK from one ledger to another.
required: required:
- amount - amount
- fromLedgerId - fromLedgerId
@@ -1377,15 +1675,24 @@ components:
fromLedgerId: fromLedgerId:
type: string type: string
format: uuid format: uuid
description: Id of the ledger the items are debited from.
example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
toLedgerId: toLedgerId:
type: string type: string
format: uuid format: uuid
description: Id of the ledger the items are credited to.
example: 5c1e6f2a-1234-4562-b3fc-2c963f66afa6
marketTypeId: marketTypeId:
type: integer type: integer
format: int64 format: int64
description: EVE market type (item) id transferred.
example: 34
quantity: quantity:
type: integer type: integer
format: int64 format: int64
description: Number of units transferred.
example: 1000
description: A transfer of an item stack from one ledger to another.
required: required:
- fromLedgerId - fromLedgerId
- marketTypeId - marketTypeId
@@ -1393,20 +1700,32 @@ components:
- toLedgerId - toLedgerId
TransactionResponse: TransactionResponse:
type: object type: object
description: "A transaction: a dated, described group of transfers between ledgers."
properties: properties:
transactionId: transactionId:
type: string type: string
format: uuid format: uuid
description: Unique transaction identifier.
example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
characterId: characterId:
type: integer type:
- integer
- "null"
format: int64 format: int64
description: "EVE character id the transaction is attributed to, if any."
example: 2112625428
datetime: datetime:
type: string type: string
format: date-time format: date-time
description: When the transaction occurred.
example: 2026-06-22T14:30:00
description: description:
type: string type: string
description: Human-readable description of the transaction.
example: Market sale of Tritanium
transfers: transfers:
type: array type: array
description: The transfers that make up this transaction.
items: items:
$ref: "#/components/schemas/TransferResponse" $ref: "#/components/schemas/TransferResponse"
required: required:
@@ -1416,6 +1735,7 @@ components:
- transactionId - transactionId
- transfers - transfers
TransferResponse: TransferResponse:
description: "A transfer between two ledgers, either of ISK or of an item stack."
discriminator: discriminator:
propertyName: type propertyName: type
mapping: mapping:
@@ -1431,11 +1751,16 @@ components:
- type - type
BalanceResponse: BalanceResponse:
type: object type: object
description: "The balance of a ledger: its ISK total plus the quantity of each\
\ item type held."
properties: properties:
iskBalance: iskBalance:
type: number type: number
description: ISK balance of the ledger.
example: 1500000.0
itemBalances: itemBalances:
type: array type: array
description: Per-item-type quantities held in the ledger.
items: items:
$ref: "#/components/schemas/ItemBalanceResponse" $ref: "#/components/schemas/ItemBalanceResponse"
required: required:
@@ -1443,44 +1768,66 @@ components:
- itemBalances - itemBalances
ItemBalanceResponse: ItemBalanceResponse:
type: object type: object
description: Quantity of a single item type held in a ledger.
properties: properties:
typeId: typeId:
type: integer type: integer
format: int64 format: int64
description: EVE market type (item) id.
example: 34
quantity: quantity:
type: integer type: integer
format: int64 format: int64
description: Number of units held.
example: 1000
required: required:
- quantity - quantity
- typeId - typeId
AcquisitionResponse: AcquisitionResponse:
type: object type: object
description: "A single acquisition of a market type, tracking the remaining\
\ quantity available in the FIFO cost pool."
properties: properties:
acquisitionId: acquisitionId:
type: string type: string
format: uuid format: uuid
description: Unique acquisition identifier.
example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
characterId: characterId:
type: integer type: integer
format: int64 format: int64
description: EVE character id that made the acquisition.
example: 2112625428
marketTypeId: marketTypeId:
type: integer type: integer
format: int64 format: int64
description: EVE market type (item) id that was acquired.
example: 34
source: source:
type: string type: string
description: How the item entered the inventory.
enum: enum:
- BOUGHT - BOUGHT
- MANUAL - MANUAL
datetime: datetime:
type: string type: string
format: date-time format: date-time
description: When the acquisition occurred.
example: 2026-06-22T14:30:00
quantity: quantity:
type: integer type: integer
format: int64 format: int64
description: Quantity originally acquired.
example: 1000
remaining: remaining:
type: integer type: integer
format: int64 format: int64
description: Quantity still remaining in the FIFO cost pool.
example: 250
unitCost: unitCost:
type: number type: number
description: "Cost per unit at acquisition time, in ISK."
example: 5.42
required: required:
- acquisitionId - acquisitionId
- characterId - characterId
+67
View File
@@ -0,0 +1,67 @@
import {defineStore} from "pinia";
import {ref} from "vue";
import {CharacterResponse} from "@/generated/mammon";
import {addCharacter as addCharacterRequest, fetchMe, mammonLoginUrl, postLogout, refreshAccessToken, setOnAuthExpired} from "@/mammon";
import {setAccessToken} from "./token";
export const useAuthStore = defineStore('auth', () => {
const userId = ref<string | null>(null);
const characters = ref<CharacterResponse[]>([]);
const isAuthenticated = ref(false);
const clear = () => {
setAccessToken(null);
userId.value = null;
characters.value = [];
isAuthenticated.value = false;
}
const refresh = async (): Promise<boolean> => {
const token = await refreshAccessToken();
if (!token) {
clear();
return false;
}
isAuthenticated.value = true;
return true;
}
const fetch = async (): Promise<void> => {
const me = await fetchMe();
userId.value = me.userId;
characters.value = me.characters;
isAuthenticated.value = true;
}
const login = (): void => {
window.location.assign(mammonLoginUrl);
}
const addCharacter = async (): Promise<void> => {
await addCharacterRequest();
window.location.assign(mammonLoginUrl);
}
const logout = async (): Promise<void> => {
try {
await postLogout();
} finally {
clear();
}
}
const bootstrap = async (): Promise<void> => {
setOnAuthExpired(() => {
clear();
login();
});
if (await refresh()) {
await fetch();
}
}
return {userId, characters, isAuthenticated, refresh, fetch, login, addCharacter, logout, bootstrap};
})
+2
View File
@@ -0,0 +1,2 @@
export * from './auth';
export * from './token';
+7
View File
@@ -0,0 +1,7 @@
let accessToken: string | null = null;
export const getAccessToken = (): string | null => accessToken;
export const setAccessToken = (token: string | null): void => {
accessToken = token;
};
+716 -3
View File
@@ -23,14 +23,41 @@ import type { RequestArgs } from './base';
// @ts-ignore // @ts-ignore
import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError, operationServerMap } from './base'; import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError, operationServerMap } from './base';
/**
* A single acquisition of a market type, tracking the remaining quantity available in the FIFO cost pool.
*/
export interface AcquisitionResponse { export interface AcquisitionResponse {
/**
* Unique acquisition identifier.
*/
'acquisitionId': string; 'acquisitionId': string;
/**
* EVE character id that made the acquisition.
*/
'characterId': number; 'characterId': number;
/**
* EVE market type (item) id that was acquired.
*/
'marketTypeId': number; 'marketTypeId': number;
/**
* How the item entered the inventory.
*/
'source': AcquisitionResponseSourceEnum; 'source': AcquisitionResponseSourceEnum;
/**
* When the acquisition occurred.
*/
'datetime': string; 'datetime': string;
/**
* Quantity originally acquired.
*/
'quantity': number; 'quantity': number;
/**
* Quantity still remaining in the FIFO cost pool.
*/
'remaining': number; 'remaining': number;
/**
* Cost per unit at acquisition time, in ISK.
*/
'unitCost': number; 'unitCost': number;
} }
@@ -41,44 +68,140 @@ export const AcquisitionResponseSourceEnum = {
export type AcquisitionResponseSourceEnum = typeof AcquisitionResponseSourceEnum[keyof typeof AcquisitionResponseSourceEnum]; export type AcquisitionResponseSourceEnum = typeof AcquisitionResponseSourceEnum[keyof typeof AcquisitionResponseSourceEnum];
/**
* The balance of a ledger: its ISK total plus the quantity of each item type held.
*/
export interface BalanceResponse { export interface BalanceResponse {
/**
* ISK balance of the ledger.
*/
'iskBalance': number; 'iskBalance': number;
/**
* Per-item-type quantities held in the ledger.
*/
'itemBalances': Array<ItemBalanceResponse>; 'itemBalances': Array<ItemBalanceResponse>;
} }
/**
* An EVE Online character.
*/
export interface CharacterResponse { export interface CharacterResponse {
/**
* EVE character id.
*/
'characterId': number; 'characterId': number;
/**
* Character name.
*/
'name': string; 'name': string;
} }
/**
* A rule book assigned to a character, with the ledger bindings that resolve its references.
*/
export interface CharacterRuleBookResponse { export interface CharacterRuleBookResponse {
/**
* The character the rule book is assigned to.
*/
'character': CharacterResponse; 'character': CharacterResponse;
/**
* Summary of the assigned rule book.
*/
'ruleBook': RuleBookSummaryResponse; 'ruleBook': RuleBookSummaryResponse;
/**
* Bindings from each ledger reference name to the ledger id it resolves to for this character.
*/
'bindings': { [key: string]: string; }; 'bindings': { [key: string]: string; };
} }
/**
* A combined ledger: an aggregate whose balance is the sum of its member ledgers.
*/
export interface CombinedLedgerResponse extends LedgerResponse { export interface CombinedLedgerResponse extends LedgerResponse {
/**
* Unique ledger identifier.
*/
'ledgerId': string; 'ledgerId': string;
/**
* Ledger name.
*/
'name': string; 'name': string;
/**
* Aggregate ISK balance across all member ledgers.
*/
'balance': number; 'balance': number;
/**
* Ids of the ledgers aggregated by this combined ledger.
*/
'memberLedgerIds': Array<string>; 'memberLedgerIds': Array<string>;
} }
/**
* Request to create a new combined ledger aggregating the given member ledgers.
*/
export interface CreateCombinedLedgerRequest { export interface CreateCombinedLedgerRequest {
/**
* Name for the new combined ledger.
*/
'name': string; 'name': string;
/**
* Ids of the ledgers to aggregate.
*/
'memberLedgerIds': Array<string>; 'memberLedgerIds': Array<string>;
} }
/**
* Request to create a new main ledger.
*/
export interface CreateMainLedgerRequest { export interface CreateMainLedgerRequest {
/**
* Name for the new ledger.
*/
'name': string; 'name': string;
} }
/**
* Request to create a new rule book.
*/
export interface CreateRuleBookRequest { export interface CreateRuleBookRequest {
/**
* Rule book name.
*/
'name': string; 'name': string;
/**
* Whether this rule book is used to derive acquisitions.
*/
'usedForAcquisitions': boolean; 'usedForAcquisitions': boolean;
/**
* Symbolic ledger references the script writes to; each must be bound to a ledger per character.
*/
'ledgerRefs': Array<string>; 'ledgerRefs': Array<string>;
/**
* The classification script source.
*/
'script': string; 'script': string;
} }
/**
* Quartile statistics computed over a market type\'s price history.
*/
export interface HistoryQuartilesResponse { export interface HistoryQuartilesResponse {
/**
* EVE market type (item) id.
*/
'marketTypeId': number; 'marketTypeId': number;
/**
* First quartile (25th percentile) price, in ISK.
*/
'q1': number; 'q1': number;
/**
* Median (50th percentile) price, in ISK.
*/
'median': number; 'median': number;
/**
* Third quartile (75th percentile) price, in ISK.
*/
'q3': number; 'q3': number;
/**
* Total traded volume over the period.
*/
'totalVolume': number; 'totalVolume': number;
/**
* Overall price trend over the period.
*/
'trend': HistoryQuartilesResponseTrendEnum; 'trend': HistoryQuartilesResponseTrendEnum;
} }
@@ -90,56 +213,177 @@ export const HistoryQuartilesResponseTrendEnum = {
export type HistoryQuartilesResponseTrendEnum = typeof HistoryQuartilesResponseTrendEnum[keyof typeof HistoryQuartilesResponseTrendEnum]; export type HistoryQuartilesResponseTrendEnum = typeof HistoryQuartilesResponseTrendEnum[keyof typeof HistoryQuartilesResponseTrendEnum];
/**
* A transfer of ISK from one ledger to another.
*/
export interface IskTransferResponse extends TransferResponse { export interface IskTransferResponse extends TransferResponse {
/**
* Id of the ledger the ISK is debited from.
*/
'fromLedgerId': string; 'fromLedgerId': string;
/**
* Id of the ledger the ISK is credited to.
*/
'toLedgerId': string; 'toLedgerId': string;
/**
* Amount of ISK transferred.
*/
'amount': number; 'amount': number;
} }
/**
* Quantity of a single item type held in a ledger.
*/
export interface ItemBalanceResponse { export interface ItemBalanceResponse {
/**
* EVE market type (item) id.
*/
'typeId': number; 'typeId': number;
/**
* Number of units held.
*/
'quantity': number; 'quantity': number;
} }
/**
* A transfer of an item stack from one ledger to another.
*/
export interface ItemTransferResponse extends TransferResponse { export interface ItemTransferResponse extends TransferResponse {
/**
* Id of the ledger the items are debited from.
*/
'fromLedgerId': string; 'fromLedgerId': string;
/**
* Id of the ledger the items are credited to.
*/
'toLedgerId': string; 'toLedgerId': string;
/**
* EVE market type (item) id transferred.
*/
'marketTypeId': number; 'marketTypeId': number;
/**
* Number of units transferred.
*/
'quantity': number; 'quantity': number;
} }
/** /**
* @type LedgerResponse * @type LedgerResponse
* A ledger, either a standalone main ledger or a combined ledger aggregating others.
*/ */
export type LedgerResponse = { type: 'COMBINED' } & CombinedLedgerResponse | { type: 'MAIN' } & MainLedgerResponse; export type LedgerResponse = { type: 'COMBINED' } & CombinedLedgerResponse | { type: 'MAIN' } & MainLedgerResponse;
/**
* A main ledger: a standalone ledger with its own balance.
*/
export interface MainLedgerResponse extends LedgerResponse { export interface MainLedgerResponse extends LedgerResponse {
/**
* Unique ledger identifier.
*/
'ledgerId': string; 'ledgerId': string;
/**
* Ledger name.
*/
'name': string; 'name': string;
/**
* Current ISK balance of the ledger.
*/
'balance': number; 'balance': number;
} }
/**
* A single day of market history for a market type.
*/
export interface MarketHistoryResponse { export interface MarketHistoryResponse {
/**
* EVE market type (item) id.
*/
'marketTypeId': number; 'marketTypeId': number;
/**
* Date the history entry covers.
*/
'date': string; 'date': string;
/**
* Average price for the day, in ISK.
*/
'average': number; 'average': number;
/**
* Highest price for the day, in ISK.
*/
'highest': number; 'highest': number;
/**
* Lowest price for the day, in ISK.
*/
'lowest': number; 'lowest': number;
/**
* Number of orders for the day.
*/
'orderCount': number; 'orderCount': number;
/**
* Traded volume for the day.
*/
'volume': number; 'volume': number;
} }
/**
* Current best buy and sell prices for a market type.
*/
export interface MarketPriceResponse { export interface MarketPriceResponse {
/**
* EVE market type (item) id.
*/
'marketTypeId': number; 'marketTypeId': number;
/**
* Best buy price, in ISK.
*/
'buy': number; 'buy': number;
/**
* Best sell price, in ISK.
*/
'sell': number; 'sell': number;
/**
* Number of market orders considered.
*/
'orderCount': number; 'orderCount': number;
} }
/**
* Result of a market scan combining current prices, history quartiles and a computed profitability score for a market type.
*/
export interface MarketScanResponse { export interface MarketScanResponse {
/**
* EVE market type (item) id.
*/
'marketTypeId': number; 'marketTypeId': number;
/**
* Current best buy price, in ISK.
*/
'buy': number; 'buy': number;
/**
* Current best sell price, in ISK.
*/
'sell': number; 'sell': number;
/**
* First quartile (25th percentile) historical price, in ISK.
*/
'q1': number; 'q1': number;
/**
* Median (50th percentile) historical price, in ISK.
*/
'median': number; 'median': number;
/**
* Third quartile (75th percentile) historical price, in ISK.
*/
'q3': number; 'q3': number;
/**
* Total traded volume over the scanned period.
*/
'totalVolume': number; 'totalVolume': number;
/**
* Overall price trend over the scanned period.
*/
'trend': MarketScanResponseTrendEnum; 'trend': MarketScanResponseTrendEnum;
/**
* Estimated profit per unit after fees and taxes, in ISK.
*/
'profit': number; 'profit': number;
/**
* Computed profitability score used to rank scan results.
*/
'score': number; 'score': number;
} }
@@ -151,79 +395,273 @@ export const MarketScanResponseTrendEnum = {
export type MarketScanResponseTrendEnum = typeof MarketScanResponseTrendEnum[keyof typeof MarketScanResponseTrendEnum]; export type MarketScanResponseTrendEnum = typeof MarketScanResponseTrendEnum[keyof typeof MarketScanResponseTrendEnum];
/**
* An EVE Online market type (item) and its static attributes.
*/
export interface MarketTypeResponse { export interface MarketTypeResponse {
/**
* EVE market type (item) id.
*/
'id': number; 'id': number;
/**
* Item name.
*/
'name': string; 'name': string;
/**
* Group id the item belongs to.
*/
'groupId': number; 'groupId': number;
/**
* Market group id the item is listed under.
*/
'marketGroupId': number; 'marketGroupId': number;
/**
* Item description.
*/
'description': string; 'description': string;
/**
* Whether the item is published and visible in game.
*/
'published': boolean; 'published': boolean;
'basePrice': number; /**
* Base price of the item, in ISK, if defined.
*/
'basePrice': number | null;
/**
* Packaged volume of a single unit, in m³.
*/
'volume': number; 'volume': number;
/**
* Number of units produced or reprocessed per portion.
*/
'portionSize': number; 'portionSize': number;
'iconId': number; /**
* Icon id for the item, if defined.
*/
'iconId': number | null;
} }
/**
* A market type stack valued at current buy and sell prices.
*/
export interface MarketTypeStackAppraisalResponse { export interface MarketTypeStackAppraisalResponse {
/**
* EVE market type (item) id.
*/
'marketTypeId': number; 'marketTypeId': number;
/**
* Number of units in the stack.
*/
'quantity': number; 'quantity': number;
/**
* Total buy value of the stack, in ISK.
*/
'buy': number; 'buy': number;
/**
* Total sell value of the stack, in ISK.
*/
'sell': number; 'sell': number;
/**
* Number of market orders considered for the valuation.
*/
'orderCount': number; 'orderCount': number;
} }
/**
* A quantity of a single market type to reprocess.
*/
export interface MarketTypeStackRequest { export interface MarketTypeStackRequest {
/**
* EVE market type (item) id to reprocess.
*/
'marketTypeId': number; 'marketTypeId': number;
/**
* Number of units to reprocess.
*/
'quantity': number; 'quantity': number;
} }
/**
* A quantity of a single market type.
*/
export interface MarketTypeStackResponse { export interface MarketTypeStackResponse {
/**
* EVE market type (item) id.
*/
'marketTypeId': number; 'marketTypeId': number;
/**
* Number of units in the stack.
*/
'quantity': number; 'quantity': number;
} }
/**
* The authenticated user and the characters they own.
*/
export interface MeResponse {
/**
* Unique user identifier.
*/
'userId': string;
/**
* Characters owned by the user.
*/
'characters': Array<CharacterResponse>;
}
/**
* A freshly minted access token.
*/
export interface RefreshResponse {
/**
* Short-lived bearer access token (JWT) for the Authorization header.
*/
'accessToken': string;
}
/**
* Request to reprocess a set of item stacks on behalf of a character.
*/
export interface ReprocessItemsRequest { export interface ReprocessItemsRequest {
/**
* EVE character id whose reprocessing skills and facility standings are applied.
*/
'characterId': number; 'characterId': number;
/**
* Item stacks to reprocess.
*/
'items': Array<MarketTypeStackRequest>; 'items': Array<MarketTypeStackRequest>;
} }
/**
* Result of reprocessing an item stack: the input stack and the resulting material outputs, each valued at market prices.
*/
export interface ReprocessingResultResponse { export interface ReprocessingResultResponse {
/**
* The input stack that was reprocessed.
*/
'input': MarketTypeStackAppraisalResponse; 'input': MarketTypeStackAppraisalResponse;
/**
* Material stacks yielded by reprocessing the input.
*/
'output': Array<MarketTypeStackAppraisalResponse>; 'output': Array<MarketTypeStackAppraisalResponse>;
} }
/**
* A rule book: a named script that classifies transactions, together with the ledger references it expects.
*/
export interface RuleBookResponse { export interface RuleBookResponse {
/**
* Unique rule book identifier.
*/
'ruleBookId': string; 'ruleBookId': string;
/**
* Rule book name.
*/
'name': string; 'name': string;
/**
* Whether this rule book is used to derive acquisitions.
*/
'usedForAcquisitions': boolean; 'usedForAcquisitions': boolean;
/**
* Symbolic ledger references the script writes to; each must be bound to a ledger per character.
*/
'ledgerRefs': Array<string>; 'ledgerRefs': Array<string>;
/**
* The classification script source.
*/
'script': string; 'script': string;
} }
/**
* Lightweight reference to a rule book: its id and name only.
*/
export interface RuleBookSummaryResponse { export interface RuleBookSummaryResponse {
/**
* Unique rule book identifier.
*/
'ruleBookId': string; 'ruleBookId': string;
/**
* Rule book name.
*/
'name': string; 'name': string;
} }
/**
* Request to assign a rule book to a character, binding each of its ledger references to a concrete ledger.
*/
export interface SetCharacterRuleBookRequest { export interface SetCharacterRuleBookRequest {
/**
* Id of the rule book to assign.
*/
'ruleBookId': string; 'ruleBookId': string;
/**
* Bindings from each ledger reference name to the ledger id it resolves to for this character.
*/
'bindings': { [key: string]: string; }; 'bindings': { [key: string]: string; };
} }
/**
* A transaction: a dated, described group of transfers between ledgers.
*/
export interface TransactionResponse { export interface TransactionResponse {
/**
* Unique transaction identifier.
*/
'transactionId': string; 'transactionId': string;
'characterId': number; /**
* EVE character id the transaction is attributed to, if any.
*/
'characterId': number | null;
/**
* When the transaction occurred.
*/
'datetime': string; 'datetime': string;
/**
* Human-readable description of the transaction.
*/
'description': string; 'description': string;
/**
* The transfers that make up this transaction.
*/
'transfers': Array<TransferResponse>; 'transfers': Array<TransferResponse>;
} }
/** /**
* @type TransferResponse * @type TransferResponse
* A transfer between two ledgers, either of ISK or of an item stack.
*/ */
export type TransferResponse = { type: 'ISK' } & IskTransferResponse | { type: 'ITEM' } & ItemTransferResponse; export type TransferResponse = { type: 'ISK' } & IskTransferResponse | { type: 'ITEM' } & ItemTransferResponse;
/**
* Request to update an existing combined ledger; replaces its name and member set.
*/
export interface UpdateCombinedLedgerRequest { export interface UpdateCombinedLedgerRequest {
/**
* New name for the combined ledger.
*/
'name': string; 'name': string;
/**
* Ids of the ledgers to aggregate.
*/
'memberLedgerIds': Array<string>; 'memberLedgerIds': Array<string>;
} }
/**
* Request to update an existing main ledger.
*/
export interface UpdateMainLedgerRequest { export interface UpdateMainLedgerRequest {
/**
* New name for the ledger.
*/
'name': string; 'name': string;
} }
/**
* Request to update an existing rule book; replaces its mutable fields.
*/
export interface UpdateRuleBookRequest { export interface UpdateRuleBookRequest {
/**
* Rule book name.
*/
'name': string; 'name': string;
/**
* Whether this rule book is used to derive acquisitions.
*/
'usedForAcquisitions': boolean; 'usedForAcquisitions': boolean;
/**
* Symbolic ledger references the script writes to; each must be bound to a ledger per character.
*/
'ledgerRefs': Array<string>; 'ledgerRefs': Array<string>;
/**
* The classification script source.
*/
'script': string; 'script': string;
} }
@@ -557,6 +995,281 @@ export class ActivityApi extends BaseAPI {
/**
* AuthApi - axios parameter creator
*/
export const AuthApiAxiosParamCreator = function (configuration?: Configuration) {
return {
/**
*
* @summary Seed the current user in the session so the next SSO links the new character to them
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
addCharacter: async (options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/characters/add`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @summary Revoke the refresh session and clear the refresh cookie
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
logout: async (options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/logout`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @summary Return the authenticated user and the characters they own
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
me: async (options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/me`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
localVarHeaderParameter['Accept'] = '*/*';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @summary Rotate the refresh session and mint a fresh access token
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
refresh: async (options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/refresh`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
localVarHeaderParameter['Accept'] = '*/*';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
}
};
/**
* AuthApi - functional programming interface
*/
export const AuthApiFp = function(configuration?: Configuration) {
const localVarAxiosParamCreator = AuthApiAxiosParamCreator(configuration)
return {
/**
*
* @summary Seed the current user in the session so the next SSO links the new character to them
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async addCharacter(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.addCharacter(options);
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
const localVarOperationServerBasePath = operationServerMap['AuthApi.addCharacter']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
/**
*
* @summary Revoke the refresh session and clear the refresh cookie
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async logout(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.logout(options);
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
const localVarOperationServerBasePath = operationServerMap['AuthApi.logout']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
/**
*
* @summary Return the authenticated user and the characters they own
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async me(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<MeResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.me(options);
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
const localVarOperationServerBasePath = operationServerMap['AuthApi.me']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
/**
*
* @summary Rotate the refresh session and mint a fresh access token
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async refresh(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<RefreshResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.refresh(options);
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
const localVarOperationServerBasePath = operationServerMap['AuthApi.refresh']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
}
};
/**
* AuthApi - factory interface
*/
export const AuthApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
const localVarFp = AuthApiFp(configuration)
return {
/**
*
* @summary Seed the current user in the session so the next SSO links the new character to them
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
addCharacter(options?: RawAxiosRequestConfig): AxiosPromise<void> {
return localVarFp.addCharacter(options).then((request) => request(axios, basePath));
},
/**
*
* @summary Revoke the refresh session and clear the refresh cookie
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
logout(options?: RawAxiosRequestConfig): AxiosPromise<void> {
return localVarFp.logout(options).then((request) => request(axios, basePath));
},
/**
*
* @summary Return the authenticated user and the characters they own
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
me(options?: RawAxiosRequestConfig): AxiosPromise<MeResponse> {
return localVarFp.me(options).then((request) => request(axios, basePath));
},
/**
*
* @summary Rotate the refresh session and mint a fresh access token
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
refresh(options?: RawAxiosRequestConfig): AxiosPromise<RefreshResponse> {
return localVarFp.refresh(options).then((request) => request(axios, basePath));
},
};
};
/**
* AuthApi - object-oriented interface
*/
export class AuthApi extends BaseAPI {
/**
*
* @summary Seed the current user in the session so the next SSO links the new character to them
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
public addCharacter(options?: RawAxiosRequestConfig) {
return AuthApiFp(this.configuration).addCharacter(options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary Revoke the refresh session and clear the refresh cookie
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
public logout(options?: RawAxiosRequestConfig) {
return AuthApiFp(this.configuration).logout(options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary Return the authenticated user and the characters they own
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
public me(options?: RawAxiosRequestConfig) {
return AuthApiFp(this.configuration).me(options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary Rotate the refresh session and mint a fresh access token
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
public refresh(options?: RawAxiosRequestConfig) {
return AuthApiFp(this.configuration).refresh(options).then((request) => request(this.axios, this.basePath));
}
}
/** /**
* CharacterApi - axios parameter creator * CharacterApi - axios parameter creator
*/ */
+16 -6
View File
@@ -1,9 +1,10 @@
import { createPinia } from 'pinia'; import {createPinia} from 'pinia';
import { createApp } from 'vue'; import {createApp} from 'vue';
import { createRouter, createWebHistory } from 'vue-router'; import {createRouter, createWebHistory} from 'vue-router';
import App from './App.vue'; import App from './App.vue';
import { initLogger } from './logger'; import {useAuthStore} from './auth';
import { routes } from './routes'; import {initLogger} from './logger';
import {routeNames, routes} from './routes';
import './style.css'; import './style.css';
initLogger(); initLogger();
@@ -18,4 +19,13 @@ const router = createRouter({
app.use(pinia); app.use(pinia);
app.use(router); app.use(router);
app.mount('#app'); const authStore = useAuthStore();
router.beforeEach(to => {
if (!to.meta.public && !authStore.isAuthenticated) {
return {name: routeNames.home};
}
return true;
});
authStore.bootstrap().finally(() => app.mount('#app'));
+76 -3
View File
@@ -1,19 +1,22 @@
import {logResource} from "@/service"; import {logResource} from "@/service";
import axios from "axios"; import {getAccessToken, setAccessToken} from "@/auth/token";
import axios, {InternalAxiosRequestConfig} from "axios";
import { import {
AcquisitionApi, AcquisitionApi,
ActivityApi, ActivityApi,
AuthApi,
CharacterApi, CharacterApi,
CharacterRuleBookApi, CharacterRuleBookApi,
LedgerApi, LedgerApi,
MarketApi, MarketApi,
MeResponse,
ReprocessingApi, ReprocessingApi,
RuleBookApi, RuleBookApi,
TransactionApi TransactionApi
} from "@/generated/mammon"; } from "@/generated/mammon";
export const mammonUrl = import.meta.env.VITE_MAMMON_URL; export const mammonUrl = import.meta.env.VITE_MAMMON_URL;
export const mammonAddCharacterUrl = mammonUrl + "oauth2/authorization/esi" export const mammonLoginUrl = mammonUrl + "oauth2/authorization/esi"
const mammonAxiosInstance = axios.create({ const mammonAxiosInstance = axios.create({
baseURL: mammonUrl, baseURL: mammonUrl,
@@ -24,6 +27,76 @@ const mammonAxiosInstance = axios.create({
}) })
logResource(mammonAxiosInstance) logResource(mammonAxiosInstance)
const credentialedClient = axios.create({
baseURL: mammonUrl,
withCredentials: true,
})
logResource(credentialedClient)
mammonAxiosInstance.interceptors.request.use(config => {
const token = getAccessToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
})
const authApi = new AuthApi(undefined, mammonUrl, mammonAxiosInstance);
const credentialedAuthApi = new AuthApi(undefined, mammonUrl, credentialedClient);
let onAuthExpired: () => void = () => {};
export const setOnAuthExpired = (callback: () => void): void => {
onAuthExpired = callback;
}
let refreshing: Promise<string | null> | null = null;
export const refreshAccessToken = (): Promise<string | null> => {
if (!refreshing) {
refreshing = credentialedAuthApi.refresh()
.then(response => {
setAccessToken(response.data.accessToken);
return response.data.accessToken;
})
.catch(() => {
setAccessToken(null);
return null;
})
.finally(() => {
refreshing = null;
});
}
return refreshing;
}
mammonAxiosInstance.interceptors.response.use(response => response, async error => {
const original = error.config as (InternalAxiosRequestConfig & { _retried?: boolean }) | undefined;
if (error.response?.status === 401 && original && !original._retried) {
original._retried = true;
const token = await refreshAccessToken();
if (token) {
original.headers.Authorization = `Bearer ${token}`;
return mammonAxiosInstance(original);
}
onAuthExpired();
}
return Promise.reject(error);
})
export const fetchMe = (): Promise<MeResponse> =>
authApi.me().then(response => response.data);
export const addCharacter = (): Promise<void> =>
authApi.addCharacter({withCredentials: true}).then(() => undefined);
export const postLogout = (): Promise<void> =>
credentialedAuthApi.logout().then(() => undefined);
export const ledgerApi = new LedgerApi(undefined, mammonUrl, mammonAxiosInstance); export const ledgerApi = new LedgerApi(undefined, mammonUrl, mammonAxiosInstance);
export const transactionApi = new TransactionApi(undefined, mammonUrl, mammonAxiosInstance); export const transactionApi = new TransactionApi(undefined, mammonUrl, mammonAxiosInstance);
export const characterApi = new CharacterApi(undefined, mammonUrl, mammonAxiosInstance); export const characterApi = new CharacterApi(undefined, mammonUrl, mammonAxiosInstance);
@@ -32,4 +105,4 @@ export const characterRuleBookApi = new CharacterRuleBookApi(undefined, mammonUr
export const activityApi = new ActivityApi(undefined, mammonUrl, mammonAxiosInstance); export const activityApi = new ActivityApi(undefined, mammonUrl, mammonAxiosInstance);
export const acquisitionApi = new AcquisitionApi(undefined, mammonUrl, mammonAxiosInstance); export const acquisitionApi = new AcquisitionApi(undefined, mammonUrl, mammonAxiosInstance);
export const marketApi = new MarketApi(undefined, mammonUrl, mammonAxiosInstance); export const marketApi = new MarketApi(undefined, mammonUrl, mammonAxiosInstance);
export const reprocessingApi = new ReprocessingApi(undefined, mammonUrl, mammonAxiosInstance); export const reprocessingApi = new ReprocessingApi(undefined, mammonUrl, mammonAxiosInstance);
+3 -4
View File
@@ -1,14 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import {mammonAddCharacterUrl} from "@/mammon";
import {CharacterLabel, useCharactersStore} from "@/characters"; import {CharacterLabel, useCharactersStore} from "@/characters";
import {useAuthStore} from "@/auth";
import {ArrowPathIcon} from '@heroicons/vue/24/outline'; import {ArrowPathIcon} from '@heroicons/vue/24/outline';
const charactersStore = useCharactersStore() const charactersStore = useCharactersStore()
const authStore = useAuthStore()
const addCharacter = () => { const addCharacter = () => authStore.addCharacter();
window.location.replace(mammonAddCharacterUrl);
}
</script> </script>
<template> <template>
+9 -1
View File
@@ -1,6 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import {useAuthStore} from '@/auth';
const authStore = useAuthStore();
</script> </script>
<template> <template>
<div></div> <div class="flex flex-col items-center justify-center h-screen gap-4">
<h1 class="text-2xl">eveal</h1>
<button v-if="!authStore.isAuthenticated" @click="authStore.login()">
Login with EVE Online
</button>
</div>
</template> </template>
+9 -3
View File
@@ -1,5 +1,11 @@
import {RouteRecordRaw} from 'vue-router'; import {RouteRecordRaw} from 'vue-router';
declare module 'vue-router' {
interface RouteMeta {
public?: boolean;
}
}
export const routeNames = { export const routeNames = {
home: 'home', home: 'home',
callback: 'callback', callback: 'callback',
@@ -15,8 +21,8 @@ export const routeNames = {
} as const; } as const;
export const routes: RouteRecordRaw[] = [ export const routes: RouteRecordRaw[] = [
{path: '/', name: routeNames.home, component: () => import('@/pages/Index.vue')}, {path: '/', name: routeNames.home, component: () => import('@/pages/Index.vue'), meta: {public: true}},
{path: '/callback', name: routeNames.callback, component: () => import('@/pages/Index.vue')}, {path: '/callback', name: routeNames.callback, component: () => import('@/pages/Index.vue'), meta: {public: true}},
{path: '/ledgers', component: () => import('@/pages/Ledgers.vue'), children: [ {path: '/ledgers', component: () => import('@/pages/Ledgers.vue'), children: [
{path: '', component: () => import('@/pages/ledger/ListLedgers.vue')}, {path: '', component: () => import('@/pages/ledger/ListLedgers.vue')},
@@ -52,5 +58,5 @@ export const routes: RouteRecordRaw[] = [
{path: '/tools', component: () => import('@/pages/Tools.vue')}, {path: '/tools', component: () => import('@/pages/Tools.vue')},
{path: '/characters', component: () => import('@/pages/Characters.vue')}, {path: '/characters', component: () => import('@/pages/Characters.vue')},
{path: '/about', name: routeNames.about, component: () => import('@/pages/About.vue')}, {path: '/about', name: routeNames.about, component: () => import('@/pages/About.vue'), meta: {public: true}},
] as const; ] as const;
+8 -3
View File
@@ -1,7 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import {Dropdown} from '@/components'; import {Dropdown} from '@/components';
import {RouterLink} from 'vue-router'; import {RouterLink} from 'vue-router';
import {computed} from 'vue';
import {routeNames} from '@/routes'; import {routeNames} from '@/routes';
import {useAuthStore} from '@/auth';
const authStore = useAuthStore();
const name = computed(() => authStore.characters[0]?.name ?? 'NAME');
const links = [ const links = [
{name: "Market", path: "/market"}, {name: "Market", path: "/market"},
@@ -12,8 +18,7 @@ const links = [
]; ];
const logout = async () => { const logout = () => authStore.logout();
}
</script> </script>
<template> <template>
@@ -22,7 +27,7 @@ const logout = async () => {
<div class="mb-2 border-b-2 border-emerald-500"> <div class="mb-2 border-b-2 border-emerald-500">
<Dropdown class="mb-2 user-dropdown"> <Dropdown class="mb-2 user-dropdown">
<template #button> <template #button>
<span>NAME</span> <span>{{ name }}</span>
</template> </template>
<ul> <ul>
<li> <li>