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
7 changes: 7 additions & 0 deletions nftopia-backend/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,12 @@ services:
volumes:
- postgres_data:/var/lib/postgresql/data

redis:
image: redis:alpine
container_name: nftopia-redis
restart: unless-stopped
ports:
- '6379:6379'

volumes:
postgres_data:
46 changes: 46 additions & 0 deletions nftopia-backend/lint_results.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

> nftopia-backend@0.0.1 lint
> eslint "{src,apps,libs,test}/**/*.ts" --fix


/home/knights/Documents/Project/Drips/nftopia-stellar/nftopia-backend/src/app.module.ts
10:0 error Parsing error: Merge conflict marker encountered

/home/knights/Documents/Project/Drips/nftopia-stellar/nftopia-backend/src/common/filters/http-exception.filter.ts
70:7 error Unsafe call of a type that could not be resolved @typescript-eslint/no-unsafe-call
70:19 error Unsafe member access .error on a type that cannot be resolved @typescript-eslint/no-unsafe-member-access
78:7 error Unsafe call of a type that could not be resolved @typescript-eslint/no-unsafe-call
78:19 error Unsafe member access .warn on a type that cannot be resolved @typescript-eslint/no-unsafe-member-access
80:7 error Unsafe call of a type that could not be resolved @typescript-eslint/no-unsafe-call
80:19 error Unsafe member access .info on a type that cannot be resolved @typescript-eslint/no-unsafe-member-access

/home/knights/Documents/Project/Drips/nftopia-stellar/nftopia-backend/src/main.ts
10:25 warning Unsafe argument of type error typed assigned to a parameter of type `string | symbol | Function | Type<any>` @typescript-eslint/no-unsafe-argument
47:9 error Unsafe assignment of an `any` value @typescript-eslint/no-unsafe-assignment
47:26 warning Unsafe argument of type error typed assigned to a parameter of type `string | symbol | Function | Type<any>` @typescript-eslint/no-unsafe-argument
49:3 error Unsafe call of an `any` typed value @typescript-eslint/no-unsafe-call
49:10 error Unsafe member access .log on an `any` value @typescript-eslint/no-unsafe-member-access
50:3 error Unsafe call of an `any` typed value @typescript-eslint/no-unsafe-call
50:10 error Unsafe member access .log on an `any` value @typescript-eslint/no-unsafe-member-access

/home/knights/Documents/Project/Drips/nftopia-stellar/nftopia-backend/src/nft/nft.controller.ts
31:36 warning Unsafe argument of type `any` assigned to a parameter of type `NftFilterDto` @typescript-eslint/no-unsafe-argument
47:5 error Unsafe return of a value of type `Promise<any>` @typescript-eslint/no-unsafe-return

/home/knights/Documents/Project/Drips/nftopia-stellar/nftopia-backend/src/nft/nft.service.ts
11:10 error 'scValToNative' is defined but never used @typescript-eslint/no-unused-vars
11:25 error 'xdr' is defined but never used @typescript-eslint/no-unused-vars
89:11 error Unsafe assignment of an `any` value @typescript-eslint/no-unsafe-assignment
98:5 error Unsafe return of a value of type `any` @typescript-eslint/no-unsafe-return
115:13 error 'events' is assigned a value but never used @typescript-eslint/no-unused-vars
126:43 error Unsafe member access .message on an `any` value @typescript-eslint/no-unsafe-member-access

/home/knights/Documents/Project/Drips/nftopia-stellar/nftopia-backend/src/nft/soroban.service.ts
4:10 error 'scValToNative' is defined but never used @typescript-eslint/no-unused-vars
35:72 error Unsafe member access .message on an `any` value @typescript-eslint/no-unsafe-member-access
36:11 error Unsafe member access .stack on an `any` value @typescript-eslint/no-unsafe-member-access
60:53 error Unsafe member access .message on an `any` value @typescript-eslint/no-unsafe-member-access
70:60 error Unsafe member access .message on an `any` value @typescript-eslint/no-unsafe-member-access

βœ– 27 problems (24 errors, 3 warnings)

57 changes: 57 additions & 0 deletions nftopia-backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions nftopia-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@
"@nestjs/jwt": "^11.0.2",
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.0.1",
"@nestjs/schedule": "^6.1.1",
"@nestjs/swagger": "^11.2.5",
"@nestjs/typeorm": "^11.0.0",
"@types/cron": "^2.0.1",
"cache-manager": "^7.2.8",
"cache-manager-redis-store": "^3.0.1",
"class-transformer": "^0.5.1",
Expand Down
2 changes: 2 additions & 0 deletions nftopia-backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
import { NftModule } from './nft/nft.module';
import { LoggerModule } from 'nestjs-pino';
import { APP_FILTER } from '@nestjs/core';
import { HttpExceptionFilter } from './common/filters/http-exception.filter';
Expand Down Expand Up @@ -68,6 +69,7 @@ import { HttpExceptionFilter } from './common/filters/http-exception.filter';
}),
UsersModule,
]),
NftModule,
],
controllers: [AppController],
providers: [
Expand Down
40 changes: 40 additions & 0 deletions nftopia-backend/src/nft/dto/nft-filter.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsOptional, IsNumber, Min } from 'class-validator';

export class NftFilterDto {
@ApiProperty({ required: false, description: 'Stellar Contract ID (C...)' })
@IsString()
@IsOptional()
contractId?: string;

@ApiProperty({ required: false, description: 'Stellar Account ID (G...)' })
@IsString()
@IsOptional()
owner?: string;

@ApiProperty({ required: false, minimum: 1, default: 1 })
@IsNumber()
@Min(1)
@IsOptional()
page?: number = 1;

@ApiProperty({ required: false, minimum: 1, default: 10 })
@IsNumber()
@Min(1)
@IsOptional()
limit?: number = 10;

@ApiProperty({ required: false, enum: ['asc', 'desc'], default: 'desc' })
@IsString()
@IsOptional()
sortOrder?: 'asc' | 'desc' = 'desc';

@ApiProperty({
required: false,
enum: ['mintedAt', 'price', 'views'],
default: 'mintedAt',
})
@IsString()
@IsOptional()
sortBy?: 'mintedAt' | 'price' | 'views' = 'mintedAt';
}
40 changes: 40 additions & 0 deletions nftopia-backend/src/nft/dto/stellar-nft.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsOptional, Length, Matches } from 'class-validator';

export class StellarNftDto {
@ApiProperty({ description: 'Stellar Contract ID (C...)' })
@IsString()
@Length(56, 56)
@Matches(/^C[A-Z0-9]{55}$/, { message: 'Invalid Stellar Contract ID format' })
contractId: string;

@ApiProperty({ description: 'Token ID (uint256 or string)' })
@IsString()
tokenId: string;

@ApiProperty({ description: 'Owner Account ID (G...)' })
@IsString()
@Length(56, 56)
@Matches(/^G[A-Z0-9]{55}$/, { message: 'Invalid Stellar Account ID format' })
owner: string;

@ApiProperty({ required: false, description: 'IPFS or HTTP URI' })
@IsString()
@IsOptional()
metadataUri?: string;

@ApiProperty({ required: false })
@IsString()
@IsOptional()
name?: string;

@ApiProperty({ required: false })
@IsString()
@IsOptional()
description?: string;

@ApiProperty({ required: false })
@IsString()
@IsOptional()
image?: string;
}
23 changes: 23 additions & 0 deletions nftopia-backend/src/nft/entities/nft-metadata.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Entity, Column, PrimaryGeneratedColumn, OneToOne } from 'typeorm';
import { StellarNft } from './stellar-nft.entity';

@Entity('nft_metadata')
export class NftMetadata {
@PrimaryGeneratedColumn('uuid')
id: string;

@Column({ nullable: true })
name: string;

@Column({ type: 'text', nullable: true })
description: string;

@Column({ nullable: true })
image: string;

@Column({ type: 'jsonb', nullable: true })
attributes: any;

@OneToOne(() => StellarNft, (nft) => nft.metadata)
nft: StellarNft;
}
53 changes: 53 additions & 0 deletions nftopia-backend/src/nft/entities/stellar-nft.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
Entity,
Column,
PrimaryColumn,
CreateDateColumn,
UpdateDateColumn,
OneToOne,
JoinColumn,
Index,
} from 'typeorm';
import { NftMetadata } from './nft-metadata.entity';

@Entity('stellar_nfts')
@Index(['owner']) // Optimize queries by owner
@Index(['contractId']) // Optimize filter by contract
export class StellarNft {
@PrimaryColumn()
contractId: string;

@PrimaryColumn()
tokenId: string;

@Column()
owner: string; // Stellar G-address

@Column({ nullable: true })
metadataUri: string;

@OneToOne(() => NftMetadata, (metadata) => metadata.nft, {
cascade: true,
eager: true,
})
@JoinColumn()
metadata: NftMetadata;

@Column({ default: 0 })
views: number; // For PopularThisWeek

@Column({ default: 0 })
salesCount: number; // For TopSellers

@Column({ type: 'decimal', precision: 20, scale: 7, default: 0 })
volume: number; // Total volume in XLM

@Column({ nullable: true })
mintedAt: Date;

@CreateDateColumn()
createdAt: Date;

@UpdateDateColumn()
updatedAt: Date;
}
41 changes: 41 additions & 0 deletions nftopia-backend/src/nft/nft.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Test, TestingModule } from '@nestjs/testing';
import { NftController } from './nft.controller';
import { NftService } from './nft.service';

const mockNftService = {
findAll: jest.fn().mockResolvedValue([]),
getPopular: jest.fn().mockResolvedValue([]),
getTopSellers: jest.fn().mockResolvedValue([]),
};

describe('NftController', () => {
let controller: NftController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [NftController],
providers: [
{
provide: NftService,
useValue: mockNftService,
},
],
}).compile();

controller = module.get<NftController>(NftController);
});

it('should be defined', () => {
expect(controller).toBeDefined();
});

it('should list NFTs', async () => {
expect(await controller.findAll({})).toEqual([]);
expect(mockNftService.findAll).toHaveBeenCalled();
});

it('should get popular NFTs', async () => {
expect(await controller.getPopular()).toEqual([]);
expect(mockNftService.getPopular).toHaveBeenCalled();
});
});
Loading