@axiom-lattice/pg-stores 1.0.13 → 1.0.14
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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +9 -0
- package/README_DATABASE_CONFIG.md +217 -0
- package/dist/index.d.mts +253 -3
- package/dist/index.d.ts +253 -3
- package/dist/index.js +759 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +752 -1
- package/dist/index.mjs.map +1 -1
- package/examples/database-config-store.example.ts +178 -0
- package/examples/workspace-project-store.example.ts +151 -0
- package/package.json +3 -3
- package/src/__tests__/workspace-project-store.test.ts +235 -0
- package/src/index.ts +24 -0
- package/src/migrations/database_config_migrations.ts +47 -0
- package/src/migrations/project_migrations.ts +50 -0
- package/src/migrations/workspace_migrations.ts +44 -0
- package/src/stores/PostgreSQLDatabaseConfigStore.ts +434 -0
- package/src/stores/PostgreSQLProjectStore.ts +282 -0
- package/src/stores/PostgreSQLWorkspaceStore.ts +286 -0
package/dist/index.mjs
CHANGED
|
@@ -1474,15 +1474,766 @@ var PostgreSQLSkillStore = class {
|
|
|
1474
1474
|
return result.rows.map(this.mapRowToSkill);
|
|
1475
1475
|
}
|
|
1476
1476
|
};
|
|
1477
|
+
|
|
1478
|
+
// src/stores/PostgreSQLDatabaseConfigStore.ts
|
|
1479
|
+
import { Pool as Pool5 } from "pg";
|
|
1480
|
+
|
|
1481
|
+
// src/migrations/database_config_migrations.ts
|
|
1482
|
+
var createDatabaseConfigsTable = {
|
|
1483
|
+
version: 4,
|
|
1484
|
+
name: "create_database_configs_table",
|
|
1485
|
+
up: async (client) => {
|
|
1486
|
+
await client.query(`
|
|
1487
|
+
CREATE TABLE IF NOT EXISTS lattice_database_configs (
|
|
1488
|
+
id VARCHAR(255) NOT NULL,
|
|
1489
|
+
tenant_id VARCHAR(255) NOT NULL,
|
|
1490
|
+
key VARCHAR(255) NOT NULL,
|
|
1491
|
+
name VARCHAR(255),
|
|
1492
|
+
description TEXT,
|
|
1493
|
+
config JSONB NOT NULL,
|
|
1494
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
1495
|
+
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
1496
|
+
|
|
1497
|
+
PRIMARY KEY (tenant_id, id),
|
|
1498
|
+
CONSTRAINT uk_lattice_database_configs_tenant_key UNIQUE (tenant_id, key)
|
|
1499
|
+
)
|
|
1500
|
+
`);
|
|
1501
|
+
await client.query(`
|
|
1502
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_database_configs_tenant_id
|
|
1503
|
+
ON lattice_database_configs(tenant_id)
|
|
1504
|
+
`);
|
|
1505
|
+
await client.query(`
|
|
1506
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_database_configs_key
|
|
1507
|
+
ON lattice_database_configs(key)
|
|
1508
|
+
`);
|
|
1509
|
+
},
|
|
1510
|
+
down: async (client) => {
|
|
1511
|
+
await client.query("DROP INDEX IF EXISTS idx_lattice_database_configs_key");
|
|
1512
|
+
await client.query("DROP INDEX IF EXISTS idx_lattice_database_configs_tenant_id");
|
|
1513
|
+
await client.query("DROP TABLE IF EXISTS lattice_database_configs");
|
|
1514
|
+
}
|
|
1515
|
+
};
|
|
1516
|
+
|
|
1517
|
+
// src/stores/PostgreSQLDatabaseConfigStore.ts
|
|
1518
|
+
import { encrypt, decrypt } from "@axiom-lattice/core";
|
|
1519
|
+
var PostgreSQLDatabaseConfigStore = class {
|
|
1520
|
+
constructor(options) {
|
|
1521
|
+
this.initialized = false;
|
|
1522
|
+
this.initPromise = null;
|
|
1523
|
+
if (typeof options.poolConfig === "string") {
|
|
1524
|
+
this.pool = new Pool5({ connectionString: options.poolConfig });
|
|
1525
|
+
} else {
|
|
1526
|
+
this.pool = new Pool5(options.poolConfig);
|
|
1527
|
+
}
|
|
1528
|
+
this.migrationManager = new MigrationManager(this.pool);
|
|
1529
|
+
this.migrationManager.register(createDatabaseConfigsTable);
|
|
1530
|
+
if (options.autoMigrate !== false) {
|
|
1531
|
+
this.initialize().catch((error) => {
|
|
1532
|
+
console.error("Failed to initialize PostgreSQLDatabaseConfigStore:", error);
|
|
1533
|
+
throw error;
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Initialize the store and run migrations
|
|
1539
|
+
* Uses a promise-based lock to prevent concurrent initialization
|
|
1540
|
+
*/
|
|
1541
|
+
async initialize() {
|
|
1542
|
+
if (this.initialized) {
|
|
1543
|
+
return;
|
|
1544
|
+
}
|
|
1545
|
+
if (this.initPromise) {
|
|
1546
|
+
return this.initPromise;
|
|
1547
|
+
}
|
|
1548
|
+
this.initPromise = (async () => {
|
|
1549
|
+
try {
|
|
1550
|
+
await this.migrationManager.migrate();
|
|
1551
|
+
this.initialized = true;
|
|
1552
|
+
} finally {
|
|
1553
|
+
this.initPromise = null;
|
|
1554
|
+
}
|
|
1555
|
+
})();
|
|
1556
|
+
return this.initPromise;
|
|
1557
|
+
}
|
|
1558
|
+
/**
|
|
1559
|
+
* Get all database configurations for a tenant
|
|
1560
|
+
*/
|
|
1561
|
+
async getAllConfigs(tenantId) {
|
|
1562
|
+
await this.ensureInitialized();
|
|
1563
|
+
const result = await this.pool.query(
|
|
1564
|
+
`
|
|
1565
|
+
SELECT id, tenant_id, key, name, description, config, created_at, updated_at
|
|
1566
|
+
FROM lattice_database_configs
|
|
1567
|
+
WHERE tenant_id = $1
|
|
1568
|
+
ORDER BY created_at DESC
|
|
1569
|
+
`,
|
|
1570
|
+
[tenantId]
|
|
1571
|
+
);
|
|
1572
|
+
return result.rows.map((row) => this.mapRowToEntry(row));
|
|
1573
|
+
}
|
|
1574
|
+
/**
|
|
1575
|
+
* Get all database configurations across all tenants
|
|
1576
|
+
*/
|
|
1577
|
+
async getAllConfigsWithoutTenant() {
|
|
1578
|
+
await this.ensureInitialized();
|
|
1579
|
+
const result = await this.pool.query(
|
|
1580
|
+
`
|
|
1581
|
+
SELECT id, tenant_id, key, name, description, config, created_at, updated_at
|
|
1582
|
+
FROM lattice_database_configs
|
|
1583
|
+
ORDER BY created_at DESC
|
|
1584
|
+
`
|
|
1585
|
+
);
|
|
1586
|
+
return result.rows.map((row) => this.mapRowToEntry(row));
|
|
1587
|
+
}
|
|
1588
|
+
/**
|
|
1589
|
+
* Get database configuration by ID
|
|
1590
|
+
*/
|
|
1591
|
+
async getConfigById(tenantId, id) {
|
|
1592
|
+
await this.ensureInitialized();
|
|
1593
|
+
const result = await this.pool.query(
|
|
1594
|
+
`
|
|
1595
|
+
SELECT id, tenant_id, key, name, description, config, created_at, updated_at
|
|
1596
|
+
FROM lattice_database_configs
|
|
1597
|
+
WHERE tenant_id = $1 AND id = $2
|
|
1598
|
+
`,
|
|
1599
|
+
[tenantId, id]
|
|
1600
|
+
);
|
|
1601
|
+
if (result.rows.length === 0) {
|
|
1602
|
+
return null;
|
|
1603
|
+
}
|
|
1604
|
+
return this.mapRowToEntry(result.rows[0]);
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* Get database configuration by business key
|
|
1608
|
+
*/
|
|
1609
|
+
async getConfigByKey(tenantId, key) {
|
|
1610
|
+
await this.ensureInitialized();
|
|
1611
|
+
const result = await this.pool.query(
|
|
1612
|
+
`
|
|
1613
|
+
SELECT id, tenant_id, key, name, description, config, created_at, updated_at
|
|
1614
|
+
FROM lattice_database_configs
|
|
1615
|
+
WHERE tenant_id = $1 AND key = $2
|
|
1616
|
+
`,
|
|
1617
|
+
[tenantId, key]
|
|
1618
|
+
);
|
|
1619
|
+
if (result.rows.length === 0) {
|
|
1620
|
+
return null;
|
|
1621
|
+
}
|
|
1622
|
+
return this.mapRowToEntry(result.rows[0]);
|
|
1623
|
+
}
|
|
1624
|
+
/**
|
|
1625
|
+
* Create a new database configuration
|
|
1626
|
+
*/
|
|
1627
|
+
async createConfig(tenantId, id, data) {
|
|
1628
|
+
await this.ensureInitialized();
|
|
1629
|
+
const now = /* @__PURE__ */ new Date();
|
|
1630
|
+
const nowString = now.toISOString();
|
|
1631
|
+
const configWithEncryptedPassword = this.encryptPasswordInConfig(data.config);
|
|
1632
|
+
await this.pool.query(
|
|
1633
|
+
`
|
|
1634
|
+
INSERT INTO lattice_database_configs (id, tenant_id, key, name, description, config, created_at, updated_at)
|
|
1635
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7::timestamp, $8::timestamp)
|
|
1636
|
+
ON CONFLICT (tenant_id, id) DO UPDATE SET
|
|
1637
|
+
key = EXCLUDED.key,
|
|
1638
|
+
name = EXCLUDED.name,
|
|
1639
|
+
description = EXCLUDED.description,
|
|
1640
|
+
config = EXCLUDED.config,
|
|
1641
|
+
updated_at = EXCLUDED.updated_at
|
|
1642
|
+
`,
|
|
1643
|
+
[
|
|
1644
|
+
id,
|
|
1645
|
+
tenantId,
|
|
1646
|
+
data.key,
|
|
1647
|
+
data.name || null,
|
|
1648
|
+
data.description || null,
|
|
1649
|
+
JSON.stringify(configWithEncryptedPassword),
|
|
1650
|
+
nowString,
|
|
1651
|
+
nowString
|
|
1652
|
+
]
|
|
1653
|
+
);
|
|
1654
|
+
return {
|
|
1655
|
+
id,
|
|
1656
|
+
tenantId,
|
|
1657
|
+
key: data.key,
|
|
1658
|
+
config: data.config,
|
|
1659
|
+
name: data.name,
|
|
1660
|
+
description: data.description,
|
|
1661
|
+
createdAt: now,
|
|
1662
|
+
updatedAt: now
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1665
|
+
/**
|
|
1666
|
+
* Update an existing database configuration
|
|
1667
|
+
*/
|
|
1668
|
+
async updateConfig(tenantId, id, updates) {
|
|
1669
|
+
await this.ensureInitialized();
|
|
1670
|
+
const existing = await this.getConfigById(tenantId, id);
|
|
1671
|
+
if (!existing) {
|
|
1672
|
+
return null;
|
|
1673
|
+
}
|
|
1674
|
+
const updateData = {};
|
|
1675
|
+
if (updates.key !== void 0) {
|
|
1676
|
+
updateData.key = updates.key;
|
|
1677
|
+
}
|
|
1678
|
+
if (updates.name !== void 0) {
|
|
1679
|
+
updateData.name = updates.name || null;
|
|
1680
|
+
}
|
|
1681
|
+
if (updates.description !== void 0) {
|
|
1682
|
+
updateData.description = updates.description || null;
|
|
1683
|
+
}
|
|
1684
|
+
if (updates.config !== void 0) {
|
|
1685
|
+
const configWithEncryptedPassword = this.encryptPasswordInConfig(updates.config);
|
|
1686
|
+
updateData.config = JSON.stringify(configWithEncryptedPassword);
|
|
1687
|
+
}
|
|
1688
|
+
if (Object.keys(updateData).length === 0) {
|
|
1689
|
+
return existing;
|
|
1690
|
+
}
|
|
1691
|
+
updateData.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1692
|
+
const fields = Object.keys(updateData);
|
|
1693
|
+
const values = Object.values(updateData);
|
|
1694
|
+
values.push(tenantId);
|
|
1695
|
+
values.push(id);
|
|
1696
|
+
const setClauses = fields.map((field, index) => {
|
|
1697
|
+
if (field === "updated_at") {
|
|
1698
|
+
return `${field} = $${index + 1}::timestamp`;
|
|
1699
|
+
}
|
|
1700
|
+
return `${field} = $${index + 1}`;
|
|
1701
|
+
});
|
|
1702
|
+
const whereTenantIndex = fields.length + 1;
|
|
1703
|
+
const whereIdIndex = fields.length + 2;
|
|
1704
|
+
const sql = `
|
|
1705
|
+
UPDATE lattice_database_configs
|
|
1706
|
+
SET ${setClauses.join(", ")}
|
|
1707
|
+
WHERE tenant_id = $${whereTenantIndex} AND id = $${whereIdIndex}
|
|
1708
|
+
`;
|
|
1709
|
+
await this.pool.query(sql, values);
|
|
1710
|
+
return await this.getConfigById(tenantId, id);
|
|
1711
|
+
}
|
|
1712
|
+
/**
|
|
1713
|
+
* Delete a database configuration by ID
|
|
1714
|
+
*/
|
|
1715
|
+
async deleteConfig(tenantId, id) {
|
|
1716
|
+
await this.ensureInitialized();
|
|
1717
|
+
const result = await this.pool.query(
|
|
1718
|
+
`
|
|
1719
|
+
DELETE FROM lattice_database_configs
|
|
1720
|
+
WHERE tenant_id = $1 AND id = $2
|
|
1721
|
+
`,
|
|
1722
|
+
[tenantId, id]
|
|
1723
|
+
);
|
|
1724
|
+
return result.rowCount !== null && result.rowCount > 0;
|
|
1725
|
+
}
|
|
1726
|
+
/**
|
|
1727
|
+
* Check if configuration exists
|
|
1728
|
+
*/
|
|
1729
|
+
async hasConfig(tenantId, id) {
|
|
1730
|
+
await this.ensureInitialized();
|
|
1731
|
+
const result = await this.pool.query(
|
|
1732
|
+
`
|
|
1733
|
+
SELECT 1 FROM lattice_database_configs
|
|
1734
|
+
WHERE tenant_id = $1 AND id = $2
|
|
1735
|
+
LIMIT 1
|
|
1736
|
+
`,
|
|
1737
|
+
[tenantId, id]
|
|
1738
|
+
);
|
|
1739
|
+
return result.rows.length > 0;
|
|
1740
|
+
}
|
|
1741
|
+
/**
|
|
1742
|
+
* Dispose resources and close the connection pool
|
|
1743
|
+
*/
|
|
1744
|
+
async dispose() {
|
|
1745
|
+
await this.pool.end();
|
|
1746
|
+
}
|
|
1747
|
+
/**
|
|
1748
|
+
* Ensure store is initialized
|
|
1749
|
+
*/
|
|
1750
|
+
async ensureInitialized() {
|
|
1751
|
+
if (!this.initialized) {
|
|
1752
|
+
await this.initialize();
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
/**
|
|
1756
|
+
* Map database row to DatabaseConfigEntry object
|
|
1757
|
+
* Automatically decrypts password if present
|
|
1758
|
+
*/
|
|
1759
|
+
mapRowToEntry(row) {
|
|
1760
|
+
const config = typeof row.config === "string" ? JSON.parse(row.config) : row.config;
|
|
1761
|
+
if (config.password) {
|
|
1762
|
+
try {
|
|
1763
|
+
config.password = decrypt(config.password);
|
|
1764
|
+
} catch (error) {
|
|
1765
|
+
console.error("Failed to decrypt database password:", error);
|
|
1766
|
+
throw new Error("Failed to decrypt database configuration");
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
return {
|
|
1770
|
+
id: row.id,
|
|
1771
|
+
tenantId: row.tenant_id,
|
|
1772
|
+
key: row.key,
|
|
1773
|
+
config,
|
|
1774
|
+
name: row.name || void 0,
|
|
1775
|
+
description: row.description || void 0,
|
|
1776
|
+
createdAt: row.created_at,
|
|
1777
|
+
updatedAt: row.updated_at
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
/**
|
|
1781
|
+
* Encrypt password in config before storing
|
|
1782
|
+
*/
|
|
1783
|
+
encryptPasswordInConfig(config) {
|
|
1784
|
+
const configCopy = { ...config };
|
|
1785
|
+
if (configCopy.password) {
|
|
1786
|
+
configCopy.password = encrypt(configCopy.password);
|
|
1787
|
+
}
|
|
1788
|
+
return configCopy;
|
|
1789
|
+
}
|
|
1790
|
+
};
|
|
1791
|
+
|
|
1792
|
+
// src/stores/PostgreSQLWorkspaceStore.ts
|
|
1793
|
+
import { Pool as Pool6 } from "pg";
|
|
1794
|
+
|
|
1795
|
+
// src/migrations/workspace_migrations.ts
|
|
1796
|
+
var createWorkspacesTable = {
|
|
1797
|
+
version: 5,
|
|
1798
|
+
name: "create_workspaces_table",
|
|
1799
|
+
up: async (client) => {
|
|
1800
|
+
await client.query(`
|
|
1801
|
+
CREATE TABLE IF NOT EXISTS lattice_workspaces (
|
|
1802
|
+
id VARCHAR(255) NOT NULL,
|
|
1803
|
+
tenant_id VARCHAR(255) NOT NULL,
|
|
1804
|
+
name VARCHAR(255) NOT NULL,
|
|
1805
|
+
description TEXT,
|
|
1806
|
+
storage_type VARCHAR(50) NOT NULL DEFAULT 'sandbox',
|
|
1807
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
1808
|
+
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
1809
|
+
PRIMARY KEY (id, tenant_id)
|
|
1810
|
+
)
|
|
1811
|
+
`);
|
|
1812
|
+
await client.query(`
|
|
1813
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_workspaces_tenant_id
|
|
1814
|
+
ON lattice_workspaces(tenant_id)
|
|
1815
|
+
`);
|
|
1816
|
+
await client.query(`
|
|
1817
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_workspaces_created_at
|
|
1818
|
+
ON lattice_workspaces(created_at DESC)
|
|
1819
|
+
`);
|
|
1820
|
+
},
|
|
1821
|
+
down: async (client) => {
|
|
1822
|
+
await client.query("DROP INDEX IF EXISTS idx_lattice_workspaces_created_at");
|
|
1823
|
+
await client.query("DROP INDEX IF EXISTS idx_lattice_workspaces_tenant_id");
|
|
1824
|
+
await client.query("DROP TABLE IF EXISTS lattice_workspaces");
|
|
1825
|
+
}
|
|
1826
|
+
};
|
|
1827
|
+
|
|
1828
|
+
// src/stores/PostgreSQLWorkspaceStore.ts
|
|
1829
|
+
var PostgreSQLWorkspaceStore = class {
|
|
1830
|
+
constructor(options) {
|
|
1831
|
+
this.initialized = false;
|
|
1832
|
+
this.ownsPool = true;
|
|
1833
|
+
if (typeof options.poolConfig === "string") {
|
|
1834
|
+
this.pool = new Pool6({ connectionString: options.poolConfig });
|
|
1835
|
+
} else {
|
|
1836
|
+
this.pool = new Pool6(options.poolConfig);
|
|
1837
|
+
}
|
|
1838
|
+
this.migrationManager = new MigrationManager(this.pool);
|
|
1839
|
+
this.migrationManager.register(createWorkspacesTable);
|
|
1840
|
+
if (options.autoMigrate !== false) {
|
|
1841
|
+
this.initialize().catch((error) => {
|
|
1842
|
+
console.error("Failed to initialize PostgreSQLWorkspaceStore:", error);
|
|
1843
|
+
throw error;
|
|
1844
|
+
});
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
/**
|
|
1848
|
+
* Dispose resources and close the connection pool
|
|
1849
|
+
* Should be called when the store is no longer needed
|
|
1850
|
+
*/
|
|
1851
|
+
async dispose() {
|
|
1852
|
+
if (this.ownsPool && this.pool) {
|
|
1853
|
+
await this.pool.end();
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
/**
|
|
1857
|
+
* Initialize the store and run migrations
|
|
1858
|
+
*/
|
|
1859
|
+
async initialize() {
|
|
1860
|
+
if (this.initialized) {
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
await this.migrationManager.migrate();
|
|
1864
|
+
this.initialized = true;
|
|
1865
|
+
}
|
|
1866
|
+
/**
|
|
1867
|
+
* Ensure store is initialized
|
|
1868
|
+
*/
|
|
1869
|
+
async ensureInitialized() {
|
|
1870
|
+
if (!this.initialized) {
|
|
1871
|
+
await this.initialize();
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
/**
|
|
1875
|
+
* Map database row to Workspace object
|
|
1876
|
+
*/
|
|
1877
|
+
mapRowToWorkspace(row) {
|
|
1878
|
+
return {
|
|
1879
|
+
id: row.id,
|
|
1880
|
+
tenantId: row.tenant_id,
|
|
1881
|
+
name: row.name,
|
|
1882
|
+
description: row.description || void 0,
|
|
1883
|
+
storageType: row.storage_type,
|
|
1884
|
+
createdAt: row.created_at,
|
|
1885
|
+
updatedAt: row.updated_at
|
|
1886
|
+
};
|
|
1887
|
+
}
|
|
1888
|
+
/**
|
|
1889
|
+
* Get all workspaces for a tenant
|
|
1890
|
+
*/
|
|
1891
|
+
async getAllWorkspaces(tenantId) {
|
|
1892
|
+
await this.ensureInitialized();
|
|
1893
|
+
const result = await this.pool.query(
|
|
1894
|
+
`
|
|
1895
|
+
SELECT id, tenant_id, name, description, storage_type, created_at, updated_at
|
|
1896
|
+
FROM lattice_workspaces
|
|
1897
|
+
WHERE tenant_id = $1
|
|
1898
|
+
ORDER BY created_at DESC
|
|
1899
|
+
`,
|
|
1900
|
+
[tenantId]
|
|
1901
|
+
);
|
|
1902
|
+
return result.rows.map(this.mapRowToWorkspace);
|
|
1903
|
+
}
|
|
1904
|
+
/**
|
|
1905
|
+
* Get a workspace by ID for a specific tenant
|
|
1906
|
+
*/
|
|
1907
|
+
async getWorkspaceById(tenantId, id) {
|
|
1908
|
+
await this.ensureInitialized();
|
|
1909
|
+
const result = await this.pool.query(
|
|
1910
|
+
`
|
|
1911
|
+
SELECT id, tenant_id, name, description, storage_type, created_at, updated_at
|
|
1912
|
+
FROM lattice_workspaces
|
|
1913
|
+
WHERE id = $1 AND tenant_id = $2
|
|
1914
|
+
`,
|
|
1915
|
+
[id, tenantId]
|
|
1916
|
+
);
|
|
1917
|
+
if (result.rows.length === 0) {
|
|
1918
|
+
return null;
|
|
1919
|
+
}
|
|
1920
|
+
return this.mapRowToWorkspace(result.rows[0]);
|
|
1921
|
+
}
|
|
1922
|
+
/**
|
|
1923
|
+
* Create a new workspace
|
|
1924
|
+
*/
|
|
1925
|
+
async createWorkspace(tenantId, id, data) {
|
|
1926
|
+
await this.ensureInitialized();
|
|
1927
|
+
const now = /* @__PURE__ */ new Date();
|
|
1928
|
+
await this.pool.query(
|
|
1929
|
+
`
|
|
1930
|
+
INSERT INTO lattice_workspaces (id, tenant_id, name, description, storage_type, created_at, updated_at)
|
|
1931
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
1932
|
+
ON CONFLICT (id, tenant_id) DO UPDATE SET
|
|
1933
|
+
name = EXCLUDED.name,
|
|
1934
|
+
description = EXCLUDED.description,
|
|
1935
|
+
storage_type = EXCLUDED.storage_type,
|
|
1936
|
+
updated_at = EXCLUDED.updated_at
|
|
1937
|
+
`,
|
|
1938
|
+
[id, tenantId, data.name, data.description || null, data.storageType, now, now]
|
|
1939
|
+
);
|
|
1940
|
+
return {
|
|
1941
|
+
id,
|
|
1942
|
+
tenantId,
|
|
1943
|
+
name: data.name,
|
|
1944
|
+
description: data.description,
|
|
1945
|
+
storageType: data.storageType,
|
|
1946
|
+
createdAt: now,
|
|
1947
|
+
updatedAt: now
|
|
1948
|
+
};
|
|
1949
|
+
}
|
|
1950
|
+
/**
|
|
1951
|
+
* Update an existing workspace
|
|
1952
|
+
*/
|
|
1953
|
+
async updateWorkspace(tenantId, id, updates) {
|
|
1954
|
+
await this.ensureInitialized();
|
|
1955
|
+
const existing = await this.getWorkspaceById(tenantId, id);
|
|
1956
|
+
if (!existing) {
|
|
1957
|
+
return null;
|
|
1958
|
+
}
|
|
1959
|
+
const updateFields = [];
|
|
1960
|
+
const updateValues = [];
|
|
1961
|
+
let paramIndex = 1;
|
|
1962
|
+
if (updates.name !== void 0) {
|
|
1963
|
+
updateFields.push(`name = $${paramIndex++}`);
|
|
1964
|
+
updateValues.push(updates.name);
|
|
1965
|
+
}
|
|
1966
|
+
if (updates.description !== void 0) {
|
|
1967
|
+
updateFields.push(`description = $${paramIndex++}`);
|
|
1968
|
+
updateValues.push(updates.description || null);
|
|
1969
|
+
}
|
|
1970
|
+
if (updates.storageType !== void 0) {
|
|
1971
|
+
updateFields.push(`storage_type = $${paramIndex++}`);
|
|
1972
|
+
updateValues.push(updates.storageType);
|
|
1973
|
+
}
|
|
1974
|
+
if (updateFields.length === 0) {
|
|
1975
|
+
return existing;
|
|
1976
|
+
}
|
|
1977
|
+
updateFields.push(`updated_at = $${paramIndex++}`);
|
|
1978
|
+
updateValues.push(/* @__PURE__ */ new Date());
|
|
1979
|
+
updateValues.push(id);
|
|
1980
|
+
updateValues.push(tenantId);
|
|
1981
|
+
await this.pool.query(
|
|
1982
|
+
`
|
|
1983
|
+
UPDATE lattice_workspaces
|
|
1984
|
+
SET ${updateFields.join(", ")}
|
|
1985
|
+
WHERE id = $${paramIndex} AND tenant_id = $${paramIndex + 1}
|
|
1986
|
+
`,
|
|
1987
|
+
updateValues
|
|
1988
|
+
);
|
|
1989
|
+
return await this.getWorkspaceById(tenantId, id);
|
|
1990
|
+
}
|
|
1991
|
+
/**
|
|
1992
|
+
* Delete a workspace by ID
|
|
1993
|
+
*/
|
|
1994
|
+
async deleteWorkspace(tenantId, id) {
|
|
1995
|
+
await this.ensureInitialized();
|
|
1996
|
+
const result = await this.pool.query(
|
|
1997
|
+
`
|
|
1998
|
+
DELETE FROM lattice_workspaces
|
|
1999
|
+
WHERE id = $1 AND tenant_id = $2
|
|
2000
|
+
`,
|
|
2001
|
+
[id, tenantId]
|
|
2002
|
+
);
|
|
2003
|
+
return result.rowCount !== null && result.rowCount > 0;
|
|
2004
|
+
}
|
|
2005
|
+
};
|
|
2006
|
+
|
|
2007
|
+
// src/stores/PostgreSQLProjectStore.ts
|
|
2008
|
+
import { Pool as Pool7 } from "pg";
|
|
2009
|
+
|
|
2010
|
+
// src/migrations/project_migrations.ts
|
|
2011
|
+
var createProjectsTable = {
|
|
2012
|
+
version: 6,
|
|
2013
|
+
name: "create_projects_table",
|
|
2014
|
+
up: async (client) => {
|
|
2015
|
+
await client.query(`
|
|
2016
|
+
CREATE TABLE IF NOT EXISTS lattice_projects (
|
|
2017
|
+
id VARCHAR(255) NOT NULL,
|
|
2018
|
+
tenant_id VARCHAR(255) NOT NULL,
|
|
2019
|
+
workspace_id VARCHAR(255) NOT NULL,
|
|
2020
|
+
name VARCHAR(255) NOT NULL,
|
|
2021
|
+
description TEXT,
|
|
2022
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
2023
|
+
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
2024
|
+
PRIMARY KEY (id, tenant_id)
|
|
2025
|
+
)
|
|
2026
|
+
`);
|
|
2027
|
+
await client.query(`
|
|
2028
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_projects_tenant_id
|
|
2029
|
+
ON lattice_projects(tenant_id)
|
|
2030
|
+
`);
|
|
2031
|
+
await client.query(`
|
|
2032
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_projects_workspace_id
|
|
2033
|
+
ON lattice_projects(workspace_id)
|
|
2034
|
+
`);
|
|
2035
|
+
await client.query(`
|
|
2036
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_projects_created_at
|
|
2037
|
+
ON lattice_projects(created_at DESC)
|
|
2038
|
+
`);
|
|
2039
|
+
},
|
|
2040
|
+
down: async (client) => {
|
|
2041
|
+
await client.query("DROP INDEX IF EXISTS idx_lattice_projects_created_at");
|
|
2042
|
+
await client.query("DROP INDEX IF EXISTS idx_lattice_projects_workspace_id");
|
|
2043
|
+
await client.query("DROP INDEX IF EXISTS idx_lattice_projects_tenant_id");
|
|
2044
|
+
await client.query("DROP TABLE IF EXISTS lattice_projects");
|
|
2045
|
+
}
|
|
2046
|
+
};
|
|
2047
|
+
|
|
2048
|
+
// src/stores/PostgreSQLProjectStore.ts
|
|
2049
|
+
var PostgreSQLProjectStore = class {
|
|
2050
|
+
constructor(options) {
|
|
2051
|
+
this.initialized = false;
|
|
2052
|
+
this.ownsPool = true;
|
|
2053
|
+
if (typeof options.poolConfig === "string") {
|
|
2054
|
+
this.pool = new Pool7({ connectionString: options.poolConfig });
|
|
2055
|
+
} else {
|
|
2056
|
+
this.pool = new Pool7(options.poolConfig);
|
|
2057
|
+
}
|
|
2058
|
+
this.migrationManager = new MigrationManager(this.pool);
|
|
2059
|
+
this.migrationManager.register(createProjectsTable);
|
|
2060
|
+
if (options.autoMigrate !== false) {
|
|
2061
|
+
this.initialize().catch((error) => {
|
|
2062
|
+
console.error("Failed to initialize PostgreSQLProjectStore:", error);
|
|
2063
|
+
throw error;
|
|
2064
|
+
});
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
/**
|
|
2068
|
+
* Dispose resources and close the connection pool
|
|
2069
|
+
* Should be called when the store is no longer needed
|
|
2070
|
+
*/
|
|
2071
|
+
async dispose() {
|
|
2072
|
+
if (this.ownsPool && this.pool) {
|
|
2073
|
+
await this.pool.end();
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
/**
|
|
2077
|
+
* Initialize the store and run migrations
|
|
2078
|
+
*/
|
|
2079
|
+
async initialize() {
|
|
2080
|
+
if (this.initialized) {
|
|
2081
|
+
return;
|
|
2082
|
+
}
|
|
2083
|
+
await this.migrationManager.migrate();
|
|
2084
|
+
this.initialized = true;
|
|
2085
|
+
}
|
|
2086
|
+
/**
|
|
2087
|
+
* Ensure store is initialized
|
|
2088
|
+
*/
|
|
2089
|
+
async ensureInitialized() {
|
|
2090
|
+
if (!this.initialized) {
|
|
2091
|
+
await this.initialize();
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
/**
|
|
2095
|
+
* Map database row to Project object
|
|
2096
|
+
*/
|
|
2097
|
+
mapRowToProject(row) {
|
|
2098
|
+
return {
|
|
2099
|
+
id: row.id,
|
|
2100
|
+
tenantId: row.tenant_id,
|
|
2101
|
+
workspaceId: row.workspace_id,
|
|
2102
|
+
name: row.name,
|
|
2103
|
+
description: row.description || void 0,
|
|
2104
|
+
createdAt: row.created_at,
|
|
2105
|
+
updatedAt: row.updated_at
|
|
2106
|
+
};
|
|
2107
|
+
}
|
|
2108
|
+
/**
|
|
2109
|
+
* Get all projects for a specific workspace
|
|
2110
|
+
*/
|
|
2111
|
+
async getProjectsByWorkspace(tenantId, workspaceId) {
|
|
2112
|
+
await this.ensureInitialized();
|
|
2113
|
+
const result = await this.pool.query(
|
|
2114
|
+
`
|
|
2115
|
+
SELECT id, tenant_id, workspace_id, name, description, created_at, updated_at
|
|
2116
|
+
FROM lattice_projects
|
|
2117
|
+
WHERE tenant_id = $1 AND workspace_id = $2
|
|
2118
|
+
ORDER BY created_at DESC
|
|
2119
|
+
`,
|
|
2120
|
+
[tenantId, workspaceId]
|
|
2121
|
+
);
|
|
2122
|
+
return result.rows.map(this.mapRowToProject);
|
|
2123
|
+
}
|
|
2124
|
+
/**
|
|
2125
|
+
* Get a project by ID for a specific tenant
|
|
2126
|
+
*/
|
|
2127
|
+
async getProjectById(tenantId, id) {
|
|
2128
|
+
await this.ensureInitialized();
|
|
2129
|
+
const result = await this.pool.query(
|
|
2130
|
+
`
|
|
2131
|
+
SELECT id, tenant_id, workspace_id, name, description, created_at, updated_at
|
|
2132
|
+
FROM lattice_projects
|
|
2133
|
+
WHERE id = $1 AND tenant_id = $2
|
|
2134
|
+
`,
|
|
2135
|
+
[id, tenantId]
|
|
2136
|
+
);
|
|
2137
|
+
if (result.rows.length === 0) {
|
|
2138
|
+
return null;
|
|
2139
|
+
}
|
|
2140
|
+
return this.mapRowToProject(result.rows[0]);
|
|
2141
|
+
}
|
|
2142
|
+
/**
|
|
2143
|
+
* Create a new project
|
|
2144
|
+
*/
|
|
2145
|
+
async createProject(tenantId, workspaceId, id, data) {
|
|
2146
|
+
await this.ensureInitialized();
|
|
2147
|
+
const now = /* @__PURE__ */ new Date();
|
|
2148
|
+
await this.pool.query(
|
|
2149
|
+
`
|
|
2150
|
+
INSERT INTO lattice_projects (id, tenant_id, workspace_id, name, description, created_at, updated_at)
|
|
2151
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
2152
|
+
ON CONFLICT (id, tenant_id) DO UPDATE SET
|
|
2153
|
+
workspace_id = EXCLUDED.workspace_id,
|
|
2154
|
+
name = EXCLUDED.name,
|
|
2155
|
+
description = EXCLUDED.description,
|
|
2156
|
+
updated_at = EXCLUDED.updated_at
|
|
2157
|
+
`,
|
|
2158
|
+
[id, tenantId, workspaceId, data.name, data.description || null, now, now]
|
|
2159
|
+
);
|
|
2160
|
+
return {
|
|
2161
|
+
id,
|
|
2162
|
+
tenantId,
|
|
2163
|
+
workspaceId,
|
|
2164
|
+
name: data.name,
|
|
2165
|
+
description: data.description,
|
|
2166
|
+
createdAt: now,
|
|
2167
|
+
updatedAt: now
|
|
2168
|
+
};
|
|
2169
|
+
}
|
|
2170
|
+
/**
|
|
2171
|
+
* Update an existing project
|
|
2172
|
+
*/
|
|
2173
|
+
async updateProject(tenantId, id, updates) {
|
|
2174
|
+
await this.ensureInitialized();
|
|
2175
|
+
const existing = await this.getProjectById(tenantId, id);
|
|
2176
|
+
if (!existing) {
|
|
2177
|
+
return null;
|
|
2178
|
+
}
|
|
2179
|
+
const updateFields = [];
|
|
2180
|
+
const updateValues = [];
|
|
2181
|
+
let paramIndex = 1;
|
|
2182
|
+
if (updates.name !== void 0) {
|
|
2183
|
+
updateFields.push(`name = $${paramIndex++}`);
|
|
2184
|
+
updateValues.push(updates.name);
|
|
2185
|
+
}
|
|
2186
|
+
if (updates.description !== void 0) {
|
|
2187
|
+
updateFields.push(`description = $${paramIndex++}`);
|
|
2188
|
+
updateValues.push(updates.description || null);
|
|
2189
|
+
}
|
|
2190
|
+
if (updateFields.length === 0) {
|
|
2191
|
+
return existing;
|
|
2192
|
+
}
|
|
2193
|
+
updateFields.push(`updated_at = $${paramIndex++}`);
|
|
2194
|
+
updateValues.push(/* @__PURE__ */ new Date());
|
|
2195
|
+
updateValues.push(id);
|
|
2196
|
+
updateValues.push(tenantId);
|
|
2197
|
+
await this.pool.query(
|
|
2198
|
+
`
|
|
2199
|
+
UPDATE lattice_projects
|
|
2200
|
+
SET ${updateFields.join(", ")}
|
|
2201
|
+
WHERE id = $${paramIndex} AND tenant_id = $${paramIndex + 1}
|
|
2202
|
+
`,
|
|
2203
|
+
updateValues
|
|
2204
|
+
);
|
|
2205
|
+
return await this.getProjectById(tenantId, id);
|
|
2206
|
+
}
|
|
2207
|
+
/**
|
|
2208
|
+
* Delete a project by ID
|
|
2209
|
+
*/
|
|
2210
|
+
async deleteProject(tenantId, id) {
|
|
2211
|
+
await this.ensureInitialized();
|
|
2212
|
+
const result = await this.pool.query(
|
|
2213
|
+
`
|
|
2214
|
+
DELETE FROM lattice_projects
|
|
2215
|
+
WHERE id = $1 AND tenant_id = $2
|
|
2216
|
+
`,
|
|
2217
|
+
[id, tenantId]
|
|
2218
|
+
);
|
|
2219
|
+
return result.rowCount !== null && result.rowCount > 0;
|
|
2220
|
+
}
|
|
2221
|
+
};
|
|
1477
2222
|
export {
|
|
1478
2223
|
MigrationManager,
|
|
1479
2224
|
PostgreSQLAssistantStore,
|
|
2225
|
+
PostgreSQLDatabaseConfigStore,
|
|
2226
|
+
PostgreSQLProjectStore,
|
|
1480
2227
|
PostgreSQLScheduleStorage,
|
|
1481
2228
|
PostgreSQLSkillStore,
|
|
1482
2229
|
PostgreSQLThreadStore,
|
|
2230
|
+
PostgreSQLWorkspaceStore,
|
|
1483
2231
|
createAssistantsTable,
|
|
2232
|
+
createDatabaseConfigsTable,
|
|
2233
|
+
createProjectsTable,
|
|
1484
2234
|
createScheduledTasksTable,
|
|
1485
2235
|
createSkillsTable,
|
|
1486
|
-
createThreadsTable
|
|
2236
|
+
createThreadsTable,
|
|
2237
|
+
createWorkspacesTable
|
|
1487
2238
|
};
|
|
1488
2239
|
//# sourceMappingURL=index.mjs.map
|