Skip to content

Commit 08ca1eb

Browse files
authored
chore: refactor storage api - code quality & reusability (#209)
1 parent f6e2f23 commit 08ca1eb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+3293
-2776
lines changed

src/admin-app.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
import fastify, { FastifyInstance, FastifyServerOptions } from 'fastify'
22
import { default as metrics } from 'fastify-metrics'
33
import { Registry } from 'prom-client'
4-
import tenantRoutes from './routes/tenant'
5-
import logRequest from './plugins/log-request'
4+
import { routes, plugins } from './http'
65

76
export interface AdminOptions {
87
register?: Registry
98
}
109

1110
const build = (opts: FastifyServerOptions = {}, adminOpts: AdminOptions = {}): FastifyInstance => {
1211
const app = fastify(opts)
13-
app.register(logRequest({ excludeUrls: ['/status'] }))
14-
app.register(tenantRoutes, { prefix: 'tenants' })
12+
app.register(plugins.logRequest({ excludeUrls: ['/status'] }))
13+
app.register(routes.tenant, { prefix: 'tenants' })
1514
app.register(metrics, {
1615
endpoint: '/metrics',
1716
defaultMetrics: {

src/app.ts

+11-16
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
import fastify, { FastifyInstance, FastifyServerOptions } from 'fastify'
22
import fastifyMultipart from '@fastify/multipart'
33
import fastifySwagger from '@fastify/swagger'
4-
import bucketRoutes from './routes/bucket/'
5-
import objectRoutes from './routes/object'
6-
import renderRoutes from './routes/render'
7-
import { authSchema } from './schemas/auth'
8-
import { errorSchema } from './schemas/error'
9-
import logTenantId from './plugins/log-tenant-id'
10-
import tenantId from './plugins/tenant-id'
11-
import logRequest from './plugins/log-request'
4+
import { routes, schemas, plugins, setErrorHandler } from './http'
125

136
interface buildOpts extends FastifyServerOptions {
147
exposeDocs?: boolean
@@ -50,15 +43,17 @@ const build = (opts: buildOpts = {}): FastifyInstance => {
5043
}
5144

5245
// add in common schemas
53-
app.addSchema(authSchema)
54-
app.addSchema(errorSchema)
46+
app.addSchema(schemas.authSchema)
47+
app.addSchema(schemas.errorSchema)
5548

56-
app.register(tenantId)
57-
app.register(logTenantId)
58-
app.register(logRequest({ excludeUrls: ['/status'] }))
59-
app.register(bucketRoutes, { prefix: 'bucket' })
60-
app.register(objectRoutes, { prefix: 'object' })
61-
app.register(renderRoutes, { prefix: 'render' })
49+
app.register(plugins.tenantId)
50+
app.register(plugins.logTenantId)
51+
app.register(plugins.logRequest({ excludeUrls: ['/status'] }))
52+
app.register(routes.bucket, { prefix: 'bucket' })
53+
app.register(routes.object, { prefix: 'object' })
54+
app.register(routes.render, { prefix: 'render' })
55+
56+
setErrorHandler(app)
6257

6358
app.get('/status', async (request, response) => response.status(200).send())
6459

Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
import AES from 'crypto-js/aes'
22
import Utf8 from 'crypto-js/enc-utf8'
3-
import { getConfig } from './config'
3+
import { getConfig } from '../config'
44

55
const { encryptionKey } = getConfig()
66

7+
/**
8+
* Decrypts a text with the configured encryption key via ENCRYPTION_KEY env
9+
* @param ciphertext
10+
*/
711
export function decrypt(ciphertext: string): string {
812
return AES.decrypt(ciphertext, encryptionKey).toString(Utf8)
913
}
1014

15+
/**
16+
* Encrypts a text with the configured encryption key via ENCRYPTION_KEY env
17+
* @param plaintext
18+
*/
1119
export function encrypt(plaintext: string): string {
1220
return AES.encrypt(plaintext, encryptionKey).toString()
1321
}

src/auth/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './crypto'
2+
export * from './jwt'

src/auth/jwt.ts

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { getJwtSecret as getJwtSecretForTenant } from '../database/tenant'
2+
import jwt from 'jsonwebtoken'
3+
import { getConfig } from '../config'
4+
5+
const { isMultitenant, jwtSecret, jwtAlgorithm } = getConfig()
6+
7+
interface jwtInterface {
8+
sub: string
9+
}
10+
11+
export type SignedToken = {
12+
url: string
13+
}
14+
15+
/**
16+
* Gets the JWT secret key from the env PGRST_JWT_SECRET when running in single-tenant
17+
* or querying the multi-tenant database by the given tenantId
18+
* @param tenantId
19+
*/
20+
export async function getJwtSecret(tenantId: string): Promise<string> {
21+
let secret = jwtSecret
22+
if (isMultitenant) {
23+
secret = await getJwtSecretForTenant(tenantId)
24+
}
25+
return secret
26+
}
27+
28+
/**
29+
* Verifies if a JWT is valid
30+
* @param token
31+
* @param secret
32+
*/
33+
export function verifyJWT(
34+
token: string,
35+
secret: string
36+
): Promise<string | jwt.JwtPayload | undefined> {
37+
return new Promise((resolve, reject) => {
38+
jwt.verify(token, secret, { algorithms: [jwtAlgorithm as jwt.Algorithm] }, (err, decoded) => {
39+
if (err) return reject(err)
40+
resolve(decoded)
41+
})
42+
})
43+
}
44+
45+
/**
46+
* Sign a JWT
47+
* @param payload
48+
* @param secret
49+
* @param expiresIn
50+
*/
51+
export function signJWT(
52+
payload: string | object | Buffer,
53+
secret: string,
54+
expiresIn: string | number
55+
): Promise<string | undefined> {
56+
return new Promise((resolve, reject) => {
57+
jwt.sign(
58+
payload,
59+
secret,
60+
{ expiresIn, algorithm: jwtAlgorithm as jwt.Algorithm },
61+
(err, token) => {
62+
if (err) return reject(err)
63+
resolve(token)
64+
}
65+
)
66+
})
67+
}
68+
69+
/**
70+
* Extract the owner (user) from the provided JWT
71+
* @param token
72+
* @param secret
73+
*/
74+
export async function getOwner(token: string, secret: string): Promise<string | undefined> {
75+
const decodedJWT = await verifyJWT(token, secret)
76+
return (decodedJWT as jwtInterface)?.sub
77+
}

src/backend/file.ts

-126
This file was deleted.

src/backend/generic.ts

-45
This file was deleted.

src/utils/config.ts renamed to src/config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export function getConfig(): StorageConfigType {
8585
'',
8686
urlLengthLimit: Number(getOptionalConfigFromEnv('URL_LENGTH_LIMIT')) || 7_500,
8787
xForwardedHostRegExp: getOptionalConfigFromEnv('X_FORWARDED_HOST_REGEXP'),
88-
logLevel: getOptionalConfigFromEnv('LOG_LEVEL') || 'trace',
88+
logLevel: getOptionalConfigFromEnv('LOG_LEVEL') || 'info',
8989
logflareEnabled: getOptionalConfigFromEnv('LOGFLARE_ENABLED') === 'true',
9090
logflareApiKey: getOptionalConfigFromEnv('LOGFLARE_API_KEY'),
9191
logflareSourceToken: getOptionalConfigFromEnv('LOGFLARE_SOURCE_TOKEN'),

src/database/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * as migrate from './migrate'
2+
export * as multiTenant from './multitenant-db'
3+
export * from './postgrest'
4+
export * as tenant from './tenant'

0 commit comments

Comments
 (0)