spindb 0.32.2 → 0.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -0
- package/README.md +105 -857
- package/cli/commands/create.ts +5 -1
- package/cli/commands/engines.ts +78 -1
- package/cli/commands/menu/backup-handlers.ts +9 -0
- package/cli/commands/menu/container-handlers.ts +2 -0
- package/cli/commands/menu/engine-handlers.ts +4 -0
- package/cli/commands/menu/settings-handlers.ts +3 -0
- package/cli/commands/menu/shell-handlers.ts +43 -1
- package/cli/constants.ts +4 -0
- package/cli/helpers.ts +73 -0
- package/config/backup-formats.ts +14 -0
- package/config/engine-defaults.ts +13 -0
- package/config/engines.json +17 -0
- package/core/config-manager.ts +10 -0
- package/core/dependency-manager.ts +2 -0
- package/core/docker-exporter.ts +13 -0
- package/engines/index.ts +4 -0
- package/engines/influxdb/README.md +180 -0
- package/engines/influxdb/api-client.ts +64 -0
- package/engines/influxdb/backup.ts +160 -0
- package/engines/influxdb/binary-manager.ts +110 -0
- package/engines/influxdb/binary-urls.ts +69 -0
- package/engines/influxdb/hostdb-releases.ts +23 -0
- package/engines/influxdb/index.ts +1227 -0
- package/engines/influxdb/restore.ts +417 -0
- package/engines/influxdb/version-maps.ts +75 -0
- package/engines/influxdb/version-validator.ts +128 -0
- package/package.json +2 -1
- package/types/index.ts +9 -0
package/cli/commands/create.ts
CHANGED
|
@@ -398,6 +398,10 @@ export function detectLocationType(location: string): {
|
|
|
398
398
|
return { type: 'connection', inferredEngine: Engine.Meilisearch }
|
|
399
399
|
}
|
|
400
400
|
|
|
401
|
+
if (location.startsWith('influxdb://')) {
|
|
402
|
+
return { type: 'connection', inferredEngine: Engine.InfluxDB }
|
|
403
|
+
}
|
|
404
|
+
|
|
401
405
|
if (existsSync(location)) {
|
|
402
406
|
// Check if it's a SQLite file (case-insensitive)
|
|
403
407
|
const lowerLocation = location.toLowerCase()
|
|
@@ -423,7 +427,7 @@ export const createCommand = new Command('create')
|
|
|
423
427
|
.argument('[name]', 'Container name')
|
|
424
428
|
.option(
|
|
425
429
|
'-e, --engine <engine>',
|
|
426
|
-
'Database engine (postgresql, mysql, mariadb, sqlite, duckdb, mongodb, ferretdb, redis, valkey, clickhouse, qdrant, meilisearch)',
|
|
430
|
+
'Database engine (postgresql, mysql, mariadb, sqlite, duckdb, mongodb, ferretdb, redis, valkey, clickhouse, qdrant, meilisearch, couchdb, cockroachdb, surrealdb, questdb, typedb, influxdb)',
|
|
427
431
|
)
|
|
428
432
|
.option('--db-version <version>', 'Database version (e.g., 17, 8.0)')
|
|
429
433
|
.option('-d, --database <database>', 'Database name')
|
package/cli/commands/engines.ts
CHANGED
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
type InstalledSurrealDBEngine,
|
|
49
49
|
type InstalledQuestDBEngine,
|
|
50
50
|
type InstalledTypeDBEngine,
|
|
51
|
+
type InstalledInfluxDBEngine,
|
|
51
52
|
} from '../helpers'
|
|
52
53
|
import { Engine, Platform } from '../../types'
|
|
53
54
|
import {
|
|
@@ -70,6 +71,7 @@ import { cockroachdbBinaryManager } from '../../engines/cockroachdb/binary-manag
|
|
|
70
71
|
import { surrealdbBinaryManager } from '../../engines/surrealdb/binary-manager'
|
|
71
72
|
import { questdbBinaryManager } from '../../engines/questdb/binary-manager'
|
|
72
73
|
import { typedbBinaryManager } from '../../engines/typedb/binary-manager'
|
|
74
|
+
import { influxdbBinaryManager } from '../../engines/influxdb/binary-manager'
|
|
73
75
|
import {
|
|
74
76
|
DEFAULT_DOCUMENTDB_VERSION,
|
|
75
77
|
normalizeDocumentDBVersion,
|
|
@@ -475,6 +477,9 @@ async function listEngines(options: { json?: boolean }): Promise<void> {
|
|
|
475
477
|
const typedbEngines = engines.filter(
|
|
476
478
|
(e): e is InstalledTypeDBEngine => e.engine === 'typedb',
|
|
477
479
|
)
|
|
480
|
+
const influxdbEngines = engines.filter(
|
|
481
|
+
(e): e is InstalledInfluxDBEngine => e.engine === 'influxdb',
|
|
482
|
+
)
|
|
478
483
|
|
|
479
484
|
// Calculate total size for PostgreSQL
|
|
480
485
|
const totalPgSize = pgEngines.reduce((acc, e) => acc + e.sizeBytes, 0)
|
|
@@ -699,6 +704,20 @@ async function listEngines(options: { json?: boolean }): Promise<void> {
|
|
|
699
704
|
)
|
|
700
705
|
}
|
|
701
706
|
|
|
707
|
+
// InfluxDB rows
|
|
708
|
+
for (const engine of influxdbEngines) {
|
|
709
|
+
const platformInfo = `${engine.platform}-${engine.arch}`
|
|
710
|
+
|
|
711
|
+
console.log(
|
|
712
|
+
chalk.gray(' ') +
|
|
713
|
+
getEngineIcon('influxdb') +
|
|
714
|
+
chalk.cyan('influxdb'.padEnd(13)) +
|
|
715
|
+
chalk.yellow(engine.version.padEnd(12)) +
|
|
716
|
+
chalk.gray(platformInfo.padEnd(18)) +
|
|
717
|
+
chalk.white(formatBytes(engine.sizeBytes)),
|
|
718
|
+
)
|
|
719
|
+
}
|
|
720
|
+
|
|
702
721
|
console.log(chalk.gray(' ' + '─'.repeat(59)))
|
|
703
722
|
|
|
704
723
|
// Summary
|
|
@@ -852,6 +871,17 @@ async function listEngines(options: { json?: boolean }): Promise<void> {
|
|
|
852
871
|
),
|
|
853
872
|
)
|
|
854
873
|
}
|
|
874
|
+
if (influxdbEngines.length > 0) {
|
|
875
|
+
const totalInfluxDBSize = influxdbEngines.reduce(
|
|
876
|
+
(acc, e) => acc + e.sizeBytes,
|
|
877
|
+
0,
|
|
878
|
+
)
|
|
879
|
+
console.log(
|
|
880
|
+
chalk.gray(
|
|
881
|
+
` InfluxDB: ${influxdbEngines.length} version(s), ${formatBytes(totalInfluxDBSize)}`,
|
|
882
|
+
),
|
|
883
|
+
)
|
|
884
|
+
}
|
|
855
885
|
console.log()
|
|
856
886
|
}
|
|
857
887
|
|
|
@@ -2013,9 +2043,56 @@ enginesCommand
|
|
|
2013
2043
|
return
|
|
2014
2044
|
}
|
|
2015
2045
|
|
|
2046
|
+
if (['influxdb', 'influx'].includes(normalizedEngine)) {
|
|
2047
|
+
if (!version) {
|
|
2048
|
+
console.error(uiError('InfluxDB requires a version (e.g., 3)'))
|
|
2049
|
+
process.exit(1)
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
const engine = getEngine(Engine.InfluxDB)
|
|
2053
|
+
|
|
2054
|
+
const spinner = createSpinner(
|
|
2055
|
+
`Checking InfluxDB ${version} binaries...`,
|
|
2056
|
+
)
|
|
2057
|
+
spinner.start()
|
|
2058
|
+
|
|
2059
|
+
let wasCached = false
|
|
2060
|
+
await engine.ensureBinaries(version, ({ stage, message }) => {
|
|
2061
|
+
if (stage === 'cached') {
|
|
2062
|
+
wasCached = true
|
|
2063
|
+
spinner.text = `InfluxDB ${version} binaries ready (cached)`
|
|
2064
|
+
} else {
|
|
2065
|
+
spinner.text = message
|
|
2066
|
+
}
|
|
2067
|
+
})
|
|
2068
|
+
|
|
2069
|
+
if (wasCached) {
|
|
2070
|
+
spinner.succeed(`InfluxDB ${version} binaries already installed`)
|
|
2071
|
+
} else {
|
|
2072
|
+
spinner.succeed(`InfluxDB ${version} binaries downloaded`)
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
// Show the path for reference
|
|
2076
|
+
const { platform: influxdbPlatform, arch: influxdbArch } =
|
|
2077
|
+
platformService.getPlatformInfo()
|
|
2078
|
+
const influxdbFullVersion =
|
|
2079
|
+
influxdbBinaryManager.getFullVersion(version)
|
|
2080
|
+
const binPath = paths.getBinaryPath({
|
|
2081
|
+
engine: 'influxdb',
|
|
2082
|
+
version: influxdbFullVersion,
|
|
2083
|
+
platform: influxdbPlatform,
|
|
2084
|
+
arch: influxdbArch,
|
|
2085
|
+
})
|
|
2086
|
+
console.log(chalk.gray(` Location: ${binPath}`))
|
|
2087
|
+
|
|
2088
|
+
// Skip client tools check for InfluxDB - it's a REST API server
|
|
2089
|
+
// with no CLI client tools (uses HTTP protocols instead)
|
|
2090
|
+
return
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2016
2093
|
console.error(
|
|
2017
2094
|
uiError(
|
|
2018
|
-
`Unknown engine "${engineName}". Supported: postgresql, mysql, mariadb, sqlite, duckdb, mongodb, ferretdb, redis, valkey, clickhouse, qdrant, meilisearch, couchdb, cockroachdb, surrealdb, questdb, typedb`,
|
|
2095
|
+
`Unknown engine "${engineName}". Supported: postgresql, mysql, mariadb, sqlite, duckdb, mongodb, ferretdb, redis, valkey, clickhouse, qdrant, meilisearch, couchdb, cockroachdb, surrealdb, questdb, typedb, influxdb`,
|
|
2019
2096
|
),
|
|
2020
2097
|
)
|
|
2021
2098
|
process.exit(1)
|
|
@@ -214,6 +214,15 @@ function validateConnectionString(
|
|
|
214
214
|
if (typedbError) return typedbError
|
|
215
215
|
}
|
|
216
216
|
break
|
|
217
|
+
case Engine.InfluxDB:
|
|
218
|
+
if (
|
|
219
|
+
!input.startsWith('influxdb://') &&
|
|
220
|
+
!input.startsWith('http://') &&
|
|
221
|
+
!input.startsWith('https://')
|
|
222
|
+
) {
|
|
223
|
+
return 'Connection string must start with influxdb://, http://, or https://'
|
|
224
|
+
}
|
|
225
|
+
break
|
|
217
226
|
case Engine.SQLite:
|
|
218
227
|
case Engine.DuckDB:
|
|
219
228
|
return 'File-based engines do not support remote connection strings'
|
|
@@ -133,6 +133,8 @@ export async function handleCreate(): Promise<'main' | string | void> {
|
|
|
133
133
|
database = '0'
|
|
134
134
|
} else if (engine === 'qdrant' || engine === 'meilisearch') {
|
|
135
135
|
database = 'default'
|
|
136
|
+
} else if (engine === 'influxdb') {
|
|
137
|
+
database = 'mydb'
|
|
136
138
|
} else {
|
|
137
139
|
database = await promptDatabaseName(name, engine)
|
|
138
140
|
}
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
type InstalledSurrealDBEngine,
|
|
32
32
|
type InstalledQuestDBEngine,
|
|
33
33
|
type InstalledTypeDBEngine,
|
|
34
|
+
type InstalledInfluxDBEngine,
|
|
34
35
|
} from '../../helpers'
|
|
35
36
|
|
|
36
37
|
import { type MenuChoice } from './shared'
|
|
@@ -97,6 +98,9 @@ export async function handleEngines(): Promise<void> {
|
|
|
97
98
|
(e): e is InstalledQuestDBEngine => e.engine === 'questdb',
|
|
98
99
|
),
|
|
99
100
|
...engines.filter((e): e is InstalledTypeDBEngine => e.engine === 'typedb'),
|
|
101
|
+
...engines.filter(
|
|
102
|
+
(e): e is InstalledInfluxDBEngine => e.engine === 'influxdb',
|
|
103
|
+
),
|
|
100
104
|
]
|
|
101
105
|
|
|
102
106
|
// Calculate total size
|
|
@@ -47,6 +47,7 @@ function generatePreviewLine(mode: IconMode): string {
|
|
|
47
47
|
[Engine.SurrealDB]: '[SR]',
|
|
48
48
|
[Engine.QuestDB]: '[QS]',
|
|
49
49
|
[Engine.TypeDB]: '[TB]',
|
|
50
|
+
[Engine.InfluxDB]: '[IX]',
|
|
50
51
|
}
|
|
51
52
|
const icons = PREVIEW_ENGINES.map((engine) => {
|
|
52
53
|
const icon = ASCII_ICONS[engine] || '[??]'
|
|
@@ -75,6 +76,7 @@ function generatePreviewLine(mode: IconMode): string {
|
|
|
75
76
|
[Engine.SurrealDB]: '\uedfe',
|
|
76
77
|
[Engine.QuestDB]: '\ued2f',
|
|
77
78
|
[Engine.TypeDB]: '\ue706',
|
|
79
|
+
[Engine.InfluxDB]: '\udb85\udf95',
|
|
78
80
|
}
|
|
79
81
|
const icons = PREVIEW_ENGINES.map((engine) => {
|
|
80
82
|
const icon = NERD_ICONS[engine] || '\ue706'
|
|
@@ -103,6 +105,7 @@ function generatePreviewLine(mode: IconMode): string {
|
|
|
103
105
|
[Engine.SurrealDB]: '\u{1F300}',
|
|
104
106
|
[Engine.QuestDB]: '\u23F1',
|
|
105
107
|
[Engine.TypeDB]: '\u{1F916}',
|
|
108
|
+
[Engine.InfluxDB]: '\u{1F4C8}',
|
|
106
109
|
}
|
|
107
110
|
const icons = PREVIEW_ENGINES.map((engine) => EMOJI_ICONS[engine] || '\u25A3')
|
|
108
111
|
return icons.join(' ')
|
|
@@ -218,6 +218,13 @@ export async function handleOpenShell(
|
|
|
218
218
|
engineSpecificInstalled = false
|
|
219
219
|
engineSpecificValue = null
|
|
220
220
|
engineSpecificInstallValue = null
|
|
221
|
+
} else if (config.engine === 'influxdb') {
|
|
222
|
+
// InfluxDB uses REST API, no interactive shell
|
|
223
|
+
defaultShellName = 'Web Dashboard'
|
|
224
|
+
engineSpecificCli = null
|
|
225
|
+
engineSpecificInstalled = false
|
|
226
|
+
engineSpecificValue = null
|
|
227
|
+
engineSpecificInstallValue = null
|
|
221
228
|
} else if (config.engine === 'couchdb') {
|
|
222
229
|
// CouchDB uses REST API, open Fauxton dashboard in browser
|
|
223
230
|
defaultShellName = 'Fauxton Dashboard'
|
|
@@ -308,6 +315,12 @@ export async function handleOpenShell(
|
|
|
308
315
|
name: `ℹ Show API info`,
|
|
309
316
|
value: 'api-info',
|
|
310
317
|
})
|
|
318
|
+
} else if (config.engine === 'influxdb') {
|
|
319
|
+
// InfluxDB: REST API only, no web dashboard or interactive shell
|
|
320
|
+
choices.push({
|
|
321
|
+
name: `ℹ Show API info`,
|
|
322
|
+
value: 'api-info',
|
|
323
|
+
})
|
|
311
324
|
} else if (config.engine === 'couchdb') {
|
|
312
325
|
// CouchDB: Fauxton dashboard is built-in at /_utils
|
|
313
326
|
choices.push({
|
|
@@ -320,7 +333,7 @@ export async function handleOpenShell(
|
|
|
320
333
|
value: 'api-info',
|
|
321
334
|
})
|
|
322
335
|
} else {
|
|
323
|
-
// Non-
|
|
336
|
+
// Non-REST-API engines: show default shell option
|
|
324
337
|
choices.push({
|
|
325
338
|
name: `>_ Use default shell (${defaultShellName})`,
|
|
326
339
|
value: 'default',
|
|
@@ -424,6 +437,17 @@ export async function handleOpenShell(
|
|
|
424
437
|
console.log(chalk.gray(` curl http://127.0.0.1:${config.port}/indexes`))
|
|
425
438
|
console.log(chalk.gray(` curl http://127.0.0.1:${config.port}/health`))
|
|
426
439
|
console.log(chalk.gray(` curl http://127.0.0.1:${config.port}/stats`))
|
|
440
|
+
} else if (config.engine === 'influxdb') {
|
|
441
|
+
console.log(chalk.cyan('InfluxDB REST API:'))
|
|
442
|
+
console.log(chalk.white(` HTTP: http://127.0.0.1:${config.port}`))
|
|
443
|
+
console.log()
|
|
444
|
+
console.log(chalk.gray('Example curl commands:'))
|
|
445
|
+
console.log(chalk.gray(` curl http://127.0.0.1:${config.port}/health`))
|
|
446
|
+
console.log(
|
|
447
|
+
chalk.gray(
|
|
448
|
+
` curl -H "Content-Type: application/json" http://127.0.0.1:${config.port}/api/v3/query_sql -d '{"db":"mydb","q":"SELECT 1"}'`,
|
|
449
|
+
),
|
|
450
|
+
)
|
|
427
451
|
} else if (config.engine === 'couchdb') {
|
|
428
452
|
console.log(chalk.cyan('CouchDB REST API:'))
|
|
429
453
|
console.log(chalk.white(` HTTP: http://127.0.0.1:${config.port}`))
|
|
@@ -919,6 +943,24 @@ async function launchShell(
|
|
|
919
943
|
openInBrowser(dashboardUrl)
|
|
920
944
|
await pressEnterToContinue()
|
|
921
945
|
return
|
|
946
|
+
} else if (config.engine === 'influxdb') {
|
|
947
|
+
// InfluxDB: REST API only, no web dashboard
|
|
948
|
+
// This branch shouldn't be reached since we removed the 'default' choice,
|
|
949
|
+
// but handle gracefully just in case
|
|
950
|
+
console.log()
|
|
951
|
+
console.log(chalk.cyan('InfluxDB REST API:'))
|
|
952
|
+
console.log(chalk.white(` HTTP: http://127.0.0.1:${config.port}`))
|
|
953
|
+
console.log()
|
|
954
|
+
console.log(chalk.gray('Example curl commands:'))
|
|
955
|
+
console.log(chalk.gray(` curl http://127.0.0.1:${config.port}/health`))
|
|
956
|
+
console.log(
|
|
957
|
+
chalk.gray(
|
|
958
|
+
` curl -H "Content-Type: application/json" http://127.0.0.1:${config.port}/api/v3/query_sql -d '{"db":"mydb","q":"SELECT 1"}'`,
|
|
959
|
+
),
|
|
960
|
+
)
|
|
961
|
+
console.log()
|
|
962
|
+
await pressEnterToContinue()
|
|
963
|
+
return
|
|
922
964
|
} else if (config.engine === 'couchdb') {
|
|
923
965
|
// CouchDB: Open Fauxton dashboard in browser (served at /_utils)
|
|
924
966
|
const dashboardUrl = `http://127.0.0.1:${config.port}/_utils`
|
package/cli/constants.ts
CHANGED
|
@@ -76,6 +76,7 @@ export const ENGINE_BRAND_COLORS: Record<Engine, BrandColor> = {
|
|
|
76
76
|
[Engine.SurrealDB]: { foreground: '#FFFFFF', background: '#FF00A0' }, // White on pink
|
|
77
77
|
[Engine.QuestDB]: { foreground: '#000000', background: '#02FC04' }, // Black on green
|
|
78
78
|
[Engine.TypeDB]: { foreground: '#FFFFFF', background: '#7B2D8E' }, // White on purple
|
|
79
|
+
[Engine.InfluxDB]: { foreground: '#FFFFFF', background: '#9394FF' }, // White on indigo/purple
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
// ASCII fallback icons - work in any terminal
|
|
@@ -97,6 +98,7 @@ const ASCII_ICONS: Record<Engine, string> = {
|
|
|
97
98
|
[Engine.SurrealDB]: '[SR]',
|
|
98
99
|
[Engine.QuestDB]: '[QS]',
|
|
99
100
|
[Engine.TypeDB]: '[TB]',
|
|
101
|
+
[Engine.InfluxDB]: '[IX]',
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
// Nerd Font icons - require a patched font
|
|
@@ -119,6 +121,7 @@ const NERD_ICONS: Record<Engine, string> = {
|
|
|
119
121
|
[Engine.SurrealDB]: '\uedfe', // nf-fa-infinity (multi-model)
|
|
120
122
|
[Engine.QuestDB]: '\ued2f', // nf-fa-gauge-high (time-series performance)
|
|
121
123
|
[Engine.TypeDB]: '\ue706', // nf-dev-database (knowledge graph)
|
|
124
|
+
[Engine.InfluxDB]: '\udb85\udf95', // nf-md-chart-line (time-series)
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
// Emoji icons - original icons, inconsistent width across terminals
|
|
@@ -140,6 +143,7 @@ const EMOJI_ICONS: Record<Engine, string> = {
|
|
|
140
143
|
[Engine.SurrealDB]: '🌀',
|
|
141
144
|
[Engine.QuestDB]: '⏱',
|
|
142
145
|
[Engine.TypeDB]: '🤖',
|
|
146
|
+
[Engine.InfluxDB]: '📈',
|
|
143
147
|
}
|
|
144
148
|
|
|
145
149
|
const DEFAULT_ICONS: Record<IconMode, string> = {
|
package/cli/helpers.ts
CHANGED
|
@@ -234,6 +234,16 @@ export type InstalledTypeDBEngine = {
|
|
|
234
234
|
source: 'downloaded'
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
export type InstalledInfluxDBEngine = {
|
|
238
|
+
engine: 'influxdb'
|
|
239
|
+
version: string
|
|
240
|
+
platform: string
|
|
241
|
+
arch: string
|
|
242
|
+
path: string
|
|
243
|
+
sizeBytes: number
|
|
244
|
+
source: 'downloaded'
|
|
245
|
+
}
|
|
246
|
+
|
|
237
247
|
export type InstalledEngine =
|
|
238
248
|
| InstalledPostgresEngine
|
|
239
249
|
| InstalledMariadbEngine
|
|
@@ -252,6 +262,7 @@ export type InstalledEngine =
|
|
|
252
262
|
| InstalledSurrealDBEngine
|
|
253
263
|
| InstalledQuestDBEngine
|
|
254
264
|
| InstalledTypeDBEngine
|
|
265
|
+
| InstalledInfluxDBEngine
|
|
255
266
|
|
|
256
267
|
async function getPostgresVersion(binPath: string): Promise<string | null> {
|
|
257
268
|
const ext = platformService.getExecutableExtension()
|
|
@@ -1150,6 +1161,63 @@ async function getInstalledTypeDBEngines(): Promise<InstalledTypeDBEngine[]> {
|
|
|
1150
1161
|
return engines
|
|
1151
1162
|
}
|
|
1152
1163
|
|
|
1164
|
+
// Get InfluxDB version from binary path
|
|
1165
|
+
async function getInfluxDBVersion(binPath: string): Promise<string | null> {
|
|
1166
|
+
const ext = platformService.getExecutableExtension()
|
|
1167
|
+
const influxdbPath = join(binPath, 'bin', `influxdb3${ext}`)
|
|
1168
|
+
if (!existsSync(influxdbPath)) {
|
|
1169
|
+
return null
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
try {
|
|
1173
|
+
const { stdout } = await execFileAsync(influxdbPath, ['--version'])
|
|
1174
|
+
const match = stdout.match(/v?(\d+\.\d+\.\d+)/)
|
|
1175
|
+
return match ? match[1] : null
|
|
1176
|
+
} catch {
|
|
1177
|
+
return null
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// Get installed InfluxDB engines from downloaded binaries
|
|
1182
|
+
async function getInstalledInfluxDBEngines(): Promise<
|
|
1183
|
+
InstalledInfluxDBEngine[]
|
|
1184
|
+
> {
|
|
1185
|
+
const binDir = paths.bin
|
|
1186
|
+
|
|
1187
|
+
if (!existsSync(binDir)) {
|
|
1188
|
+
return []
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
const entries = await readdir(binDir, { withFileTypes: true })
|
|
1192
|
+
const engines: InstalledInfluxDBEngine[] = []
|
|
1193
|
+
|
|
1194
|
+
for (const entry of entries) {
|
|
1195
|
+
if (!entry.isDirectory()) continue
|
|
1196
|
+
if (!entry.name.startsWith('influxdb-')) continue
|
|
1197
|
+
|
|
1198
|
+
const parsed = parseEngineDirectory(entry.name, 'influxdb-', binDir)
|
|
1199
|
+
if (!parsed) continue
|
|
1200
|
+
|
|
1201
|
+
const actualVersion =
|
|
1202
|
+
(await getInfluxDBVersion(parsed.path)) || parsed.version
|
|
1203
|
+
const sizeBytes = await calculateDirectorySize(parsed.path)
|
|
1204
|
+
|
|
1205
|
+
engines.push({
|
|
1206
|
+
engine: 'influxdb',
|
|
1207
|
+
version: actualVersion,
|
|
1208
|
+
platform: parsed.platform,
|
|
1209
|
+
arch: parsed.arch,
|
|
1210
|
+
path: parsed.path,
|
|
1211
|
+
sizeBytes,
|
|
1212
|
+
source: 'downloaded',
|
|
1213
|
+
})
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
engines.sort((a, b) => compareVersions(b.version, a.version))
|
|
1217
|
+
|
|
1218
|
+
return engines
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1153
1221
|
// Get FerretDB version from binary path
|
|
1154
1222
|
async function getFerretDBVersion(binPath: string): Promise<string | null> {
|
|
1155
1223
|
const ext = platformService.getExecutableExtension()
|
|
@@ -1245,6 +1313,7 @@ const ENGINE_PREFIXES = [
|
|
|
1245
1313
|
'surrealdb-',
|
|
1246
1314
|
'questdb-',
|
|
1247
1315
|
'typedb-',
|
|
1316
|
+
'influxdb-',
|
|
1248
1317
|
] as const
|
|
1249
1318
|
|
|
1250
1319
|
/**
|
|
@@ -1292,6 +1361,7 @@ export async function getInstalledEngines(): Promise<InstalledEngine[]> {
|
|
|
1292
1361
|
surrealdbEngines,
|
|
1293
1362
|
questdbEngines,
|
|
1294
1363
|
typedbEngines,
|
|
1364
|
+
influxdbEngines,
|
|
1295
1365
|
] = await Promise.all([
|
|
1296
1366
|
getInstalledPostgresEngines(),
|
|
1297
1367
|
getInstalledMariadbEngines(),
|
|
@@ -1310,6 +1380,7 @@ export async function getInstalledEngines(): Promise<InstalledEngine[]> {
|
|
|
1310
1380
|
getInstalledSurrealDBEngines(),
|
|
1311
1381
|
getInstalledQuestDBEngines(),
|
|
1312
1382
|
getInstalledTypeDBEngines(),
|
|
1383
|
+
getInstalledInfluxDBEngines(),
|
|
1313
1384
|
])
|
|
1314
1385
|
|
|
1315
1386
|
return [
|
|
@@ -1330,6 +1401,7 @@ export async function getInstalledEngines(): Promise<InstalledEngine[]> {
|
|
|
1330
1401
|
...surrealdbEngines,
|
|
1331
1402
|
...questdbEngines,
|
|
1332
1403
|
...typedbEngines,
|
|
1404
|
+
...influxdbEngines,
|
|
1333
1405
|
]
|
|
1334
1406
|
}
|
|
1335
1407
|
|
|
@@ -1350,4 +1422,5 @@ export {
|
|
|
1350
1422
|
getInstalledSurrealDBEngines,
|
|
1351
1423
|
getInstalledQuestDBEngines,
|
|
1352
1424
|
getInstalledTypeDBEngines,
|
|
1425
|
+
getInstalledInfluxDBEngines,
|
|
1353
1426
|
}
|
package/config/backup-formats.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
type SurrealDBFormat,
|
|
28
28
|
type QuestDBFormat,
|
|
29
29
|
type TypeDBFormat,
|
|
30
|
+
type InfluxDBFormat,
|
|
30
31
|
type BackupFormatType,
|
|
31
32
|
} from '../types'
|
|
32
33
|
|
|
@@ -63,6 +64,7 @@ export const BACKUP_FORMATS: {
|
|
|
63
64
|
[Engine.SurrealDB]: EngineBackupFormats<SurrealDBFormat>
|
|
64
65
|
[Engine.QuestDB]: EngineBackupFormats<QuestDBFormat>
|
|
65
66
|
[Engine.TypeDB]: EngineBackupFormats<TypeDBFormat>
|
|
67
|
+
[Engine.InfluxDB]: EngineBackupFormats<InfluxDBFormat>
|
|
66
68
|
} = {
|
|
67
69
|
[Engine.PostgreSQL]: {
|
|
68
70
|
formats: {
|
|
@@ -322,6 +324,18 @@ export const BACKUP_FORMATS: {
|
|
|
322
324
|
supportsFormatChoice: false, // Only TypeQL format supported
|
|
323
325
|
defaultFormat: 'typeql',
|
|
324
326
|
},
|
|
327
|
+
[Engine.InfluxDB]: {
|
|
328
|
+
formats: {
|
|
329
|
+
sql: {
|
|
330
|
+
extension: '.sql',
|
|
331
|
+
label: '.sql',
|
|
332
|
+
description: 'SQL dump - time-series data as SQL statements',
|
|
333
|
+
spinnerLabel: 'SQL',
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
supportsFormatChoice: false, // Only SQL format supported
|
|
337
|
+
defaultFormat: 'sql',
|
|
338
|
+
},
|
|
325
339
|
}
|
|
326
340
|
|
|
327
341
|
/**
|
|
@@ -252,6 +252,19 @@ export const engineDefaults: Record<Engine, EngineDefaults> = {
|
|
|
252
252
|
clientTools: ['typedb', 'typedb_console_bin'],
|
|
253
253
|
maxConnections: 0, // Not applicable - managed internally
|
|
254
254
|
},
|
|
255
|
+
[Engine.InfluxDB]: {
|
|
256
|
+
defaultVersion: '3',
|
|
257
|
+
defaultPort: 8086, // InfluxDB HTTP API port
|
|
258
|
+
portRange: { start: 8086, end: 8186 },
|
|
259
|
+
latestVersion: '3',
|
|
260
|
+
superuser: '', // No auth by default for local dev
|
|
261
|
+
connectionScheme: 'http',
|
|
262
|
+
logFileName: 'influxdb.log',
|
|
263
|
+
pidFileName: 'influxdb.pid',
|
|
264
|
+
dataSubdir: 'data',
|
|
265
|
+
clientTools: [], // InfluxDB uses REST API, no separate CLI tools
|
|
266
|
+
maxConnections: 0, // Not applicable for time-series DB
|
|
267
|
+
},
|
|
255
268
|
}
|
|
256
269
|
|
|
257
270
|
/**
|
package/config/engines.json
CHANGED
|
@@ -281,6 +281,23 @@
|
|
|
281
281
|
"clientTools": ["typedb", "typedb-console"],
|
|
282
282
|
"licensing": "MPL-2.0",
|
|
283
283
|
"notes": "Strongly-typed database for knowledge representation and reasoning. TypeQL query language. Main port 1729, HTTP port 8000. Default credentials: admin/password."
|
|
284
|
+
},
|
|
285
|
+
"influxdb": {
|
|
286
|
+
"displayName": "InfluxDB",
|
|
287
|
+
"icon": "📈",
|
|
288
|
+
"status": "integrated",
|
|
289
|
+
"binarySource": "hostdb",
|
|
290
|
+
"supportedVersions": ["3.8.0"],
|
|
291
|
+
"defaultVersion": "3.8.0",
|
|
292
|
+
"defaultPort": 8086,
|
|
293
|
+
"runtime": "server",
|
|
294
|
+
"queryLanguage": "sql",
|
|
295
|
+
"scriptFileLabel": null,
|
|
296
|
+
"connectionScheme": "http",
|
|
297
|
+
"superuser": null,
|
|
298
|
+
"clientTools": ["influxdb3"],
|
|
299
|
+
"licensing": ["Apache-2.0", "MIT"],
|
|
300
|
+
"notes": "Purpose-built time-series database with SQL support. InfluxDB 3.x is a Rust rewrite using Apache Arrow/DataFusion. HTTP API on port 8086."
|
|
284
301
|
}
|
|
285
302
|
}
|
|
286
303
|
}
|
package/core/config-manager.ts
CHANGED
|
@@ -80,6 +80,8 @@ const QUESTDB_TOOLS: BinaryTool[] = ['questdb']
|
|
|
80
80
|
|
|
81
81
|
const TYPEDB_TOOLS: BinaryTool[] = ['typedb', 'typedb_console_bin']
|
|
82
82
|
|
|
83
|
+
const INFLUXDB_TOOLS: BinaryTool[] = ['influxdb3']
|
|
84
|
+
|
|
83
85
|
const ENHANCED_SHELLS: BinaryTool[] = [
|
|
84
86
|
'pgcli',
|
|
85
87
|
'mycli',
|
|
@@ -103,6 +105,7 @@ const ALL_TOOLS: BinaryTool[] = [
|
|
|
103
105
|
...SURREALDB_TOOLS,
|
|
104
106
|
...QUESTDB_TOOLS,
|
|
105
107
|
...TYPEDB_TOOLS,
|
|
108
|
+
...INFLUXDB_TOOLS,
|
|
106
109
|
...SQLITE_TOOLS,
|
|
107
110
|
...DUCKDB_TOOLS,
|
|
108
111
|
...ENHANCED_SHELLS,
|
|
@@ -125,6 +128,7 @@ const ENGINE_BINARY_MAP: Partial<Record<Engine, BinaryTool[]>> = {
|
|
|
125
128
|
[Engine.SurrealDB]: SURREALDB_TOOLS,
|
|
126
129
|
[Engine.QuestDB]: QUESTDB_TOOLS,
|
|
127
130
|
[Engine.TypeDB]: TYPEDB_TOOLS,
|
|
131
|
+
[Engine.InfluxDB]: INFLUXDB_TOOLS,
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
export class ConfigManager {
|
|
@@ -357,6 +361,7 @@ export class ConfigManager {
|
|
|
357
361
|
valkey: { found: BinaryTool[]; missing: BinaryTool[] }
|
|
358
362
|
meilisearch: { found: BinaryTool[]; missing: BinaryTool[] }
|
|
359
363
|
typedb: { found: BinaryTool[]; missing: BinaryTool[] }
|
|
364
|
+
influxdb: { found: BinaryTool[]; missing: BinaryTool[] }
|
|
360
365
|
enhanced: { found: BinaryTool[]; missing: BinaryTool[] }
|
|
361
366
|
}> {
|
|
362
367
|
// First, scan ~/.spindb/bin/ for downloaded (bundled) binaries
|
|
@@ -414,6 +419,10 @@ export class ConfigManager {
|
|
|
414
419
|
found: found.filter((t) => TYPEDB_TOOLS.includes(t)),
|
|
415
420
|
missing: missing.filter((t) => TYPEDB_TOOLS.includes(t)),
|
|
416
421
|
},
|
|
422
|
+
influxdb: {
|
|
423
|
+
found: found.filter((t) => INFLUXDB_TOOLS.includes(t)),
|
|
424
|
+
missing: missing.filter((t) => INFLUXDB_TOOLS.includes(t)),
|
|
425
|
+
},
|
|
417
426
|
enhanced: {
|
|
418
427
|
found: found.filter((t) => ENHANCED_SHELLS.includes(t)),
|
|
419
428
|
missing: missing.filter((t) => ENHANCED_SHELLS.includes(t)),
|
|
@@ -611,6 +620,7 @@ export {
|
|
|
611
620
|
SURREALDB_TOOLS,
|
|
612
621
|
QUESTDB_TOOLS,
|
|
613
622
|
TYPEDB_TOOLS,
|
|
623
|
+
INFLUXDB_TOOLS,
|
|
614
624
|
SQLITE_TOOLS,
|
|
615
625
|
DUCKDB_TOOLS,
|
|
616
626
|
ENHANCED_SHELLS,
|
package/core/docker-exporter.ts
CHANGED
|
@@ -71,6 +71,7 @@ function getEngineDisplayName(engine: Engine): string {
|
|
|
71
71
|
[Engine.SurrealDB]: 'SurrealDB',
|
|
72
72
|
[Engine.QuestDB]: 'QuestDB',
|
|
73
73
|
[Engine.TypeDB]: 'TypeDB',
|
|
74
|
+
[Engine.InfluxDB]: 'InfluxDB',
|
|
74
75
|
}
|
|
75
76
|
return displayNames[engine] || engine
|
|
76
77
|
}
|
|
@@ -141,6 +142,9 @@ const _ENGINE_BINARY_CONFIG: Record<
|
|
|
141
142
|
[Engine.TypeDB]: {
|
|
142
143
|
primaryBinaries: ['typedb', 'typedb_console_bin'],
|
|
143
144
|
},
|
|
145
|
+
[Engine.InfluxDB]: {
|
|
146
|
+
primaryBinaries: [], // REST API only, no CLI tools
|
|
147
|
+
},
|
|
144
148
|
}
|
|
145
149
|
|
|
146
150
|
/**
|
|
@@ -193,6 +197,7 @@ function getConnectionStringTemplate(
|
|
|
193
197
|
return useTLS ? `https://<host>:${port}` : `http://<host>:${port}`
|
|
194
198
|
|
|
195
199
|
case Engine.Meilisearch:
|
|
200
|
+
case Engine.InfluxDB:
|
|
196
201
|
return useTLS ? `https://<host>:${port}` : `http://<host>:${port}`
|
|
197
202
|
|
|
198
203
|
case Engine.CouchDB:
|
|
@@ -492,6 +497,13 @@ echo "User configured via server settings"
|
|
|
492
497
|
userCreationCommands = `
|
|
493
498
|
# API key is configured at server start
|
|
494
499
|
echo "API key configured via server settings"
|
|
500
|
+
`
|
|
501
|
+
break
|
|
502
|
+
|
|
503
|
+
case Engine.InfluxDB:
|
|
504
|
+
userCreationCommands = `
|
|
505
|
+
# InfluxDB 3.x local dev runs without authentication
|
|
506
|
+
echo "No authentication required for local InfluxDB 3.x"
|
|
495
507
|
`
|
|
496
508
|
break
|
|
497
509
|
|
|
@@ -1284,6 +1296,7 @@ export async function getDockerConnectionString(
|
|
|
1284
1296
|
return `http://${host}:${port}`
|
|
1285
1297
|
|
|
1286
1298
|
case Engine.Meilisearch:
|
|
1299
|
+
case Engine.InfluxDB:
|
|
1287
1300
|
return `http://${host}:${port}`
|
|
1288
1301
|
|
|
1289
1302
|
case Engine.CouchDB:
|
package/engines/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { cockroachdbEngine } from './cockroachdb'
|
|
|
15
15
|
import { surrealdbEngine } from './surrealdb'
|
|
16
16
|
import { questdbEngine } from './questdb'
|
|
17
17
|
import { typedbEngine } from './typedb'
|
|
18
|
+
import { influxdbEngine } from './influxdb'
|
|
18
19
|
import { platformService } from '../core/platform-service'
|
|
19
20
|
import { Engine, Platform } from '../types'
|
|
20
21
|
import type { BaseEngine } from './base-engine'
|
|
@@ -80,6 +81,9 @@ export const engines: Record<string, BaseEngine> = {
|
|
|
80
81
|
// TypeDB and aliases
|
|
81
82
|
[Engine.TypeDB]: typedbEngine,
|
|
82
83
|
tdb: typedbEngine,
|
|
84
|
+
// InfluxDB and aliases
|
|
85
|
+
[Engine.InfluxDB]: influxdbEngine,
|
|
86
|
+
influx: influxdbEngine,
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
// Get an engine by name
|