tambahakan banyak
This commit is contained in:
21
bin/g3n.ts
21
bin/g3n.ts
@@ -5,7 +5,8 @@ import path from "path";
|
|||||||
import { generateEnvTypes } from "../generate/env.generate";
|
import { generateEnvTypes } from "../generate/env.generate";
|
||||||
import checkPort from "./src/port";
|
import checkPort from "./src/port";
|
||||||
import route from "./src/route";
|
import route from "./src/route";
|
||||||
import not3 from "./src/not3";
|
import compose from "./src/compose";
|
||||||
|
import generateDockerfile from "./src/docker-file";
|
||||||
|
|
||||||
interface CheckPortResult {
|
interface CheckPortResult {
|
||||||
port: number;
|
port: number;
|
||||||
@@ -21,6 +22,7 @@ Commands:
|
|||||||
env Generate env.d.ts from .env file
|
env Generate env.d.ts from .env file
|
||||||
scan-port Scan port range (default 3000-4000)
|
scan-port Scan port range (default 3000-4000)
|
||||||
route Generate routes.ts from AppRoutes.tsx
|
route Generate routes.ts from AppRoutes.tsx
|
||||||
|
compose Generate compose.yml from name
|
||||||
Options:
|
Options:
|
||||||
--env Path ke file .env (default: .env)
|
--env Path ke file .env (default: .env)
|
||||||
--out Path file output (default: types/env.d.ts)
|
--out Path file output (default: types/env.d.ts)
|
||||||
@@ -32,7 +34,7 @@ Examples:
|
|||||||
g3n env --env .env.local --out src/types/env.d.ts
|
g3n env --env .env.local --out src/types/env.d.ts
|
||||||
g3n scan-port --start 7700 --end 7800 --host 127.0.0.1
|
g3n scan-port --start 7700 --end 7800 --host 127.0.0.1
|
||||||
g3n route
|
g3n route
|
||||||
`;
|
g3n compose <name>`;
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const cmd = args._[0];
|
const cmd = args._[0];
|
||||||
@@ -70,13 +72,22 @@ Examples:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cmd === "route") {
|
if (cmd === "route") {
|
||||||
route();
|
route();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cmd === "note") {
|
if (cmd === "compose") {
|
||||||
not3()
|
if (!args._[1]) {
|
||||||
|
console.error("❌ Name is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
compose(args._[1] as string);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd === "docker-file") {
|
||||||
|
generateDockerfile();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
275
bin/not3.ts
275
bin/not3.ts
@@ -1,275 +0,0 @@
|
|||||||
#!/usr/bin/env bun
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as path from 'path';
|
|
||||||
import * as os from 'os';
|
|
||||||
import { execSync } from 'child_process';
|
|
||||||
|
|
||||||
const CONFIG_FILE = path.join(os.homedir(), '.note.conf');
|
|
||||||
|
|
||||||
interface Config {
|
|
||||||
TOKEN?: string;
|
|
||||||
REPO?: string;
|
|
||||||
URL?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Check dependencies ---
|
|
||||||
function checkDependency(cmd: string): boolean {
|
|
||||||
try {
|
|
||||||
execSync(`command -v ${cmd}`, { stdio: 'ignore' });
|
|
||||||
return true;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkDependencies(): void {
|
|
||||||
const deps = ['curl', 'jq'];
|
|
||||||
for (const cmd of deps) {
|
|
||||||
if (!checkDependency(cmd)) {
|
|
||||||
console.error(`❌ Missing dependency: ${cmd} (please install it)`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Config management ---
|
|
||||||
function createDefaultConfig(): void {
|
|
||||||
const defaultConfig = `TOKEN=
|
|
||||||
REPO=
|
|
||||||
URL=https://cld-dkr-makuro-seafile.wibudev.com/api2
|
|
||||||
`;
|
|
||||||
fs.writeFileSync(CONFIG_FILE, defaultConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
function editConfig(): void {
|
|
||||||
if (!fs.existsSync(CONFIG_FILE)) {
|
|
||||||
createDefaultConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
const editor = process.env.EDITOR || 'vim';
|
|
||||||
try {
|
|
||||||
execSync(`${editor} "${CONFIG_FILE}"`, { stdio: 'inherit' });
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Failed to open editor');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadConfig(): Config {
|
|
||||||
if (!fs.existsSync(CONFIG_FILE)) {
|
|
||||||
console.error(`⚠️ Config file not found at ${CONFIG_FILE}`);
|
|
||||||
console.error('Run: node note.ts config to create/edit it.');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const configContent = fs.readFileSync(CONFIG_FILE, 'utf8');
|
|
||||||
const config: Config = {};
|
|
||||||
|
|
||||||
for (const line of configContent.split('\n')) {
|
|
||||||
const trimmed = line.trim();
|
|
||||||
if (trimmed && !trimmed.startsWith('#')) {
|
|
||||||
const [key, ...valueParts] = trimmed.split('=');
|
|
||||||
if (key && valueParts.length > 0) {
|
|
||||||
const value = valueParts.join('=');
|
|
||||||
config[key as keyof Config] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.TOKEN || !config.REPO) {
|
|
||||||
console.error(`❌ Config invalid. Please set TOKEN=... and REPO=... inside ${CONFIG_FILE}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set default URL if not provided
|
|
||||||
if (!config.URL) {
|
|
||||||
console.error(`❌ Config invalid. Please set URL=... inside ${CONFIG_FILE}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- HTTP helpers ---
|
|
||||||
function curlCommand(config: Config, options: string): string {
|
|
||||||
try {
|
|
||||||
return execSync(`curl -s -H "Authorization: Token ${config.TOKEN}" ${options}`,
|
|
||||||
{ encoding: 'utf8' });
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Request failed');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function curlCommandSilent(config: Config, options: string): void {
|
|
||||||
try {
|
|
||||||
execSync(`curl -s -H "Authorization: Token ${config.TOKEN}" ${options} >/dev/null`,
|
|
||||||
{ stdio: 'ignore' });
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Request failed');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Commands ---
|
|
||||||
function listFiles(config: Config): void {
|
|
||||||
const response = curlCommand(config, `"${config.URL}/${config.REPO}/dir/?p=/"`);
|
|
||||||
try {
|
|
||||||
const files = JSON.parse(response);
|
|
||||||
for (const file of files) {
|
|
||||||
console.log(file.name);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Failed to parse response');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function catFile(config: Config, fileName: string): void {
|
|
||||||
const downloadUrlResponse = curlCommand(config, `"${config.URL}/${config.REPO}/file/?p=/${fileName}"`);
|
|
||||||
const downloadUrl = downloadUrlResponse.replace(/"/g, '');
|
|
||||||
const content = curlCommand(config, `"${downloadUrl}"`);
|
|
||||||
console.log(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
function uploadFile(config: Config, localFile: string, remoteFile?: string): void {
|
|
||||||
if (!fs.existsSync(localFile)) {
|
|
||||||
console.error(`❌ File not found: ${localFile}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const remoteName = remoteFile || path.basename(localFile);
|
|
||||||
const uploadUrlResponse = curlCommand(config, `"${config.URL}/${config.REPO}/upload-link/?p=/"`);
|
|
||||||
const uploadUrl = uploadUrlResponse.replace(/"/g, '');
|
|
||||||
|
|
||||||
try {
|
|
||||||
execSync(`curl -s -H "Authorization: Token ${config.TOKEN}" \
|
|
||||||
-F "file=@${localFile}" \
|
|
||||||
-F "filename=${remoteName}" \
|
|
||||||
-F "parent_dir=/" \
|
|
||||||
"${uploadUrl}" >/dev/null`, { stdio: 'ignore' });
|
|
||||||
console.log(`✅ Uploaded ${localFile} → ${remoteName}`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Upload failed');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeFile(config: Config, fileName: string): void {
|
|
||||||
curlCommandSilent(config, `-X DELETE "${config.URL}/${config.REPO}/file/?p=/${fileName}"`);
|
|
||||||
console.log(`🗑️ Removed ${fileName}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveFile(config: Config, oldName: string, newName: string): void {
|
|
||||||
try {
|
|
||||||
execSync(`curl -s -X POST -H "Authorization: Token ${config.TOKEN}" \
|
|
||||||
-d "operation=rename" \
|
|
||||||
-d "newname=${newName}" \
|
|
||||||
"${config.URL}/${config.REPO}/file/?p=/${oldName}" >/dev/null`, { stdio: 'ignore' });
|
|
||||||
console.log(`✏️ Renamed ${oldName} → ${newName}`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Rename failed');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function downloadFile(config: Config, remoteFile: string, localFile?: string): void {
|
|
||||||
const localName = localFile || remoteFile;
|
|
||||||
const downloadUrlResponse = curlCommand(config, `"${config.URL}/${config.REPO}/file/?p=/${remoteFile}"`);
|
|
||||||
const downloadUrl = downloadUrlResponse.replace(/"/g, '');
|
|
||||||
|
|
||||||
try {
|
|
||||||
execSync(`curl -s -H "Authorization: Token ${config.TOKEN}" "${downloadUrl}" -o "${localName}"`,
|
|
||||||
{ stdio: 'ignore' });
|
|
||||||
console.log(`⬇️ Downloaded ${remoteFile} → ${localName}`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Download failed');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showHelp(): void {
|
|
||||||
console.log(`note - simple CLI for Seafile notes
|
|
||||||
Usage:
|
|
||||||
node note.ts ls List files
|
|
||||||
node note.ts cat <file> Show file content
|
|
||||||
node note.ts cp <local> [remote] Upload file
|
|
||||||
node note.ts rm <remote> Remove file
|
|
||||||
node note.ts mv <old> <new> Rename/move file
|
|
||||||
node note.ts get <remote> [local] Download file
|
|
||||||
node note.ts config Edit config (~/.note.conf)
|
|
||||||
|
|
||||||
Config (~/.note.conf):
|
|
||||||
TOKEN=your_seafile_token
|
|
||||||
REPO=repos/<repo-id>
|
|
||||||
URL=your_seafile_url/api2`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Main ---
|
|
||||||
function not3(): void {
|
|
||||||
const args = process.argv.slice(2);
|
|
||||||
const cmd = args[0] || 'help';
|
|
||||||
|
|
||||||
// Handle config command without dependency check
|
|
||||||
if (cmd === 'config') {
|
|
||||||
editConfig();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check dependencies for other commands
|
|
||||||
checkDependencies();
|
|
||||||
|
|
||||||
// Load config for other commands
|
|
||||||
const config = loadConfig();
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case 'ls':
|
|
||||||
listFiles(config);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'cat':
|
|
||||||
if (!args[1]) {
|
|
||||||
console.error('Usage: node note.ts cat <file>');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
catFile(config, args[1]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'cp':
|
|
||||||
if (!args[1]) {
|
|
||||||
console.error('Usage: node note.ts cp <local_file> [remote_file]');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
uploadFile(config, args[1], args[2]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'rm':
|
|
||||||
if (!args[1]) {
|
|
||||||
console.error('Usage: node note.ts rm <remote_file>');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
removeFile(config, args[1]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'mv':
|
|
||||||
if (!args[1] || !args[2]) {
|
|
||||||
console.error('Usage: node note.ts mv <old_name> <new_name>');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
moveFile(config, args[1], args[2]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'get':
|
|
||||||
if (!args[1]) {
|
|
||||||
console.error('Usage: node note.ts get <remote_file> [local_file]');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
downloadFile(config, args[1], args[2]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'help':
|
|
||||||
default:
|
|
||||||
showHelp();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
79
bin/src/assets/Dockerfile
Normal file
79
bin/src/assets/Dockerfile
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
FROM ubuntu:22.04 AS dev
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# --- Install runtime dependencies ---
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
curl git unzip ca-certificates openssh-server bash tini vim docker.io tmux \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# --- Install Node.js 22 ---
|
||||||
|
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
|
||||||
|
&& apt-get install -y nodejs \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install Bun
|
||||||
|
RUN curl -fsSL https://bun.sh/install | bash \
|
||||||
|
&& cp /root/.bun/bin/bun /usr/local/bin/bun \
|
||||||
|
&& cp /root/.bun/bin/bunx /usr/local/bin/bunx \
|
||||||
|
&& bun --version
|
||||||
|
|
||||||
|
# --- Create non-root user `bip` ---
|
||||||
|
ARG SSH_USER=bip
|
||||||
|
RUN useradd -ms /bin/bash $SSH_USER \
|
||||||
|
&& mkdir -p /home/$SSH_USER/.ssh \
|
||||||
|
&& chmod 700 /home/$SSH_USER/.ssh \
|
||||||
|
&& chown -R $SSH_USER:$SSH_USER /home/$SSH_USER/.ssh
|
||||||
|
|
||||||
|
# --- Configure SSH ---
|
||||||
|
RUN mkdir -p /var/run/sshd \
|
||||||
|
&& sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config \
|
||||||
|
&& sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config \
|
||||||
|
&& echo "AllowUsers $SSH_USER" >> /etc/ssh/sshd_config
|
||||||
|
|
||||||
|
# Copy deploy script (milik user bip)
|
||||||
|
COPY --chown=$SSH_USER:$SSH_USER deploy /usr/local/bin/deploy
|
||||||
|
RUN chmod +x /usr/local/bin/deploy
|
||||||
|
|
||||||
|
# Authorized keys mount point
|
||||||
|
VOLUME ["/home/$SSH_USER/.ssh"]
|
||||||
|
|
||||||
|
# Expose SSH port
|
||||||
|
EXPOSE 22
|
||||||
|
|
||||||
|
# Use Tini as entrypoint for signal handling
|
||||||
|
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||||||
|
|
||||||
|
# Start SSH daemon in foreground
|
||||||
|
CMD ["/usr/sbin/sshd", "-D"]
|
||||||
|
|
||||||
|
FROM ubuntu:22.04 AS prod
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# --- Install runtime dependencies ---
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
curl git unzip ca-certificates bash tini \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# --- Install Node.js 22 ---
|
||||||
|
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
|
||||||
|
&& apt-get install -y nodejs \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install Bun
|
||||||
|
RUN curl -fsSL https://bun.sh/install | bash \
|
||||||
|
&& cp /root/.bun/bin/bun /usr/local/bin/bun \
|
||||||
|
&& cp /root/.bun/bin/bunx /usr/local/bin/bunx \
|
||||||
|
&& bun --version
|
||||||
|
|
||||||
|
# --- Set working dir ---
|
||||||
|
WORKDIR /app/current
|
||||||
|
|
||||||
|
# Expose port (ubah sesuai app)
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# Use Tini as entrypoint for signal handling
|
||||||
|
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||||||
|
|
||||||
|
CMD ["bun", "run", "start"]
|
||||||
237
bin/src/assets/deploy
Normal file
237
bin/src/assets/deploy
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Deploy Script WhatsApp Server (dijalankan di dalam container prod)
|
||||||
|
# =====================================================
|
||||||
|
|
||||||
|
CONFIG_FILE="/app/.deploy.conf"
|
||||||
|
ENV_FILE="/app/.env"
|
||||||
|
DOCKER_HOST_PROXY=""
|
||||||
|
|
||||||
|
# --- Default config ---
|
||||||
|
GIT_URL_DEFAULT=""
|
||||||
|
BRANCH_DEFAULT=""
|
||||||
|
SERVICE_DEFAULT=""
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Utils
|
||||||
|
# =====================================================
|
||||||
|
log() {
|
||||||
|
echo "[ $(date '+%Y-%m-%d %H:%M:%S') ] $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Load config
|
||||||
|
# =====================================================
|
||||||
|
if [ ! -f "$CONFIG_FILE" ]; then
|
||||||
|
# Kalau ada env DEPLOY_SERVICE, jadikan default SERVICE
|
||||||
|
SERVICE_DEFAULT="${DEPLOY_SERVICE:-}"
|
||||||
|
cat > "$CONFIG_FILE" <<EOF
|
||||||
|
GIT_URL="$GIT_URL_DEFAULT"
|
||||||
|
BRANCH="$BRANCH_DEFAULT"
|
||||||
|
SERVICE="$SERVICE_DEFAULT"
|
||||||
|
EOF
|
||||||
|
log "⚠️ File .deploy.conf belum ada ($CONFIG_FILE), membuat dengan default"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Source config
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$CONFIG_FILE"
|
||||||
|
|
||||||
|
# Kalau SERVICE kosong, fallback ke DEPLOY_SERVICE
|
||||||
|
if [ -z "${SERVICE:-}" ] && [ -n "${DEPLOY_SERVICE:-}" ]; then
|
||||||
|
SERVICE="$DEPLOY_SERVICE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set docker proxy kalau SERVICE sudah ada
|
||||||
|
if [ -n "${SERVICE:-}" ]; then
|
||||||
|
DOCKER_HOST_PROXY="tcp://$SERVICE-docker-proxy:2375"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ensure_config() {
|
||||||
|
if [ -z "${GIT_URL:-}" ] || [ -z "${BRANCH:-}" ] || [ -z "${SERVICE:-}" ]; then
|
||||||
|
log "⚠️ Config tidak lengkap, pastikan ada di $CONFIG_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Fungsi Edit Config
|
||||||
|
# =====================================================
|
||||||
|
edit_config() {
|
||||||
|
vi "$CONFIG_FILE"
|
||||||
|
log "✅ Perubahan tersimpan di $CONFIG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Fungsi Edit .env
|
||||||
|
# =====================================================
|
||||||
|
edit_env() {
|
||||||
|
vi "$ENV_FILE"
|
||||||
|
log "✅ Perubahan tersimpan di $ENV_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Fungsi Show .env
|
||||||
|
# =====================================================
|
||||||
|
show_env() {
|
||||||
|
if [ -f "$ENV_FILE" ]; then
|
||||||
|
echo "=== 📄 Isi $ENV_FILE ==="
|
||||||
|
cat "$ENV_FILE"
|
||||||
|
echo "========================="
|
||||||
|
else
|
||||||
|
echo "⚠️ File .env belum ada ($ENV_FILE)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
show_log() {
|
||||||
|
docker -H "$DOCKER_HOST_PROXY" logs "$SERVICE-prod"
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Direktori Deploy
|
||||||
|
# =====================================================
|
||||||
|
WORKDIR="/app"
|
||||||
|
NEW_DIR="$WORKDIR/current-new"
|
||||||
|
OLD_DIR="$WORKDIR/current-old"
|
||||||
|
CURR_DIR="$WORKDIR/current"
|
||||||
|
|
||||||
|
# --- Default Flags ---
|
||||||
|
IS_DB_PUSH=false
|
||||||
|
IS_DB_SEED=false
|
||||||
|
IS_BUILD=true
|
||||||
|
IS_CACHE=true
|
||||||
|
|
||||||
|
# --- Parse args ---
|
||||||
|
COMMAND=${1:-""}
|
||||||
|
shift || true
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--db-push) IS_DB_PUSH=true ;;
|
||||||
|
--seed) IS_DB_SEED=true ;;
|
||||||
|
--build) IS_BUILD=true ;;
|
||||||
|
--no-build) IS_BUILD=false ;;
|
||||||
|
--cache) IS_CACHE=true ;;
|
||||||
|
--no-cache) IS_CACHE=false ;;
|
||||||
|
*) echo "Unknown option: $1"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Fungsi Deploy Start
|
||||||
|
# =====================================================
|
||||||
|
deploy_start() {
|
||||||
|
ensure_config
|
||||||
|
|
||||||
|
log "🚀 Mulai proses deploy..."
|
||||||
|
log "Gunakan repo: $GIT_URL (branch: $BRANCH, service: $SERVICE)"
|
||||||
|
|
||||||
|
if [ -d "$NEW_DIR" ]; then
|
||||||
|
log "hapus folder current-new ..."
|
||||||
|
rm -rf "$NEW_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "git clone ..."
|
||||||
|
git clone --depth 1 --branch "$BRANCH" "$GIT_URL" "$NEW_DIR"
|
||||||
|
|
||||||
|
if [ -f "$ENV_FILE" ]; then
|
||||||
|
log "copy .env ..."
|
||||||
|
cp "$ENV_FILE" "$NEW_DIR/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$IS_CACHE" = "true" ] && [ -d "$OLD_DIR/node_modules" ]; then
|
||||||
|
log "copy node_modules ..."
|
||||||
|
cp -r "$OLD_DIR/node_modules" "$NEW_DIR/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$IS_CACHE" = "true" ] && [ -d "$OLD_DIR/generated/prisma" ]; then
|
||||||
|
log "copy prisma ..."
|
||||||
|
mkdir -p "$NEW_DIR/generated"
|
||||||
|
cp -r "$OLD_DIR/generated/prisma" "$NEW_DIR/generated/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$NEW_DIR"
|
||||||
|
|
||||||
|
if [ "$IS_CACHE" = "true" ] && [ -f "$OLD_DIR/bun.lock" ]; then
|
||||||
|
log "using cached dependencies ..."
|
||||||
|
bun install --frozen-lockfile
|
||||||
|
else
|
||||||
|
bun install
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$IS_DB_PUSH" = "true" ]; then
|
||||||
|
log "prisma db push ..."
|
||||||
|
bun x prisma db push
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$IS_DB_SEED" = "true" ]; then
|
||||||
|
log "seeding database ..."
|
||||||
|
bun x prisma db seed
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$IS_BUILD" = "true" ]; then
|
||||||
|
log "building app ..."
|
||||||
|
bun run build
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "$OLD_DIR" ]; then
|
||||||
|
log "hapus current-old ..."
|
||||||
|
rm -rf "$OLD_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "$CURR_DIR" ]; then
|
||||||
|
log "backup current -> current-old ..."
|
||||||
|
mv "$CURR_DIR" "$OLD_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "deploy current-new -> current ..."
|
||||||
|
mv "$NEW_DIR" "$CURR_DIR"
|
||||||
|
|
||||||
|
log "restart service ..."
|
||||||
|
docker -H "$DOCKER_HOST_PROXY" restart "$SERVICE-prod"
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Fungsi Deploy Restart
|
||||||
|
# =====================================================
|
||||||
|
deploy_restart() {
|
||||||
|
ensure_config
|
||||||
|
log "restart service ..."
|
||||||
|
docker -H "$DOCKER_HOST_PROXY" restart "$SERVICE-prod"
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Main
|
||||||
|
# =====================================================
|
||||||
|
case "$COMMAND" in
|
||||||
|
start)
|
||||||
|
deploy_start
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
deploy_restart
|
||||||
|
;;
|
||||||
|
config)
|
||||||
|
edit_config
|
||||||
|
;;
|
||||||
|
env)
|
||||||
|
edit_env
|
||||||
|
;;
|
||||||
|
log)
|
||||||
|
show_log
|
||||||
|
;;
|
||||||
|
show-env)
|
||||||
|
show_env
|
||||||
|
;;
|
||||||
|
show-config)
|
||||||
|
echo "📌 Git URL : $GIT_URL"
|
||||||
|
echo "📌 Branch : $BRANCH"
|
||||||
|
echo "📌 Service : $SERVICE"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: deploy {start|restart|config|env|show-env|show-config} [--db-push] [--seed] [--build|--no-build] [--cache|--no-cache]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
134
bin/src/compose.ts
Normal file
134
bin/src/compose.ts
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import fs from "fs/promises";
|
||||||
|
|
||||||
|
|
||||||
|
const text = (name: string) => {
|
||||||
|
return `
|
||||||
|
services:
|
||||||
|
${name}-docker-proxy:
|
||||||
|
image: tecnativa/docker-socket-proxy
|
||||||
|
container_name: ${name}-docker-proxy
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
environment:
|
||||||
|
CONTAINERS: 1
|
||||||
|
POST: 1
|
||||||
|
PING: 1
|
||||||
|
networks:
|
||||||
|
- ${name}
|
||||||
|
${name}-dev:
|
||||||
|
image: bip/dev:latest
|
||||||
|
build:
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
context: .
|
||||||
|
target: dev
|
||||||
|
container_name: ${name}-dev
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./data/app:/app
|
||||||
|
- ./data/ssh/authorized_keys:/home/bip/.ssh/authorized_keys:ro
|
||||||
|
networks:
|
||||||
|
- ${name}
|
||||||
|
depends_on:
|
||||||
|
${name}-postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
${name}-prod:
|
||||||
|
build:
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
context: .
|
||||||
|
target: prod
|
||||||
|
image: bip/prod:latest
|
||||||
|
container_name: ${name}-prod
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./data/app:/app
|
||||||
|
networks:
|
||||||
|
- ${name}
|
||||||
|
depends_on:
|
||||||
|
${name}-postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
${name}-postgres:
|
||||||
|
image: postgres:16
|
||||||
|
container_name: ${name}-postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=bip
|
||||||
|
- POSTGRES_PASSWORD=Production_123
|
||||||
|
- POSTGRES_DB=${name}
|
||||||
|
volumes:
|
||||||
|
- ./data/postgres:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
- ${name}
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD-SHELL', 'pg_isready -U bip -d ${name}']
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
${name}-frpc:
|
||||||
|
image: snowdreamtech/frpc:latest
|
||||||
|
container_name: ${name}-frpc
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./data/frpc/frpc.toml:/etc/frp/frpc.toml:ro
|
||||||
|
networks:
|
||||||
|
- ${name}
|
||||||
|
networks:
|
||||||
|
${name}:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
const generate = (name: string) => {
|
||||||
|
return `
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "Generating directory..."
|
||||||
|
mkdir -p data data/app data/postgres data/frpc data/ssh
|
||||||
|
|
||||||
|
echo "Generating authorized_keys..."
|
||||||
|
touch data/ssh/authorized_keys
|
||||||
|
|
||||||
|
echo "Generating frpc.toml..."
|
||||||
|
touch data/frpc/frpc.toml
|
||||||
|
|
||||||
|
echo "Generating frpc.toml content..."
|
||||||
|
cat > data/frpc/frpc.toml <<EOF
|
||||||
|
[common]
|
||||||
|
server_addr = "85.31.224.xxx"
|
||||||
|
server_port = 7000
|
||||||
|
transport.tcp_mux = true
|
||||||
|
transport.pool_count = 5
|
||||||
|
transport.tls.enable = true
|
||||||
|
|
||||||
|
auth_token = ""
|
||||||
|
|
||||||
|
[ssh-cld-dkr-staging-${name}.wibudev.com]
|
||||||
|
type = tcp
|
||||||
|
local_ip = ${name}-dev
|
||||||
|
local_port = 22
|
||||||
|
remote_port = 5102
|
||||||
|
|
||||||
|
[postgres-cld-dkr-staging-${name}.wibudev.com]
|
||||||
|
type = tcp
|
||||||
|
local_ip = ${name}-postgres
|
||||||
|
local_port = 5432
|
||||||
|
remote_port = 5202
|
||||||
|
|
||||||
|
[cld-dkr-staging-${name}.wibudev.com]
|
||||||
|
type = http
|
||||||
|
local_ip = ${name}-prod
|
||||||
|
local_port = 3000
|
||||||
|
custom_domains = "cld-dkr-staging-${name}.wibudev.com"
|
||||||
|
EOF
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
async function compose(name: string) {
|
||||||
|
const composeFile = text(name);
|
||||||
|
await fs.writeFile(`./compose.yml`, composeFile);
|
||||||
|
Bun.spawnSync(["bash", "-c", generate(name)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose
|
||||||
|
|
||||||
17
bin/src/docker-file.ts
Normal file
17
bin/src/docker-file.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import fs from "fs/promises";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
const dockerfile = Bun.file(path.join(__dirname, "./assets/Dockerfile"));
|
||||||
|
const deployFile = Bun.file(path.join(__dirname, "./assets/deploy"));
|
||||||
|
|
||||||
|
async function generateDockerfile() {
|
||||||
|
const dockerfileText = await dockerfile.text();
|
||||||
|
const deployText = await deployFile.text();
|
||||||
|
|
||||||
|
await fs.writeFile("./Dockerfile", dockerfileText);
|
||||||
|
await fs.writeFile("./deploy", deployText);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default generateDockerfile;
|
||||||
|
|
||||||
|
|
||||||
277
bin/src/not3.ts
277
bin/src/not3.ts
@@ -1,277 +0,0 @@
|
|||||||
#!/usr/bin/env bun
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as path from 'path';
|
|
||||||
import * as os from 'os';
|
|
||||||
import { execSync } from 'child_process';
|
|
||||||
|
|
||||||
const CONFIG_FILE = path.join(os.homedir(), '.note.conf');
|
|
||||||
|
|
||||||
interface Config {
|
|
||||||
TOKEN?: string;
|
|
||||||
REPO?: string;
|
|
||||||
URL?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Check dependencies ---
|
|
||||||
function checkDependency(cmd: string): boolean {
|
|
||||||
try {
|
|
||||||
execSync(`command -v ${cmd}`, { stdio: 'ignore' });
|
|
||||||
return true;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkDependencies(): void {
|
|
||||||
const deps = ['curl', 'jq'];
|
|
||||||
for (const cmd of deps) {
|
|
||||||
if (!checkDependency(cmd)) {
|
|
||||||
console.error(`❌ Missing dependency: ${cmd} (please install it)`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Config management ---
|
|
||||||
function createDefaultConfig(): void {
|
|
||||||
const defaultConfig = `TOKEN=
|
|
||||||
REPO=
|
|
||||||
URL=https://cld-dkr-makuro-seafile.wibudev.com/api2
|
|
||||||
`;
|
|
||||||
fs.writeFileSync(CONFIG_FILE, defaultConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
function editConfig(): void {
|
|
||||||
if (!fs.existsSync(CONFIG_FILE)) {
|
|
||||||
createDefaultConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
const editor = process.env.EDITOR || 'vim';
|
|
||||||
try {
|
|
||||||
execSync(`${editor} "${CONFIG_FILE}"`, { stdio: 'inherit' });
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Failed to open editor');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadConfig(): Config {
|
|
||||||
if (!fs.existsSync(CONFIG_FILE)) {
|
|
||||||
console.error(`⚠️ Config file not found at ${CONFIG_FILE}`);
|
|
||||||
console.error('Run: node note.ts config to create/edit it.');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const configContent = fs.readFileSync(CONFIG_FILE, 'utf8');
|
|
||||||
const config: Config = {};
|
|
||||||
|
|
||||||
for (const line of configContent.split('\n')) {
|
|
||||||
const trimmed = line.trim();
|
|
||||||
if (trimmed && !trimmed.startsWith('#')) {
|
|
||||||
const [key, ...valueParts] = trimmed.split('=');
|
|
||||||
if (key && valueParts.length > 0) {
|
|
||||||
const value = valueParts.join('=');
|
|
||||||
config[key as keyof Config] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.TOKEN || !config.REPO) {
|
|
||||||
console.error(`❌ Config invalid. Please set TOKEN=... and REPO=... inside ${CONFIG_FILE}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set default URL if not provided
|
|
||||||
if (!config.URL) {
|
|
||||||
console.error(`❌ Config invalid. Please set URL=... inside ${CONFIG_FILE}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- HTTP helpers ---
|
|
||||||
function curlCommand(config: Config, options: string): string {
|
|
||||||
try {
|
|
||||||
return execSync(`curl -s -H "Authorization: Token ${config.TOKEN}" ${options}`,
|
|
||||||
{ encoding: 'utf8' });
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Request failed');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function curlCommandSilent(config: Config, options: string): void {
|
|
||||||
try {
|
|
||||||
execSync(`curl -s -H "Authorization: Token ${config.TOKEN}" ${options} >/dev/null`,
|
|
||||||
{ stdio: 'ignore' });
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Request failed');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Commands ---
|
|
||||||
function listFiles(config: Config): void {
|
|
||||||
const response = curlCommand(config, `"${config.URL}/${config.REPO}/dir/?p=/"`);
|
|
||||||
try {
|
|
||||||
const files = JSON.parse(response);
|
|
||||||
for (const file of files) {
|
|
||||||
console.log(file.name);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Failed to parse response');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function catFile(config: Config, fileName: string): void {
|
|
||||||
const downloadUrlResponse = curlCommand(config, `"${config.URL}/${config.REPO}/file/?p=/${fileName}"`);
|
|
||||||
const downloadUrl = downloadUrlResponse.replace(/"/g, '');
|
|
||||||
const content = curlCommand(config, `"${downloadUrl}"`);
|
|
||||||
console.log(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
function uploadFile(config: Config, localFile: string, remoteFile?: string): void {
|
|
||||||
if (!fs.existsSync(localFile)) {
|
|
||||||
console.error(`❌ File not found: ${localFile}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const remoteName = remoteFile || path.basename(localFile);
|
|
||||||
const uploadUrlResponse = curlCommand(config, `"${config.URL}/${config.REPO}/upload-link/?p=/"`);
|
|
||||||
const uploadUrl = uploadUrlResponse.replace(/"/g, '');
|
|
||||||
|
|
||||||
try {
|
|
||||||
execSync(`curl -s -H "Authorization: Token ${config.TOKEN}" \
|
|
||||||
-F "file=@${localFile}" \
|
|
||||||
-F "filename=${remoteName}" \
|
|
||||||
-F "parent_dir=/" \
|
|
||||||
"${uploadUrl}" >/dev/null`, { stdio: 'ignore' });
|
|
||||||
console.log(`✅ Uploaded ${localFile} → ${remoteName}`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Upload failed');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeFile(config: Config, fileName: string): void {
|
|
||||||
curlCommandSilent(config, `-X DELETE "${config.URL}/${config.REPO}/file/?p=/${fileName}"`);
|
|
||||||
console.log(`🗑️ Removed ${fileName}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveFile(config: Config, oldName: string, newName: string): void {
|
|
||||||
try {
|
|
||||||
execSync(`curl -s -X POST -H "Authorization: Token ${config.TOKEN}" \
|
|
||||||
-d "operation=rename" \
|
|
||||||
-d "newname=${newName}" \
|
|
||||||
"${config.URL}/${config.REPO}/file/?p=/${oldName}" >/dev/null`, { stdio: 'ignore' });
|
|
||||||
console.log(`✏️ Renamed ${oldName} → ${newName}`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Rename failed');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function downloadFile(config: Config, remoteFile: string, localFile?: string): void {
|
|
||||||
const localName = localFile || remoteFile;
|
|
||||||
const downloadUrlResponse = curlCommand(config, `"${config.URL}/${config.REPO}/file/?p=/${remoteFile}"`);
|
|
||||||
const downloadUrl = downloadUrlResponse.replace(/"/g, '');
|
|
||||||
|
|
||||||
try {
|
|
||||||
execSync(`curl -s -H "Authorization: Token ${config.TOKEN}" "${downloadUrl}" -o "${localName}"`,
|
|
||||||
{ stdio: 'ignore' });
|
|
||||||
console.log(`⬇️ Downloaded ${remoteFile} → ${localName}`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Download failed');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showHelp(): void {
|
|
||||||
console.log(`note - simple CLI for Seafile notes
|
|
||||||
Usage:
|
|
||||||
node note.ts ls List files
|
|
||||||
node note.ts cat <file> Show file content
|
|
||||||
node note.ts cp <local> [remote] Upload file
|
|
||||||
node note.ts rm <remote> Remove file
|
|
||||||
node note.ts mv <old> <new> Rename/move file
|
|
||||||
node note.ts get <remote> [local] Download file
|
|
||||||
node note.ts config Edit config (~/.note.conf)
|
|
||||||
|
|
||||||
Config (~/.note.conf):
|
|
||||||
TOKEN=your_seafile_token
|
|
||||||
REPO=repos/<repo-id>
|
|
||||||
URL=your_seafile_url/api2`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Main ---
|
|
||||||
function not3(): void {
|
|
||||||
const args = process.argv.slice(2);
|
|
||||||
const cmd = args[0] || 'help';
|
|
||||||
|
|
||||||
// Handle config command without dependency check
|
|
||||||
if (cmd === 'config') {
|
|
||||||
editConfig();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check dependencies for other commands
|
|
||||||
checkDependencies();
|
|
||||||
|
|
||||||
// Load config for other commands
|
|
||||||
const config = loadConfig();
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case 'ls':
|
|
||||||
listFiles(config);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'cat':
|
|
||||||
if (!args[1]) {
|
|
||||||
console.error('Usage: node note.ts cat <file>');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
catFile(config, args[1]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'cp':
|
|
||||||
if (!args[1]) {
|
|
||||||
console.error('Usage: node note.ts cp <local_file> [remote_file]');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
uploadFile(config, args[1], args[2]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'rm':
|
|
||||||
if (!args[1]) {
|
|
||||||
console.error('Usage: node note.ts rm <remote_file>');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
removeFile(config, args[1]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'mv':
|
|
||||||
if (!args[1] || !args[2]) {
|
|
||||||
console.error('Usage: node note.ts mv <old_name> <new_name>');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
moveFile(config, args[1], args[2]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'get':
|
|
||||||
if (!args[1]) {
|
|
||||||
console.error('Usage: node note.ts get <remote_file> [local_file]');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
downloadFile(config, args[1], args[2]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'help':
|
|
||||||
default:
|
|
||||||
showHelp();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default not3;
|
|
||||||
3
bun.lock
3
bun.lock
@@ -9,6 +9,7 @@
|
|||||||
"@babel/types": "^7.28.2",
|
"@babel/types": "^7.28.2",
|
||||||
"@types/babel__traverse": "^7.28.0",
|
"@types/babel__traverse": "^7.28.0",
|
||||||
"@types/minimist": "^1.2.5",
|
"@types/minimist": "^1.2.5",
|
||||||
|
"dedent": "^1.7.0",
|
||||||
"dotenv": "^17.2.1",
|
"dotenv": "^17.2.1",
|
||||||
"minimist": "^1.2.8",
|
"minimist": "^1.2.8",
|
||||||
},
|
},
|
||||||
@@ -50,6 +51,8 @@
|
|||||||
|
|
||||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||||
|
|
||||||
|
"dedent": ["dedent@1.7.0", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ=="],
|
||||||
|
|
||||||
"dotenv": ["dotenv@17.2.1", "", {}, "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ=="],
|
"dotenv": ["dotenv@17.2.1", "", {}, "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ=="],
|
||||||
|
|
||||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||||
|
|||||||
75
compose.yml
Normal file
75
compose.yml
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
services:
|
||||||
|
hipmi-docker-proxy:
|
||||||
|
image: tecnativa/docker-socket-proxy
|
||||||
|
container_name: hipmi-docker-proxy
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
environment:
|
||||||
|
CONTAINERS: 1
|
||||||
|
POST: 1
|
||||||
|
PING: 1
|
||||||
|
networks:
|
||||||
|
- hipmi
|
||||||
|
hipmi-dev:
|
||||||
|
image: bip/dev:latest
|
||||||
|
build:
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
context: .
|
||||||
|
target: dev
|
||||||
|
container_name: hipmi-dev
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./data/app:/app
|
||||||
|
- ./data/ssh/authorized_keys:/home/bip/.ssh/authorized_keys:ro
|
||||||
|
networks:
|
||||||
|
- hipmi
|
||||||
|
depends_on:
|
||||||
|
hipmi-postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
hipmi-prod:
|
||||||
|
build:
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
context: .
|
||||||
|
target: prod
|
||||||
|
image: bip/prod:latest
|
||||||
|
container_name: hipmi-prod
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./data/app:/app
|
||||||
|
networks:
|
||||||
|
- hipmi
|
||||||
|
depends_on:
|
||||||
|
hipmi-postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
hipmi-postgres:
|
||||||
|
image: postgres:16
|
||||||
|
container_name: hipmi-postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=bip
|
||||||
|
- POSTGRES_PASSWORD=Production_123
|
||||||
|
- POSTGRES_DB=hipmi
|
||||||
|
volumes:
|
||||||
|
- ./data/postgres:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
- hipmi
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD-SHELL', 'pg_isready -U bip -d hipmi']
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
hipmi-frpc:
|
||||||
|
image: snowdreamtech/frpc:latest
|
||||||
|
container_name: hipmi-frpc
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./data/frpc/frpc.toml:/etc/frp/frpc.toml:ro
|
||||||
|
networks:
|
||||||
|
- hipmi
|
||||||
|
networks:
|
||||||
|
hipmi:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
|
||||||
@@ -3,8 +3,7 @@
|
|||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"bin": {
|
"bin": {
|
||||||
"g3n": "./bin/g3n.ts",
|
"g3n": "./bin/g3n.ts"
|
||||||
"not3": "./bin/not3.ts"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
@@ -15,6 +14,7 @@
|
|||||||
"@babel/types": "^7.28.2",
|
"@babel/types": "^7.28.2",
|
||||||
"@types/babel__traverse": "^7.28.0",
|
"@types/babel__traverse": "^7.28.0",
|
||||||
"@types/minimist": "^1.2.5",
|
"@types/minimist": "^1.2.5",
|
||||||
|
"dedent": "^1.7.0",
|
||||||
"dotenv": "^17.2.1",
|
"dotenv": "^17.2.1",
|
||||||
"minimist": "^1.2.8"
|
"minimist": "^1.2.8"
|
||||||
}
|
}
|
||||||
|
|||||||
9
x.ts
9
x.ts
@@ -61,12 +61,6 @@ function loadConfig(): Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug: show loaded config (without showing actual token for security)
|
|
||||||
// console.log(`🔍 Config loaded:`);
|
|
||||||
// console.log(` TOKEN: ${config.TOKEN ? '[SET]' : '[NOT SET]'}`);
|
|
||||||
// console.log(` REPO: ${config.REPO || '[NOT SET]'}`);
|
|
||||||
// console.log(` URL: ${config.URL || '[NOT SET]'}`);
|
|
||||||
|
|
||||||
if (!config.TOKEN || !config.REPO) {
|
if (!config.TOKEN || !config.REPO) {
|
||||||
console.error(`❌ Config invalid. Please set TOKEN=... and REPO=... inside ${CONFIG_FILE}`);
|
console.error(`❌ Config invalid. Please set TOKEN=... and REPO=... inside ${CONFIG_FILE}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -87,9 +81,6 @@ async function fetchWithAuth(config: Config, url: string, options: RequestInit =
|
|||||||
...options.headers
|
...options.headers
|
||||||
};
|
};
|
||||||
|
|
||||||
// Debug: log the URL being called
|
|
||||||
// console.log(`🔍 Calling URL: ${url}`);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url, { ...options, headers });
|
const response = await fetch(url, { ...options, headers });
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
|||||||
Reference in New Issue
Block a user