feat: add link generation support (short/sentence links) + wordlist
All checks were successful
Update changelog / changelog (push) Successful in 26s
All checks were successful
Update changelog / changelog (push) Successful in 26s
This commit is contained in:
137
src/services/linkService.ts
Normal file
137
src/services/linkService.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { Link } from '../entities/Link';
|
||||
import { User } from '../entities/User';
|
||||
import { AppDataSource } from '../data-source';
|
||||
import { getEnvString } from '../tools/jwt';
|
||||
|
||||
export type IdResponse = {
|
||||
id: number;
|
||||
exists: boolean;
|
||||
};
|
||||
|
||||
export class LinkService {
|
||||
dataSource = AppDataSource;
|
||||
linkRepo = this.dataSource.getRepository(Link);
|
||||
|
||||
// Retrieve config to check whether subdomains are allowed
|
||||
useSubdomains: boolean = getEnvString('useSubdomains', true) === 'true';
|
||||
|
||||
/**
|
||||
* Simply insert a new link entity anonymously.
|
||||
*
|
||||
* @param {Link} link The link to insert
|
||||
*/
|
||||
async addAnonymous(link: Link): Promise<IdResponse> {
|
||||
let result: IdResponse = { id: -1, exists: false };
|
||||
|
||||
// Sanity check: don't allow for adding links
|
||||
// with subdomains if server has it disabled.
|
||||
if (link.subdomain && !this.useSubdomains) link.subdomain = null;
|
||||
|
||||
// Check if entry can be inserted.
|
||||
if (await this.canInsert(link)) {
|
||||
await this.linkRepo.insert(link);
|
||||
|
||||
// Then get new link's ID
|
||||
const insertedLink: Link[] = await this.linkRepo.findBy({
|
||||
shortUri: link.shortUri,
|
||||
fullUrl: link.fullUrl
|
||||
});
|
||||
|
||||
// Return appropriate id (or error)
|
||||
if (insertedLink.length !== 1) {
|
||||
result.id = -2;
|
||||
result.exists = false;
|
||||
} else {
|
||||
result.id = insertedLink[0].id;
|
||||
result.exists = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new link entity, and links it with passed user.
|
||||
*
|
||||
* USE ONLY FOR AUTHENTICATED USERS. For unauthenticated users
|
||||
* use addAnonymous() instead. Returns IdResponse, containing
|
||||
* exists field indicating if an existing field has been found,
|
||||
* and if so, what is it's id. If exists is false, ID of the
|
||||
* newly created entity gets returned.
|
||||
*
|
||||
* Errors: an error ocurred if ID is negative.
|
||||
*
|
||||
* This can be:
|
||||
*
|
||||
* - -1 - link can't be inserted (because an entry with shortUri or shortUri+subdomain combo exist),
|
||||
*
|
||||
* - -2 - no conflicting entry exists but transaction failed anyway.
|
||||
*
|
||||
* @param {Link} link The link
|
||||
* @param {User} user The user
|
||||
* @return {Promise<IdResponse>} Dictionary containing link ID and whether it already existed, or was just inserted
|
||||
*/
|
||||
async addIfNew(link: Link, user: User): Promise<IdResponse> {
|
||||
|
||||
let result: IdResponse = { id: -1, exists: false };
|
||||
|
||||
// If no conflicts are found,
|
||||
// proceed with creating a new link entry.
|
||||
if (await this.canInsert(link)) {
|
||||
// Commit a transaction
|
||||
this.dataSource.transaction(async (t) => {
|
||||
link.author = user;
|
||||
user.links.push(link);
|
||||
t.insert(Link, link);
|
||||
t.save(user);
|
||||
});
|
||||
|
||||
// Then get new link's ID
|
||||
const insertedLink: Link[] = await this.linkRepo.findBy({
|
||||
shortUri: link.shortUri,
|
||||
fullUrl: link.fullUrl
|
||||
});
|
||||
|
||||
// Return appropriate id (or error)
|
||||
if (insertedLink.length !== 1) {
|
||||
result.id = -2;
|
||||
result.exists = false;
|
||||
} else {
|
||||
result.id = insertedLink[0].id;
|
||||
result.exists = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async canInsert(link: Link): Promise<boolean> {
|
||||
let shortUriExists: boolean = true
|
||||
|
||||
// If both subdomains are enabled, and user
|
||||
// provided a subdomain, find if a record exists
|
||||
// that either has the exact shortUri
|
||||
// or shortUri and subdomain combo.
|
||||
// If any of these turns out to be true, the request
|
||||
// must be invalidated and we can't proceed
|
||||
// with creation of a new link.
|
||||
if (this.useSubdomains && link.subdomain)
|
||||
{
|
||||
shortUriExists = await this.linkRepo.existsBy({
|
||||
shortUri: link.shortUri,
|
||||
subdomain: link.subdomain
|
||||
});
|
||||
}
|
||||
// If custom subdomains are disabled, fallback to
|
||||
// checking only by the URIs - thus discarding
|
||||
// any possible subdomains.
|
||||
else
|
||||
{
|
||||
shortUriExists = await this.linkRepo.existsBy({ shortUri: link.shortUri });
|
||||
}
|
||||
|
||||
return !shortUriExists;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user