Skip to content

Commit

Permalink
🚧 wip: BYOK implement
Browse files Browse the repository at this point in the history
  • Loading branch information
kms0219kms committed Oct 23, 2024
1 parent 9de132b commit 568bba7
Show file tree
Hide file tree
Showing 51 changed files with 3,711 additions and 934 deletions.
5 changes: 4 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,17 @@
"plugin:tailwindcss/recommended"
],
"rules": {
"react/react-in-jsx-scope": "off",
"react/jsx-uses-react": "off",
"tailwindcss/classnames-order": [
"warn",
{
"officialSorting": true
}
],
"implicit-arrow-linebreak": "off",
"operator-linebreak": "off"
"operator-linebreak": "off",
"no-nested-ternary": "off"
},
"settings": {
"react": {
Expand Down
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
5 changes: 5 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/am-to-mxm.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

603 changes: 603 additions & 0 deletions .idea/codeStyles/Project.xml

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions .idea/codeStyles/codeStyleConfig.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/prettier.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/backend/envoy-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,5 @@ static_resources:
- endpoint:
address:
socket_address:
address: 172.29.64.1
address: 127.0.0.1
port_value: 52345
7 changes: 3 additions & 4 deletions apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@
},
"dependencies": {
"@grpc/grpc-js": "^1.11.3",
"@keyv/sqlite": "^4.0.1",
"@packages/grpc": "workspace:^",
"@types/apple-music-api": "^0.4.4",
"cache-manager": "^6.1.0",
"better-sqlite3": "^11.3.0",
"es-toolkit": "^1.23.0",
"jose": "^5.9.3",
"keyv": "^5.0.3",
"jose": "^5.9.4",
"knex": "^3.1.0",
"tslog": "^4.9.3"
},
"devDependencies": {
Expand Down
24 changes: 6 additions & 18 deletions apps/backend/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,10 @@
/*
*
* Copyright 2015 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import { ILogObj, Logger } from 'tslog';
import * as grpc from '@grpc/grpc-js';

import { SearchService } from './services/search.service';
import { ByokService } from './services/byok.service';

import DatabaseUtil from './utils/db.util';

try {
process.loadEnvFile('.env');
Expand All @@ -32,8 +17,11 @@ const logger: Logger<ILogObj> = new Logger({

const server = new grpc.Server();
server.addService(SearchService.definition, new SearchService());
server.addService(ByokService.definition, new ByokService());

if (require.main === module) {
new DatabaseUtil().init();

server.bindAsync(
'0.0.0.0:52345',
grpc.ServerCredentials.createInsecure(),
Expand Down
178 changes: 178 additions & 0 deletions apps/backend/src/services/byok.service.ts
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;
Loading

0 comments on commit 568bba7

Please sign in to comment.