Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
c9a74d7
refactor: remove unused env and rename drive-url
larryrider Jul 2, 2025
3e80275
chore: update dependencies to latest versions
larryrider Jul 2, 2025
9cc5ca3
refactor: streamline workflow scripts and improve environment setup
larryrider Jul 2, 2025
4029cdc
chore: update clean script to remove coverage directory
larryrider Jul 2, 2025
7e0fdcc
refactor: remove unused Payments SDK method and related tests
larryrider Jul 2, 2025
4d1ff81
feat: add workflow to mark inactive PRs as stalled
larryrider Jul 2, 2025
b0e41a1
chore: configure git to disable core.autocrlf in workflow
larryrider Jul 2, 2025
cf21498
chore: update commands unit tests workflow to set core.autocrlf globally
larryrider Jul 2, 2025
c42358a
chore: reorder steps in commands unit tests workflow for clarity
larryrider Jul 2, 2025
bcbaa4f
docs: add project maintenance section with quality metrics and goals
larryrider Jul 2, 2025
075d535
refactor: streamline OPTIONS handler logic for improved readability
larryrider Jul 2, 2025
f5400dc
refactor: change WEBDAV_APP_NAME to readonly for better immutability
larryrider Jul 2, 2025
c65ae4f
refactor: change getThumbnailFromImageBuffer to readonly for better i…
larryrider Jul 2, 2025
b5ca809
refactor: change getAllTrashSubfolders and getAllTrashSubfiles to rea…
larryrider Jul 2, 2025
0dd0e33
refactor: use nullish coalescing operator for salt assignment and mar…
larryrider Jul 2, 2025
d94994d
fix: correct spelling of 'maintenance' in README.md
larryrider Jul 3, 2025
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
4 changes: 1 addition & 3 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
DRIVE_URL=https://drive.internxt.com
DRIVE_WEB_URL=https://drive.internxt.com
DRIVE_NEW_API_URL=https://api.internxt.com/drive
PAYMENTS_API_URL=https://api.internxt.com/payments
NETWORK_URL=https://api.internxt.com
APP_CRYPTO_SECRET=6KYQBP847D4ATSFA
APP_CRYPTO_SECRET2=8Q8VMUE3BJZV87GT
APP_MAGIC_IV=d139cb9a2cd17092e79e1861cf9d7023
APP_MAGIC_SALT=38dce0391b49efba88dbc8c39ebf868f0267eb110bb0012ab27dc52a528d61b1d1ed9d76f400ff58e3240028442b1eab9bb84e111d9dadd997982dbde9dbd25e
36 changes: 23 additions & 13 deletions .github/workflows/commands-unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,29 @@ jobs:
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- name: Set core.autocrlf to false
run: git config --global core.autocrlf false

- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22.x'
cache: yarn
# Generate a .npmrc file with the NPM_TOKEN
- run: echo "registry=https://registry.yarnpkg.com/" > .npmrc
- run: echo "@internxt:registry=https://npm.pkg.github.com" >> .npmrc
- run: echo //npm.pkg.github.com/:_authToken=${{ secrets.NPM_TOKEN }} >> .npmrc
- run: echo "always-auth=true" >> .npmrc
# Create env file
- run: echo "${{ secrets.TEST_ENV}}" >> .env
# Install dependencies, build and run tests
- run: yarn install --network-timeout 600000
- run: yarn build
- run: yarn test:unit

- name: Add .env
run: cp .env.template .env

- name: Add .npmrc
run: cp .npmrc.template .npmrc

- name: Install dependencies
run: yarn

- name: Build
run: yarn run build

- name: Run tests
run: yarn test
32 changes: 13 additions & 19 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,24 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: '22.x'
cache: yarn

- run: |
echo "@internxt:registry=https://npm.pkg.github.com/" > .npmrc
echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" >> .npmrc
- name: Add .env
run: cp .env.template .env

- run: yarn
- name: Add .npmrc
run: cp .npmrc.template .npmrc

- name: Add .env
run: |
echo "DRIVE_URL=https://drive.internxt.com" >> .env
echo "DRIVE_NEW_API_URL=https://api.internxt.com/drive" >> .env
echo "PAYMENTS_API_URL=https://api.internxt.com/payments" >> .env
echo "NETWORK_URL=https://api.internxt.com" >> .env
echo "APP_CRYPTO_SECRET=6KYQBP847D4ATSFA" >> .env
echo "APP_CRYPTO_SECRET2=8Q8VMUE3BJZV87GT" >> .env
echo "APP_MAGIC_IV=d139cb9a2cd17092e79e1861cf9d7023" >> .env
echo "APP_MAGIC_SALT=38dce0391b49efba88dbc8c39ebf868f0267eb110bb0012ab27dc52a528d61b1d1ed9d76f400ff58e3240028442b1eab9bb84e111d9dadd997982dbde9dbd25e" >> .env
echo "NODE_ENV=production" >> .env
- name: Install dependencies
run: yarn

- run: yarn run build
- name: Build
run: yarn run build

# Change the registry to npmjs (to publish)
- run: |
- name: Set the NPM registry with token
run: |
echo "registry=https://registry.npmjs.org/" > .npmrc
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" >> .npmrc

- run: npm publish --scope=@internxt --access public
- name: Publish on NPM
run: npm publish --scope=@internxt --access public
17 changes: 8 additions & 9 deletions .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,14 @@ jobs:
with:
node-version: '22.x'
cache: yarn
# Generate a .npmrc file with the NPM_TOKEN
- run: echo "registry=https://registry.yarnpkg.com/" > .npmrc
- run: echo "@internxt:registry=https://npm.pkg.github.com" >> .npmrc
- run: echo //npm.pkg.github.com/:_authToken=${{ secrets.NPM_TOKEN }} >> .npmrc
- run: echo "always-auth=true" >> .npmrc
# Create env file
- run: echo "${{ secrets.TEST_ENV}}" >> .env
# Install dependencies, build and run tests
- run: yarn install --network-timeout 600000

- name: Add .env
run: cp .env.template .env

- name: Add .npmrc
run: cp .npmrc.template .npmrc

- run: yarn
- run: yarn build
- run: yarn test:unit
# Analyze with SonarCloud
Expand Down
35 changes: 35 additions & 0 deletions .github/workflows/stale.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Mark inactive PRs as stalled

on:
schedule:
- cron: '0 7 * * *'
workflow_dispatch:

permissions:
issues: read
pull-requests: write

jobs:
stale:
runs-on: ubuntu-latest

steps:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 30
days-before-close: 15
# Custom messages
stale-pr-message: >
This pull request has been inactive for 30 days. Is it still in progress?
If so, please leave a comment or make an update to keep it open. Otherwise, it will be automatically closed in 15 days.
close-pr-message: >
This pull request was automatically closed due to prolonged inactivity.
# Labels
stale-pr-label: stalled
exempt-pr-labels: 'dependencies,blocked'
exempt-draft-pr: true
ascending: true
# Do not process issues
days-before-issue-stale: -1
days-before-issue-close: -1
6 changes: 2 additions & 4 deletions .npmrc.template
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
registry=https://registry.yarnpkg.com/

@internxt:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}
always-auth=true

@internxt:registry=https://registry.yarnpkg.com/
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,25 @@ A CLI tool to interact with your Internxt encrypted files
* [Current Limitations](#current-limitations)
<!-- tocstop -->

# Project Maintenance

<!-- maintenance -->
[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=internxt_cli&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=internxt_cli)
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=internxt_cli&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=internxt_cli)
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=internxt_cli&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=internxt_cli)
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=internxt_cli&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=internxt_cli)
[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=internxt_cli&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=internxt_cli)
[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=internxt_cli&metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=internxt_cli)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=internxt_cli&metric=coverage)](https://sonarcloud.io/summary/new_code?id=internxt_cli)

We aim to achieve:

- An 'A' rating in Maintainability
- An 'A' rating in Security
- Less than 3% duplicated lines
- At least 80% test coverage
<!-- maintenancestop -->

# Installation

You can install the Internxt CLI by using NPM:
Expand Down
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Internxt CLI to manage your encrypted storage",
"scripts": {
"build": "yarn clean && tsc",
"clean": "rimraf dist tsconfig.tsbuildinfo oclif.manifest.json",
"clean": "rimraf dist coverage tsconfig.tsbuildinfo oclif.manifest.json",
"lint": "eslint .",
"pretty": "prettier --write **/*.{js,jsx,tsx,ts}",
"postpack": "rimraf oclif.manifest.json",
Expand Down Expand Up @@ -35,10 +35,10 @@
"/oclif.manifest.json"
],
"dependencies": {
"@inquirer/prompts": "7.5.3",
"@inquirer/prompts": "7.6.0",
"@internxt/inxt-js": "2.2.1",
"@internxt/lib": "1.3.1",
"@internxt/sdk": "1.10.0",
"@internxt/sdk": "1.10.3",
"@oclif/core": "4.4.0",
"@types/validator": "13.15.2",
"async": "3.2.6",
Expand All @@ -47,7 +47,7 @@
"body-parser": "2.2.0",
"cli-progress": "3.12.0",
"dayjs": "1.11.13",
"dotenv": "16.5.0",
"dotenv": "17.0.1",
"express": "5.1.0",
"express-async-handler": "1.2.0",
"express-basic-auth": "1.2.1",
Expand Down Expand Up @@ -75,13 +75,13 @@
"@types/range-parser": "1.2.7",
"@vitest/coverage-istanbul": "3.2.4",
"@vitest/spy": "3.2.4",
"eslint": "9.29.0",
"eslint": "9.30.1",
"husky": "9.1.7",
"lint-staged": "16.1.2",
"nock": "14.0.5",
"nodemon": "3.1.10",
"oclif": "4.20.1",
"prettier": "3.6.1",
"prettier": "3.6.2",
"rimraf": "6.0.1",
"ts-node": "10.9.2",
"typescript": "5.8.3",
Expand Down
2 changes: 1 addition & 1 deletion src/commands/create-folder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default class CreateFolder extends Command {
const newFolder = await createNewFolder;
CLIUtils.done(flags['json']);
// eslint-disable-next-line max-len
const message = `Folder ${newFolder.plainName} created successfully, view it at ${ConfigService.instance.get('DRIVE_URL')}/folder/${newFolder.uuid}`;
const message = `Folder ${newFolder.plainName} created successfully, view it at ${ConfigService.instance.get('DRIVE_WEB_URL')}/folder/${newFolder.uuid}`;
CLIUtils.success(this.log.bind(this), message);
return { success: true, message, folder: newFolder };
};
Expand Down
2 changes: 1 addition & 1 deletion src/commands/upload-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export default class UploadFile extends Command {
const uploadTime = timer.stop();
this.log('\n');
// eslint-disable-next-line max-len
const message = `File uploaded in ${uploadTime}ms, view it at ${ConfigService.instance.get('DRIVE_URL')}/file/${createdDriveFile.uuid}`;
const message = `File uploaded in ${uploadTime}ms, view it at ${ConfigService.instance.get('DRIVE_WEB_URL')}/file/${createdDriveFile.uuid}`;
CLIUtils.success(this.log.bind(this), message);
return { success: true, message, file: createdDriveFile };
};
Expand Down
4 changes: 2 additions & 2 deletions src/services/crypto.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class CryptoService {
* @returns The hashed password and the salt
**/
public passToHash = (passObject: { password: string; salt?: string | null }): { salt: string; hash: string } => {
const salt = passObject.salt ? passObject.salt : randomBytes(128 / 8).toString('hex');
const salt = passObject.salt ?? randomBytes(128 / 8).toString('hex');
const hash = pbkdf2Sync(passObject.password, Buffer.from(salt, 'hex'), 10000, 256 / 8, 'sha1').toString('hex');
const hashedObjetc = {
salt,
Expand Down Expand Up @@ -173,7 +173,7 @@ export class CryptoService {
* @param salt The salt used to encrypt
* @returns The key and the iv resulted from the secret and the salt combination
**/
private getKeyAndIvFrom = (secret: string, salt: Buffer) => {
private readonly getKeyAndIvFrom = (secret: string, salt: Buffer) => {
const TRANSFORM_ROUNDS = 3;
const password = Buffer.concat([Buffer.from(secret, 'binary'), salt]);
const md5Hashes = [];
Expand Down
10 changes: 8 additions & 2 deletions src/services/drive/trash.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ export class TrashService {
return { folders, files };
};

private getAllTrashSubfolders = async (storageClient: Trash, offset: number): Promise<FetchPaginatedFolder[]> => {
private readonly getAllTrashSubfolders = async (
storageClient: Trash,
offset: number,
): Promise<FetchPaginatedFolder[]> => {
const folderContentPromise = storageClient.getTrashedFilesPaginated(50, offset, 'folders', true);
const { result: folders } = (await folderContentPromise) as unknown as { result: FetchPaginatedFolder[] };

Expand All @@ -43,7 +46,10 @@ export class TrashService {
}
};

private getAllTrashSubfiles = async (storageClient: Trash, offset: number): Promise<FetchPaginatedFile[]> => {
private readonly getAllTrashSubfiles = async (
storageClient: Trash,
offset: number,
): Promise<FetchPaginatedFile[]> => {
const folderContentPromise = storageClient.getTrashedFilesPaginated(50, offset, 'files', true);
const { result: folders } = (await folderContentPromise) as unknown as { result: FetchPaginatedFile[] };

Expand Down
12 changes: 0 additions & 12 deletions src/services/sdk-manager.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,6 @@ export class SdkManager {
});
}

/** Payments SDK */
getPayments() {
const PAYMENTS_API_URL = ConfigService.instance.get('PAYMENTS_API_URL');

const apiSecurity = SdkManager.getApiSecurity();
const appDetails = SdkManager.getAppDetails();

return Drive.Payments.client(PAYMENTS_API_URL, appDetails, {
token: apiSecurity.newToken,
});
}

/** Users SDK */
getUsers() {
const DRIVE_API_URL = ConfigService.instance.get('DRIVE_NEW_API_URL');
Expand Down
2 changes: 1 addition & 1 deletion src/services/thumbnail.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class ThumbnailService {
}
};

private getThumbnailFromImageBuffer = (buffer: Buffer): Promise<Buffer> => {
private readonly getThumbnailFromImageBuffer = (buffer: Buffer): Promise<Buffer> => {
return sharp(buffer)
.resize({
height: ThumbnailConfig.MaxHeight,
Expand Down
4 changes: 1 addition & 3 deletions src/types/config.types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
export interface ConfigKeys {
readonly DRIVE_URL: string;
readonly DRIVE_WEB_URL: string;
readonly DRIVE_NEW_API_URL: string;
readonly PAYMENTS_API_URL: string;
readonly APP_CRYPTO_SECRET: string;
readonly APP_CRYPTO_SECRET2: string;
readonly APP_MAGIC_IV: string;
readonly APP_MAGIC_SALT: string;
readonly NETWORK_URL: string;
Expand Down
2 changes: 1 addition & 1 deletion src/utils/pm2.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export type WebDavProcessStatus =
| 'unknown';

export class PM2Utils {
private static WEBDAV_APP_NAME = 'Internxt CLI WebDav';
private static readonly WEBDAV_APP_NAME = 'Internxt CLI WebDav';

static connect() {
return new Promise<void>((resolve, reject) => {
Expand Down
14 changes: 5 additions & 9 deletions src/webdav/handlers/OPTIONS.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,26 @@ export class OPTIONSRequestHandler implements WebDavMethodHandler {
webdavLogger.info(`[OPTIONS] Request received for ${resource.type} at ${resource.url}`);

if (resource.url === '/' || resource.url === '') {
// Root Folder
const allowedMethods = 'DELETE, GET, HEAD, MKCOL, MOVE, OPTIONS, PROPFIND, PUT';
webdavLogger.info(`[OPTIONS] Returning Allowed Options: ${allowedMethods}`);
res.header('Allow', 'DELETE, GET, HEAD, MKCOL, MOVE, OPTIONS, PROPFIND, PUT');
res.header('DAV', '1, 2, ordered-collections');
res.status(200).send();
return;
}

if (resource.type === 'folder') {
} else if (resource.type === 'folder') {
// Children Folder
const allowedMethods = 'DELETE, HEAD, MKCOL, MOVE, OPTIONS, PROPFIND';
webdavLogger.info(`[OPTIONS] Returning Allowed Options: ${allowedMethods}`);
res.header('Allow', allowedMethods);
res.header('DAV', '1, 2, ordered-collections');
res.status(200).send();
return;
}

if (resource.type === 'file') {
} else {
// Children File
const allowedMethods = 'DELETE, GET, HEAD, MOVE, OPTIONS, PROPFIND, PUT';
webdavLogger.info(`[OPTIONS] Returning Allowed Options: ${allowedMethods}`);
res.header('Allow', allowedMethods);
res.header('DAV', '1, 2, ordered-collections');
res.status(200).send();
return;
}
};
}
Loading
Loading