fix(backend): fix production crash with vite, make asaas async and add prisma db push retries
Build and Deploy (Gitea) / build-and-deploy (push) Failing after 44s
Details
Build and Deploy (Gitea) / build-and-deploy (push) Failing after 44s
Details
This commit is contained in:
parent
159ae1e8db
commit
fa180dee0c
|
|
@ -24,6 +24,7 @@
|
|||
"jsonwebtoken": "^9.0.3",
|
||||
"lucide-react": "^0.546.0",
|
||||
"motion": "^12.23.24",
|
||||
"prisma": "^6.4.1",
|
||||
"react": "^19.0.1",
|
||||
"react-dom": "^19.0.1",
|
||||
"recharts": "^2.15.0",
|
||||
|
|
@ -37,10 +38,8 @@
|
|||
"@types/node": "^22.14.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"esbuild": "^0.25.0",
|
||||
"prisma": "^6.4.1",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "~5.8.2",
|
||||
"vite": "^6.2.3"
|
||||
"typescript": "~5.8.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
26
server.ts
26
server.ts
|
|
@ -6,7 +6,6 @@ import multer from 'multer';
|
|||
import axios from 'axios';
|
||||
import { execSync } from 'child_process';
|
||||
import { S3Client, PutObjectCommand, CreateBucketCommand, HeadBucketCommand, PutBucketPolicyCommand } from '@aws-sdk/client-s3';
|
||||
import { createServer as createViteServer } from 'vite';
|
||||
import { prisma } from './src/db/prisma';
|
||||
import { AsaasService } from './src/services/asaas';
|
||||
import { User, Course, Module, Lesson, Note, LessonProgress, WebhookLog, SubscriptionPayment, SubscriptionStatus } from './src/types';
|
||||
|
|
@ -461,7 +460,7 @@ app.post('/api/courses/:id/unlock', authenticateToken, checkoutRateLimiter, asyn
|
|||
|
||||
try {
|
||||
let customerId = user.asaasCustomerId;
|
||||
const apiKey = AsaasService.getApiKey();
|
||||
const apiKey = await AsaasService.getApiKey();
|
||||
if (apiKey && (customerId?.startsWith('cus_sim_') || customerId?.startsWith('cus_fallback_') || ['cus_test', 'cus_00001', 'cus_00002', 'cus_00003', 'cus_00004'].includes(customerId || ''))) {
|
||||
customerId = null;
|
||||
}
|
||||
|
|
@ -659,7 +658,7 @@ app.post('/api/subscription/subscribe', authenticateToken, async (req: Authentic
|
|||
|
||||
try {
|
||||
let customerId = user.asaasCustomerId;
|
||||
const apiKey = AsaasService.getApiKey();
|
||||
const apiKey = await AsaasService.getApiKey();
|
||||
if (apiKey && (customerId?.startsWith('cus_sim_') || customerId?.startsWith('cus_fallback_') || ['cus_test', 'cus_00001', 'cus_00002', 'cus_00003', 'cus_00004'].includes(customerId || ''))) {
|
||||
customerId = null;
|
||||
}
|
||||
|
|
@ -1827,8 +1826,24 @@ app.put('/api/admin/courses/:id/price', authenticateToken, requireAdmin, async (
|
|||
async function startServer() {
|
||||
try {
|
||||
console.log('🔄 Sincronizando banco de dados com Prisma...');
|
||||
execSync('npx prisma db push --accept-data-loss', { stdio: 'inherit' });
|
||||
console.log('✅ Banco de dados sincronizado!');
|
||||
let retries = 5;
|
||||
while (retries > 0) {
|
||||
try {
|
||||
execSync('npx prisma db push --accept-data-loss', { stdio: 'inherit' });
|
||||
console.log('✅ Banco de dados sincronizado!');
|
||||
break;
|
||||
} catch (err) {
|
||||
console.error(`⚠️ Erro ao sincronizar banco (tentativas restantes: ${retries - 1})... Aguardando Postgres...`);
|
||||
retries -= 1;
|
||||
if (retries === 0) {
|
||||
console.error('❌ Falha definitiva ao inicializar banco de dados:', err);
|
||||
// Don't throw, let it attempt anyway (might just be a timeout issue, though count() will probably fail)
|
||||
} else {
|
||||
// Wait 3 seconds synchronously using a busy wait or just use a helper
|
||||
await new Promise(res => setTimeout(res, 3000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Seed test users if DB is empty
|
||||
const adminCount = await prisma.user.count({ where: { email: 'sidney.gomes1989@gmail.com' } });
|
||||
|
|
@ -1891,6 +1906,7 @@ async function startServer() {
|
|||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const { createServer: createViteServer } = await import('vite');
|
||||
const vite = await createViteServer({
|
||||
server: { middlewareMode: true },
|
||||
appType: 'spa',
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
import { SubscriptionPayment } from '../types';
|
||||
import { loadDb } from '../db/db';
|
||||
import { prisma } from '../db/prisma';
|
||||
|
||||
export class AsaasService {
|
||||
public static getApiKey(): string | null {
|
||||
const db = loadDb();
|
||||
return db.settings?.asaasApiKey || process.env.ASAAS_API_KEY || null;
|
||||
public static async getApiKey(): Promise<string | null> {
|
||||
const db = await prisma.systemSettings.findUnique({ where: { id: 'default' } });
|
||||
return db?.asaasApiKey || process.env.ASAAS_API_KEY || null;
|
||||
}
|
||||
|
||||
private static getApiUrl(): string {
|
||||
const db = loadDb();
|
||||
const env = db.settings?.asaasEnvironment || 'sandbox';
|
||||
private static async getApiUrl(): Promise<string> {
|
||||
const db = await prisma.systemSettings.findUnique({ where: { id: 'default' } });
|
||||
const env = db?.asaasEnvironment || 'sandbox';
|
||||
return env === 'production' ? 'https://api.asaas.com/v3' : 'https://sandbox.asaas.com/api/v3';
|
||||
}
|
||||
|
||||
private static getHeaders() {
|
||||
const key = this.getApiKey();
|
||||
private static async getHeaders(): Promise<Record<string, string>> {
|
||||
const key = await this.getApiKey();
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
'access_token': key || ''
|
||||
|
|
@ -25,8 +25,9 @@ export class AsaasService {
|
|||
* Test the connection to Asaas API
|
||||
*/
|
||||
public static async testConnection(customApiKey?: string, customEnv?: string): Promise<{ success: boolean; message: string; environment: string }> {
|
||||
const apiKey = customApiKey || this.getApiKey();
|
||||
const env = customEnv || loadDb().settings?.asaasEnvironment || 'sandbox';
|
||||
const apiKey = customApiKey || await this.getApiKey();
|
||||
const db = await prisma.systemSettings.findUnique({ where: { id: 'default' } });
|
||||
const env = customEnv || db?.asaasEnvironment || 'sandbox';
|
||||
const apiUrl = env === 'production' ? 'https://api.asaas.com/v3' : 'https://sandbox.asaas.com/v3';
|
||||
|
||||
if (!apiKey || apiKey.trim() === '') {
|
||||
|
|
@ -74,7 +75,7 @@ export class AsaasService {
|
|||
* Create a customer in Asaas
|
||||
*/
|
||||
public static async createCustomer(name: string, email: string, cpfCnpj?: string): Promise<string> {
|
||||
const apiKey = this.getApiKey();
|
||||
const apiKey = await this.getApiKey();
|
||||
if (!apiKey) {
|
||||
console.warn('ASAAS_API_KEY not configured. Simulating customer creation.');
|
||||
return `cus_sim_${Math.random().toString(36).substring(2, 9)}`;
|
||||
|
|
@ -91,9 +92,9 @@ export class AsaasService {
|
|||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.getApiUrl()}/customers`, {
|
||||
const response = await fetch(`${await this.getApiUrl()}/customers`, {
|
||||
method: 'POST',
|
||||
headers: this.getHeaders(),
|
||||
headers: await this.getHeaders(),
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
|
|
@ -122,7 +123,7 @@ export class AsaasService {
|
|||
cycle: 'MONTHLY' | 'YEARLY' = 'MONTHLY',
|
||||
description: string = 'Assinatura Mensal DevFlix'
|
||||
): Promise<{ subscriptionId: string; payment: Partial<SubscriptionPayment> }> {
|
||||
const apiKey = this.getApiKey();
|
||||
const apiKey = await this.getApiKey();
|
||||
if (!apiKey) {
|
||||
throw new Error('A Chave de API do Asaas (API Key) não foi configurada. Acesse o Painel Admin -> Configurações Globais, cole a sua chave do Asaas Sandbox e clique em Salvar Configurações.');
|
||||
}
|
||||
|
|
@ -142,9 +143,9 @@ export class AsaasService {
|
|||
payload.creditCardHolderInfo = creditCardInfo.creditCardHolderInfo;
|
||||
}
|
||||
|
||||
const response = await fetch(`${this.getApiUrl()}/subscriptions`, {
|
||||
const response = await fetch(`${await this.getApiUrl()}/subscriptions`, {
|
||||
method: 'POST',
|
||||
headers: this.getHeaders(),
|
||||
headers: await this.getHeaders(),
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
|
|
@ -192,7 +193,7 @@ export class AsaasService {
|
|||
* Fetch PIX QR Code for a given payment ID
|
||||
*/
|
||||
public static async getPixQrCode(paymentId: string): Promise<{ encodedImage: string, payload: string } | null> {
|
||||
const apiKey = this.getApiKey();
|
||||
const apiKey = await this.getApiKey();
|
||||
const fallbackPayload = `00020126580014br.gov.bcb.pix0136${paymentId}_microtecflix520400005303986540549.905802BR5915MicrotecFlix6009Sao Paulo62070503***6304`;
|
||||
|
||||
if (!apiKey || paymentId.startsWith('pay_sim_') || paymentId.startsWith('pay_fb_')) {
|
||||
|
|
@ -203,9 +204,9 @@ export class AsaasService {
|
|||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.getApiUrl()}/payments/${paymentId}/pixQrCode`, {
|
||||
const response = await fetch(`${await this.getApiUrl()}/payments/${paymentId}/pixQrCode`, {
|
||||
method: 'GET',
|
||||
headers: this.getHeaders()
|
||||
headers: await this.getHeaders()
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
|
|
@ -237,7 +238,7 @@ export class AsaasService {
|
|||
creditCard: any,
|
||||
creditCardHolderInfo: any
|
||||
): Promise<{ creditCardToken: string; creditCardNumber: string; creditCardBrand: string }> {
|
||||
const apiKey = this.getApiKey();
|
||||
const apiKey = await this.getApiKey();
|
||||
if (!apiKey) {
|
||||
return {
|
||||
creditCardToken: `tok_sim_${Math.random().toString(36).substring(2, 9)}`,
|
||||
|
|
@ -247,9 +248,9 @@ export class AsaasService {
|
|||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.getApiUrl()}/creditCard/tokenize`, {
|
||||
const response = await fetch(`${await this.getApiUrl()}/creditCard/tokenize`, {
|
||||
method: 'POST',
|
||||
headers: this.getHeaders(),
|
||||
headers: await this.getHeaders(),
|
||||
body: JSON.stringify({
|
||||
customer: customerId,
|
||||
creditCard,
|
||||
|
|
@ -280,7 +281,7 @@ export class AsaasService {
|
|||
creditCardInfo?: any,
|
||||
installmentCount?: number
|
||||
): Promise<any> {
|
||||
const apiKey = this.getApiKey();
|
||||
const apiKey = await this.getApiKey();
|
||||
const futureDueDate = new Date(Date.now() + 3 * 24 * 3600 * 1000).toISOString().split('T')[0];
|
||||
|
||||
if (!apiKey) {
|
||||
|
|
@ -310,9 +311,9 @@ export class AsaasService {
|
|||
}
|
||||
}
|
||||
|
||||
const response = await fetch(`${this.getApiUrl()}/payments`, {
|
||||
const response = await fetch(`${await this.getApiUrl()}/payments`, {
|
||||
method: 'POST',
|
||||
headers: this.getHeaders(),
|
||||
headers: await this.getHeaders(),
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue