@mostajs/orm-cli 0.5.5 → 0.5.6
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/bin/mostajs.sh +113 -62
- package/package.json +1 -1
package/bin/mostajs.sh
CHANGED
|
@@ -1962,41 +1962,53 @@ action_rep_add_replica() {
|
|
|
1962
1962
|
else
|
|
1963
1963
|
lag="0"
|
|
1964
1964
|
fi
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
}
|
|
1978
|
-
await rm.addReplica('$project', {
|
|
1979
|
-
name: '$name', role: '$role', dialect: '$dialect', uri: '$uri',
|
|
1980
|
-
lagTolerance: $lag,
|
|
1981
|
-
});
|
|
1982
|
-
await save();
|
|
1965
|
+
# Direct tree-JSON patch — preserves URI verbatim (no masking), no DB
|
|
1966
|
+
# connection needed. Validation of the URI happens later when replicator
|
|
1967
|
+
# service or sync action actually touches the DB.
|
|
1968
|
+
_tree_patch "
|
|
1969
|
+
tree.replicas['$project'] = tree.replicas['$project'] || {};
|
|
1970
|
+
tree.replicas['$project']['$name'] = {
|
|
1971
|
+
role: '$role',
|
|
1972
|
+
dialect: '$dialect',
|
|
1973
|
+
uri: '$uri',
|
|
1974
|
+
pool: { min: 2, max: 20 },
|
|
1975
|
+
...( '$role' === 'slave' ? { lagTolerance: $lag } : {} ),
|
|
1976
|
+
};
|
|
1983
1977
|
console.log(' ✓ replica added : $name (' + '$role' + ') on project $project');
|
|
1984
1978
|
"
|
|
1985
1979
|
pause
|
|
1986
1980
|
}
|
|
1987
1981
|
|
|
1982
|
+
# ----------------------------------------------------------------
|
|
1983
|
+
# Tree-file direct manipulation helpers
|
|
1984
|
+
# ----------------------------------------------------------------
|
|
1985
|
+
# The replicator's saveToFile() masks credentials (oracle://u:***@host)
|
|
1986
|
+
# which makes loadFromFile() unable to reconnect. For add/list/remove we
|
|
1987
|
+
# bypass the lib and patch the tree JSON directly, preserving the
|
|
1988
|
+
# original URI verbatim. The lib is still used for sync() / promote()
|
|
1989
|
+
# where it has logic beyond state tracking.
|
|
1990
|
+
|
|
1991
|
+
_tree_file() { _replicator_tree_file; }
|
|
1992
|
+
|
|
1993
|
+
_tree_read_json() {
|
|
1994
|
+
# Dump the tree JSON on stdout, empty-tree fallback if file missing.
|
|
1995
|
+
local tree_file
|
|
1996
|
+
tree_file=$(_tree_file)
|
|
1997
|
+
node -e "
|
|
1998
|
+
const fs = require('fs');
|
|
1999
|
+
const def = { replicas: {}, rules: {}, routing: {} };
|
|
2000
|
+
try { console.log(JSON.stringify(Object.assign(def, JSON.parse(fs.readFileSync('$tree_file','utf8'))))); }
|
|
2001
|
+
catch { console.log(JSON.stringify(def)); }
|
|
2002
|
+
"
|
|
2003
|
+
}
|
|
2004
|
+
|
|
1988
2005
|
# List known projects from the replicator-tree.json — if any — and propose
|
|
1989
2006
|
# the first one as default. Echoes the chosen project name to stdout.
|
|
1990
|
-
# Returns 1 if user cancelled.
|
|
1991
2007
|
_pick_project() {
|
|
1992
|
-
local tree_file
|
|
1993
|
-
tree_file=$(_replicator_tree_file)
|
|
1994
2008
|
local projects=()
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
)
|
|
1999
|
-
fi
|
|
2009
|
+
while IFS= read -r p; do projects+=("$p"); done < <(
|
|
2010
|
+
_tree_read_json | node -e "let d='';process.stdin.on('data',c=>d+=c).on('end',()=>{for(const k of Object.keys(JSON.parse(d).replicas||{}))console.log(k)})"
|
|
2011
|
+
)
|
|
2000
2012
|
local default_project="fitzone"
|
|
2001
2013
|
if [[ ${#projects[@]} -gt 0 ]]; then
|
|
2002
2014
|
default_project="${projects[0]}"
|
|
@@ -2010,18 +2022,46 @@ _pick_project() {
|
|
|
2010
2022
|
echo "$picked"
|
|
2011
2023
|
}
|
|
2012
2024
|
|
|
2025
|
+
# Patch the tree JSON : exec a Node snippet with `tree` in scope.
|
|
2026
|
+
# After the snippet runs, the mutated tree is written back.
|
|
2027
|
+
_tree_patch() {
|
|
2028
|
+
local snippet="$1"
|
|
2029
|
+
local tree_file
|
|
2030
|
+
tree_file=$(_tree_file)
|
|
2031
|
+
mkdir -p "$(dirname "$tree_file")"
|
|
2032
|
+
TREE_FILE="$tree_file" node --input-type=module -e "
|
|
2033
|
+
const { readFileSync, writeFileSync, existsSync } = await import('fs');
|
|
2034
|
+
const { mkdirSync } = await import('fs');
|
|
2035
|
+
const def = { replicas: {}, rules: {}, routing: {} };
|
|
2036
|
+
let tree = def;
|
|
2037
|
+
if (existsSync(process.env.TREE_FILE)) {
|
|
2038
|
+
try { tree = Object.assign(def, JSON.parse(readFileSync(process.env.TREE_FILE, 'utf8'))); }
|
|
2039
|
+
catch { /* start fresh */ }
|
|
2040
|
+
}
|
|
2041
|
+
try { $snippet }
|
|
2042
|
+
catch (e) { console.error(' ✗ ' + e.message); process.exit(1); }
|
|
2043
|
+
writeFileSync(process.env.TREE_FILE, JSON.stringify(tree, null, 2) + '\n');
|
|
2044
|
+
"
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2013
2047
|
action_rep_list_replicas() {
|
|
2014
2048
|
local project
|
|
2015
2049
|
project=$(_pick_project)
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2050
|
+
echo
|
|
2051
|
+
_tree_read_json | PROJECT="$project" node -e "
|
|
2052
|
+
let d='';process.stdin.on('data',c=>d+=c).on('end',()=>{
|
|
2053
|
+
const tree = JSON.parse(d);
|
|
2054
|
+
const reps = tree.replicas[process.env.PROJECT] || {};
|
|
2055
|
+
const entries = Object.entries(reps);
|
|
2056
|
+
if (entries.length === 0) {
|
|
2057
|
+
console.log(' (no replicas registered for project ' + process.env.PROJECT + ')');
|
|
2058
|
+
return;
|
|
2023
2059
|
}
|
|
2024
|
-
|
|
2060
|
+
for (const [name, cfg] of entries) {
|
|
2061
|
+
const star = cfg.role === 'master' ? '\x1b[36m★\x1b[0m' : '•';
|
|
2062
|
+
console.log(' ' + star + ' ' + name + ' [' + cfg.role + '] ' + cfg.dialect + ' ' + (cfg.uri ?? '').replace(/:[^:@]+@/, ':***@'));
|
|
2063
|
+
}
|
|
2064
|
+
});
|
|
2025
2065
|
"
|
|
2026
2066
|
pause
|
|
2027
2067
|
}
|
|
@@ -2044,10 +2084,14 @@ action_rep_remove_replica() {
|
|
|
2044
2084
|
project=$(_pick_project)
|
|
2045
2085
|
name=$(ask "Replica name" "slave-1")
|
|
2046
2086
|
if ! confirm "Remove replica '$name' from project '$project'?"; then return; fi
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2087
|
+
_tree_patch "
|
|
2088
|
+
if (!tree.replicas['$project'] || !tree.replicas['$project']['$name']) {
|
|
2089
|
+
console.log(' • not found : $project/$name');
|
|
2090
|
+
} else {
|
|
2091
|
+
delete tree.replicas['$project']['$name'];
|
|
2092
|
+
if (Object.keys(tree.replicas['$project']).length === 0) delete tree.replicas['$project'];
|
|
2093
|
+
console.log(' ✓ removed : $project/$name');
|
|
2094
|
+
}
|
|
2051
2095
|
"
|
|
2052
2096
|
pause
|
|
2053
2097
|
}
|
|
@@ -2056,9 +2100,8 @@ action_rep_set_routing() {
|
|
|
2056
2100
|
local project strategy
|
|
2057
2101
|
project=$(_pick_project)
|
|
2058
2102
|
strategy=$(ask "Strategy (round-robin | least-lag | random)" "least-lag")
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
await save();
|
|
2103
|
+
_tree_patch "
|
|
2104
|
+
tree.routing['$project'] = '$strategy';
|
|
2062
2105
|
console.log(' ✓ read routing on $project = $strategy');
|
|
2063
2106
|
"
|
|
2064
2107
|
pause
|
|
@@ -2066,36 +2109,41 @@ action_rep_set_routing() {
|
|
|
2066
2109
|
|
|
2067
2110
|
action_rep_add_rule() {
|
|
2068
2111
|
local name source target mode colls conflict
|
|
2069
|
-
|
|
2070
|
-
source=$(
|
|
2071
|
-
|
|
2112
|
+
# Pick source/target from known projects
|
|
2113
|
+
source=$(_pick_project)
|
|
2114
|
+
local src_default="$source"
|
|
2115
|
+
name=$(ask "Rule name (short, e.g. 'pg-to-mongo')" "cdc-${src_default}")
|
|
2116
|
+
target=$(ask "Target project" "$src_default")
|
|
2072
2117
|
mode=$(ask "Mode (snapshot | cdc | bidirectional)" "cdc")
|
|
2073
2118
|
colls=$(ask "Collections (comma-separated)" "users,clients")
|
|
2074
2119
|
conflict=$(ask "Conflict resolution (source-wins | target-wins | timestamp)" "source-wins")
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2120
|
+
_tree_patch "
|
|
2121
|
+
tree.rules['$name'] = {
|
|
2122
|
+
source: '$source', target: '$target', mode: '$mode',
|
|
2078
2123
|
collections: '$colls'.split(',').map(s => s.trim()).filter(Boolean),
|
|
2079
2124
|
conflictResolution: '$conflict',
|
|
2080
2125
|
enabled: true,
|
|
2081
|
-
}
|
|
2082
|
-
await save();
|
|
2126
|
+
};
|
|
2083
2127
|
console.log(' ✓ rule added : $name ($source → $target, mode=$mode)');
|
|
2084
2128
|
"
|
|
2085
2129
|
pause
|
|
2086
2130
|
}
|
|
2087
2131
|
|
|
2088
2132
|
action_rep_list_rules() {
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2133
|
+
echo
|
|
2134
|
+
_tree_read_json | node -e "
|
|
2135
|
+
let d='';process.stdin.on('data',c=>d+=c).on('end',()=>{
|
|
2136
|
+
const tree = JSON.parse(d);
|
|
2137
|
+
const rules = Object.entries(tree.rules || {});
|
|
2138
|
+
if (rules.length === 0) {
|
|
2139
|
+
console.log(' (no CDC rules registered)');
|
|
2140
|
+
return;
|
|
2097
2141
|
}
|
|
2098
|
-
|
|
2142
|
+
for (const [name, r] of rules) {
|
|
2143
|
+
const flag = r.enabled ? '\x1b[32m✓\x1b[0m' : '\x1b[31m✗\x1b[0m';
|
|
2144
|
+
console.log(' ' + flag + ' ' + name + ' ' + r.source + ' → ' + r.target + ' [' + r.mode + '] ' + (r.collections||[]).join(','));
|
|
2145
|
+
}
|
|
2146
|
+
});
|
|
2099
2147
|
"
|
|
2100
2148
|
pause
|
|
2101
2149
|
}
|
|
@@ -2119,10 +2167,13 @@ action_rep_remove_rule() {
|
|
|
2119
2167
|
local name
|
|
2120
2168
|
name=$(ask "Rule name" "pg-to-mongo")
|
|
2121
2169
|
if ! confirm "Remove CDC rule '$name'?"; then return; fi
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2170
|
+
_tree_patch "
|
|
2171
|
+
if (!tree.rules['$name']) {
|
|
2172
|
+
console.log(' • not found : $name');
|
|
2173
|
+
} else {
|
|
2174
|
+
delete tree.rules['$name'];
|
|
2175
|
+
console.log(' ✓ removed : $name');
|
|
2176
|
+
}
|
|
2126
2177
|
"
|
|
2127
2178
|
pause
|
|
2128
2179
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mostajs/orm-cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "Universal CLI to integrate @mostajs/orm into any project — one-shot `mostajs bootstrap` migrates a Prisma project (codemod + deps + schema convert + DDL) to 13 databases with zero code change.",
|
|
5
5
|
"author": "Dr Hamid MADANI <drmdh@msn.com>",
|
|
6
6
|
"license": "AGPL-3.0-or-later",
|