-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(env): load and parse database url in runtime
- Loading branch information
Victor Korzunin
committed
Aug 21, 2021
1 parent
d6e3c33
commit efe8b97
Showing
11 changed files
with
458 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import fs from 'fs'; | ||
import path from 'path'; | ||
|
||
type ItemType = 'd' | 'f' | 'l'; | ||
type Handler = (base: string, item: string, type: ItemType) => boolean | string; | ||
|
||
/** | ||
* Transform a dirent to a file type | ||
* @param dirent | ||
* @returns | ||
*/ | ||
function direntToType(dirent: fs.Dirent | fs.Stats) { | ||
return dirent.isFile() ? 'f' : dirent.isDirectory() ? 'd' : dirent.isSymbolicLink() ? 'l' : undefined; | ||
} | ||
|
||
/** | ||
* Is true if at least one matched | ||
* @param string to match aigainst | ||
* @param regexs to be matched with | ||
* @returns | ||
*/ | ||
function isMatched(string: string, regexs: (RegExp | string)[]) { | ||
for (const regex of regexs) { | ||
if (typeof regex === 'string') { | ||
if (string.includes(regex)) { | ||
return true; | ||
} | ||
} else if (regex.exec(string)) { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/** | ||
* Find paths that match a set of regexes | ||
* @param root to start from | ||
* @param match to match against | ||
* @param types to select files, folders, links | ||
* @param deep to recurse in the directory tree | ||
* @param limit to limit the results | ||
* @param handler to further filter results | ||
* @param found to add to already found | ||
* @param seen to add to already seen | ||
* @returns found paths (symlinks preserved) | ||
*/ | ||
export function findSync( | ||
root: string, | ||
match: (RegExp | string)[], | ||
types: ('f' | 'd' | 'l')[] = ['f', 'd', 'l'], | ||
deep: ('d' | 'l')[] = [], | ||
limit: number = Infinity, | ||
handler: Handler = () => true, | ||
found: string[] = [], | ||
seen: Record<string, true> = {} | ||
) { | ||
try { | ||
const realRoot = fs.realpathSync(root); | ||
|
||
// we make sure not to loop infinitely | ||
if (seen[realRoot]) { | ||
return found; | ||
} | ||
|
||
// we stop if we found enough results | ||
if (limit - found.length <= 0) { | ||
return found; | ||
} | ||
|
||
// we check that the root is a directory | ||
if (direntToType(fs.statSync(realRoot)) !== 'd') { | ||
return found; | ||
} | ||
|
||
// we list the items in the current root | ||
const items = fs.readdirSync(root, { withFileTypes: true }); | ||
|
||
//seen[realRoot] = true | ||
for (const item of items) { | ||
// we get the file info for each item | ||
const itemName = item.name; | ||
const itemType = direntToType(item); | ||
const itemPath = path.join(root, item.name); | ||
|
||
// if the item is one of the selected | ||
if (itemType && types.includes(itemType)) { | ||
// if the path of an item has matched | ||
if (isMatched(itemPath, match)) { | ||
const value = handler(root, itemName, itemType); | ||
|
||
// if we changed the path value | ||
if (typeof value === 'string') { | ||
found.push(value); | ||
} | ||
// if we kept the default path | ||
else if (value === true) { | ||
found.push(itemPath); | ||
} | ||
} | ||
} | ||
|
||
if (deep.includes(itemType as any)) { | ||
// dive within the directory tree | ||
// we recurse and continue mutating `found` | ||
findSync(itemPath, match, types, deep, limit, handler, found, seen); | ||
} | ||
} | ||
} catch {} | ||
|
||
return found; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './find'; | ||
export * from './parseDatabaseUrl'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import url from 'url'; | ||
import querystring from 'querystring'; | ||
|
||
export function parseDatabaseUrl(databaseUrl: string) { | ||
const parsedUrl = url.parse(databaseUrl, false, true); | ||
|
||
// Query parameters end up directly in the configuration. | ||
const config = querystring.parse(parsedUrl.query); | ||
|
||
config.driver = (parsedUrl.protocol || 'sqlite3:') | ||
// The protocol coming from url.parse() has a trailing : | ||
.replace(/\:$/, ''); | ||
|
||
// Cloud Foundry will sometimes set a 'mysql2' scheme instead of 'mysql'. | ||
if (config.driver == 'mysql2') config.driver = 'mysql'; | ||
|
||
// url.parse() produces an "auth" that looks like "user:password". No | ||
// individual fields, unfortunately. | ||
if (parsedUrl.auth) { | ||
const userPassword = parsedUrl.auth.split(':', 2); | ||
config.user = userPassword[0]; | ||
if (userPassword.length > 1) { | ||
config.password = userPassword[1]; | ||
} | ||
} | ||
|
||
if (config.driver === 'sqlite3') { | ||
if (parsedUrl.hostname) { | ||
if (parsedUrl.pathname) { | ||
// Relative path. | ||
config.filename = parsedUrl.hostname + parsedUrl.pathname; | ||
} else { | ||
// Just a filename. | ||
config.filename = parsedUrl.hostname; | ||
} | ||
} else { | ||
// Absolute path. | ||
config.filename = parsedUrl.pathname; | ||
} | ||
} else { | ||
// Some drivers (e.g., redis) don't have database names. | ||
if (parsedUrl.pathname) { | ||
config.database = parsedUrl.pathname.replace(/^\//, '').replace(/\/$/, ''); | ||
} | ||
|
||
if (parsedUrl.hostname) config.host = parsedUrl.hostname; | ||
if (parsedUrl.port) config.port = parsedUrl.port; | ||
} | ||
|
||
return config; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.