Release v3.2.4 - Fix metadata mirroring authentication issues

Fixed critical authentication issue causing "user does not exist [uid: 0]" errors during metadata mirroring operations. This release addresses Issue #68 and ensures proper authentication validation before all Gitea operations.

Key improvements:
- Pre-flight authentication validation for all Gitea operations
- Consistent token decryption across all API calls
- Repository existence verification before metadata operations
- Graceful fallback to user account when org creation fails
- Enhanced error messages with specific troubleshooting guidance
- Added diagnostic test scripts for authentication validation

This patch ensures metadata mirroring (issues, PRs, labels, milestones) works reliably without authentication errors.
This commit is contained in:
Arunavo Ray
2025-08-09 12:35:34 +05:30
parent df1738a44d
commit b41438f686
9 changed files with 720 additions and 10 deletions

View File

@@ -0,0 +1,161 @@
#!/usr/bin/env bun
/**
* Test script to validate Gitea authentication and permissions
* Run with: bun run src/tests/test-gitea-auth.ts
*/
import { validateGiteaAuth, canCreateOrganizations, validateGiteaConfigForMirroring } from "@/lib/gitea-auth-validator";
import { getConfigsByUserId } from "@/lib/db/queries/configs";
import { db, users } from "@/lib/db";
import { eq } from "drizzle-orm";
async function testGiteaAuthentication() {
console.log("=".repeat(60));
console.log("GITEA AUTHENTICATION TEST");
console.log("=".repeat(60));
try {
// Get the first user for testing
const userList = await db.select().from(users).limit(1);
if (userList.length === 0) {
console.error("❌ No users found in database. Please set up a user first.");
process.exit(1);
}
const user = userList[0];
console.log(`\n✅ Found user: ${user.email} (ID: ${user.id})`);
// Get the user's configuration
const configs = await getConfigsByUserId(user.id);
if (configs.length === 0) {
console.error("❌ No configuration found for user. Please configure Gitea settings.");
process.exit(1);
}
const config = configs[0];
console.log(`✅ Found configuration (ID: ${config.id})`);
if (!config.giteaConfig?.url || !config.giteaConfig?.token) {
console.error("❌ Gitea configuration is incomplete. URL or token is missing.");
process.exit(1);
}
console.log(`\n📡 Testing connection to: ${config.giteaConfig.url}`);
console.log("-".repeat(60));
// Test 1: Validate authentication
console.log("\n🔐 Test 1: Validating authentication...");
try {
const giteaUser = await validateGiteaAuth(config);
console.log(`✅ Authentication successful!`);
console.log(` - Username: ${giteaUser.username || giteaUser.login}`);
console.log(` - User ID: ${giteaUser.id}`);
console.log(` - Is Admin: ${giteaUser.is_admin}`);
console.log(` - Email: ${giteaUser.email || 'Not provided'}`);
} catch (error) {
console.error(`❌ Authentication failed: ${error instanceof Error ? error.message : String(error)}`);
process.exit(1);
}
// Test 2: Check organization creation permissions
console.log("\n🏢 Test 2: Checking organization creation permissions...");
try {
const canCreate = await canCreateOrganizations(config);
if (canCreate) {
console.log(`✅ User can create organizations`);
} else {
console.log(`⚠️ User cannot create organizations (will use fallback to user account)`);
}
} catch (error) {
console.error(`❌ Error checking permissions: ${error instanceof Error ? error.message : String(error)}`);
}
// Test 3: Full validation for mirroring
console.log("\n🔍 Test 3: Full validation for mirroring...");
try {
const validation = await validateGiteaConfigForMirroring(config);
if (validation.valid) {
console.log(`✅ Configuration is valid for mirroring`);
} else {
console.log(`❌ Configuration is not valid for mirroring`);
}
if (validation.warnings.length > 0) {
console.log(`\n⚠ Warnings:`);
validation.warnings.forEach(warning => {
console.log(` - ${warning}`);
});
}
if (validation.errors.length > 0) {
console.log(`\n❌ Errors:`);
validation.errors.forEach(error => {
console.log(` - ${error}`);
});
}
} catch (error) {
console.error(`❌ Validation error: ${error instanceof Error ? error.message : String(error)}`);
}
// Test 4: Check specific API endpoints
console.log("\n🔧 Test 4: Testing specific API endpoints...");
// Import HTTP client for direct API testing
const { httpGet } = await import("@/lib/http-client");
const { decryptConfigTokens } = await import("@/lib/utils/config-encryption");
const decryptedConfig = decryptConfigTokens(config);
// Test organization listing
try {
const orgsResponse = await httpGet(
`${config.giteaConfig.url}/api/v1/user/orgs`,
{
Authorization: `token ${decryptedConfig.giteaConfig.token}`,
}
);
console.log(`✅ Can list organizations (found ${orgsResponse.data.length})`);
} catch (error) {
console.log(`⚠️ Cannot list organizations: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
// Test repository listing
try {
const reposResponse = await httpGet(
`${config.giteaConfig.url}/api/v1/user/repos`,
{
Authorization: `token ${decryptedConfig.giteaConfig.token}`,
}
);
console.log(`✅ Can list repositories (found ${reposResponse.data.length})`);
} catch (error) {
console.error(`❌ Cannot list repositories: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
console.log("\n" + "=".repeat(60));
console.log("TEST COMPLETE");
console.log("=".repeat(60));
// Summary
console.log("\n📊 Summary:");
console.log(` - Gitea URL: ${config.giteaConfig.url}`);
console.log(` - Default Owner: ${config.giteaConfig.defaultOwner || 'Not set'}`);
console.log(` - Mirror Strategy: ${config.githubConfig?.mirrorStrategy || 'Not set'}`);
console.log(` - Organization: ${config.giteaConfig.organization || 'Not set'}`);
console.log(` - Preserve Org Structure: ${config.giteaConfig.preserveOrgStructure || false}`);
console.log("\n✨ All tests completed successfully!");
} catch (error) {
console.error("\n❌ Test failed with error:", error);
process.exit(1);
}
process.exit(0);
}
// Run the test
testGiteaAuthentication().catch(console.error);

View File

@@ -0,0 +1,173 @@
#!/usr/bin/env bun
/**
* Test script to verify metadata mirroring authentication works correctly
* This tests the fix for issue #68 - "user does not exist [uid: 0, name: ]"
* Run with: bun run src/tests/test-metadata-mirroring.ts
*/
import { mirrorGitRepoIssuesToGitea } from "@/lib/gitea";
import { validateGiteaAuth } from "@/lib/gitea-auth-validator";
import { getConfigsByUserId } from "@/lib/db/queries/configs";
import { db, users, repositories } from "@/lib/db";
import { eq } from "drizzle-orm";
import { Octokit } from "@octokit/rest";
import type { Repository } from "@/lib/db/schema";
async function testMetadataMirroringAuth() {
console.log("=".repeat(60));
console.log("METADATA MIRRORING AUTHENTICATION TEST");
console.log("=".repeat(60));
try {
// Get the first user for testing
const userList = await db.select().from(users).limit(1);
if (userList.length === 0) {
console.error("❌ No users found in database. Please set up a user first.");
process.exit(1);
}
const user = userList[0];
console.log(`\n✅ Found user: ${user.email} (ID: ${user.id})`);
// Get the user's configuration
const configs = await getConfigsByUserId(user.id);
if (configs.length === 0) {
console.error("❌ No configuration found for user. Please configure GitHub and Gitea settings.");
process.exit(1);
}
const config = configs[0];
console.log(`✅ Found configuration (ID: ${config.id})`);
if (!config.giteaConfig?.url || !config.giteaConfig?.token) {
console.error("❌ Gitea configuration is incomplete. URL or token is missing.");
process.exit(1);
}
if (!config.githubConfig?.token) {
console.error("❌ GitHub configuration is incomplete. Token is missing.");
process.exit(1);
}
console.log(`\n📡 Testing Gitea connection to: ${config.giteaConfig.url}`);
console.log("-".repeat(60));
// Test 1: Validate Gitea authentication
console.log("\n🔐 Test 1: Validating Gitea authentication...");
let giteaUser;
try {
giteaUser = await validateGiteaAuth(config);
console.log(`✅ Gitea authentication successful!`);
console.log(` - Username: ${giteaUser.username || giteaUser.login}`);
console.log(` - User ID: ${giteaUser.id}`);
console.log(` - Is Admin: ${giteaUser.is_admin}`);
} catch (error) {
console.error(`❌ Gitea authentication failed: ${error instanceof Error ? error.message : String(error)}`);
console.error(` This is the root cause of the "user does not exist [uid: 0]" error`);
process.exit(1);
}
// Test 2: Check if we can access a test repository
console.log("\n📦 Test 2: Looking for a test repository...");
// Get a repository from the database
const repos = await db.select().from(repositories)
.where(eq(repositories.userId, user.id))
.limit(1);
if (repos.length === 0) {
console.log("⚠️ No repositories found in database. Skipping metadata mirroring test.");
console.log(" Please run a mirror operation first to test metadata mirroring.");
} else {
const testRepo = repos[0] as Repository;
console.log(`✅ Found test repository: ${testRepo.fullName}`);
// Test 3: Verify repository exists in Gitea
console.log("\n🔍 Test 3: Verifying repository exists in Gitea...");
const { isRepoPresentInGitea } = await import("@/lib/gitea");
const giteaOwner = giteaUser.username || giteaUser.login;
const repoExists = await isRepoPresentInGitea({
config,
owner: giteaOwner,
repoName: testRepo.name,
});
if (!repoExists) {
console.log(`⚠️ Repository ${testRepo.name} not found in Gitea at ${giteaOwner}`);
console.log(` This would cause metadata mirroring to fail with authentication errors`);
console.log(` Please ensure the repository is mirrored first before attempting metadata sync`);
} else {
console.log(`✅ Repository exists in Gitea at ${giteaOwner}/${testRepo.name}`);
// Test 4: Attempt to mirror metadata (dry run)
console.log("\n🔄 Test 4: Testing metadata mirroring authentication...");
try {
// Create Octokit instance
const octokit = new Octokit({
auth: config.githubConfig.token,
});
// Test by attempting to fetch labels (lightweight operation)
const { httpGet } = await import("@/lib/http-client");
const { decryptConfigTokens } = await import("@/lib/utils/config-encryption");
const decryptedConfig = decryptConfigTokens(config);
const labelsResponse = await httpGet(
`${config.giteaConfig.url}/api/v1/repos/${giteaOwner}/${testRepo.name}/labels`,
{
Authorization: `token ${decryptedConfig.giteaConfig.token}`,
}
);
console.log(`✅ Successfully authenticated for metadata operations`);
console.log(` - Can access repository labels endpoint`);
console.log(` - Found ${labelsResponse.data.length} existing labels`);
console.log(` - Authentication token is valid and has proper permissions`);
} catch (error) {
if (error instanceof Error && error.message.includes('uid: 0')) {
console.error(`❌ CRITICAL: Authentication failed with "uid: 0" error!`);
console.error(` This is the exact issue from GitHub issue #68`);
console.error(` Error: ${error.message}`);
} else {
console.error(`❌ Metadata operation failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
}
}
console.log("\n" + "=".repeat(60));
console.log("TEST COMPLETE");
console.log("=".repeat(60));
// Summary
console.log("\n📊 Summary:");
console.log(` - Gitea URL: ${config.giteaConfig.url}`);
console.log(` - Gitea User: ${giteaUser?.username || giteaUser?.login || 'Unknown'}`);
console.log(` - Authentication: ${giteaUser ? '✅ Valid' : '❌ Invalid'}`);
console.log(` - Metadata Mirroring: ${config.giteaConfig.mirrorMetadata ? 'Enabled' : 'Disabled'}`);
if (config.giteaConfig.mirrorMetadata) {
console.log(` - Issues: ${config.giteaConfig.mirrorIssues ? 'Yes' : 'No'}`);
console.log(` - Pull Requests: ${config.giteaConfig.mirrorPullRequests ? 'Yes' : 'No'}`);
console.log(` - Labels: ${config.giteaConfig.mirrorLabels ? 'Yes' : 'No'}`);
console.log(` - Milestones: ${config.giteaConfig.mirrorMilestones ? 'Yes' : 'No'}`);
}
console.log("\n✨ If all tests passed, metadata mirroring should work without uid:0 errors!");
} catch (error) {
console.error("\n❌ Test failed with error:", error);
process.exit(1);
}
process.exit(0);
}
// Run the test
testMetadataMirroringAuth().catch(console.error);