Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,6 @@ STATSIG_API_KEY=

# Other environment variables
NODE_ENV=development
PORT=8080
CONTRIBUTOR_APP_URL="http://localhost:4000"
PORT=5000
CORS_ORIGINS=http://localhost:3000,http://localhost:4000,http://localhost:3001
CONTRIBUTOR_APP_URL=http://localhost:4000
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,5 @@ This project is licensed under the Apache 2.0 License. See [LICENSE](https://git

## Related Projects

- [DevAsign Project Maintainer App](https://github.com/devasignhq/app.devasign.com) — Frontend for project maintainers
- [DevAsign Contributor App](https://github.com/devasignhq/contributor.devasign.com) — Frontend for contributors
- [DevAsign App](https://github.com/devasignhq/apps) — Frontend for project maintainers and contributors
- [Soroban Task Escrow Contract](https://github.com/devasignhq/soroban-contract) — Task escrow smart contract on Stellar
7 changes: 4 additions & 3 deletions api/config/firebase.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import admin, { ServiceAccount } from "firebase-admin";
import { Env } from "../utils/env.js";

const serviceAccount: ServiceAccount = {
projectId: process.env.FIREBASE_PROJECT_ID,
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
privateKey: process.env.FIREBASE_PRIVATE_KEY?.toString().replace(/\\n/g, "\n")
projectId: (Env.firebaseProjectId() || ""),
clientEmail: (Env.firebaseClientEmail() || ""),
privateKey: (Env.firebasePrivateKey() || "")?.replace(/\\n/g, "\n")
};

admin.initializeApp({
Expand Down
5 changes: 3 additions & 2 deletions api/config/logger.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import winston from "winston";
import { Env } from "../utils/env.js";

const LOG_LEVEL = process.env.LOG_LEVEL || "info";
const NODE_ENV = process.env.NODE_ENV || "development";
const LOG_LEVEL = Env.logLevel() || "info";
const NODE_ENV = Env.nodeEnv() || "development";

// Define log levels and colors
const logLevels = {
Expand Down
49 changes: 18 additions & 31 deletions api/config/stellar.config.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,24 @@
import swSdk from "@stellar/typescript-wallet-sdk";
const {
ApplicationConfiguration,
DefaultSigner,
Wallet,
StellarConfiguration,
IssuedAssetId,
NativeAssetId
} = swSdk;
import axios from "axios";
import { Horizon, Asset, Networks } from "@stellar/stellar-sdk";
import { Env } from "../utils/env.js";

const customClient = axios.create({
timeout: 20000
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const appConfig = new ApplicationConfiguration(DefaultSigner, customClient as any);
export const isMainnet = (Env.stellarNetwork() || "") === "public";

export const wallet = new Wallet({
stellarConfiguration: process.env.STELLAR_NETWORK === "public"
? StellarConfiguration.MainNet()
: StellarConfiguration.TestNet(),
applicationConfiguration: appConfig
});
export const horizonUrl = isMainnet
? "https://horizon.stellar.org"
: "https://horizon-testnet.stellar.org";

export const stellar = wallet.stellar();
export const account = stellar.account();
export const anchor = wallet.anchor({
homeDomain: process.env.STELLAR_NETWORK === "public"
? "anchor.stellar.org"
: "testanchor.stellar.org"
});
export const networkPassphrase = isMainnet
? Networks.PUBLIC
: Networks.TESTNET;

export const xlmAssetId = new NativeAssetId();
export const usdcAssetId = new IssuedAssetId(
export const stellarServer = new Horizon.Server(horizonUrl);

export const anchorHomeDomain = isMainnet
? "anchor.stellar.org"
: "testanchor.stellar.org";

export const xlmAsset = Asset.native();
export const usdcAsset = new Asset(
"USDC",
process.env.USDC_ASSET_ID!
(Env.usdcAssetId(true) || "")
);
12 changes: 6 additions & 6 deletions api/controllers/installation/github.controller.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Request, Response, NextFunction } from "express";
import { OctokitService } from "../../services/octokit.service.js";
import { IssueFilters } from "../../models/github.model.js";
import { responseWrapper } from "../../utilities/helper.js";
import { STATUS_CODES } from "../../utilities/data.js";
import { responseWrapper } from "../../utils/helper.js";
import { STATUS_CODES } from "../../utils/data.js";

/**
* Retrieves repositories accessible by a specific GitHub App installation.
Expand All @@ -17,7 +17,7 @@ export const getInstallationRepositories = async (req: Request, res: Response, n
// Return repositories in response
responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: repositories,
message: "Repositories retrieved successfully"
});
Expand Down Expand Up @@ -64,7 +64,7 @@ export const getRepositoryIssues = async (req: Request, res: Response, next: Nex
// Return issues with pagination in response
responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: result.issues,
pagination: { hasMore: result.hasMore },
message: "Issues retrieved successfully"
Expand All @@ -91,7 +91,7 @@ export const getRepositoryResources = async (req: Request, res: Response, next:
// Return labels and milestones
responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: resources,
message: "Repository resources retrieved successfully"
});
Expand Down Expand Up @@ -137,7 +137,7 @@ export const getOrCreateBountyLabel = async (req: Request, res: Response, next:
// Return bounty label
responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: bountyLabel,
message: "Bounty label created successfully"
});
Expand Down
17 changes: 9 additions & 8 deletions api/controllers/installation/index.controller.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { NextFunction, Request, Response } from "express";
import { prisma } from "../../config/database.config.js";
import { stellarService } from "../../services/stellar.service.js";
import { responseWrapper } from "../../utilities/helper.js";
import { STATUS_CODES } from "../../utilities/data.js";
import { responseWrapper } from "../../utils/helper.js";
import { STATUS_CODES } from "../../utils/data.js";
import { OctokitService } from "../../services/octokit.service.js";
import { NotFoundError, ValidationError } from "../../models/error.model.js";
import { ContractService } from "../../services/contract.service.js";
import { dataLogger } from "../../config/logger.config.js";
import { KMSService } from "../../services/kms.service.js";
import { InstallationStatus, Task } from "../../../prisma_client/index.js";
import { TaskIssue } from "../../models/task.model.js";
import { Env } from "../../utils/env.js";

/**
* Create a new installation.
Expand Down Expand Up @@ -159,7 +160,7 @@ export const createInstallation = async (req: Request, res: Response, next: Next
connect: { userId }
},
subscriptionPackage: {
connect: { id: process.env.DEFAULT_SUBSCRIPTION_PACKAGE_ID! }
connect: { id: Env.defaultSubscriptionPackageId(true)! }
}
},
select
Expand All @@ -169,7 +170,7 @@ export const createInstallation = async (req: Request, res: Response, next: Next
try {
// Add USDC trustline only if it's a new wallet
if (isNewWallet) {
const masterAccountSecret = process.env.STELLAR_MASTER_SECRET_KEY!;
const masterAccountSecret = Env.stellarMasterSecretKey(true)!;

await stellarService.addTrustLineViaSponsor(
masterAccountSecret,
Expand All @@ -191,7 +192,7 @@ export const createInstallation = async (req: Request, res: Response, next: Next
// If trustline addition fails, return installation but indicate partial success
responseWrapper({
res,
status: STATUS_CODES.PARTIAL_SUCCESS,
status: STATUS_CODES.OK,
data: installation,
message: existingAccountInstallation
? "Installation reactivated successfully"
Expand Down Expand Up @@ -261,7 +262,7 @@ export const getInstallations = async (req: Request, res: Response, next: NextFu
// Return paginated response
responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: results,
pagination: { hasMore },
message: "Installations retrieved successfully"
Expand Down Expand Up @@ -366,7 +367,7 @@ export const getInstallation = async (req: Request, res: Response, next: NextFun
// Return installation details with stats
responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: { ...installation, stats },
message: "Installation details retrieved successfully"
});
Expand Down Expand Up @@ -447,7 +448,7 @@ export const archiveInstallation = async (req: Request, res: Response, next: Nex
// Return success confirmation
responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: { installationId, refundedAmount: `${refundedAmount} USDC` },
message: `Installation archived and ${refundedAmount} USDC refunded`
});
Expand Down
14 changes: 7 additions & 7 deletions api/controllers/installation/team.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NextFunction, Request, Response } from "express";
import { prisma } from "../../config/database.config.js";
import { responseWrapper } from "../../utilities/helper.js";
import { STATUS_CODES } from "../../utilities/data.js";
import { responseWrapper } from "../../utils/helper.js";
import { STATUS_CODES } from "../../utils/data.js";
import { AuthorizationError, NotFoundError } from "../../models/error.model.js";
import { OctokitService } from "../../services/octokit.service.js";

Expand Down Expand Up @@ -42,7 +42,7 @@ export const addTeamMember = async (req: Request, res: Response, next: NextFunct
if (!existingUser) {
return responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: { status: "not_found" },
message: "User not found in our system. We currently do not support adding users who haven't signed up on our platform"
});
Expand All @@ -53,7 +53,7 @@ export const addTeamMember = async (req: Request, res: Response, next: NextFunct
if (isAlreadyMember) {
return responseWrapper({
res,
status: STATUS_CODES.SERVER_ERROR,
status: STATUS_CODES.BAD_REQUEST,
data: { username, status: "already_member" },
message: "User is already a member of this installation"
});
Expand Down Expand Up @@ -97,7 +97,7 @@ export const addTeamMember = async (req: Request, res: Response, next: NextFunct
// Return result
responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: { username, status: "added" },
message: "Team member added successfully"
});
Expand Down Expand Up @@ -151,7 +151,7 @@ export const updateTeamMember = async (req: Request, res: Response, next: NextFu
// Return success message
responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: {},
message: "Permissions updated successfully"
});
Expand Down Expand Up @@ -208,7 +208,7 @@ export const removeTeamMember = async (req: Request, res: Response, next: NextFu
// Return success message
responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: {},
message: "Team member removed successfully"
});
Expand Down
14 changes: 7 additions & 7 deletions api/controllers/internal/index.controller.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Request, Response, NextFunction } from "express";
import { statsigService } from "../../services/statsig.service.js";
import { STATUS_CODES } from "../../utilities/data.js";
import { STATUS_CODES } from "../../utils/data.js";
import { dataLogger } from "../../config/logger.config.js";
import { responseWrapper, stellarTimestampToDate } from "../../utilities/helper.js";
import { responseWrapper, stellarTimestampToDate } from "../../utils/helper.js";
import { prisma } from "../../config/database.config.js";
import { KMSService } from "../../services/kms.service.js";
import { ContractService } from "../../services/contract.service.js";
Expand All @@ -29,7 +29,7 @@ export const handleBountyPayoutJob = async (req: Request, res: Response, next: N
if (!taskId) {
return responseWrapper({
res,
status: STATUS_CODES.SERVER_ERROR,
status: STATUS_CODES.INTERNAL_SERVER_ERROR,
data: { prNumber, repositoryName, prUrl },
message: "Task ID is missing from payload"
});
Expand Down Expand Up @@ -63,7 +63,7 @@ export const handleBountyPayoutJob = async (req: Request, res: Response, next: N
if (!relatedTask) {
return responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: { prNumber, repositoryName, prUrl },
message: "Task not found"
});
Expand All @@ -73,7 +73,7 @@ export const handleBountyPayoutJob = async (req: Request, res: Response, next: N
if (!relatedTask.contributor || !relatedTask.contributor.wallet || !relatedTask.contributor.wallet.address) {
return responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: { prNumber, repositoryName, prUrl, linkedIssues: linkedIssues.map(i => i.number) },
message: "No wallet address found for contributor"
});
Expand All @@ -86,7 +86,7 @@ export const handleBountyPayoutJob = async (req: Request, res: Response, next: N

// Decrypt installation wallet secret
const decryptedWalletSecret = await KMSService.decryptWallet(relatedTask.installation.wallet);

// Approve completion via smart contract
const transactionResponse = await ContractService.approveCompletion(
decryptedWalletSecret,
Expand Down Expand Up @@ -189,7 +189,7 @@ export const handleBountyPayoutJob = async (req: Request, res: Response, next: N
// Send success response
responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: {
prNumber,
repositoryName,
Expand Down
8 changes: 4 additions & 4 deletions api/controllers/task/activities.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NextFunction, Request, Response } from "express";
import { prisma } from "../../config/database.config.js";
import { responseWrapper } from "../../utilities/helper.js";
import { STATUS_CODES } from "../../utilities/data.js";
import { responseWrapper } from "../../utils/helper.js";
import { STATUS_CODES } from "../../utils/data.js";
import { NotFoundError } from "../../models/error.model.js";

/**
Expand Down Expand Up @@ -69,7 +69,7 @@ export const getTaskActivities = async (req: Request, res: Response, next: NextF
// Return paginated activities
responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: results,
pagination: { hasMore }
});
Expand Down Expand Up @@ -117,7 +117,7 @@ export const markActivityAsViewed = async (req: Request, res: Response, next: Ne
// Return updated activity
responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: updatedActivity,
message: "Activity marked as viewed"
});
Expand Down
10 changes: 5 additions & 5 deletions api/controllers/task/contributor.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NextFunction, Request, Response } from "express";
import { prisma } from "../../config/database.config.js";
import { responseWrapper } from "../../utilities/helper.js";
import { STATUS_CODES } from "../../utilities/data.js";
import { responseWrapper } from "../../utils/helper.js";
import { STATUS_CODES } from "../../utils/data.js";
import { FilterTasks } from "../../models/task.model.js";
import { Prisma, TaskStatus } from "../../../prisma_client/index.js";
import { NotFoundError } from "../../models/error.model.js";
Expand Down Expand Up @@ -165,7 +165,7 @@ export const getContributorTasks = async (req: Request, res: Response, next: Nex
// Return paginated tasks
responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: results.map((task) => {
if (task.contributorId !== userId) {
return {
Expand Down Expand Up @@ -259,7 +259,7 @@ export const getContributorTask = async (req: Request, res: Response, next: Next
if (task.contributorId !== userId) {
return responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: {
id: task.id,
issue: task.issue,
Expand All @@ -280,7 +280,7 @@ export const getContributorTask = async (req: Request, res: Response, next: Next
// Return task
responseWrapper({
res,
status: STATUS_CODES.SUCCESS,
status: STATUS_CODES.OK,
data: task
});
} catch (error) {
Expand Down
Loading
Loading