feat(cloud): add find-workspace and email verification endpoints (#2020)

* feat: add find-workspace and email verification endpoints
* sync
This commit is contained in:
Philip Okugbe
2026-03-14 13:36:30 +00:00
committed by GitHub
parent d0ed6865cb
commit 97c459be67
25 changed files with 479 additions and 13 deletions
@@ -0,0 +1,34 @@
// MIT - https://github.com/typestack/class-validator/pull/2626
import isISO6391Validator from 'validator/lib/isISO6391';
import { buildMessage, ValidateBy, ValidationOptions } from 'class-validator';
export const IS_ISO6391 = 'isISO6391';
/**
* Check if the string is a valid [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) officially assigned language code.
*/
export function isISO6391(value: unknown): boolean {
return typeof value === 'string' && isISO6391Validator(value);
}
/**
* Check if the string is a valid [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) officially assigned language code.
*/
export function IsISO6391(
validationOptions?: ValidationOptions,
): PropertyDecorator {
return ValidateBy(
{
name: IS_ISO6391,
validator: {
validate: (value, args): boolean => isISO6391(value),
defaultMessage: buildMessage(
(eachPrefix) =>
eachPrefix + '$property must be a valid ISO 639-1 language code',
validationOptions,
),
},
},
validationOptions,
);
}
@@ -0,0 +1,142 @@
import { containsDomain } from './no-urls.validator';
// containsDomain returns true if value contains a domain-like pattern
// The full NoUrls validator also checks for https:// URLs separately
describe('containsDomain', () => {
describe('bare domains with real TLDs — should block', () => {
it.each([
'example.com',
'example.net',
'example.org',
'example.io',
'example.co',
'example.dev',
'example.app',
'example.me',
'example.info',
'example.tech',
'example.aero',
'example.cloud',
'example.museum',
'example.abc',
'example.uk',
'example.de',
'example.fr',
'example.ru',
])('blocks "%s"', (value) => {
expect(containsDomain(value)).toBe(true);
});
});
describe('domains with paths — should block', () => {
it.each([
'example.com/reset',
'example.com/reset-password',
'click example.com/page',
'go to example.net/login',
])('blocks "%s"', (value) => {
expect(containsDomain(value)).toBe(true);
});
});
describe('multi-part domains — should block', () => {
it.each([
'Foo.com.net',
'Foo.com.',
'Foo.mine.net',
'Foo.mine.ne',
'sub.example.com',
'login.example.co.uk',
])('blocks "%s"', (value) => {
expect(containsDomain(value)).toBe(true);
});
});
describe('domain in sentence — should block', () => {
it.each([
'Reset your password at example.com',
'URGENT click example.com/reset',
'Visit example.org for details',
'go to mysite.io now',
])('blocks "%s"', (value) => {
expect(containsDomain(value)).toBe(true);
});
});
describe('case insensitive — should block', () => {
it.each(['EXAMPLE.COM', 'Example.Com', 'example.COM'])('blocks "%s"', (value) => {
expect(containsDomain(value)).toBe(true);
});
});
describe('fake TLDs — should allow', () => {
it.each([
'Foo.mine',
'Foo.blarg',
'Foo.qqq',
'Foo.zz',
'Foo.abcd',
'Foo.abcde',
'Foo.abcdef',
'Foo.abcdefg',
])('allows "%s"', (value) => {
expect(containsDomain(value)).toBe(false);
});
});
describe('too short suffix — should allow', () => {
it.each(['Foo.a', 'Foo.c', 'A.B'])('allows "%s"', (value) => {
expect(containsDomain(value)).toBe(false);
});
});
describe('multi-part with fake TLD — should allow', () => {
it.each(['Foo.mine.', 'Foo.mine.n'])('allows "%s"', (value) => {
expect(containsDomain(value)).toBe(false);
});
});
describe('emails — should allow', () => {
it.each([
'user@example.com',
'admin@company.org',
'test@sub.domain.co.uk',
])('allows "%s"', (value) => {
expect(containsDomain(value)).toBe(false);
});
});
describe('normal names — should allow', () => {
it.each([
'John Smith',
'Dr. Smith',
'A. B. Charlie',
'John',
'Mary Jane',
"O'Brien",
'Jean-Pierre',
'José García',
])('allows "%s"', (value) => {
expect(containsDomain(value)).toBe(false);
});
});
describe('IP addresses — should allow', () => {
it.each(['192.168.1.1', '10.0.0.1', '127.0.0.1'])(
'allows "%s"',
(value) => {
expect(containsDomain(value)).toBe(false);
},
);
});
describe('edge cases — should allow', () => {
it.each(['', ' ', '.', '..', 'hello', '.com', 'a.b'])(
'allows "%s"',
(value) => {
expect(containsDomain(value)).toBe(false);
},
);
});
});
@@ -0,0 +1,42 @@
import { registerDecorator, ValidationOptions } from 'class-validator';
import * as tlds from 'tlds';
const URL_PATTERN = /https?:\/\//i;
const tldSet = new Set(tlds.map((t) => t.toLowerCase()));
export function containsDomain(value: string): boolean {
const tokens = value.split(/\s+/);
for (const token of tokens) {
if (token.includes('@')) continue;
const segments = token.split('.');
for (let i = 1; i < segments.length; i++) {
const suffix = segments[i].replace(/[^\w].*/g, '');
if (segments[i - 1] && suffix && tldSet.has(suffix.toLowerCase())) {
return true;
}
}
}
return false;
}
export function NoUrls(validationOptions?: ValidationOptions) {
return function (object: object, propertyName: string) {
registerDecorator({
name: 'noUrls',
target: object.constructor,
propertyName,
options: {
message: 'Must not contain URLs or domain names',
...validationOptions,
},
validator: {
validate(value: unknown) {
if (typeof value !== 'string') return true;
if (URL_PATTERN.test(value)) return false;
if (containsDomain(value)) return false;
return true;
},
},
});
};
}