Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
3cc43cd
remove encryptedName from createFile
TamaraFinogina Jan 12, 2026
54f6b6c
revert lint changes to src/lib/newrelic.interceptor.ts
TamaraFinogina Jan 12, 2026
9b5b347
make name an opeional attribute
TamaraFinogina Jan 12, 2026
7b89968
revert create file
TamaraFinogina Jan 12, 2026
f61b33f
remove encrypted name from findByNameAndParentUuid
TamaraFinogina Jan 8, 2026
0653adc
add comments
TamaraFinogina Jan 9, 2026
1cb6dcb
use folder.plainName if available
TamaraFinogina Jan 9, 2026
26eebca
decrypt only if no plainName
TamaraFinogina Jan 9, 2026
186bef5
add tests
TamaraFinogina Jan 9, 2026
fd6f346
fix: refactor name decryption logic to use plainName if available in …
jzunigax2 Jan 15, 2026
384dab0
test: add tests for handling plainName in file and folder use cases
jzunigax2 Jan 15, 2026
519bca0
fix(file): ensure proper BigInt comparison for file size
jzunigax2 Jan 15, 2026
86295bc
feat(cache): add getRecord and increment functions for the rate limit…
sg-gs Jan 16, 2026
489e566
refactor(guards): simplify CustomThrottlerGuard significatively
sg-gs Jan 16, 2026
3e33020
feat(guards): add custom throttler
sg-gs Jan 16, 2026
ac984e0
feat(guards): create custom interceptor for global throttling
sg-gs Jan 16, 2026
096f842
feat(guards): wire the throttler guard and interceptor with the app
sg-gs Jan 16, 2026
bafa749
fix(cache): dont keep the expiration time when the record is already …
sg-gs Jan 16, 2026
bc08bc3
fix(folders): rate limit folder meta to 30 x min
sg-gs Jan 16, 2026
54ebf20
fix(files): rate limit file meta requests x min
sg-gs Jan 16, 2026
be0940f
refactor: remove deprecated prometheus integration
sg-gs Jan 16, 2026
06a096d
fix(users): custom rate limit for usage requests
sg-gs Jan 16, 2026
58271d4
fix(files): throttle listing requests x min
sg-gs Jan 16, 2026
b4561cb
fix(folders): add custom throttle guard for folder listing requests
jzunigax2 Jan 16, 2026
04f182e
fix(users, folders): set custom throttle to refresh and folder creation
sg-gs Jan 19, 2026
4158481
fix(folder): increase create folder throttler to 30k/h
sg-gs Jan 19, 2026
bc8d864
feat(migration): add user_id column to file_versions table
douglas-xt Dec 16, 2025
cb3ed94
fix(migration): use user uuid instead of id for file_versions
douglas-xt Dec 30, 2025
6919cf0
chore(migration): update timestamp for add-user-id-to-file-versions m…
douglas-xt Jan 16, 2026
46fd9a5
fix(migration): make user_id NOT NULL in file_versions
douglas-xt Jan 16, 2026
6cfcf2f
fix(migration): create file_versions user_id status index concurrently
douglas-xt Jan 19, 2026
288b648
perf(migration): use partial index for file_versions user_id lookup
douglas-xt Jan 19, 2026
b0d9040
perf(migration): use partial index for file_versions user_id lookup
douglas-xt Jan 19, 2026
e6ad566
fix(migration): remove duplicate index creation from user_id migration
douglas-xt Jan 19, 2026
ac95601
feat(usage): include file versions in storage calculation
douglas-xt Jan 19, 2026
f9714a6
remove encryptedName from updateFileMetaData
TamaraFinogina Jan 12, 2026
1bde81c
Revert "[PB-5673] Remove encryptedName from createFile"
sg-gs Jan 20, 2026
4f8205e
Revert "[PB-5673] Remove encryptedName from updateFileMetaData"
jzunigax2 Jan 20, 2026
6d0a53a
Revert "[PB-5673] Remove encrypted name from findByNameAndParentUuid"
TamaraFinogina Jan 20, 2026
af5b175
fix: reduce usage limit x min and folders listing x min
sg-gs Jan 20, 2026
de859fb
fix: update folder mapping to include plain_name, temporary hotfix fo…
jzunigax2 Jan 20, 2026
0e93da2
feat: add migration to update CLI access limits
jzunigax2 Jan 21, 2026
138d507
feat: add migration to grant CLI access for ultimate lifetime tier
jzunigax2 Jan 21, 2026
894e6ed
refactor(file-versions): extract get file versions to action layer
douglas-xt Jan 20, 2026
4461c33
test(file-versions): cover empty versions case and add result assertions
douglas-xt Jan 21, 2026
52ff20d
feat(migrations): add rclone access limit migration with tier relations
jzunigax2 Jan 21, 2026
4424f07
feat(migrations): add migration to clean up orphaned devices linked t…
jzunigax2 Jan 8, 2026
06c3308
fix(migrations): update orphaned devices cleanup to include removed f…
jzunigax2 Jan 9, 2026
61e4626
feat(migration): modify trigger function that handles file deletion t…
jzunigax2 Jan 22, 2026
9153957
fix(migrations): update trigger to avoid adding empty files to the de…
sg-gs Jan 22, 2026
4a2b609
feat: enhance CLI and Rclone login access with new client mappings an…
jzunigax2 Jan 21, 2026
7a11659
refactor(file-versions): extract delete version to action layer
douglas-xt Jan 20, 2026
bdc660b
simplify delete tests
douglas-xt Jan 21, 2026
55f2736
update jest
TamaraFinogina Jan 23, 2026
dc77aa9
run yarn format
TamaraFinogina Jan 23, 2026
5cb0d5e
migration: disable file versioning for essential tier
douglas-xt Jan 13, 2026
add694e
chore(migration): update timestamp for update-file-versioning-limits …
douglas-xt Jan 16, 2026
b448bf8
refactor(migrations): apply review feedback on deleted_file_versions
douglas-xt Jan 27, 2026
9df5731
refactor(file-versions): extract create file version to action layer
douglas-xt Jan 20, 2026
79e6d82
fix(file-versions): remove unique constraint on file_id and network_f…
douglas-xt Jan 21, 2026
a74907a
refactor(file-versions): simplify retention policy logic
douglas-xt Jan 21, 2026
007dc7c
refactor(fv-action): improve test readability
douglas-xt Jan 23, 2026
770b254
refactor(fv): remove unnecessary unique constraint removal migration
douglas-xt Jan 26, 2026
8663f8e
test(file-versions): complete assertions and improve code clarity
douglas-xt Jan 27, 2026
6b8bc97
fix(file-versions): correct retention policy to make space for new ve…
douglas-xt Jan 27, 2026
f740508
feat(exception-filter): handle database connection errors with specif…
jzunigax2 Jan 27, 2026
8c1ec5e
feat(folder): implement request timeout handling for folder metadata …
jzunigax2 Jan 27, 2026
1174c54
feat: allow empty files in workspace
apsantiso Dec 18, 2025
ad54b53
fix: remove double empty file check, skipping flawed check on workspa…
jzunigax2 Jan 29, 2026
c1c0798
fix: update workspace empty file check to use owner ID
jzunigax2 Jan 29, 2026
fe03a53
fix: update tests to use workspace owner ID for empty file limit checks
jzunigax2 Jan 29, 2026
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
23 changes: 23 additions & 0 deletions migrations/20260116150327-add-user-id-to-file-versions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict';

const tableName = 'file_versions';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.addColumn(tableName, 'user_id', {
type: Sequelize.STRING(36),
allowNull: false,
references: {
model: 'users',
key: 'uuid',
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
});
},

async down(queryInterface) {
await queryInterface.removeColumn(tableName, 'user_id');
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.sequelize.query(`
CREATE INDEX CONCURRENTLY file_versions_user_id_exists_idx
ON file_versions (user_id)
WHERE status = 'EXISTS';
`);
},

async down(queryInterface, Sequelize) {
await queryInterface.sequelize.query(`
DROP INDEX CONCURRENTLY IF EXISTS file_versions_user_id_exists_idx;
`);
}
};
94 changes: 94 additions & 0 deletions migrations/20260116154444-update-file-versioning-limits.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
'use strict';

const FILE_VERSION_LABELS = {
ENABLED: 'file-version-enabled',
MAX_SIZE: 'file-version-max-size',
RETENTION_DAYS: 'file-version-retention-days',
MAX_NUMBER: 'file-version-max-number',
};

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface) {
await queryInterface.sequelize.query(
`UPDATE limits
SET value = 'false', updated_at = NOW()
FROM tiers_limits tl, tiers t
WHERE tl.limit_id = limits.id
AND t.id = tl.tier_id
AND t.label = 'essential_individual'
AND limits.label = :enabledLabel`,
{ replacements: { enabledLabel: FILE_VERSION_LABELS.ENABLED } },
);

await queryInterface.sequelize.query(
`UPDATE limits
SET value = '0', updated_at = NOW()
FROM tiers_limits tl, tiers t
WHERE tl.limit_id = limits.id
AND t.id = tl.tier_id
AND t.label = 'essential_individual'
AND limits.label IN (:counterLabels)`,
{
replacements: {
counterLabels: [
FILE_VERSION_LABELS.MAX_SIZE,
FILE_VERSION_LABELS.RETENTION_DAYS,
FILE_VERSION_LABELS.MAX_NUMBER,
],
},
},
);
},

async down(queryInterface) {
await queryInterface.sequelize.query(
`UPDATE limits
SET value = 'true', updated_at = NOW()
FROM tiers_limits tl, tiers t
WHERE tl.limit_id = limits.id
AND t.id = tl.tier_id
AND t.label = 'essential_individual'
AND limits.label = :enabledLabel`,
{ replacements: { enabledLabel: FILE_VERSION_LABELS.ENABLED } },
);

await queryInterface.sequelize.query(
`UPDATE limits
SET value = :maxSize, updated_at = NOW()
FROM tiers_limits tl, tiers t
WHERE tl.limit_id = limits.id
AND t.id = tl.tier_id
AND t.label = 'essential_individual'
AND limits.label = :label`,
{
replacements: {
maxSize: String(1 * 1024 * 1024),
label: FILE_VERSION_LABELS.MAX_SIZE,
},
},
);

await queryInterface.sequelize.query(
`UPDATE limits
SET value = '10', updated_at = NOW()
FROM tiers_limits tl, tiers t
WHERE tl.limit_id = limits.id
AND t.id = tl.tier_id
AND t.label = 'essential_individual'
AND limits.label = :label`,
{ replacements: { label: FILE_VERSION_LABELS.RETENTION_DAYS } },
);

await queryInterface.sequelize.query(
`UPDATE limits
SET value = '1', updated_at = NOW()
FROM tiers_limits tl, tiers t
WHERE tl.limit_id = limits.id
AND t.id = tl.tier_id
AND t.label = 'essential_individual'
AND limits.label = :label`,
{ replacements: { label: FILE_VERSION_LABELS.MAX_NUMBER } },
);
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,17 @@ module.exports = {
allowNull: false,
primaryKey: true,
},
file_id: {
type: Sequelize.UUID,
allowNull: true,
},
network_file_id: {
type: Sequelize.STRING,
allowNull: true,
allowNull: false,
},
size: {
type: Sequelize.BIGINT,
allowNull: true,
allowNull: false,
},
processed: {
type: Sequelize.BOOLEAN,
allowNull: true,
allowNull: false,
defaultValue: false,
},
created_at: {
Expand All @@ -41,7 +37,7 @@ module.exports = {
},
enqueued: {
type: Sequelize.BOOLEAN,
allowNull: true,
allowNull: false,
defaultValue: false,
},
enqueued_at: {
Expand Down
7 changes: 4 additions & 3 deletions migrations/20260116154629-deleted-file-versions-function.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ module.exports = {
LANGUAGE plpgsql
AS $function$
BEGIN
IF OLD.status != 'DELETED' AND NEW.status = 'DELETED' THEN
IF OLD.status != 'DELETED' AND NEW.status = 'DELETED'
AND NEW.network_file_id IS NOT NULL
AND NEW.size IS NOT NULL
AND NEW.size > 0 THEN
IF NOT EXISTS (SELECT 1 FROM deleted_file_versions WHERE file_version_id = NEW.id) THEN
INSERT INTO deleted_file_versions (
file_version_id,
file_id,
network_file_id,
size,
processed,
Expand All @@ -22,7 +24,6 @@ module.exports = {
updated_at
) VALUES (
NEW.id,
NEW.file_id,
NEW.network_file_id,
NEW.size,
false,
Expand Down
140 changes: 140 additions & 0 deletions migrations/20260120204639-update-cli-access-limits.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
'use strict';

const CLI_LIMIT_LABEL = 'cli-access';

const CLI_DISABLED_TIER_LABELS = [
// Legacy tiers
'200gb_individual',
'2tb_individual',
'5tb_individual',
'10tb_individual',
// Current tiers (except ultimate_individual and pro_business)
'essential_individual',
'premium_individual',
'standard_business',
'essential_lifetime_individual',
'premium_lifetime_individual',
'ultimate_lifetime_individual',
];

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface) {
const transaction = await queryInterface.sequelize.transaction();

try {
const [limits] = await queryInterface.sequelize.query(
`SELECT id, value FROM limits WHERE label = :limitLabel`,
{ replacements: { limitLabel: CLI_LIMIT_LABEL }, transaction },
);

if (limits.length === 0) {
throw new Error('CLI access limits not found in database');
}

const disabledLimitId = limits.find((l) => l.value === 'false')?.id;
const enabledLimitId = limits.find((l) => l.value === 'true')?.id;

if (!disabledLimitId || !enabledLimitId) {
throw new Error('CLI access enabled or disabled limit not found');
}

const [tiersToDisable] = await queryInterface.sequelize.query(
`SELECT id, label FROM tiers WHERE label IN (:labels)`,
{
replacements: { labels: CLI_DISABLED_TIER_LABELS },
transaction,
},
);

if (tiersToDisable.length === 0) {
console.log(
'No tiers found to restrict, skipping CLI access restriction',
);
await transaction.commit();
return;
}

const tierIds = tiersToDisable.map((t) => t.id);

console.log(
`Restricting CLI access for ${tiersToDisable.length} tiers: ${tiersToDisable.map((t) => t.label).join(', ')}`,
);

await queryInterface.sequelize.query(
`UPDATE tiers_limits
SET limit_id = :disabledLimitId, updated_at = NOW()
WHERE tier_id IN (:tierIds)
AND limit_id = :enabledLimitId`,
{
replacements: {
disabledLimitId,
enabledLimitId,
tierIds,
},
transaction,
},
);

await transaction.commit();

console.log('Successfully restricted CLI access for specified tiers');
} catch (error) {
await transaction.rollback();
throw error;
}
},

async down(queryInterface) {
const transaction = await queryInterface.sequelize.transaction();

try {
const [limits] = await queryInterface.sequelize.query(
`SELECT id, value FROM limits WHERE label = :limitLabel`,
{ replacements: { limitLabel: CLI_LIMIT_LABEL }, transaction },
);

const disabledLimitId = limits.find((l) => l.value === 'false')?.id;
const enabledLimitId = limits.find((l) => l.value === 'true')?.id;

if (!disabledLimitId || !enabledLimitId) {
throw new Error('CLI access limits not found');
}

const [tiers] = await queryInterface.sequelize.query(
`SELECT id FROM tiers WHERE label IN (:labels)`,
{
replacements: { labels: CLI_DISABLED_TIER_LABELS },
transaction,
},
);

if (tiers.length === 0) {
await transaction.commit();
return;
}

const tierIds = tiers.map((t) => t.id);

await queryInterface.sequelize.query(
`UPDATE tiers_limits
SET limit_id = :enabledLimitId, updated_at = NOW()
WHERE tier_id IN (:tierIds)
AND limit_id = :disabledLimitId`,
{
replacements: {
disabledLimitId,
enabledLimitId,
tierIds,
},
transaction,
},
);

await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
},
};
Loading