@nitsan-ai/ragsuite-test 0.1.1 → 0.1.3
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/README.md +61 -63
- package/package.json +3 -2
- package/src/commands/doctor.js +17 -13
- package/src/commands/init.js +67 -112
- package/src/commands/start.js +25 -17
- package/src/commands/stop.js +23 -13
- package/src/index.js +4 -3
- package/src/utils/args.js +6 -0
- package/src/utils/config.js +11 -5
- package/src/utils/distribution.js +11 -11
- package/src/utils/images-install.js +100 -0
- package/src/utils/paths.js +34 -9
- package/templates/images/.env.example +76 -0
- package/templates/images/docker-compose.yml +165 -0
- package/templates/images/scripts/docker-doctor-images.sh +48 -0
- package/templates/images/scripts/docker-start-images.sh +107 -0
- package/templates/images/scripts/docker-stop-images.sh +22 -0
package/src/index.js
CHANGED
|
@@ -31,15 +31,16 @@ function globalHelp() {
|
|
|
31
31
|
'Global options:',
|
|
32
32
|
' --help, -h Show help',
|
|
33
33
|
' --repo-root <path> Monorepo / install root',
|
|
34
|
-
' --from-git [url] init: clone public
|
|
35
|
-
' --
|
|
34
|
+
' --from-git [url] init: clone source (default public repo)',
|
|
35
|
+
' --from-images init: Docker images only (no app source on disk)',
|
|
36
|
+
' --install-dir <path> init: install target (default ~/ragsuite-test)',
|
|
36
37
|
' --llm-api-key <key> init: optional override for CUSTOM_LLM_INTERNAL_API_KEY',
|
|
37
38
|
' --smtp-host <host> init: optional SMTP override',
|
|
38
39
|
' --smtp-user <user> init: optional SMTP override',
|
|
39
40
|
' --smtp-password <pass> init: optional SMTP override',
|
|
40
41
|
' --email-from <email> init: optional SMTP override',
|
|
41
42
|
' --force init: overwrite non-empty install dir / .env',
|
|
42
|
-
' --yes, -y Non-interactive',
|
|
43
|
+
' --yes, -y Non-interactive (same defaults as plain init)',
|
|
43
44
|
' --mode <docker|native> Override config (Docker optional)',
|
|
44
45
|
' --dry-run Print actions without running (start/stop/doctor/logs)',
|
|
45
46
|
'',
|
package/src/utils/args.js
CHANGED
|
@@ -11,6 +11,7 @@ function parseArgv(argv) {
|
|
|
11
11
|
dryRun: false,
|
|
12
12
|
mode: null,
|
|
13
13
|
fromGit: null,
|
|
14
|
+
fromImages: false,
|
|
14
15
|
fromRelease: null,
|
|
15
16
|
installDir: null,
|
|
16
17
|
force: false,
|
|
@@ -79,6 +80,11 @@ function parseArgv(argv) {
|
|
|
79
80
|
continue;
|
|
80
81
|
}
|
|
81
82
|
|
|
83
|
+
if (t === '--from-images') {
|
|
84
|
+
globals.fromImages = true;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
82
88
|
if (t === '--from-git' || t.startsWith('--from-git=')) {
|
|
83
89
|
if (t === '--from-git') {
|
|
84
90
|
const next = tokens[i + 1];
|
package/src/utils/config.js
CHANGED
|
@@ -21,6 +21,12 @@ function defaultConfig(repoRoot, mode = 'docker') {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
function normalizeSource(raw) {
|
|
25
|
+
const s = String(raw || '').trim();
|
|
26
|
+
if (s === 'images' || s === 'git' || s === 'zip' || s === 'checkout') return s;
|
|
27
|
+
return 'checkout';
|
|
28
|
+
}
|
|
29
|
+
|
|
24
30
|
function readConfig(repoRoot) {
|
|
25
31
|
const file = configPath(repoRoot);
|
|
26
32
|
if (!fs.existsSync(file)) {
|
|
@@ -31,11 +37,10 @@ function readConfig(repoRoot) {
|
|
|
31
37
|
if (!raw || typeof raw !== 'object') {
|
|
32
38
|
return null;
|
|
33
39
|
}
|
|
34
|
-
const source = raw.source === 'zip' ? 'zip' : 'checkout';
|
|
35
40
|
return {
|
|
36
41
|
version: Number(raw.version) || CONFIG_VERSION,
|
|
37
42
|
mode: raw.mode === 'native' ? 'native' : 'docker',
|
|
38
|
-
source,
|
|
43
|
+
source: normalizeSource(raw.source),
|
|
39
44
|
repoRoot: path.resolve(String(raw.repoRoot || repoRoot)),
|
|
40
45
|
installDir: path.resolve(String(raw.installDir || raw.repoRoot || repoRoot)),
|
|
41
46
|
};
|
|
@@ -48,10 +53,11 @@ function writeConfig(repoRoot, partial = {}) {
|
|
|
48
53
|
const dir = path.join(repoRoot, CONFIG_DIR);
|
|
49
54
|
fs.mkdirSync(dir, { recursive: true });
|
|
50
55
|
const existing = readConfig(repoRoot) || defaultConfig(repoRoot);
|
|
51
|
-
const source =
|
|
52
|
-
partial.source
|
|
56
|
+
const source = normalizeSource(
|
|
57
|
+
partial.source !== undefined && partial.source !== null
|
|
53
58
|
? partial.source
|
|
54
|
-
: existing.source || 'checkout'
|
|
59
|
+
: existing.source || 'checkout',
|
|
60
|
+
);
|
|
55
61
|
const next = {
|
|
56
62
|
...existing,
|
|
57
63
|
...partial,
|
|
@@ -3,22 +3,21 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Distribution modes for the testing CLI.
|
|
5
5
|
*
|
|
6
|
-
* - git-clone: init
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
6
|
+
* - git-clone: `ragsuite-test init` (source on disk)
|
|
7
|
+
* - images: `ragsuite-test init --from-images` (pull GHCR, no app source)
|
|
8
|
+
* - local-checkout: --repo-root
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
const MODE_LOCAL = 'local-checkout';
|
|
12
12
|
const MODE_GIT = 'git-clone';
|
|
13
|
+
const MODE_IMAGES = 'images';
|
|
13
14
|
const MODE_REGISTRY = 'registry-compose';
|
|
14
15
|
|
|
15
|
-
function assertSupportedInstall() {
|
|
16
|
-
// git + checkout supported
|
|
17
|
-
}
|
|
16
|
+
function assertSupportedInstall() {}
|
|
18
17
|
|
|
19
18
|
function resolveComposeBundle() {
|
|
20
19
|
const err = new Error(
|
|
21
|
-
'
|
|
20
|
+
'Use: ragsuite-test init --from-images (pull-only) OR ragsuite-test init (git source)',
|
|
22
21
|
);
|
|
23
22
|
err.code = 'DIST_NOT_READY';
|
|
24
23
|
throw err;
|
|
@@ -26,16 +25,17 @@ function resolveComposeBundle() {
|
|
|
26
25
|
|
|
27
26
|
function installHint() {
|
|
28
27
|
return [
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
'
|
|
28
|
+
'Choose an install style:',
|
|
29
|
+
' Source on disk: ragsuite-test init',
|
|
30
|
+
' No source: ragsuite-test init --from-images',
|
|
31
|
+
'Then: ragsuite-test start --repo-root ~/ragsuite-test --detach',
|
|
33
32
|
].join('\n');
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
module.exports = {
|
|
37
36
|
MODE_LOCAL,
|
|
38
37
|
MODE_GIT,
|
|
38
|
+
MODE_IMAGES,
|
|
39
39
|
MODE_REGISTRY,
|
|
40
40
|
mode: MODE_GIT,
|
|
41
41
|
assertSupportedInstall,
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { defaultInstallDir } = require('./git-install');
|
|
6
|
+
|
|
7
|
+
function templatesRoot() {
|
|
8
|
+
return path.join(__dirname, '..', '..', 'templates', 'images');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function isDirEmptyOrMissing(dir) {
|
|
12
|
+
if (!fs.existsSync(dir)) return true;
|
|
13
|
+
const items = fs.readdirSync(dir).filter((n) => n !== '.DS_Store');
|
|
14
|
+
return items.length === 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function copyFile(src, dest) {
|
|
18
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
19
|
+
fs.copyFileSync(src, dest);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Materialize a pull-only install dir (compose + scripts + .env.example).
|
|
24
|
+
* No monorepo source.
|
|
25
|
+
*/
|
|
26
|
+
function materializeImagesInstall(options = {}) {
|
|
27
|
+
const { installDir = defaultInstallDir(), force = false } = options;
|
|
28
|
+
const srcRoot = templatesRoot();
|
|
29
|
+
if (!fs.existsSync(path.join(srcRoot, 'docker-compose.yml'))) {
|
|
30
|
+
const err = new Error(
|
|
31
|
+
`Images templates missing in CLI package (${srcRoot}). Reinstall @nitsan-ai/ragsuite-test.`,
|
|
32
|
+
);
|
|
33
|
+
err.code = 'IMAGES_TEMPLATE_MISSING';
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const target = path.resolve(installDir);
|
|
38
|
+
if (!isDirEmptyOrMissing(target)) {
|
|
39
|
+
if (!force) {
|
|
40
|
+
const err = new Error(
|
|
41
|
+
`Install directory is not empty: ${target}. Pass --force to replace, or choose another --install-dir.`,
|
|
42
|
+
);
|
|
43
|
+
err.code = 'INSTALL_DIR_NONEMPTY';
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fs.mkdirSync(target, { recursive: true });
|
|
50
|
+
copyFile(
|
|
51
|
+
path.join(srcRoot, 'docker-compose.yml'),
|
|
52
|
+
path.join(target, 'docker-compose.yml'),
|
|
53
|
+
);
|
|
54
|
+
copyFile(
|
|
55
|
+
path.join(srcRoot, '.env.example'),
|
|
56
|
+
path.join(target, '.env.example'),
|
|
57
|
+
);
|
|
58
|
+
copyFile(
|
|
59
|
+
path.join(srcRoot, 'scripts', 'docker-start-images.sh'),
|
|
60
|
+
path.join(target, 'scripts', 'docker-start-images.sh'),
|
|
61
|
+
);
|
|
62
|
+
copyFile(
|
|
63
|
+
path.join(srcRoot, 'scripts', 'docker-stop-images.sh'),
|
|
64
|
+
path.join(target, 'scripts', 'docker-stop-images.sh'),
|
|
65
|
+
);
|
|
66
|
+
copyFile(
|
|
67
|
+
path.join(srcRoot, 'scripts', 'docker-doctor-images.sh'),
|
|
68
|
+
path.join(target, 'scripts', 'docker-doctor-images.sh'),
|
|
69
|
+
);
|
|
70
|
+
try {
|
|
71
|
+
fs.chmodSync(path.join(target, 'scripts', 'docker-start-images.sh'), 0o755);
|
|
72
|
+
fs.chmodSync(path.join(target, 'scripts', 'docker-stop-images.sh'), 0o755);
|
|
73
|
+
fs.chmodSync(path.join(target, 'scripts', 'docker-doctor-images.sh'), 0o755);
|
|
74
|
+
} catch {
|
|
75
|
+
/* windows */
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Minimal package.json so tooling can identify the install dir
|
|
79
|
+
fs.writeFileSync(
|
|
80
|
+
path.join(target, 'package.json'),
|
|
81
|
+
`${JSON.stringify(
|
|
82
|
+
{
|
|
83
|
+
name: 'ragsuite-images-install',
|
|
84
|
+
private: true,
|
|
85
|
+
description: 'RAGSuite pull-only images install (no source tree)',
|
|
86
|
+
},
|
|
87
|
+
null,
|
|
88
|
+
2,
|
|
89
|
+
)}\n`,
|
|
90
|
+
'utf8',
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
return { repoRoot: target };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = {
|
|
97
|
+
templatesRoot,
|
|
98
|
+
materializeImagesInstall,
|
|
99
|
+
defaultInstallDir,
|
|
100
|
+
};
|
package/src/utils/paths.js
CHANGED
|
@@ -4,14 +4,35 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { readConfig, CONFIG_DIR } = require('./config');
|
|
6
6
|
|
|
7
|
-
function
|
|
7
|
+
function looksLikeGitCheckout(dir) {
|
|
8
8
|
const start = path.join(dir, 'scripts', 'docker-start.sh');
|
|
9
9
|
const pkg = path.join(dir, 'package.json');
|
|
10
10
|
return fs.existsSync(start) && fs.existsSync(pkg);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
function looksLikeImagesInstall(dir) {
|
|
14
|
+
const compose = path.join(dir, 'docker-compose.yml');
|
|
15
|
+
const start = path.join(dir, 'scripts', 'docker-start-images.sh');
|
|
16
|
+
if (!fs.existsSync(compose) || !fs.existsSync(start)) return false;
|
|
17
|
+
const cfgPath = path.join(dir, CONFIG_DIR, 'config.json');
|
|
18
|
+
if (fs.existsSync(cfgPath)) {
|
|
19
|
+
try {
|
|
20
|
+
const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
|
|
21
|
+
if (cfg.source === 'images') return true;
|
|
22
|
+
} catch {
|
|
23
|
+
/* fall through */
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Before config is written, or force-detect by scripts present without docker-start.sh
|
|
27
|
+
return !fs.existsSync(path.join(dir, 'scripts', 'docker-start.sh'));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function looksLikeRepoRoot(dir) {
|
|
31
|
+
return looksLikeGitCheckout(dir) || looksLikeImagesInstall(dir);
|
|
32
|
+
}
|
|
33
|
+
|
|
13
34
|
function looksLikeFullBundleRoot(dir) {
|
|
14
|
-
if (!
|
|
35
|
+
if (!looksLikeGitCheckout(dir)) return false;
|
|
15
36
|
const compose = path.join(dir, 'docker-compose.yml');
|
|
16
37
|
return fs.existsSync(compose);
|
|
17
38
|
}
|
|
@@ -42,10 +63,6 @@ function envRepoRoot(env = process.env) {
|
|
|
42
63
|
return null;
|
|
43
64
|
}
|
|
44
65
|
|
|
45
|
-
/**
|
|
46
|
-
* Resolve monorepo root:
|
|
47
|
-
* --repo-root → RAGSUITE_REPO_ROOT / RAGSUITE_INSTALL_DIR → walk-up / config.
|
|
48
|
-
*/
|
|
49
66
|
function resolveRepoRoot({
|
|
50
67
|
cwd = process.cwd(),
|
|
51
68
|
repoRootFlag,
|
|
@@ -56,7 +73,7 @@ function resolveRepoRoot({
|
|
|
56
73
|
const root = path.resolve(repoRootFlag);
|
|
57
74
|
if (!looksLikeRepoRoot(root)) {
|
|
58
75
|
const err = new Error(
|
|
59
|
-
`Not a RAGSuite
|
|
76
|
+
`Not a RAGSuite install root: ${root} (need git checkout or images install)`,
|
|
60
77
|
);
|
|
61
78
|
err.code = 'NOT_REPO_ROOT';
|
|
62
79
|
throw err;
|
|
@@ -91,7 +108,7 @@ function resolveRepoRoot({
|
|
|
91
108
|
}
|
|
92
109
|
|
|
93
110
|
const err = new Error(
|
|
94
|
-
'Could not find RAGSuite
|
|
111
|
+
'Could not find RAGSuite install. Run: ragsuite-test init OR ragsuite-test init --from-images',
|
|
95
112
|
);
|
|
96
113
|
err.code = 'NOT_REPO_ROOT';
|
|
97
114
|
throw err;
|
|
@@ -107,13 +124,20 @@ function ensureRagsuiteDir(repoRoot) {
|
|
|
107
124
|
return dir;
|
|
108
125
|
}
|
|
109
126
|
|
|
110
|
-
/** Native PID/logs stay under .ragsuite/native (Phase 4 scripts). */
|
|
111
127
|
function nativeLogDir(repoRoot) {
|
|
112
128
|
return path.join(repoRoot, '.ragsuite', 'native');
|
|
113
129
|
}
|
|
114
130
|
|
|
131
|
+
function isImagesInstall(repoRoot) {
|
|
132
|
+
const cfg = readConfig(repoRoot);
|
|
133
|
+
if (cfg && cfg.source === 'images') return true;
|
|
134
|
+
return looksLikeImagesInstall(repoRoot) && !looksLikeGitCheckout(repoRoot);
|
|
135
|
+
}
|
|
136
|
+
|
|
115
137
|
module.exports = {
|
|
116
138
|
looksLikeRepoRoot,
|
|
139
|
+
looksLikeGitCheckout,
|
|
140
|
+
looksLikeImagesInstall,
|
|
117
141
|
looksLikeFullBundleRoot,
|
|
118
142
|
walkUpForRepoRoot,
|
|
119
143
|
envRepoRoot,
|
|
@@ -121,4 +145,5 @@ module.exports = {
|
|
|
121
145
|
scriptPath,
|
|
122
146
|
ensureRagsuiteDir,
|
|
123
147
|
nativeLogDir,
|
|
148
|
+
isImagesInstall,
|
|
124
149
|
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# RAGSuite Server — copy to .env: cp .env.example .env
|
|
2
|
+
# Never commit .env.
|
|
3
|
+
#
|
|
4
|
+
# Used by docker compose (backend + worker env_file) and native-start (host process).
|
|
5
|
+
# Compose injects DATABASE_URL, REDIS_HOST/PORT, CHROMA_* for containers — those override
|
|
6
|
+
# anything duplicated here when using Docker mode.
|
|
7
|
+
# For native mode (npm run start:native), set the Native block below (or rely on script defaults).
|
|
8
|
+
|
|
9
|
+
# --- Required secrets ---
|
|
10
|
+
# JWT is regenerated by `ragsuite-test init` (do not put real secrets here — use .env).
|
|
11
|
+
JWT_SECRET_KEY=change-me-use-a-long-random-secret-in-production
|
|
12
|
+
# First-run default (not a cloud vendor key). Override in .env anytime.
|
|
13
|
+
CUSTOM_LLM_INTERNAL_API_KEY=ragsuite-default-llm-internal-key
|
|
14
|
+
|
|
15
|
+
# --- Transactional email (invites, 2FA, password reset) ---
|
|
16
|
+
# Template only — real SMTP_USER / SMTP_PASSWORD / EMAIL_FROM belong in .env (gitignored).
|
|
17
|
+
# `ragsuite-test init` copies this file to .env and does NOT ask for SMTP.
|
|
18
|
+
SMTP_HOST=smtp.gmail.com
|
|
19
|
+
SMTP_PORT=587
|
|
20
|
+
SMTP_USER=your-smtp-user@example.com
|
|
21
|
+
SMTP_PASSWORD=change-me-app-password-or-smtp-secret
|
|
22
|
+
SMTP_USE_TLS=true
|
|
23
|
+
EMAIL_FROM=your-smtp-user@example.com
|
|
24
|
+
|
|
25
|
+
# --- Frontend Docker build (baked into env.json at image build) ---
|
|
26
|
+
FRONTEND_API_URL=http://localhost:9090
|
|
27
|
+
# Optional: public origin that serves /widget assets (defaults to FRONTEND_API_URL in Dockerfile)
|
|
28
|
+
# WIDGET_ASSET_BASE=
|
|
29
|
+
|
|
30
|
+
# --- App policy ---
|
|
31
|
+
HF_HUB_OFFLINE=1
|
|
32
|
+
SSO_ENABLED=false
|
|
33
|
+
SSO_REQUIRE_REDIS=true
|
|
34
|
+
|
|
35
|
+
# --- RAG / ingest (optional) ---
|
|
36
|
+
ENABLE_ASYNC_DOCUMENT_INGEST=true
|
|
37
|
+
EMBEDDING_PREFERRED_SOURCE=chat
|
|
38
|
+
COMPARE_MODEL_CONFIG_SOURCE=chat
|
|
39
|
+
|
|
40
|
+
# --- Optional: Docker host-port overrides (defaults match npm start) ---
|
|
41
|
+
# API_PORT=9090
|
|
42
|
+
# WEB_PORT=9091
|
|
43
|
+
# POSTGRES_HOST_PORT=5436
|
|
44
|
+
# REDIS_HOST_PORT=6382
|
|
45
|
+
|
|
46
|
+
# --- Optional: Compose Postgres credentials (defaults: ragsuite / ragsuite / ragsuite_v3) ---
|
|
47
|
+
# POSTGRES_USER=ragsuite
|
|
48
|
+
# POSTGRES_PASSWORD=ragsuite
|
|
49
|
+
# POSTGRES_DB=ragsuite_v3
|
|
50
|
+
|
|
51
|
+
# --- Optional: App URL / CORS (compose sets local defaults for Docker) ---
|
|
52
|
+
# CORS_ORIGINS=http://localhost:9091
|
|
53
|
+
# FRONTEND_BASE_URL=http://localhost:9091
|
|
54
|
+
# PUBLIC_API_BASE_URL=http://localhost:9090/api/v1
|
|
55
|
+
# SSO_CALLBACK_BASE_URL=http://localhost:9090/api/v1/auth/sso/callback
|
|
56
|
+
|
|
57
|
+
# --- Native mode (npm run start:native) — host Postgres/Redis/Chroma ---
|
|
58
|
+
# Do not use Docker service DNS names (postgres/redis/chromadb) on the host.
|
|
59
|
+
# DATABASE_URL=postgresql://ragsuite:ragsuite@localhost:5436/ragsuite_v3
|
|
60
|
+
# REDIS_HOST=localhost
|
|
61
|
+
# REDIS_PORT=6382
|
|
62
|
+
# CHROMA_MODE=http
|
|
63
|
+
# CHROMA_HOST=127.0.0.1
|
|
64
|
+
# CHROMA_PORT=8004
|
|
65
|
+
# CHROMA_PERSIST_PATH=data/chroma_db
|
|
66
|
+
# CORS_ORIGINS=http://localhost:9191,http://127.0.0.1:9191,http://localhost:9091
|
|
67
|
+
# FRONTEND_BASE_URL=http://localhost:9191
|
|
68
|
+
# PUBLIC_API_BASE_URL=http://localhost:9090/api/v1
|
|
69
|
+
# EXPO_DEV_SERVER_PORT=9191
|
|
70
|
+
# RAGSUITE_MODE=native
|
|
71
|
+
|
|
72
|
+
# Advanced tuning: backend/docs/backend/configuration.md
|
|
73
|
+
|
|
74
|
+
# --- Images install (init --from-images) ---
|
|
75
|
+
# IMAGE_REGISTRY=ghcr.io/nitsan-ai
|
|
76
|
+
# IMAGE_TAG=latest
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Pull-only stack (no monorepo source / no local build).
|
|
2
|
+
# Used by: ragsuite-test init --from-images
|
|
3
|
+
# Images: ghcr.io/nitsan-ai/ragsuite-backend and ragsuite-frontend
|
|
4
|
+
#
|
|
5
|
+
# Override tag: IMAGE_TAG=1.0.0 (default: latest)
|
|
6
|
+
# Registry: IMAGE_REGISTRY=ghcr.io/nitsan-ai (default)
|
|
7
|
+
|
|
8
|
+
name: ragsuite-server
|
|
9
|
+
|
|
10
|
+
networks:
|
|
11
|
+
ragsuite-server-net:
|
|
12
|
+
name: ragsuite-server-net
|
|
13
|
+
|
|
14
|
+
x-backend-image: &backend_image
|
|
15
|
+
image: ${IMAGE_REGISTRY:-ghcr.io/nitsan-ai}/ragsuite-backend:${IMAGE_TAG:-latest}
|
|
16
|
+
pull_policy: ${PULL_POLICY:-always}
|
|
17
|
+
|
|
18
|
+
services:
|
|
19
|
+
postgres:
|
|
20
|
+
image: postgres:15-alpine
|
|
21
|
+
environment:
|
|
22
|
+
POSTGRES_USER: ${POSTGRES_USER:-ragsuite}
|
|
23
|
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-ragsuite}
|
|
24
|
+
POSTGRES_DB: ${POSTGRES_DB:-ragsuite_v3}
|
|
25
|
+
ports:
|
|
26
|
+
- "${POSTGRES_HOST_PORT:-5436}:5432"
|
|
27
|
+
volumes:
|
|
28
|
+
- postgres_data:/var/lib/postgresql/data
|
|
29
|
+
healthcheck:
|
|
30
|
+
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
|
|
31
|
+
interval: 5s
|
|
32
|
+
timeout: 5s
|
|
33
|
+
retries: 10
|
|
34
|
+
networks:
|
|
35
|
+
- ragsuite-server-net
|
|
36
|
+
restart: unless-stopped
|
|
37
|
+
|
|
38
|
+
redis:
|
|
39
|
+
image: redis:7-alpine
|
|
40
|
+
ports:
|
|
41
|
+
- "${REDIS_HOST_PORT:-6382}:6379"
|
|
42
|
+
healthcheck:
|
|
43
|
+
test: ["CMD", "redis-cli", "ping"]
|
|
44
|
+
interval: 5s
|
|
45
|
+
timeout: 3s
|
|
46
|
+
retries: 10
|
|
47
|
+
networks:
|
|
48
|
+
- ragsuite-server-net
|
|
49
|
+
restart: unless-stopped
|
|
50
|
+
|
|
51
|
+
chromadb:
|
|
52
|
+
<<: *backend_image
|
|
53
|
+
entrypoint: []
|
|
54
|
+
command:
|
|
55
|
+
- chroma
|
|
56
|
+
- run
|
|
57
|
+
- --host
|
|
58
|
+
- "0.0.0.0"
|
|
59
|
+
- --port
|
|
60
|
+
- "8000"
|
|
61
|
+
- --path
|
|
62
|
+
- /data/chroma
|
|
63
|
+
volumes:
|
|
64
|
+
- chroma_data:/data/chroma
|
|
65
|
+
healthcheck:
|
|
66
|
+
test: ["CMD", "curl", "-sf", "http://localhost:8000/api/v2/heartbeat"]
|
|
67
|
+
interval: 10s
|
|
68
|
+
timeout: 5s
|
|
69
|
+
retries: 10
|
|
70
|
+
start_period: 30s
|
|
71
|
+
networks:
|
|
72
|
+
- ragsuite-server-net
|
|
73
|
+
restart: unless-stopped
|
|
74
|
+
|
|
75
|
+
backend:
|
|
76
|
+
<<: *backend_image
|
|
77
|
+
env_file:
|
|
78
|
+
- .env
|
|
79
|
+
environment:
|
|
80
|
+
DEBUG: "True"
|
|
81
|
+
CORS_ORIGINS: "http://localhost:${WEB_PORT:-9091}"
|
|
82
|
+
ENABLE_SCHEDULER: "true"
|
|
83
|
+
ENABLE_DURABLE_JOBS: "true"
|
|
84
|
+
DATABASE_URL: postgresql://${POSTGRES_USER:-ragsuite}:${POSTGRES_PASSWORD:-ragsuite}@postgres:5432/${POSTGRES_DB:-ragsuite_v3}
|
|
85
|
+
REDIS_HOST: redis
|
|
86
|
+
REDIS_PORT: "6379"
|
|
87
|
+
CHROMA_MODE: http
|
|
88
|
+
CHROMA_HOST: chromadb
|
|
89
|
+
CHROMA_PORT: "8000"
|
|
90
|
+
CHROMA_PERSIST_PATH: /data/chroma
|
|
91
|
+
DOCUMENT_STAGING_DIR: /data/staging
|
|
92
|
+
RUN_INLINE_WORKER: "false"
|
|
93
|
+
WEB_CONCURRENCY: "2"
|
|
94
|
+
FRONTEND_BASE_URL: http://localhost:${WEB_PORT:-9091}
|
|
95
|
+
PUBLIC_API_BASE_URL: http://localhost:${API_PORT:-9090}/api/v1
|
|
96
|
+
SSO_CALLBACK_BASE_URL: http://localhost:${API_PORT:-9090}/api/v1/auth/sso/callback
|
|
97
|
+
ports:
|
|
98
|
+
- "${API_PORT:-9090}:8000"
|
|
99
|
+
depends_on:
|
|
100
|
+
postgres:
|
|
101
|
+
condition: service_healthy
|
|
102
|
+
redis:
|
|
103
|
+
condition: service_healthy
|
|
104
|
+
chromadb:
|
|
105
|
+
condition: service_healthy
|
|
106
|
+
healthcheck:
|
|
107
|
+
test: ["CMD", "curl", "-sf", "http://localhost:8000/api/v1/health/ready"]
|
|
108
|
+
interval: 30s
|
|
109
|
+
timeout: 10s
|
|
110
|
+
retries: 5
|
|
111
|
+
start_period: 120s
|
|
112
|
+
volumes:
|
|
113
|
+
- staging_data:/data/staging
|
|
114
|
+
networks:
|
|
115
|
+
- ragsuite-server-net
|
|
116
|
+
restart: unless-stopped
|
|
117
|
+
|
|
118
|
+
worker:
|
|
119
|
+
<<: *backend_image
|
|
120
|
+
entrypoint: []
|
|
121
|
+
command: ["python", "-m", "app.worker"]
|
|
122
|
+
env_file:
|
|
123
|
+
- .env
|
|
124
|
+
environment:
|
|
125
|
+
DEBUG: "False"
|
|
126
|
+
CORS_ORIGINS: "http://localhost:${WEB_PORT:-9091}"
|
|
127
|
+
ENABLE_SCHEDULER: "true"
|
|
128
|
+
ENABLE_DURABLE_JOBS: "true"
|
|
129
|
+
DATABASE_URL: postgresql://${POSTGRES_USER:-ragsuite}:${POSTGRES_PASSWORD:-ragsuite}@postgres:5432/${POSTGRES_DB:-ragsuite_v3}
|
|
130
|
+
REDIS_HOST: redis
|
|
131
|
+
REDIS_PORT: "6379"
|
|
132
|
+
CHROMA_MODE: http
|
|
133
|
+
CHROMA_HOST: chromadb
|
|
134
|
+
CHROMA_PORT: "8000"
|
|
135
|
+
CHROMA_PERSIST_PATH: /data/chroma
|
|
136
|
+
DOCUMENT_STAGING_DIR: /data/staging
|
|
137
|
+
RUN_INLINE_WORKER: "false"
|
|
138
|
+
depends_on:
|
|
139
|
+
backend:
|
|
140
|
+
condition: service_healthy
|
|
141
|
+
volumes:
|
|
142
|
+
- staging_data:/data/staging
|
|
143
|
+
networks:
|
|
144
|
+
- ragsuite-server-net
|
|
145
|
+
restart: unless-stopped
|
|
146
|
+
|
|
147
|
+
frontend:
|
|
148
|
+
image: ${IMAGE_REGISTRY:-ghcr.io/nitsan-ai}/ragsuite-frontend:${IMAGE_TAG:-latest}
|
|
149
|
+
pull_policy: ${PULL_POLICY:-always}
|
|
150
|
+
ports:
|
|
151
|
+
- "${WEB_PORT:-9091}:80"
|
|
152
|
+
healthcheck:
|
|
153
|
+
test: ["CMD", "wget", "-q", "--spider", "http://localhost/healthz"]
|
|
154
|
+
interval: 15s
|
|
155
|
+
timeout: 5s
|
|
156
|
+
retries: 5
|
|
157
|
+
start_period: 10s
|
|
158
|
+
networks:
|
|
159
|
+
- ragsuite-server-net
|
|
160
|
+
restart: unless-stopped
|
|
161
|
+
|
|
162
|
+
volumes:
|
|
163
|
+
postgres_data:
|
|
164
|
+
chroma_data:
|
|
165
|
+
staging_data:
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Lightweight doctor for images-only installs.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
7
|
+
cd "$ROOT"
|
|
8
|
+
|
|
9
|
+
ok=0
|
|
10
|
+
fail=0
|
|
11
|
+
|
|
12
|
+
check() {
|
|
13
|
+
local label="$1"
|
|
14
|
+
shift
|
|
15
|
+
if "$@"; then
|
|
16
|
+
echo " ✓ $label"
|
|
17
|
+
ok=$((ok + 1))
|
|
18
|
+
else
|
|
19
|
+
echo " ✗ $label"
|
|
20
|
+
fail=$((fail + 1))
|
|
21
|
+
fi
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
echo "Doctor (images install):"
|
|
25
|
+
check "docker CLI" command -v docker >/dev/null
|
|
26
|
+
check "docker daemon" docker info >/dev/null 2>&1
|
|
27
|
+
check "docker compose" docker compose version >/dev/null 2>&1
|
|
28
|
+
check ".env present" test -f .env
|
|
29
|
+
check "compose file" test -f docker-compose.yml
|
|
30
|
+
|
|
31
|
+
API_PORT="${API_PORT:-9090}"
|
|
32
|
+
if curl -sf "http://127.0.0.1:${API_PORT}/api/v1/health/ping" >/dev/null 2>&1 \
|
|
33
|
+
|| curl -sf "http://127.0.0.1:${API_PORT}/api/v1/crawl/auth/public-config" >/dev/null 2>&1; then
|
|
34
|
+
echo " ✓ API reachable :${API_PORT}"
|
|
35
|
+
ok=$((ok + 1))
|
|
36
|
+
else
|
|
37
|
+
echo " ○ API not up yet on :${API_PORT} (start the stack if needed)"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
echo "Summary: ${ok} ok, ${fail} failed"
|
|
41
|
+
if [[ "$fail" -gt 0 ]]; then
|
|
42
|
+
echo "How to fix:"
|
|
43
|
+
echo " → Install Docker Desktop: https://www.docker.com/products/docker-desktop/"
|
|
44
|
+
echo " → Publish images: GitHub Actions → Publish images (GHCR)"
|
|
45
|
+
echo " → Start: ragsuite-test start --repo-root $ROOT --detach"
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
exit 0
|