-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* text=auto eol=lf |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Large diffs are not rendered by default.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
import * as grpc from '@grpc/grpc-js'; | ||
|
||
import { ILogObj, Logger } from 'tslog'; | ||
|
||
import { | ||
ByokQuery, | ||
ByokResult, | ||
ByokEncResult, | ||
ByokEncJwk, | ||
UnimplementedByokService, | ||
} from '@packages/grpc/__generated__/am2mxm-api'; | ||
import { Empty } from '@packages/grpc/__generated__/google/protobuf/empty'; | ||
|
||
import DatabaseUtil from '../utils/db.util'; | ||
import { requestToMxm } from '../utils/request.util'; | ||
import { decryptByokKey, getNewKeyPair } from '../utils/byokKey.util'; | ||
|
||
import { isNilOrBlank } from '../utils/es-toolkit-inspired/isNilOrBlank'; | ||
|
||
import { BadRequestError } from '../exceptions/BadRequest.exception'; | ||
|
||
import { EMxmUrlType } from '../types/mxmUrl.type'; | ||
|
||
const logger: Logger<ILogObj> = new Logger({ | ||
name: 'lunaiz.am2mxm.api.v1.Byok', | ||
type: 'pretty', | ||
}); | ||
|
||
const database = new DatabaseUtil(); | ||
|
||
export class ByokService extends UnimplementedByokService { | ||
// eslint-disable-next-line class-methods-use-this | ||
async getEncPublicKey( | ||
_: grpc.ServerUnaryCall<Empty, ByokEncResult>, | ||
callback: grpc.sendUnaryData<ByokEncResult>, | ||
): Promise<void> { | ||
try { | ||
// Get RSA Public Key for encrypt of BYOK key | ||
|
||
const knex = database.connection; | ||
const { sessionKey, publicKey } = await getNewKeyPair(knex); | ||
|
||
return callback( | ||
null, | ||
new ByokEncResult({ | ||
session_key: sessionKey, | ||
public_key: new ByokEncJwk({ | ||
...(await crypto.subtle.exportKey('jwk', publicKey)), | ||
}), | ||
}), | ||
); | ||
} catch (error) { | ||
logger.error(error); | ||
|
||
if (error instanceof BadRequestError) { | ||
return callback({ | ||
code: grpc.status.INVALID_ARGUMENT, | ||
details: error.message, | ||
} as grpc.ServiceError); | ||
} | ||
|
||
return callback({ | ||
code: grpc.status.INTERNAL, | ||
details: (error as Error).message || 'Internal Server Error', | ||
} as grpc.ServiceError); | ||
} | ||
} | ||
|
||
// eslint-disable-next-line class-methods-use-this | ||
async checkMxMKeyValidity( | ||
call: grpc.ServerUnaryCall<ByokQuery, ByokResult>, | ||
callback: grpc.sendUnaryData<ByokResult>, | ||
): Promise<void> { | ||
try { | ||
// Validate Musixmatch BYOK key | ||
const knex = database.connection; | ||
|
||
if (isNilOrBlank(call.request.session_key)) { | ||
throw new BadRequestError( | ||
'Request missing required field: session_key', | ||
); | ||
} | ||
|
||
if (isNilOrBlank(call.request.byok_key)) { | ||
throw new BadRequestError('Request missing required field: byok_key'); | ||
} | ||
|
||
const byokKey = await decryptByokKey( | ||
call.request.session_key, | ||
call.request.byok_key, | ||
knex, | ||
); | ||
|
||
if (!byokKey) { | ||
return callback(null, new ByokResult({ valid: false })); | ||
} | ||
|
||
const r = await requestToMxm( | ||
{ | ||
type: EMxmUrlType.TRACK, | ||
isrc: 'GBARL9300135', // Never gonna give you up | ||
}, | ||
byokKey, | ||
); | ||
|
||
return callback(null, new ByokResult({ valid: r.ok })); | ||
} catch (error) { | ||
logger.error(error); | ||
|
||
if (error instanceof BadRequestError) { | ||
return callback({ | ||
code: grpc.status.INVALID_ARGUMENT, | ||
details: error.message, | ||
} as grpc.ServiceError); | ||
} | ||
|
||
return callback({ | ||
code: grpc.status.INTERNAL, | ||
details: (error as Error).message || 'Internal Server Error', | ||
} as grpc.ServiceError); | ||
} | ||
} | ||
|
||
// eslint-disable-next-line class-methods-use-this | ||
async checkAMKeyValidity( | ||
call: grpc.ServerUnaryCall<ByokQuery, ByokResult>, | ||
callback: grpc.sendUnaryData<ByokResult>, | ||
): Promise<void> { | ||
try { | ||
// Validate Apple Music BYOK key | ||
const knex = database.connection; | ||
|
||
if (isNilOrBlank(call.request.session_key)) { | ||
throw new BadRequestError( | ||
'Request missing required field: session_key', | ||
); | ||
} | ||
|
||
if (isNilOrBlank(call.request.byok_key)) { | ||
throw new BadRequestError('Request missing required field: byok_key'); | ||
} | ||
|
||
const byokKey = await decryptByokKey( | ||
call.request.session_key, | ||
call.request.byok_key, | ||
knex, | ||
); | ||
|
||
if (!byokKey) { | ||
return callback(null, new ByokResult({ valid: false })); | ||
} | ||
|
||
const r = await fetch('https://api.music.apple.com/v1/test', { | ||
headers: { | ||
Authorization: `Bearer ${byokKey}`, | ||
}, | ||
}); | ||
|
||
return callback(null, new ByokResult({ valid: r.ok })); | ||
} catch (error) { | ||
logger.error(error); | ||
|
||
if (error instanceof BadRequestError) { | ||
return callback({ | ||
code: grpc.status.INVALID_ARGUMENT, | ||
details: error.message, | ||
} as grpc.ServiceError); | ||
} | ||
|
||
return callback({ | ||
code: grpc.status.INTERNAL, | ||
details: (error as Error).message || 'Internal Server Error', | ||
} as grpc.ServiceError); | ||
} | ||
} | ||
} | ||
|
||
export default ByokService; |