Compare commits
6 commits
8ce8bf72f7
...
2ad727b4d4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ad727b4d4 | ||
|
|
7c33b70122 | ||
|
|
37047bd1af | ||
|
|
e4da2ef485 | ||
|
|
b3ec0c9695 | ||
|
|
376fb9ee00 |
7 changed files with 95 additions and 114 deletions
9
.dockerignore
Normal file
9
.dockerignore
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-server
|
||||||
|
.git
|
||||||
|
claude-data
|
||||||
|
cloudcli-data
|
||||||
|
workspace
|
||||||
|
*.log
|
||||||
|
.env*
|
||||||
30
Dockerfile
30
Dockerfile
|
|
@ -6,22 +6,30 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
jq ripgrep sqlite3 zip unzip tree vim-tiny curl git \
|
jq ripgrep sqlite3 zip unzip tree vim-tiny curl git \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Install Claude Code CLI globally
|
# Install Claude Code CLI and Taskmaster globally
|
||||||
RUN npm install -g @anthropic-ai/claude-code
|
RUN npm install -g @anthropic-ai/claude-code task-master-ai
|
||||||
|
|
||||||
# Install CloudCLI (claudecodeui) globally
|
WORKDIR /app
|
||||||
RUN npm install -g @cloudcli-ai/cloudcli
|
|
||||||
|
|
||||||
# Install Taskmaster MCP
|
# Install dependencies (separate layer for cache efficiency)
|
||||||
RUN npm install -g task-master-ai
|
COPY package*.json ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
# Create workspace and data dirs with correct ownership
|
# VITE_ vars must be present at build time (baked into frontend bundle)
|
||||||
RUN mkdir -p /home/node/workspace /home/node/.cloudcli \
|
ARG FORGEJO_BASE_URL=https://forge.wilddragon.net
|
||||||
&& chown -R node:node /home/node/
|
ENV VITE_FORGEJO_BASE_URL=${FORGEJO_BASE_URL}
|
||||||
|
|
||||||
|
# Copy source and build
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Create persistent data dirs with correct ownership
|
||||||
|
RUN mkdir -p /home/node/workspace /home/node/.cloudcli /home/node/.claude \
|
||||||
|
&& chown -R node:node /home/node/ \
|
||||||
|
&& chown -R node:node /app
|
||||||
|
|
||||||
USER node
|
USER node
|
||||||
WORKDIR /home/node
|
|
||||||
|
|
||||||
EXPOSE 3001
|
EXPOSE 3001
|
||||||
|
|
||||||
CMD ["cloudcli", "start", "--port", "3001"]
|
CMD ["node", "dist-server/server/index.js"]
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,16 @@ services:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
- FORGEJO_BASE_URL=https://forge.wilddragon.net
|
||||||
image: claudecodeui:local
|
image: claudecodeui:local
|
||||||
container_name: claudecodeui
|
container_name: claudecodeui
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "3001:3001"
|
- "3001:3001"
|
||||||
volumes:
|
volumes:
|
||||||
# Claude Code credentials & config (persistent auth)
|
|
||||||
- /mnt/NVME/Docker/Claude/claudecodeui/claude-data:/home/node/.claude
|
- /mnt/NVME/Docker/Claude/claudecodeui/claude-data:/home/node/.claude
|
||||||
# CloudCLI database (sessions, auth, history)
|
|
||||||
- /mnt/NVME/Docker/Claude/claudecodeui/cloudcli-data:/home/node/.cloudcli
|
- /mnt/NVME/Docker/Claude/claudecodeui/cloudcli-data:/home/node/.cloudcli
|
||||||
# Workspace - project files you work on via the UI
|
|
||||||
- /mnt/NVME/Docker/Claude/claudecodeui/workspace:/home/node/workspace
|
- /mnt/NVME/Docker/Claude/claudecodeui/workspace:/home/node/workspace
|
||||||
environment:
|
environment:
|
||||||
- SERVER_PORT=3001
|
- SERVER_PORT=3001
|
||||||
|
|
@ -22,6 +21,6 @@ services:
|
||||||
- VITE_CONTEXT_WINDOW=160000
|
- VITE_CONTEXT_WINDOW=160000
|
||||||
- ANTHROPIC_BASE_URL=https://ollama.wilddragon.net/v1
|
- ANTHROPIC_BASE_URL=https://ollama.wilddragon.net/v1
|
||||||
- ANTHROPIC_API_KEY=sk-6610655970c8b144-4lhp46-ac73a34f
|
- ANTHROPIC_API_KEY=sk-6610655970c8b144-4lhp46-ac73a34f
|
||||||
# node-pty needs /dev/ptmx access for terminal emulation
|
- FORGEJO_BASE_URL=https://forge.wilddragon.net
|
||||||
devices:
|
devices:
|
||||||
- /dev/ptmx:/dev/ptmx
|
- /dev/ptmx:/dev/ptmx
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { queryClaudeSDK } from '../claude-sdk.js';
|
||||||
import { spawnCursor } from '../cursor-cli.js';
|
import { spawnCursor } from '../cursor-cli.js';
|
||||||
import { queryCodex } from '../openai-codex.js';
|
import { queryCodex } from '../openai-codex.js';
|
||||||
import { spawnGemini } from '../gemini-cli.js';
|
import { spawnGemini } from '../gemini-cli.js';
|
||||||
import { Octokit } from '@octokit/rest';
|
const FORGEJO_BASE_URL = process.env.FORGEJO_BASE_URL || 'https://forge.wilddragon.net';
|
||||||
import { CLAUDE_MODELS, CURSOR_MODELS, CODEX_MODELS } from '../../shared/modelConstants.js';
|
import { CLAUDE_MODELS, CURSOR_MODELS, CODEX_MODELS } from '../../shared/modelConstants.js';
|
||||||
import { IS_PLATFORM } from '../constants/config.js';
|
import { IS_PLATFORM } from '../constants/config.js';
|
||||||
import { normalizeProjectPath } from '../shared/utils.js';
|
import { normalizeProjectPath } from '../shared/utils.js';
|
||||||
|
|
@ -99,36 +99,28 @@ async function getGitRemoteUrl(repoPath) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize GitHub URLs for comparison
|
* Normalize git URLs for comparison
|
||||||
* @param {string} url - GitHub URL
|
* @param {string} url - Git URL (any host)
|
||||||
* @returns {string} - Normalized URL
|
* @returns {string} - Normalized URL
|
||||||
*/
|
*/
|
||||||
function normalizeGitHubUrl(url) {
|
function normalizeGitUrl(url) {
|
||||||
// Remove .git suffix
|
|
||||||
let normalized = url.replace(/\.git$/, '');
|
let normalized = url.replace(/\.git$/, '');
|
||||||
// Convert SSH to HTTPS format for comparison
|
normalized = normalized.replace(/^git@([^:]+):/, 'https://$1/');
|
||||||
normalized = normalized.replace(/^git@github\.com:/, 'https://github.com/');
|
|
||||||
// Remove trailing slash
|
|
||||||
normalized = normalized.replace(/\/$/, '');
|
normalized = normalized.replace(/\/$/, '');
|
||||||
return normalized.toLowerCase();
|
return normalized.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse GitHub URL to extract owner and repo
|
* Parse git URL to extract owner and repo (works with any host)
|
||||||
* @param {string} url - GitHub URL (HTTPS or SSH)
|
* @param {string} url - Git URL (HTTPS or SSH, any host)
|
||||||
* @returns {{owner: string, repo: string}} - Parsed owner and repo
|
* @returns {{owner: string, repo: string}} - Parsed owner and repo
|
||||||
*/
|
*/
|
||||||
function parseGitHubUrl(url) {
|
function parseGitUrl(url) {
|
||||||
// Handle HTTPS URLs: https://github.com/owner/repo or https://github.com/owner/repo.git
|
const httpsMatch = url.match(/https?:\/\/[^/]+\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/.*)?$/);
|
||||||
// Handle SSH URLs: git@github.com:owner/repo or git@github.com:owner/repo.git
|
const sshMatch = url.match(/git@[^:]+:([^/]+)\/([^/]+?)(?:\.git)?$/);
|
||||||
const match = url.match(/github\.com[:/]([^/]+)\/([^/]+?)(?:\.git)?$/);
|
const match = httpsMatch || sshMatch;
|
||||||
if (!match) {
|
if (!match) { throw new Error('Invalid git URL format'); }
|
||||||
throw new Error('Invalid GitHub URL format');
|
return { owner: match[1], repo: match[2] };
|
||||||
}
|
|
||||||
return {
|
|
||||||
owner: match[1],
|
|
||||||
repo: match[2].replace(/\.git$/, '')
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -257,47 +249,11 @@ async function getCommitMessages(projectPath, limit = 5) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new branch on GitHub using the API
|
|
||||||
* @param {Octokit} octokit - Octokit instance
|
|
||||||
* @param {string} owner - Repository owner
|
|
||||||
* @param {string} repo - Repository name
|
|
||||||
* @param {string} branchName - Name of the new branch
|
|
||||||
* @param {string} baseBranch - Base branch to branch from (default: 'main')
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
async function createGitHubBranch(octokit, owner, repo, branchName, baseBranch = 'main') {
|
|
||||||
try {
|
|
||||||
// Get the SHA of the base branch
|
|
||||||
const { data: ref } = await octokit.git.getRef({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
ref: `heads/${baseBranch}`
|
|
||||||
});
|
|
||||||
|
|
||||||
const baseSha = ref.object.sha;
|
|
||||||
|
|
||||||
// Create the new branch
|
|
||||||
await octokit.git.createRef({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
ref: `refs/heads/${branchName}`,
|
|
||||||
sha: baseSha
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`✅ Created branch '${branchName}' on GitHub`);
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 422 && error.message.includes('Reference already exists')) {
|
|
||||||
console.log(`ℹ️ Branch '${branchName}' already exists on GitHub`);
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a pull request on GitHub
|
* Create a pull request on a Forgejo instance
|
||||||
* @param {Octokit} octokit - Octokit instance
|
* @param {string} baseUrl - Forgejo base URL
|
||||||
|
* @param {string} token - Forgejo access token
|
||||||
* @param {string} owner - Repository owner
|
* @param {string} owner - Repository owner
|
||||||
* @param {string} repo - Repository name
|
* @param {string} repo - Repository name
|
||||||
* @param {string} branchName - Head branch name
|
* @param {string} branchName - Head branch name
|
||||||
|
|
@ -306,22 +262,24 @@ async function createGitHubBranch(octokit, owner, repo, branchName, baseBranch =
|
||||||
* @param {string} baseBranch - Base branch (default: 'main')
|
* @param {string} baseBranch - Base branch (default: 'main')
|
||||||
* @returns {Promise<{number: number, url: string}>} - PR number and URL
|
* @returns {Promise<{number: number, url: string}>} - PR number and URL
|
||||||
*/
|
*/
|
||||||
async function createGitHubPR(octokit, owner, repo, branchName, title, body, baseBranch = 'main') {
|
async function createForgejoPR(baseUrl, token, owner, repo, branchName, title, body, baseBranch = 'main') {
|
||||||
const { data: pr } = await octokit.pulls.create({
|
const response = await fetch(`${baseUrl}/api/v1/repos/${owner}/${repo}/pulls`, {
|
||||||
owner,
|
method: 'POST',
|
||||||
repo,
|
headers: {
|
||||||
title,
|
'Authorization': `token ${token}`,
|
||||||
head: branchName,
|
'Content-Type': 'application/json',
|
||||||
base: baseBranch,
|
},
|
||||||
body
|
body: JSON.stringify({ title, body, head: branchName, base: baseBranch }),
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`✅ Created pull request #${pr.number}: ${pr.html_url}`);
|
if (!response.ok) {
|
||||||
|
const err = await response.json().catch(() => ({ message: response.statusText }));
|
||||||
|
throw new Error(`Failed to create PR: ${err.message || response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
const pr = await response.json();
|
||||||
number: pr.number,
|
console.log(`Created pull request #${pr.number}: ${pr.html_url}`);
|
||||||
url: pr.html_url
|
return { number: pr.number, url: pr.html_url };
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -331,12 +289,12 @@ async function createGitHubPR(octokit, owner, repo, branchName, title, body, bas
|
||||||
* @param {string} projectPath - Path for cloning the repository
|
* @param {string} projectPath - Path for cloning the repository
|
||||||
* @returns {Promise<string>} - Path to the cloned repository
|
* @returns {Promise<string>} - Path to the cloned repository
|
||||||
*/
|
*/
|
||||||
async function cloneGitHubRepo(githubUrl, githubToken = null, projectPath) {
|
async function cloneGitRepo(repoUrl, gitToken = null, projectPath) {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
// Validate GitHub URL
|
// Validate GitHub URL
|
||||||
if (!githubUrl || !githubUrl.includes('github.com')) {
|
if (!repoUrl || !repoUrl.trim()) {
|
||||||
throw new Error('Invalid GitHub URL');
|
throw new Error('Repository URL is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
const cloneDir = path.resolve(projectPath);
|
const cloneDir = path.resolve(projectPath);
|
||||||
|
|
@ -347,14 +305,14 @@ async function cloneGitHubRepo(githubUrl, githubToken = null, projectPath) {
|
||||||
// Directory exists - check if it's a git repo with the same URL
|
// Directory exists - check if it's a git repo with the same URL
|
||||||
try {
|
try {
|
||||||
const existingUrl = await getGitRemoteUrl(cloneDir);
|
const existingUrl = await getGitRemoteUrl(cloneDir);
|
||||||
const normalizedExisting = normalizeGitHubUrl(existingUrl);
|
const normalizedExisting = normalizeGitUrl(existingUrl);
|
||||||
const normalizedRequested = normalizeGitHubUrl(githubUrl);
|
const normalizedRequested = normalizeGitUrl(repoUrl);
|
||||||
|
|
||||||
if (normalizedExisting === normalizedRequested) {
|
if (normalizedExisting === normalizedRequested) {
|
||||||
console.log('✅ Repository already exists at path with correct URL');
|
console.log('✅ Repository already exists at path with correct URL');
|
||||||
return resolve(cloneDir);
|
return resolve(cloneDir);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Directory ${cloneDir} already exists with a different repository (${existingUrl}). Expected: ${githubUrl}`);
|
throw new Error(`Directory ${cloneDir} already exists with a different repository (${existingUrl}). Expected: ${repoUrl}`);
|
||||||
}
|
}
|
||||||
} catch (gitError) {
|
} catch (gitError) {
|
||||||
throw new Error(`Directory ${cloneDir} already exists but is not a valid git repository or git command failed`);
|
throw new Error(`Directory ${cloneDir} already exists but is not a valid git repository or git command failed`);
|
||||||
|
|
@ -367,11 +325,16 @@ async function cloneGitHubRepo(githubUrl, githubToken = null, projectPath) {
|
||||||
await fs.mkdir(path.dirname(cloneDir), { recursive: true });
|
await fs.mkdir(path.dirname(cloneDir), { recursive: true });
|
||||||
|
|
||||||
// Prepare the git clone URL with authentication if token is provided
|
// Prepare the git clone URL with authentication if token is provided
|
||||||
let cloneUrl = githubUrl;
|
let cloneUrl = repoUrl;
|
||||||
if (githubToken) {
|
if (gitToken) {
|
||||||
// Convert HTTPS URL to authenticated URL
|
try {
|
||||||
// Example: https://github.com/user/repo -> https://token@github.com/user/repo
|
const url = new URL(repoUrl);
|
||||||
cloneUrl = githubUrl.replace('https://github.com', `https://${githubToken}@github.com`);
|
url.username = gitToken;
|
||||||
|
url.password = '';
|
||||||
|
cloneUrl = url.toString();
|
||||||
|
} catch {
|
||||||
|
// SSH URLs used as-is
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('🔄 Cloning repository:', githubUrl);
|
console.log('🔄 Cloning repository:', githubUrl);
|
||||||
|
|
@ -887,7 +850,7 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
||||||
targetPath = path.join(os.homedir(), '.claude', 'external-projects', repoHash);
|
targetPath = path.join(os.homedir(), '.claude', 'external-projects', repoHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
finalProjectPath = await cloneGitHubRepo(githubUrl.trim(), tokenToUse, targetPath);
|
finalProjectPath = await cloneGitRepo(githubUrl.trim(), tokenToUse, targetPath);
|
||||||
} else {
|
} else {
|
||||||
// Use existing project path
|
// Use existing project path
|
||||||
finalProjectPath = normalizeProjectPath(path.resolve(projectPath));
|
finalProjectPath = normalizeProjectPath(path.resolve(projectPath));
|
||||||
|
|
@ -997,17 +960,14 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
||||||
throw new Error('GitHub token required for branch/PR creation. Please configure a GitHub token in settings.');
|
throw new Error('GitHub token required for branch/PR creation. Please configure a GitHub token in settings.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize Octokit
|
|
||||||
const octokit = new Octokit({ auth: tokenToUse });
|
|
||||||
|
|
||||||
// Get GitHub URL - either from parameter or from git remote
|
// Get GitHub URL - either from parameter or from git remote
|
||||||
let repoUrl = githubUrl;
|
let repoUrl = githubUrl;
|
||||||
if (!repoUrl) {
|
if (!repoUrl) {
|
||||||
console.log('🔍 Getting GitHub URL from git remote...');
|
console.log('🔍 Getting GitHub URL from git remote...');
|
||||||
try {
|
try {
|
||||||
repoUrl = await getGitRemoteUrl(finalProjectPath);
|
repoUrl = await getGitRemoteUrl(finalProjectPath);
|
||||||
if (!repoUrl.includes('github.com')) {
|
if (!repoUrl.trim()) {
|
||||||
throw new Error('Project does not have a GitHub remote configured');
|
throw new Error('Project does not have a git remote configured');
|
||||||
}
|
}
|
||||||
console.log(`✅ Found GitHub remote: ${repoUrl}`);
|
console.log(`✅ Found GitHub remote: ${repoUrl}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -1016,7 +976,7 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse GitHub URL to get owner and repo
|
// Parse GitHub URL to get owner and repo
|
||||||
const { owner, repo } = parseGitHubUrl(repoUrl);
|
const { owner, repo } = parseGitUrl(repoUrl);
|
||||||
console.log(`📦 Repository: ${owner}/${repo}`);
|
console.log(`📦 Repository: ${owner}/${repo}`);
|
||||||
|
|
||||||
// Use provided branch name or auto-generate from message
|
// Use provided branch name or auto-generate from message
|
||||||
|
|
@ -1101,7 +1061,7 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
||||||
|
|
||||||
branchInfo = {
|
branchInfo = {
|
||||||
name: finalBranchName,
|
name: finalBranchName,
|
||||||
url: `https://github.com/${owner}/${repo}/tree/${finalBranchName}`
|
url: `${FORGEJO_BASE_URL}/${owner}/${repo}/src/branch/${finalBranchName}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1126,7 +1086,7 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
||||||
|
|
||||||
// Create the pull request
|
// Create the pull request
|
||||||
console.log('🔄 Creating pull request...');
|
console.log('🔄 Creating pull request...');
|
||||||
prInfo = await createGitHubPR(octokit, owner, repo, finalBranchName, prTitle, prBody, 'main');
|
prInfo = await createForgejoPR(FORGEJO_BASE_URL, tokenToUse, owner, repo, finalBranchName, prTitle, prBody, 'main');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send branch/PR info in response
|
// Send branch/PR info in response
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ export default function GithubCredentialsSection({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/settings/tokens"
|
href={`${import.meta.env.VITE_FORGEJO_BASE_URL || 'https://forge.wilddragon.net'}/user/settings/applications`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="block text-xs text-primary hover:underline"
|
className="block text-xs text-primary hover:underline"
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,11 @@ export const useGitHubStars = (owner: string, repo: string) => {
|
||||||
|
|
||||||
const fetchStars = async () => {
|
const fetchStars = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}`);
|
const baseUrl = import.meta.env.VITE_FORGEJO_BASE_URL || 'https://forge.wilddragon.net';
|
||||||
|
const response = await fetch(`${baseUrl}/api/v1/repos/${owner}/${repo}`);
|
||||||
if (!response.ok) return;
|
if (!response.ok) return;
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const count = data.stargazers_count;
|
const count = data.stars_count;
|
||||||
if (typeof count === 'number') {
|
if (typeof count === 'number') {
|
||||||
setStarCount(count);
|
setStarCount(count);
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,12 @@ export const useVersionCheck = (owner: string, repo: string) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const checkVersion = async () => {
|
const checkVersion = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/releases/latest`);
|
const baseUrl = import.meta.env.VITE_FORGEJO_BASE_URL || 'https://forge.wilddragon.net';
|
||||||
const data = await response.json();
|
const response = await fetch(`${baseUrl}/api/v1/repos/${owner}/${repo}/releases?limit=1&page=1`);
|
||||||
|
if (!response.ok) return;
|
||||||
|
const releases = await response.json();
|
||||||
|
const data = releases[0];
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
// Handle the case where there might not be any releases
|
// Handle the case where there might not be any releases
|
||||||
if (data.tag_name) {
|
if (data.tag_name) {
|
||||||
|
|
@ -61,7 +65,7 @@ export const useVersionCheck = (owner: string, repo: string) => {
|
||||||
setReleaseInfo({
|
setReleaseInfo({
|
||||||
title: data.name || data.tag_name,
|
title: data.name || data.tag_name,
|
||||||
body: data.body || '',
|
body: data.body || '',
|
||||||
htmlUrl: data.html_url || `https://github.com/${owner}/${repo}/releases/latest`,
|
htmlUrl: data.html_url || `${import.meta.env.VITE_FORGEJO_BASE_URL || 'https://forge.wilddragon.net'}/${owner}/${repo}/releases`,
|
||||||
publishedAt: data.published_at
|
publishedAt: data.published_at
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue