mirror of
https://github.com/RocketChat/Rocket.Chat.git
synced 2025-12-28 06:47:25 +00:00
Chore: Move micro services to packages (#26884)
This commit is contained in:
parent
9625a994b8
commit
4ab9c35f61
218
.github/workflows/build_and_test.yml
vendored
218
.github/workflows/build_and_test.yml
vendored
@ -156,9 +156,8 @@ jobs:
|
||||
|
||||
- name: Reset Meteor
|
||||
if: startsWith(github.ref, 'refs/tags/') == 'true' || github.ref == 'refs/heads/develop'
|
||||
run: |
|
||||
cd ./apps/meteor
|
||||
meteor reset
|
||||
working-directory: ./apps/meteor
|
||||
run: meteor reset
|
||||
|
||||
- name: Build Rocket.Chat From Pull Request
|
||||
if: startsWith(github.ref, 'refs/pull/') == true
|
||||
@ -232,6 +231,29 @@ jobs:
|
||||
mongodb-version: ${{ matrix.mongodb-version }}
|
||||
mongodb-replica-set: rs0
|
||||
|
||||
- name: Docker env vars
|
||||
id: docker-env
|
||||
run: |
|
||||
LOWERCASE_REPOSITORY=$(echo "${{ github.repository_owner }}" | tr "[:upper:]" "[:lower:]")
|
||||
|
||||
echo "LOWERCASE_REPOSITORY: ${LOWERCASE_REPOSITORY}"
|
||||
echo "::set-output name=lowercase-repo::${LOWERCASE_REPOSITORY}"
|
||||
|
||||
# test alpine image on mongo 5.0 (no special reason to be mongo 5.0 but we need to test alpine at least once)
|
||||
if [[ '${{ matrix.mongodb-version }}' = '5.0' ]]; then
|
||||
RC_DOCKERFILE="${{ github.workspace }}/apps/meteor/.docker/Dockerfile.alpine"
|
||||
RC_DOCKER_TAG="${{ needs.release-versions.outputs.gh-docker-tag }}.alpine"
|
||||
else
|
||||
RC_DOCKERFILE="${{ github.workspace }}/apps/meteor/.docker/Dockerfile"
|
||||
RC_DOCKER_TAG="${{ needs.release-versions.outputs.gh-docker-tag }}.official"
|
||||
fi;
|
||||
|
||||
echo "RC_DOCKERFILE: ${RC_DOCKERFILE}"
|
||||
echo "::set-output name=rc-dockerfile::${RC_DOCKERFILE}"
|
||||
|
||||
echo "RC_DOCKER_TAG: ${RC_DOCKER_TAG}"
|
||||
echo "::set-output name=rc-docker-tag::${RC_DOCKER_TAG}"
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
@ -264,29 +286,6 @@ jobs:
|
||||
tar xzf Rocket.Chat.tar.gz
|
||||
rm Rocket.Chat.tar.gz
|
||||
|
||||
- name: Docker env vars
|
||||
id: docker-env
|
||||
run: |
|
||||
LOWERCASE_REPOSITORY=$(echo "${{ github.repository_owner }}" | tr "[:upper:]" "[:lower:]")
|
||||
|
||||
echo "LOWERCASE_REPOSITORY: ${LOWERCASE_REPOSITORY}"
|
||||
echo "::set-output name=lowercase-repo::${LOWERCASE_REPOSITORY}"
|
||||
|
||||
# test alpine image on mongo 5.0 (no special reason to be mongo 5.0 but we need to test alpine at least once)
|
||||
if [[ '${{ matrix.mongodb-version }}' = '5.0' ]]; then
|
||||
RC_DOCKERFILE="${{ github.workspace }}/apps/meteor/.docker/Dockerfile.alpine"
|
||||
RC_DOCKER_TAG="${{ needs.release-versions.outputs.gh-docker-tag }}.alpine"
|
||||
else
|
||||
RC_DOCKERFILE="${{ github.workspace }}/apps/meteor/.docker/Dockerfile"
|
||||
RC_DOCKER_TAG="${{ needs.release-versions.outputs.gh-docker-tag }}.official"
|
||||
fi;
|
||||
|
||||
echo "RC_DOCKERFILE: ${RC_DOCKERFILE}"
|
||||
echo "::set-output name=rc-dockerfile::${RC_DOCKERFILE}"
|
||||
|
||||
echo "RC_DOCKER_TAG: ${RC_DOCKER_TAG}"
|
||||
echo "::set-output name=rc-docker-tag::${RC_DOCKER_TAG}"
|
||||
|
||||
- name: Start containers
|
||||
env:
|
||||
MONGO_URL: 'mongodb://host.docker.internal:27017/rocketchat?replicaSet=rs0&directConnection=true'
|
||||
@ -298,14 +297,6 @@ jobs:
|
||||
run: |
|
||||
docker compose -f docker-compose-ci.yml up -d --build rocketchat
|
||||
|
||||
sleep 10
|
||||
|
||||
until echo "$(docker compose -f docker-compose-ci.yml logs rocketchat)" | grep -q "SERVER RUNNING"; do
|
||||
echo "Waiting Rocket.Chat to start up"
|
||||
((c++)) && ((c==10)) && docker compose -f docker-compose-ci.yml logs rocketchat && exit 1
|
||||
sleep 10
|
||||
done
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop'
|
||||
uses: docker/login-action@v2
|
||||
@ -333,7 +324,7 @@ jobs:
|
||||
docker push $IMAGE_NAME_BASE
|
||||
fi;
|
||||
|
||||
- name: E2E Test API
|
||||
- name: Wait for Rocket.Chat to start up
|
||||
env:
|
||||
LOWERCASE_REPOSITORY: ${{ steps.docker-env.outputs.lowercase-repo }}
|
||||
RC_DOCKERFILE: ${{ steps.docker-env.outputs.rc-dockerfile }}
|
||||
@ -341,9 +332,21 @@ jobs:
|
||||
DOCKER_TAG: ${{ needs.release-versions.outputs.gh-docker-tag }}
|
||||
run: |
|
||||
docker ps
|
||||
docker compose -f docker-compose-ci.yml logs rocketchat --tail=50
|
||||
|
||||
cd ./apps/meteor
|
||||
until echo "$(docker compose -f docker-compose-ci.yml logs rocketchat)" | grep -q "SERVER RUNNING"; do
|
||||
echo "Waiting Rocket.Chat to start up"
|
||||
((c++)) && ((c==10)) && docker compose -f docker-compose-ci.yml logs rocketchat && exit 1
|
||||
sleep 10
|
||||
done;
|
||||
|
||||
- name: E2E Test API
|
||||
env:
|
||||
LOWERCASE_REPOSITORY: ${{ steps.docker-env.outputs.lowercase-repo }}
|
||||
RC_DOCKERFILE: ${{ steps.docker-env.outputs.rc-dockerfile }}
|
||||
RC_DOCKER_TAG: ${{ steps.docker-env.outputs.rc-docker-tag }}
|
||||
DOCKER_TAG: ${{ needs.release-versions.outputs.gh-docker-tag }}
|
||||
working-directory: ./apps/meteor
|
||||
run: |
|
||||
for i in $(seq 1 5); do
|
||||
npm run testapi && s=0 && break || s=$?
|
||||
|
||||
@ -376,18 +379,18 @@ jobs:
|
||||
|
||||
- name: Install Playwright
|
||||
if: steps.cache-playwright.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd ./apps/meteor
|
||||
npx playwright install --with-deps
|
||||
working-directory: ./apps/meteor
|
||||
run: npx playwright install --with-deps
|
||||
|
||||
- name: E2E Test UI
|
||||
- name: Reset containers
|
||||
env:
|
||||
LOWERCASE_REPOSITORY: ${{ steps.docker-env.outputs.lowercase-repo }}
|
||||
RC_DOCKERFILE: ${{ steps.docker-env.outputs.rc-dockerfile }}
|
||||
RC_DOCKER_TAG: ${{ steps.docker-env.outputs.rc-docker-tag }}
|
||||
run: |
|
||||
docker ps
|
||||
docker compose -f docker-compose-ci.yml logs rocketchat --tail=50
|
||||
|
||||
docker compose -f docker-compose-ci.yml stop rocketchat
|
||||
|
||||
docker exec mongodb mongo rocketchat --eval 'db.dropDatabase()'
|
||||
|
||||
@ -399,10 +402,11 @@ jobs:
|
||||
echo "Waiting Rocket.Chat to start up"
|
||||
((c++)) && ((c==10)) && exit 1
|
||||
sleep 10
|
||||
done
|
||||
done;
|
||||
|
||||
cd ./apps/meteor
|
||||
yarn test:e2e
|
||||
- name: E2E Test UI
|
||||
working-directory: ./apps/meteor
|
||||
run: yarn test:e2e
|
||||
|
||||
- name: Store playwright test trace
|
||||
uses: actions/upload-artifact@v2
|
||||
@ -444,6 +448,9 @@ jobs:
|
||||
- name: yarn install
|
||||
run: yarn
|
||||
|
||||
- name: yarn build
|
||||
run: yarn build
|
||||
|
||||
- name: Unit Test
|
||||
run: yarn testunit --api="http://127.0.0.1:9080" --token="${{ secrets.TURBO_SERVER_TOKEN }}" --team='rc'
|
||||
|
||||
@ -459,33 +466,26 @@ jobs:
|
||||
tar xzf Rocket.Chat.tar.gz
|
||||
rm Rocket.Chat.tar.gz
|
||||
|
||||
- name: Docker env vars
|
||||
id: docker-env
|
||||
run: |
|
||||
LOWERCASE_REPOSITORY=$(echo "${{ github.repository_owner }}" | tr "[:upper:]" "[:lower:]")
|
||||
|
||||
echo "LOWERCASE_REPOSITORY: ${LOWERCASE_REPOSITORY}"
|
||||
echo "::set-output name=lowercase-repo::${LOWERCASE_REPOSITORY}"
|
||||
|
||||
- name: Start containers
|
||||
env:
|
||||
MONGO_URL: 'mongodb://host.docker.internal:27017/rocketchat?replicaSet=rs0&directConnection=true'
|
||||
LOWERCASE_REPOSITORY: ${{ steps.docker-env.outputs.lowercase-repo }}
|
||||
RC_DOCKERFILE: '${{ github.workspace }}/apps/meteor/.docker/Dockerfile'
|
||||
RC_DOCKER_TAG: '${{ needs.release-versions.outputs.gh-docker-tag }}.official'
|
||||
DOCKER_TAG: ${{ needs.release-versions.outputs.gh-docker-tag }}
|
||||
TRANSPORTER: nats://nats:4222
|
||||
ENTERPRISE_LICENSE: ${{ secrets.ENTERPRISE_LICENSE }}
|
||||
run: |
|
||||
export LOWERCASE_REPOSITORY=$(echo "${{ github.repository_owner }}" | tr "[:upper:]" "[:lower:]")
|
||||
|
||||
docker compose -f docker-compose-ci.yml up -d --build
|
||||
|
||||
sleep 10
|
||||
|
||||
until echo "$(docker compose -f docker-compose-ci.yml logs ddp-streamer-service)" | grep -q "NetworkBroker started successfully"; do
|
||||
echo "Waiting 'ddp-streamer' to start up"
|
||||
((c++)) && ((c==10)) && docker compose -f docker-compose-ci.yml logs ddp-streamer-service && exit 1
|
||||
sleep 10
|
||||
done
|
||||
|
||||
until echo "$(docker compose -f docker-compose-ci.yml logs rocketchat)" | grep -q "SERVER RUNNING"; do
|
||||
echo "Waiting Rocket.Chat to start up"
|
||||
((c++)) && ((c==10)) && docker compose -f docker-compose-ci.yml logs rocketchat && exit 1
|
||||
sleep 10
|
||||
done
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop'
|
||||
uses: docker/login-action@v2
|
||||
@ -498,9 +498,8 @@ jobs:
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop'
|
||||
env:
|
||||
DOCKER_TAG: ${{ needs.release-versions.outputs.gh-docker-tag }}
|
||||
LOWERCASE_REPOSITORY: ${{ steps.docker-env.outputs.lowercase-repo }}
|
||||
run: |
|
||||
export LOWERCASE_REPOSITORY=$(echo "${{ github.repository_owner }}" | tr "[:upper:]" "[:lower:]")
|
||||
|
||||
docker compose -f docker-compose-ci.yml push \
|
||||
authorization-service \
|
||||
account-service \
|
||||
@ -508,14 +507,37 @@ jobs:
|
||||
presence-service \
|
||||
stream-hub-service
|
||||
|
||||
- name: E2E Test API
|
||||
- name: Wait services to start up
|
||||
env:
|
||||
LOWERCASE_REPOSITORY: ${{ steps.docker-env.outputs.lowercase-repo }}
|
||||
RC_DOCKERFILE: '${{ github.workspace }}/apps/meteor/.docker/Dockerfile'
|
||||
RC_DOCKER_TAG: '${{ needs.release-versions.outputs.gh-docker-tag }}.official'
|
||||
DOCKER_TAG: ${{ needs.release-versions.outputs.gh-docker-tag }}
|
||||
run: |
|
||||
docker ps
|
||||
docker compose -f docker-compose-ci.yml logs --tail=50
|
||||
|
||||
cd ./apps/meteor
|
||||
until echo "$(docker compose -f docker-compose-ci.yml logs ddp-streamer-service)" | grep -q "NetworkBroker started successfully"; do
|
||||
echo "Waiting 'ddp-streamer' to start up"
|
||||
((c++)) && ((c==10)) && docker compose -f docker-compose-ci.yml logs ddp-streamer-service && exit 1
|
||||
sleep 10
|
||||
done;
|
||||
|
||||
until echo "$(docker compose -f docker-compose-ci.yml logs rocketchat)" | grep -q "SERVER RUNNING"; do
|
||||
echo "Waiting Rocket.Chat to start up"
|
||||
((c++)) && ((c==10)) && docker compose -f docker-compose-ci.yml logs rocketchat && exit 1
|
||||
sleep 10
|
||||
done;
|
||||
|
||||
- name: E2E Test API
|
||||
env:
|
||||
LOWERCASE_REPOSITORY: ${{ steps.docker-env.outputs.lowercase-repo }}
|
||||
RC_DOCKERFILE: '${{ github.workspace }}/apps/meteor/.docker/Dockerfile'
|
||||
RC_DOCKER_TAG: '${{ needs.release-versions.outputs.gh-docker-tag }}.official'
|
||||
DOCKER_TAG: ${{ needs.release-versions.outputs.gh-docker-tag }}
|
||||
working-directory: ./apps/meteor
|
||||
run: |
|
||||
for i in $(seq 1 5); do
|
||||
IS_EE=true npm run testapi && s=0 && break || s=$?;
|
||||
IS_EE=true npm run testapi && s=0 && break || s=$?
|
||||
|
||||
docker compose -f ../../docker-compose-ci.yml logs --tail=100
|
||||
|
||||
@ -525,7 +547,7 @@ jobs:
|
||||
|
||||
NOW=$(date "+%Y-%m-%dT%H:%M:%S.000Z")
|
||||
|
||||
docker compose -f ../../docker-compose-ci.yml start
|
||||
docker compose -f ../../docker-compose-ci.yml restart
|
||||
|
||||
until echo "$(docker compose -f ../../docker-compose-ci.yml logs rocketchat --since $NOW)" | grep -q "SERVER RUNNING"; do
|
||||
echo "Waiting Rocket.Chat to start up"
|
||||
@ -535,6 +557,33 @@ jobs:
|
||||
done;
|
||||
exit $s
|
||||
|
||||
- name: Reset containers
|
||||
env:
|
||||
LOWERCASE_REPOSITORY: ${{ steps.docker-env.outputs.lowercase-repo }}
|
||||
RC_DOCKERFILE: '${{ github.workspace }}/apps/meteor/.docker/Dockerfile'
|
||||
RC_DOCKER_TAG: '${{ needs.release-versions.outputs.gh-docker-tag }}.official'
|
||||
DOCKER_TAG: ${{ needs.release-versions.outputs.gh-docker-tag }}
|
||||
run: |
|
||||
docker compose -f docker-compose-ci.yml stop
|
||||
|
||||
docker exec mongodb mongo rocketchat --eval 'db.dropDatabase()'
|
||||
|
||||
NOW=$(date "+%Y-%m-%dT%H:%M:%S.000Z")
|
||||
|
||||
docker compose -f docker-compose-ci.yml restart
|
||||
|
||||
until echo "$(docker compose -f docker-compose-ci.yml logs ddp-streamer-service)" | grep -q "NetworkBroker started successfully"; do
|
||||
echo "Waiting 'ddp-streamer' to start up"
|
||||
((c++)) && ((c==10)) && docker compose -f docker-compose-ci.yml logs ddp-streamer-service && exit 1
|
||||
sleep 10
|
||||
done;
|
||||
|
||||
until echo "$(docker compose -f docker-compose-ci.yml logs rocketchat)" | grep -q "SERVER RUNNING"; do
|
||||
echo "Waiting Rocket.Chat to start up"
|
||||
((c++)) && ((c==10)) && docker compose -f docker-compose-ci.yml logs rocketchat && exit 1
|
||||
sleep 10
|
||||
done;
|
||||
|
||||
- name: Cache Playwright binaries
|
||||
uses: actions/cache@v3
|
||||
id: cache-playwright
|
||||
@ -545,30 +594,12 @@ jobs:
|
||||
key: playwright-1.23.1
|
||||
|
||||
- name: Install Playwright
|
||||
run: |
|
||||
cd ./apps/meteor
|
||||
npx playwright install --with-deps
|
||||
working-directory: ./apps/meteor
|
||||
run: npx playwright install --with-deps
|
||||
|
||||
- name: E2E Test UI
|
||||
run: |
|
||||
docker ps
|
||||
docker compose -f docker-compose-ci.yml logs rocketchat --tail=50
|
||||
|
||||
docker exec mongodb mongo rocketchat --eval 'db.dropDatabase()'
|
||||
|
||||
NOW=$(date "+%Y-%m-%dT%H:%M:%S.000Z")
|
||||
|
||||
docker compose -f docker-compose-ci.yml restart
|
||||
|
||||
until echo "$(docker compose -f docker-compose-ci.yml logs rocketchat --since $NOW)" | grep -q "SERVER RUNNING"; do
|
||||
echo "Waiting Rocket.Chat to start up"
|
||||
((c++)) && ((c==10)) && exit 1
|
||||
sleep 10
|
||||
done
|
||||
|
||||
cd ./apps/meteor
|
||||
|
||||
E2E_COVERAGE=true IS_EE=true yarn test:e2e
|
||||
working-directory: ./apps/meteor
|
||||
run: E2E_COVERAGE=true IS_EE=true yarn test:e2e
|
||||
|
||||
- name: Store playwright test trace
|
||||
uses: actions/upload-artifact@v2
|
||||
@ -578,9 +609,8 @@ jobs:
|
||||
path: ./apps/meteor/tests/e2e/.playwright*
|
||||
|
||||
- name: Extract e2e:ee:coverage
|
||||
run: |
|
||||
cd ./apps/meteor
|
||||
yarn test:e2e:nyc
|
||||
working-directory: ./apps/meteor
|
||||
run: yarn test:e2e:nyc
|
||||
|
||||
- uses: codecov/codecov-action@v3
|
||||
with:
|
||||
@ -597,7 +627,7 @@ jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.event_name == 'release' || github.ref == 'refs/heads/develop'
|
||||
needs: [test, release-versions]
|
||||
needs: [test, test-ee, release-versions]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@ -36,6 +36,7 @@ import { getServicesStatistics } from './getServicesStatistics';
|
||||
import { getStatistics as getEnterpriseStatistics } from '../../../../ee/app/license/server';
|
||||
import { Analytics, Team, VideoConf } from '../../../../server/sdk';
|
||||
import { getSettingsStatistics } from '../../../../server/lib/statistics/getSettingsStatistics';
|
||||
import { isRunningMs } from '../../../../server/lib/isRunningMs';
|
||||
|
||||
const wizardFields = ['Organization_Type', 'Industry', 'Size', 'Country', 'Language', 'Server_Type', 'Register_Server'];
|
||||
|
||||
@ -342,6 +343,7 @@ export const statistics = {
|
||||
);
|
||||
|
||||
const { oplogEnabled, mongoVersion, mongoStorageEngine } = getMongoInfo();
|
||||
statistics.msEnabled = isRunningMs();
|
||||
statistics.oplogEnabled = oplogEnabled;
|
||||
statistics.mongoVersion = mongoVersion;
|
||||
statistics.mongoStorageEngine = mongoStorageEngine;
|
||||
|
||||
@ -125,6 +125,7 @@ export default {
|
||||
lockedAt: '',
|
||||
},
|
||||
instanceCount: 0,
|
||||
msEnabled: false,
|
||||
oplogEnabled: false,
|
||||
mongoVersion: '',
|
||||
mongoStorageEngine: '',
|
||||
|
||||
@ -56,9 +56,9 @@ const DeploymentCard = ({ info, statistics, instances }: DeploymentCardProps): R
|
||||
</Card.Col.Section>
|
||||
<Card.Col.Section>
|
||||
<Card.Col.Title>{t('MongoDB')}</Card.Col.Title>
|
||||
{`${statistics.mongoVersion} / ${statistics.mongoStorageEngine} (oplog ${
|
||||
statistics.oplogEnabled ? t('Enabled') : t('Disabled')
|
||||
})`}
|
||||
{`${statistics.mongoVersion} / ${statistics.mongoStorageEngine} ${
|
||||
!statistics.msEnabled ? `(oplog ${statistics.oplogEnabled ? t('Enabled') : t('Disabled')})` : ''
|
||||
}`}
|
||||
</Card.Col.Section>
|
||||
<Card.Col.Section>
|
||||
<Card.Col.Title>{t('Commit_details')}</Card.Col.Title>
|
||||
|
||||
@ -155,6 +155,7 @@ export default {
|
||||
lockedAt: '',
|
||||
},
|
||||
instanceCount: 0,
|
||||
msEnabled: false,
|
||||
oplogEnabled: false,
|
||||
mongoVersion: '',
|
||||
mongoStorageEngine: '',
|
||||
|
||||
@ -33,8 +33,8 @@ const InformationPage = memo(function InformationPage({
|
||||
return null;
|
||||
}
|
||||
|
||||
const usingMultipleInstances = statistics?.instanceCount > 1;
|
||||
const alertOplogForMultipleInstances = usingMultipleInstances && !statistics.oplogEnabled;
|
||||
const warningMultipleInstances = !statistics?.msEnabled && statistics?.instanceCount > 1;
|
||||
const alertOplogForMultipleInstances = warningMultipleInstances && !statistics.oplogEnabled;
|
||||
|
||||
return (
|
||||
<Page data-qa='admin-info'>
|
||||
@ -53,7 +53,9 @@ const InformationPage = memo(function InformationPage({
|
||||
|
||||
<Page.ScrollableContentWithShadow>
|
||||
<Box marginBlock='none' marginInline='auto' width='full'>
|
||||
{usingMultipleInstances && <Callout type='danger' title={t('Multiple_monolith_instances_alert')} marginBlockEnd='x16'></Callout>}
|
||||
{warningMultipleInstances && (
|
||||
<Callout type='danger' title={t('Multiple_monolith_instances_alert')} marginBlockEnd='x16'></Callout>
|
||||
)}
|
||||
{alertOplogForMultipleInstances && (
|
||||
<Callout
|
||||
type='danger'
|
||||
|
||||
@ -103,6 +103,7 @@ export default {
|
||||
lockedAt: '',
|
||||
},
|
||||
instanceCount: 0,
|
||||
msEnabled: false,
|
||||
oplogEnabled: false,
|
||||
mongoVersion: '',
|
||||
mongoStorageEngine: '',
|
||||
|
||||
@ -34,9 +34,6 @@ export class NetworkBroker implements IBroker {
|
||||
this.broker = broker;
|
||||
|
||||
this.metrics = broker.metrics;
|
||||
|
||||
// TODO move this to a proper startup method?
|
||||
this.started = this.broker.start();
|
||||
}
|
||||
|
||||
async call(method: string, data: any): Promise<any> {
|
||||
@ -85,11 +82,6 @@ export class NetworkBroker implements IBroker {
|
||||
: Object.getOwnPropertyNames(Object.getPrototypeOf(instance))
|
||||
).filter((name) => name !== 'constructor');
|
||||
|
||||
const instanceEvents = instance.getEvents();
|
||||
if (!instanceEvents && !methods.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const serviceInstance = instance as any;
|
||||
|
||||
const name = instance.getName();
|
||||
@ -97,7 +89,7 @@ export class NetworkBroker implements IBroker {
|
||||
if (!instance.isInternal()) {
|
||||
instance.onEvent('shutdown', async (services) => {
|
||||
if (!services[name]?.includes(this.broker.nodeID)) {
|
||||
this.broker.logger.debug({ msg: 'Not shutting down, different node.', nodeID: this.broker.nodeID });
|
||||
this.broker.logger.info({ msg: 'Not shutting down, different node.', nodeID: this.broker.nodeID });
|
||||
return;
|
||||
}
|
||||
this.broker.logger.warn({ msg: 'Received shutdown event, destroying service.', nodeID: this.broker.nodeID });
|
||||
@ -105,6 +97,11 @@ export class NetworkBroker implements IBroker {
|
||||
});
|
||||
}
|
||||
|
||||
const instanceEvents = instance.getEvents();
|
||||
if (!instanceEvents && !methods.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dependencies = name !== 'license' ? { dependencies: ['license'] } : {};
|
||||
|
||||
const service: ServiceSchema = {
|
||||
@ -185,4 +182,8 @@ export class NetworkBroker implements IBroker {
|
||||
async nodeList(): Promise<IBrokerNode[]> {
|
||||
return this.broker.call('$node.list');
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
this.started = this.broker.start();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,147 +0,0 @@
|
||||
import type { IUser } from '@rocket.chat/core-typings';
|
||||
|
||||
import type { IHashedStampedToken } from './lib/utils';
|
||||
import { _generateStampedLoginToken, _hashStampedToken, _hashLoginToken, _tokenExpiration, validatePassword } from './lib/utils';
|
||||
import { getCollection, Collections } from '../mongo';
|
||||
import { ServiceClass } from '../../../../server/sdk/types/ServiceClass';
|
||||
import type { IAccount, ILoginResult } from '../../../../server/sdk/types/IAccount';
|
||||
import { MeteorError } from '../../../../server/sdk/errors';
|
||||
|
||||
const saveSession = async (uid: string, newToken: IHashedStampedToken): Promise<void> => {
|
||||
const Users = await getCollection<IUser>(Collections.User);
|
||||
await Users.updateOne(
|
||||
{ _id: uid },
|
||||
{
|
||||
$push: {
|
||||
'services.resume.loginTokens': newToken.hashedToken,
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const removeSession = async (uid: string, loginToken: string): Promise<void> => {
|
||||
const Users = await getCollection<IUser>(Collections.User);
|
||||
await Users.updateOne(
|
||||
{ _id: uid },
|
||||
{
|
||||
$pull: {
|
||||
'services.resume.loginTokens': {
|
||||
$or: [{ hashedToken: loginToken }, { token: loginToken }],
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const loginViaResume = async (resume: string, loginExpiration: number): Promise<false | ILoginResult> => {
|
||||
const Users = await getCollection<IUser>(Collections.User);
|
||||
const hashedToken = _hashLoginToken(resume);
|
||||
|
||||
const user = await Users.findOne<IUser>(
|
||||
{
|
||||
'services.resume.loginTokens.hashedToken': hashedToken,
|
||||
},
|
||||
{
|
||||
projection: {
|
||||
'services.resume.loginTokens': 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
if (!user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { when } = user.services?.resume?.loginTokens?.find((token) => token.hashedToken === hashedToken) || {};
|
||||
|
||||
const tokenExpires = when && _tokenExpiration(when, loginExpiration);
|
||||
if (tokenExpires && new Date() >= tokenExpires) {
|
||||
throw new MeteorError(403, 'Your session has expired. Please log in again.');
|
||||
}
|
||||
|
||||
return {
|
||||
uid: user._id,
|
||||
token: resume,
|
||||
hashedToken,
|
||||
type: 'resume',
|
||||
...(tokenExpires && { tokenExpires }),
|
||||
};
|
||||
};
|
||||
|
||||
const loginViaUsername = async (
|
||||
{ username }: { username: string },
|
||||
password: string,
|
||||
loginExpiration: number,
|
||||
): Promise<false | ILoginResult> => {
|
||||
const Users = await getCollection<IUser>(Collections.User);
|
||||
const user = await Users.findOne<IUser>({ username }, { projection: { 'services.password.bcrypt': 1 } });
|
||||
if (!user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const valid = user.services?.password?.bcrypt && validatePassword(password, user.services.password.bcrypt);
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const newToken = _generateStampedLoginToken();
|
||||
|
||||
const hashedToken = _hashStampedToken(newToken);
|
||||
|
||||
await saveSession(user._id, hashedToken);
|
||||
|
||||
return {
|
||||
uid: user._id,
|
||||
token: newToken.token,
|
||||
hashedToken: hashedToken.hashedToken,
|
||||
tokenExpires: _tokenExpiration(newToken.when, loginExpiration),
|
||||
type: 'password',
|
||||
};
|
||||
};
|
||||
|
||||
export class Account extends ServiceClass implements IAccount {
|
||||
protected name = 'accounts';
|
||||
|
||||
private loginExpiration = 90;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onEvent('watch.settings', async ({ clientAction, setting }): Promise<void> => {
|
||||
if (clientAction === 'removed') {
|
||||
return;
|
||||
}
|
||||
const { _id, value } = setting;
|
||||
if (_id !== 'Accounts_LoginExpiration') {
|
||||
return;
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
this.loginExpiration = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async login({ resume, user, password }: { resume: string; user: { username: string }; password: string }): Promise<false | ILoginResult> {
|
||||
if (resume) {
|
||||
return loginViaResume(resume, this.loginExpiration);
|
||||
}
|
||||
|
||||
if (user && password) {
|
||||
return loginViaUsername(user, password, this.loginExpiration);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async logout({ userId, token }: { userId: string; token: string }): Promise<void> {
|
||||
return removeSession(userId, token);
|
||||
}
|
||||
|
||||
async started(): Promise<void> {
|
||||
const Settings = await getCollection<any>(Collections.Settings);
|
||||
|
||||
const expiry = await Settings.findOne({ _id: 'Accounts_LoginExpiration' }, { projection: { value: 1 } });
|
||||
if (expiry?.value) {
|
||||
this.loginExpiration = expiry.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
import '../../startup/broker';
|
||||
|
||||
import { api } from '../../../../server/sdk/api';
|
||||
import { Account } from './Account';
|
||||
|
||||
api.registerService(new Account());
|
||||
@ -1,16 +0,0 @@
|
||||
import type { Document } from 'mongodb';
|
||||
|
||||
import '../../startup/broker';
|
||||
|
||||
import { api } from '../../../../server/sdk/api';
|
||||
import { Authorization } from '../../../../server/services/authorization/service';
|
||||
import { Collections, getCollection, getConnection } from '../mongo';
|
||||
import { registerServiceModels } from '../../lib/registerServiceModels';
|
||||
|
||||
getConnection().then(async (db) => {
|
||||
const trash = await getCollection<Document>(Collections.Trash);
|
||||
|
||||
registerServiceModels(db, trash);
|
||||
|
||||
api.registerService(new Authorization(db));
|
||||
});
|
||||
@ -1,33 +0,0 @@
|
||||
const watch = ['.', '../broker.ts', '../../../server/sdk'];
|
||||
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: 'authorization',
|
||||
watch: [...watch, '../../../server/services/authorization'],
|
||||
},
|
||||
// {
|
||||
// name: 'presence',
|
||||
// },
|
||||
{
|
||||
name: 'account',
|
||||
},
|
||||
{
|
||||
name: 'stream-hub',
|
||||
},
|
||||
// {
|
||||
// name: 'ddp-streamer',
|
||||
// },
|
||||
].map((app) =>
|
||||
Object.assign(app, {
|
||||
script: app.script || `ts-node --files ${app.name}/service.ts`,
|
||||
watch: app.watch || ['.', '../broker.ts', '../../../server/sdk', '../../../server/modules'],
|
||||
instances: 1,
|
||||
env: {
|
||||
MOLECULER_LOG_LEVEL: 'info',
|
||||
TRANSPORTER: 'nats://localhost:4222',
|
||||
MONGO_URL: 'mongodb://localhost:3001/meteor',
|
||||
},
|
||||
}),
|
||||
),
|
||||
};
|
||||
@ -7,10 +7,6 @@
|
||||
"scripts": {
|
||||
"dev": "pm2 start ecosystem.config.js",
|
||||
"pm2": "pm2",
|
||||
"ms": "MONGO_URL=${MONGO_URL:-mongodb://localhost:3001/meteor} run-p start:account start:authorization start:stream-hub",
|
||||
"start:account": "ts-node --files ./account/service.ts",
|
||||
"start:authorization": "ts-node --files ./authorization/service.ts",
|
||||
"start:stream-hub": "ts-node --files ./stream-hub/service.ts",
|
||||
"typecheck": "tsc --noEmit --skipLibCheck",
|
||||
"build": "tsc",
|
||||
"build-containers": "npm run build && docker-compose build && rm -rf ./dist",
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
import type { IServiceClass } from '../../../../server/sdk/types/ServiceClass';
|
||||
import { ServiceClass } from '../../../../server/sdk/types/ServiceClass';
|
||||
import { getConnection } from '../mongo';
|
||||
import { initWatchers } from '../../../../server/modules/watchers/watchers.module';
|
||||
import { api } from '../../../../server/sdk/api';
|
||||
import { DatabaseWatcher } from '../../../../server/database/DatabaseWatcher';
|
||||
|
||||
export class StreamHub extends ServiceClass implements IServiceClass {
|
||||
protected name = 'hub';
|
||||
|
||||
async created(): Promise<void> {
|
||||
const db = await getConnection({ maxPoolSize: 1 });
|
||||
|
||||
const watcher = new DatabaseWatcher({ db });
|
||||
|
||||
initWatchers(watcher, api.broadcast.bind(api));
|
||||
|
||||
watcher.watch();
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
import type { Document } from 'mongodb';
|
||||
|
||||
import '../../startup/broker';
|
||||
|
||||
import { api } from '../../../../server/sdk/api';
|
||||
import { Collections, getCollection, getConnection } from '../mongo';
|
||||
import { registerServiceModels } from '../../lib/registerServiceModels';
|
||||
|
||||
getConnection().then(async (db) => {
|
||||
const trash = await getCollection<Document>(Collections.Trash);
|
||||
|
||||
registerServiceModels(db, trash);
|
||||
|
||||
// need to import StreamHub service after models are registered
|
||||
const { StreamHub } = await import('./StreamHub');
|
||||
|
||||
api.registerService(new StreamHub());
|
||||
});
|
||||
@ -43,6 +43,6 @@
|
||||
// "emitDecoratorMetadata": true,
|
||||
// "experimentalDecorators": true,
|
||||
},
|
||||
"include": ["./**/*", "../../../definition/externals/meteor/rocketchat-streamer.d.ts"],
|
||||
"include": ["./**/*", "../../../definition/externals/meteor/rocketchat-streamer.d.ts", "../../../../../ee/apps/account-service/src/lib"],
|
||||
"exclude": ["./dist", "./ecosystem.config.js", "../../../definition/methods"]
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ import EJSON from 'ejson';
|
||||
import { Errors, Serializers, ServiceBroker } from 'moleculer';
|
||||
import { pino } from 'pino';
|
||||
|
||||
import { api } from '../../../server/sdk/api';
|
||||
import { isMeteorError, MeteorError } from '../../../server/sdk/errors';
|
||||
import { NetworkBroker } from '../NetworkBroker';
|
||||
|
||||
@ -171,4 +170,4 @@ const network = new ServiceBroker({
|
||||
},
|
||||
});
|
||||
|
||||
api.setBroker(new NetworkBroker(network));
|
||||
export const broker = new NetworkBroker(network);
|
||||
|
||||
@ -3,9 +3,15 @@ import './engagementDashboard';
|
||||
import './seatsCap';
|
||||
import './services';
|
||||
import './upsell';
|
||||
import { api } from '../../../server/sdk/api';
|
||||
import { isRunningMs } from '../../../server/lib/isRunningMs';
|
||||
|
||||
// only starts network broker if running in micro services mode
|
||||
if (isRunningMs()) {
|
||||
require('./broker');
|
||||
(async () => {
|
||||
const { broker } = await import('./broker');
|
||||
|
||||
api.setBroker(broker);
|
||||
api.start();
|
||||
})();
|
||||
}
|
||||
|
||||
@ -6,4 +6,5 @@ export const api = new Api();
|
||||
|
||||
if (!isRunningMs()) {
|
||||
api.setBroker(new LocalBroker());
|
||||
api.start();
|
||||
}
|
||||
|
||||
@ -64,4 +64,11 @@ export class Api implements IApiService {
|
||||
nodeList(): Promise<IBrokerNode[]> {
|
||||
return this.broker.nodeList();
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
if (!this.broker) {
|
||||
throw new Error('No broker set to start.');
|
||||
}
|
||||
await this.broker.start();
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@ export class LocalBroker implements IBroker {
|
||||
|
||||
private events = new EventEmitter();
|
||||
|
||||
private services = new Set<IServiceClass>();
|
||||
|
||||
async call(method: string, data: any): Promise<any> {
|
||||
const result = await asyncLocalStorage.run(
|
||||
{
|
||||
@ -54,6 +56,8 @@ export class LocalBroker implements IBroker {
|
||||
createService(instance: IServiceClass): void {
|
||||
const namespace = instance.getName();
|
||||
|
||||
this.services.add(instance);
|
||||
|
||||
instance.created();
|
||||
|
||||
instance.getEvents().forEach((eventName) => {
|
||||
@ -74,8 +78,6 @@ export class LocalBroker implements IBroker {
|
||||
|
||||
this.methods.set(`${namespace}.${method}`, i[method].bind(i));
|
||||
}
|
||||
|
||||
instance.started();
|
||||
}
|
||||
|
||||
async broadcast<T extends keyof EventSignatures>(event: T, ...args: Parameters<EventSignatures[T]>): Promise<void> {
|
||||
@ -102,4 +104,8 @@ export class LocalBroker implements IBroker {
|
||||
|
||||
return instances.map(({ _id }) => ({ id: _id, available: true }));
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
await Promise.all([...this.services].map((service) => service.started()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,4 +59,5 @@ export interface IBroker {
|
||||
broadcast<T extends keyof EventSignatures>(event: T, ...args: Parameters<EventSignatures[T]>): Promise<void>;
|
||||
broadcastLocal<T extends keyof EventSignatures>(event: T, ...args: Parameters<EventSignatures[T]>): Promise<void>;
|
||||
nodeList(): Promise<IBrokerNode[]>;
|
||||
start(): Promise<void>;
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import type { Db, Collection } from 'mongodb';
|
||||
import mem from 'mem';
|
||||
import type { IUser, IRole, IRoom, ISubscription } from '@rocket.chat/core-typings';
|
||||
import { Subscriptions, Rooms, Users, Roles } from '@rocket.chat/models';
|
||||
import { Subscriptions, Rooms, Users, Roles, Permissions } from '@rocket.chat/models';
|
||||
|
||||
import type { IAuthorization, RoomAccessValidator } from '../../sdk/types/IAuthorization';
|
||||
import { ServiceClass } from '../../sdk/types/ServiceClass';
|
||||
@ -15,8 +14,6 @@ import './canAccessRoomLivechat';
|
||||
export class Authorization extends ServiceClass implements IAuthorization {
|
||||
protected name = 'authorization';
|
||||
|
||||
private Permissions: Collection;
|
||||
|
||||
private getRolesCached = mem(this.getRoles.bind(this), {
|
||||
maxAge: 1000,
|
||||
cacheKey: JSON.stringify,
|
||||
@ -27,11 +24,9 @@ export class Authorization extends ServiceClass implements IAuthorization {
|
||||
...(process.env.TEST_MODE === 'true' && { maxAge: 1 }),
|
||||
});
|
||||
|
||||
constructor(db: Db) {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.Permissions = db.collection('rocketchat_permissions');
|
||||
|
||||
const clearCache = (): void => {
|
||||
mem.clear(this.getRolesCached);
|
||||
mem.clear(this.rolesHasPermissionCached);
|
||||
@ -148,7 +143,7 @@ export class Authorization extends ServiceClass implements IAuthorization {
|
||||
return false;
|
||||
}
|
||||
|
||||
const result = await this.Permissions.findOne({ _id: permission, roles: { $in: roles } }, { projection: { _id: 1 } });
|
||||
const result = await Permissions.findOne({ _id: permission, roles: { $in: roles } }, { projection: { _id: 1 } });
|
||||
return !!result;
|
||||
}
|
||||
|
||||
|
||||
@ -50,6 +50,6 @@ if (!isRunningMs()) {
|
||||
const { Authorization } = await import('./authorization/service');
|
||||
|
||||
api.registerService(new Presence());
|
||||
api.registerService(new Authorization(db));
|
||||
api.registerService(new Authorization());
|
||||
})();
|
||||
}
|
||||
|
||||
@ -29,6 +29,8 @@ Meteor.startup(function () {
|
||||
const desiredNodeVersionMajor = String(semver.parse(desiredNodeVersion).major);
|
||||
|
||||
return Meteor.setTimeout(function () {
|
||||
const replicaSet = isRunningMs() ? 'Not required (running micro services)' : `${oplogEnabled ? 'Enabled' : 'Disabled'}`;
|
||||
|
||||
let msg = [
|
||||
`Rocket.Chat Version: ${Info.version}`,
|
||||
` NodeJS Version: ${process.versions.node} - ${process.arch}`,
|
||||
@ -37,7 +39,7 @@ Meteor.startup(function () {
|
||||
` Platform: ${process.platform}`,
|
||||
` Process Port: ${process.env.PORT}`,
|
||||
` Site URL: ${settings.get('Site_Url')}`,
|
||||
` ReplicaSet OpLog: ${oplogEnabled ? 'Enabled' : 'Disabled'}`,
|
||||
` ReplicaSet OpLog: ${replicaSet}`,
|
||||
];
|
||||
|
||||
if (Info.commit && Info.commit.hash) {
|
||||
|
||||
@ -26,9 +26,9 @@ services:
|
||||
|
||||
authorization-service:
|
||||
build:
|
||||
dockerfile: apps/meteor/ee/server/services/Dockerfile
|
||||
dockerfile: ee/apps/authorization-service/Dockerfile
|
||||
args:
|
||||
SERVICE: authorization
|
||||
SERVICE: authorization-service
|
||||
image: ghcr.io/${LOWERCASE_REPOSITORY}/authorization-service:${DOCKER_TAG}
|
||||
environment:
|
||||
- 'MONGO_URL=${MONGO_URL}'
|
||||
@ -41,9 +41,9 @@ services:
|
||||
|
||||
account-service:
|
||||
build:
|
||||
dockerfile: apps/meteor/ee/server/services/Dockerfile
|
||||
dockerfile: ee/apps/account-service/Dockerfile
|
||||
args:
|
||||
SERVICE: account
|
||||
SERVICE: account-service
|
||||
image: ghcr.io/${LOWERCASE_REPOSITORY}/account-service:${DOCKER_TAG}
|
||||
environment:
|
||||
- MONGO_URL=${MONGO_URL}
|
||||
@ -92,9 +92,9 @@ services:
|
||||
|
||||
stream-hub-service:
|
||||
build:
|
||||
dockerfile: apps/meteor/ee/server/services/Dockerfile
|
||||
dockerfile: ee/apps/stream-hub-service/Dockerfile
|
||||
args:
|
||||
SERVICE: stream-hub
|
||||
SERVICE: stream-hub-service
|
||||
image: ghcr.io/${LOWERCASE_REPOSITORY}/stream-hub-service:${DOCKER_TAG}
|
||||
environment:
|
||||
- MONGO_URL=${MONGO_URL}
|
||||
@ -107,10 +107,6 @@ services:
|
||||
|
||||
nats:
|
||||
image: nats:2.6-alpine
|
||||
extra_hosts:
|
||||
- 'host.docker.internal:host-gateway'
|
||||
ports:
|
||||
- '4222:4222'
|
||||
|
||||
traefik:
|
||||
image: traefik:v2.8
|
||||
|
||||
16
ee/apps/account-service/.eslintrc
Normal file
16
ee/apps/account-service/.eslintrc
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": ["@rocket.chat/eslint-config"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.spec.js", "**/*.spec.jsx"],
|
||||
"env": {
|
||||
"jest": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"ignorePatterns": ["**/dist"],
|
||||
"plugins": ["jest"],
|
||||
"env": {
|
||||
"jest/globals": true
|
||||
}
|
||||
}
|
||||
34
ee/apps/account-service/Dockerfile
Normal file
34
ee/apps/account-service/Dockerfile
Normal file
@ -0,0 +1,34 @@
|
||||
FROM node:14.19.3-alpine
|
||||
|
||||
ARG SERVICE
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./packages/core-typings/package.json packages/core-typings/package.json
|
||||
COPY ./packages/core-typings/dist packages/core-typings/dist
|
||||
COPY ./packages/rest-typings/package.json packages/rest-typings/package.json
|
||||
COPY ./packages/rest-typings/dist packages/rest-typings/dist
|
||||
COPY ./packages/model-typings/package.json packages/model-typings/package.json
|
||||
COPY ./packages/model-typings/dist packages/model-typings/dist
|
||||
COPY ./packages/models/package.json packages/models/package.json
|
||||
COPY ./packages/models/dist packages/models/dist
|
||||
|
||||
COPY ./ee/apps/${SERVICE}/dist .
|
||||
|
||||
COPY ./package.json .
|
||||
COPY ./yarn.lock .
|
||||
COPY ./.yarnrc.yml .
|
||||
COPY ./.yarn/plugins .yarn/plugins
|
||||
COPY ./.yarn/releases .yarn/releases
|
||||
COPY ./ee/apps/${SERVICE}/package.json ee/apps/${SERVICE}/package.json
|
||||
|
||||
ENV NODE_ENV=production \
|
||||
PORT=3000
|
||||
|
||||
WORKDIR /app/ee/apps/${SERVICE}
|
||||
|
||||
RUN yarn workspaces focus --production
|
||||
|
||||
EXPOSE 3000 9458
|
||||
|
||||
CMD ["node", "src/service.js"]
|
||||
50
ee/apps/account-service/package.json
Normal file
50
ee/apps/account-service/package.json
Normal file
@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "@rocket.chat/account-service",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"description": "Rocket.Chat Account service",
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"ms": "MONGO_URL=${MONGO_URL:-mongodb://localhost:3001/meteor} ts-node --files src/service.ts",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"lint": "eslint src",
|
||||
"typecheck": "tsc --noEmit --skipLibCheck -p tsconfig.json"
|
||||
},
|
||||
"keywords": [
|
||||
"rocketchat"
|
||||
],
|
||||
"author": "Rocket.Chat",
|
||||
"dependencies": {
|
||||
"@rocket.chat/core-typings": "workspace:^",
|
||||
"@rocket.chat/emitter": "next",
|
||||
"@rocket.chat/model-typings": "workspace:^",
|
||||
"@rocket.chat/models": "workspace:^",
|
||||
"@rocket.chat/rest-typings": "workspace:^",
|
||||
"@rocket.chat/string-helpers": "next",
|
||||
"@types/node": "^14.18.21",
|
||||
"bcrypt": "^5.0.1",
|
||||
"ejson": "^2.2.2",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"fibers": "^5.0.3",
|
||||
"mem": "^8.1.1",
|
||||
"moleculer": "^0.14.21",
|
||||
"mongodb": "^4.3.1",
|
||||
"nats": "^2.4.0",
|
||||
"pino": "^8.4.2",
|
||||
"polka": "^0.5.2",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rocket.chat/eslint-config": "workspace:^",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/eslint": "^8",
|
||||
"@types/polka": "^0.5.4",
|
||||
"eslint": "^8.21.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "~4.5.5"
|
||||
},
|
||||
"main": "./dist/ee/apps/account-service/src/service.js",
|
||||
"files": [
|
||||
"/dist"
|
||||
]
|
||||
}
|
||||
53
ee/apps/account-service/src/Account.ts
Normal file
53
ee/apps/account-service/src/Account.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { Settings } from '@rocket.chat/models';
|
||||
|
||||
import { ServiceClass } from '../../../../apps/meteor/server/sdk/types/ServiceClass';
|
||||
import type { IAccount, ILoginResult } from '../../../../apps/meteor/server/sdk/types/IAccount';
|
||||
import { removeSession } from './lib/removeSession';
|
||||
import { loginViaResume } from './lib/loginViaResume';
|
||||
import { loginViaUsername } from './lib/loginViaUsername';
|
||||
|
||||
export class Account extends ServiceClass implements IAccount {
|
||||
protected name = 'accounts';
|
||||
|
||||
private loginExpiration = 90;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onEvent('watch.settings', async ({ clientAction, setting }): Promise<void> => {
|
||||
if (clientAction === 'removed') {
|
||||
return;
|
||||
}
|
||||
const { _id, value } = setting;
|
||||
if (_id !== 'Accounts_LoginExpiration') {
|
||||
return;
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
this.loginExpiration = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async login({ resume, user, password }: { resume: string; user: { username: string }; password: string }): Promise<false | ILoginResult> {
|
||||
if (resume) {
|
||||
return loginViaResume(resume, this.loginExpiration);
|
||||
}
|
||||
|
||||
if (user && password) {
|
||||
return loginViaUsername(user, password, this.loginExpiration);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async logout({ userId, token }: { userId: string; token: string }): Promise<void> {
|
||||
return removeSession(userId, token);
|
||||
}
|
||||
|
||||
async started(): Promise<void> {
|
||||
const expiry = await Settings.findOne({ _id: 'Accounts_LoginExpiration' }, { projection: { value: 1 } });
|
||||
if (expiry?.value) {
|
||||
this.loginExpiration = expiry.value as number;
|
||||
}
|
||||
}
|
||||
}
|
||||
39
ee/apps/account-service/src/lib/loginViaResume.ts
Normal file
39
ee/apps/account-service/src/lib/loginViaResume.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import type { IUser } from '@rocket.chat/core-typings';
|
||||
import { Users } from '@rocket.chat/models';
|
||||
|
||||
import { _hashLoginToken, _tokenExpiration } from './utils';
|
||||
import type { ILoginResult } from '../../../../../apps/meteor/server/sdk/types/IAccount';
|
||||
import { MeteorError } from '../../../../../apps/meteor/server/sdk/errors';
|
||||
|
||||
export async function loginViaResume(resume: string, loginExpiration: number): Promise<false | ILoginResult> {
|
||||
const hashedToken = _hashLoginToken(resume);
|
||||
|
||||
const user = await Users.findOne<IUser>(
|
||||
{
|
||||
'services.resume.loginTokens.hashedToken': hashedToken,
|
||||
},
|
||||
{
|
||||
projection: {
|
||||
'services.resume.loginTokens': 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
if (!user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { when } = user.services?.resume?.loginTokens?.find((token) => token.hashedToken === hashedToken) || {};
|
||||
|
||||
const tokenExpires = when && _tokenExpiration(when, loginExpiration);
|
||||
if (tokenExpires && new Date() >= tokenExpires) {
|
||||
throw new MeteorError(403, 'Your session has expired. Please log in again.');
|
||||
}
|
||||
|
||||
return {
|
||||
uid: user._id,
|
||||
token: resume,
|
||||
hashedToken,
|
||||
type: 'resume',
|
||||
...(tokenExpires && { tokenExpires }),
|
||||
};
|
||||
}
|
||||
36
ee/apps/account-service/src/lib/loginViaUsername.ts
Normal file
36
ee/apps/account-service/src/lib/loginViaUsername.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import type { IUser } from '@rocket.chat/core-typings';
|
||||
import { Users } from '@rocket.chat/models';
|
||||
|
||||
import { _generateStampedLoginToken, _hashStampedToken, _tokenExpiration, validatePassword } from './utils';
|
||||
import type { ILoginResult } from '../../../../../apps/meteor/server/sdk/types/IAccount';
|
||||
import { saveSession } from './saveSession';
|
||||
|
||||
export async function loginViaUsername(
|
||||
{ username }: { username: string },
|
||||
password: string,
|
||||
loginExpiration: number,
|
||||
): Promise<false | ILoginResult> {
|
||||
const user = await Users.findOne<IUser>({ username }, { projection: { 'services.password.bcrypt': 1 } });
|
||||
if (!user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const valid = user.services?.password?.bcrypt && validatePassword(password, user.services.password.bcrypt);
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const newToken = _generateStampedLoginToken();
|
||||
|
||||
const hashedToken = _hashStampedToken(newToken);
|
||||
|
||||
await saveSession(user._id, hashedToken);
|
||||
|
||||
return {
|
||||
uid: user._id,
|
||||
token: newToken.token,
|
||||
hashedToken: hashedToken.hashedToken,
|
||||
tokenExpires: _tokenExpiration(newToken.when, loginExpiration),
|
||||
type: 'password',
|
||||
};
|
||||
}
|
||||
14
ee/apps/account-service/src/lib/removeSession.ts
Normal file
14
ee/apps/account-service/src/lib/removeSession.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Users } from '@rocket.chat/models';
|
||||
|
||||
export async function removeSession(uid: string, loginToken: string): Promise<void> {
|
||||
await Users.updateOne(
|
||||
{ _id: uid },
|
||||
{
|
||||
$pull: {
|
||||
'services.resume.loginTokens': {
|
||||
$or: [{ hashedToken: loginToken }, { token: loginToken }],
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
14
ee/apps/account-service/src/lib/saveSession.ts
Normal file
14
ee/apps/account-service/src/lib/saveSession.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Users } from '@rocket.chat/models';
|
||||
|
||||
import type { IHashedStampedToken } from './utils';
|
||||
|
||||
export async function saveSession(uid: string, newToken: IHashedStampedToken): Promise<void> {
|
||||
await Users.updateOne(
|
||||
{ _id: uid },
|
||||
{
|
||||
$push: {
|
||||
'services.resume.loginTokens': newToken.hashedToken,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
33
ee/apps/account-service/src/service.ts
Executable file
33
ee/apps/account-service/src/service.ts
Executable file
@ -0,0 +1,33 @@
|
||||
import type { Document } from 'mongodb';
|
||||
import polka from 'polka';
|
||||
|
||||
import { api } from '../../../../apps/meteor/server/sdk/api';
|
||||
import { broker } from '../../../../apps/meteor/ee/server/startup/broker';
|
||||
import { Collections, getCollection, getConnection } from '../../../../apps/meteor/ee/server/services/mongo';
|
||||
import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels';
|
||||
|
||||
const PORT = process.env.PORT || 3033;
|
||||
|
||||
(async () => {
|
||||
const db = await getConnection();
|
||||
|
||||
const trash = await getCollection<Document>(Collections.Trash);
|
||||
|
||||
registerServiceModels(db, trash);
|
||||
|
||||
api.setBroker(broker);
|
||||
|
||||
// need to import service after models are registered
|
||||
const { Account } = await import('./Account');
|
||||
|
||||
api.registerService(new Account());
|
||||
|
||||
await api.start();
|
||||
|
||||
polka()
|
||||
.get('/health', async function (_req, res) {
|
||||
await api.nodeList();
|
||||
res.end('ok');
|
||||
})
|
||||
.listen(PORT);
|
||||
})();
|
||||
31
ee/apps/account-service/tsconfig.json
Normal file
31
ee/apps/account-service/tsconfig.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"target": "es2018",
|
||||
"lib": ["esnext", "dom"],
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"incremental": true,
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"strictPropertyInitialization": false,
|
||||
"strictFunctionTypes": false,
|
||||
|
||||
/* Additional Checks */
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": false,
|
||||
"noFallthroughCasesInSwitch": false,
|
||||
|
||||
/* Module Resolution Options */
|
||||
"outDir": "./dist",
|
||||
"importsNotUsedAsValues": "preserve",
|
||||
"declaration": false,
|
||||
"declarationMap": false
|
||||
},
|
||||
"files": ["./src/service.ts"],
|
||||
"include": ["../../../apps/meteor/definition/externals/meteor"],
|
||||
"exclude": ["./dist"]
|
||||
}
|
||||
16
ee/apps/authorization-service/.eslintrc
Normal file
16
ee/apps/authorization-service/.eslintrc
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": ["@rocket.chat/eslint-config"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.spec.js", "**/*.spec.jsx"],
|
||||
"env": {
|
||||
"jest": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"ignorePatterns": ["**/dist"],
|
||||
"plugins": ["jest"],
|
||||
"env": {
|
||||
"jest/globals": true
|
||||
}
|
||||
}
|
||||
34
ee/apps/authorization-service/Dockerfile
Normal file
34
ee/apps/authorization-service/Dockerfile
Normal file
@ -0,0 +1,34 @@
|
||||
FROM node:14.19.3-alpine
|
||||
|
||||
ARG SERVICE
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./packages/core-typings/package.json packages/core-typings/package.json
|
||||
COPY ./packages/core-typings/dist packages/core-typings/dist
|
||||
COPY ./packages/rest-typings/package.json packages/rest-typings/package.json
|
||||
COPY ./packages/rest-typings/dist packages/rest-typings/dist
|
||||
COPY ./packages/model-typings/package.json packages/model-typings/package.json
|
||||
COPY ./packages/model-typings/dist packages/model-typings/dist
|
||||
COPY ./packages/models/package.json packages/models/package.json
|
||||
COPY ./packages/models/dist packages/models/dist
|
||||
|
||||
COPY ./ee/apps/${SERVICE}/dist .
|
||||
|
||||
COPY ./package.json .
|
||||
COPY ./yarn.lock .
|
||||
COPY ./.yarnrc.yml .
|
||||
COPY ./.yarn/plugins .yarn/plugins
|
||||
COPY ./.yarn/releases .yarn/releases
|
||||
COPY ./ee/apps/${SERVICE}/package.json ee/apps/${SERVICE}/package.json
|
||||
|
||||
ENV NODE_ENV=production \
|
||||
PORT=3000
|
||||
|
||||
WORKDIR /app/ee/apps/${SERVICE}
|
||||
|
||||
RUN yarn workspaces focus --production
|
||||
|
||||
EXPOSE 3000 9458
|
||||
|
||||
CMD ["node", "src/service.js"]
|
||||
47
ee/apps/authorization-service/package.json
Normal file
47
ee/apps/authorization-service/package.json
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "@rocket.chat/authorization-service",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"description": "Rocket.Chat Authorization service",
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"ms": "MONGO_URL=${MONGO_URL:-mongodb://localhost:3001/meteor} ts-node --files src/service.ts",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"lint": "eslint src",
|
||||
"typecheck": "tsc --noEmit --skipLibCheck -p tsconfig.json"
|
||||
},
|
||||
"keywords": [
|
||||
"rocketchat"
|
||||
],
|
||||
"author": "Rocket.Chat",
|
||||
"dependencies": {
|
||||
"@rocket.chat/core-typings": "workspace:^",
|
||||
"@rocket.chat/emitter": "next",
|
||||
"@rocket.chat/model-typings": "workspace:^",
|
||||
"@rocket.chat/models": "workspace:^",
|
||||
"@rocket.chat/rest-typings": "workspace:^",
|
||||
"@rocket.chat/string-helpers": "next",
|
||||
"@types/node": "^14.18.21",
|
||||
"ejson": "^2.2.2",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"fibers": "^5.0.3",
|
||||
"mem": "^8.1.1",
|
||||
"moleculer": "^0.14.21",
|
||||
"mongodb": "^4.3.1",
|
||||
"nats": "^2.4.0",
|
||||
"pino": "^8.4.2",
|
||||
"polka": "^0.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rocket.chat/eslint-config": "workspace:^",
|
||||
"@types/eslint": "^8",
|
||||
"@types/polka": "^0.5.4",
|
||||
"eslint": "^8.21.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "~4.5.5"
|
||||
},
|
||||
"main": "./dist/ee/apps/authorization-service/src/service.js",
|
||||
"files": [
|
||||
"/dist"
|
||||
]
|
||||
}
|
||||
33
ee/apps/authorization-service/src/service.ts
Executable file
33
ee/apps/authorization-service/src/service.ts
Executable file
@ -0,0 +1,33 @@
|
||||
import type { Document } from 'mongodb';
|
||||
import polka from 'polka';
|
||||
|
||||
import { api } from '../../../../apps/meteor/server/sdk/api';
|
||||
import { broker } from '../../../../apps/meteor/ee/server/startup/broker';
|
||||
import { Collections, getCollection, getConnection } from '../../../../apps/meteor/ee/server/services/mongo';
|
||||
import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels';
|
||||
|
||||
const PORT = process.env.PORT || 3034;
|
||||
|
||||
(async () => {
|
||||
const db = await getConnection();
|
||||
|
||||
const trash = await getCollection<Document>(Collections.Trash);
|
||||
|
||||
registerServiceModels(db, trash);
|
||||
|
||||
api.setBroker(broker);
|
||||
|
||||
// need to import service after models are registered
|
||||
const { Authorization } = await import('../../../../apps/meteor/server/services/authorization/service');
|
||||
|
||||
api.registerService(new Authorization());
|
||||
|
||||
await api.start();
|
||||
|
||||
polka()
|
||||
.get('/health', async function (_req, res) {
|
||||
await api.nodeList();
|
||||
res.end('ok');
|
||||
})
|
||||
.listen(PORT);
|
||||
})();
|
||||
31
ee/apps/authorization-service/tsconfig.json
Normal file
31
ee/apps/authorization-service/tsconfig.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"target": "es2018",
|
||||
"lib": ["esnext", "dom"],
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"incremental": true,
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"strictPropertyInitialization": false,
|
||||
"strictFunctionTypes": false,
|
||||
|
||||
/* Additional Checks */
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": false,
|
||||
"noFallthroughCasesInSwitch": false,
|
||||
|
||||
/* Module Resolution Options */
|
||||
"outDir": "./dist",
|
||||
"importsNotUsedAsValues": "preserve",
|
||||
"declaration": false,
|
||||
"declarationMap": false
|
||||
},
|
||||
"files": ["./src/service.ts"],
|
||||
"include": ["../../../apps/meteor/definition/externals/meteor"],
|
||||
"exclude": ["./dist"]
|
||||
}
|
||||
@ -12,8 +12,6 @@ COPY ./packages/model-typings/package.json packages/model-typings/package.json
|
||||
COPY ./packages/model-typings/dist packages/model-typings/dist
|
||||
COPY ./packages/models/package.json packages/models/package.json
|
||||
COPY ./packages/models/dist packages/models/dist
|
||||
COPY ./packages/ui-contexts/package.json packages/ui-contexts/package.json
|
||||
COPY ./packages/ui-contexts/dist packages/ui-contexts/dist
|
||||
|
||||
COPY ./ee/apps/${SERVICE}/dist .
|
||||
|
||||
|
||||
@ -22,16 +22,16 @@
|
||||
"@rocket.chat/models": "workspace:^",
|
||||
"@rocket.chat/rest-typings": "workspace:^",
|
||||
"@rocket.chat/string-helpers": "next",
|
||||
"@rocket.chat/ui-contexts": "workspace:^",
|
||||
"colorette": "^1.4.0",
|
||||
"ejson": "^2.2.2",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"fibers": "^5.0.1",
|
||||
"fibers": "^5.0.3",
|
||||
"jaeger-client": "^3.19.0",
|
||||
"moleculer": "^0.14.21",
|
||||
"mongodb": "^4.3.1",
|
||||
"nats": "^2.4.0",
|
||||
"pino": "^7.11.0",
|
||||
"polka": "^0.5.2",
|
||||
"sharp": "^0.30.7",
|
||||
"underscore": "^1.13.4",
|
||||
"uuid": "^7.0.3",
|
||||
@ -43,6 +43,7 @@
|
||||
"@types/eslint": "^8",
|
||||
"@types/meteor": "2.7.1",
|
||||
"@types/node": "^14.18.21",
|
||||
"@types/polka": "^0.5.4",
|
||||
"@types/sharp": "^0.30.4",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/ws": "^8.5.3",
|
||||
|
||||
@ -1,68 +1,29 @@
|
||||
import type { IncomingMessage, RequestOptions, ServerResponse } from 'http';
|
||||
import http from 'http';
|
||||
import url from 'url';
|
||||
import crypto from 'crypto';
|
||||
|
||||
import polka from 'polka';
|
||||
import WebSocket from 'ws';
|
||||
|
||||
import type { NotificationsModule } from '../../../../apps/meteor/server/modules/notifications/notifications.module';
|
||||
import { ListenersModule } from '../../../../apps/meteor/server/modules/listeners/listeners.module';
|
||||
import { StreamerCentral } from '../../../../apps/meteor/server/modules/streamer/streamer.module';
|
||||
import { MeteorService, Presence } from '../../../../apps/meteor/server/sdk';
|
||||
import { ServiceClass } from '../../../../apps/meteor/server/sdk/types/ServiceClass';
|
||||
import { api } from '../../../../apps/meteor/server/sdk/api';
|
||||
import { Client } from './Client';
|
||||
import { events, server } from './configureServer';
|
||||
import { DDP_EVENTS } from './constants';
|
||||
import { Autoupdate } from './lib/Autoupdate';
|
||||
import { notifications } from './streams';
|
||||
import { proxy } from './proxy';
|
||||
|
||||
const { PORT: port = 4000 } = process.env;
|
||||
|
||||
const proxy = function (req: IncomingMessage, res: ServerResponse): void {
|
||||
req.pause();
|
||||
const options: RequestOptions = url.parse(req.url || '');
|
||||
options.headers = req.headers;
|
||||
options.method = req.method;
|
||||
options.agent = false;
|
||||
options.hostname = 'localhost';
|
||||
options.port = 3000;
|
||||
|
||||
const connector = http.request(options, function (serverResponse) {
|
||||
serverResponse.pause();
|
||||
if (serverResponse.statusCode) {
|
||||
res.writeHead(serverResponse.statusCode, serverResponse.headers);
|
||||
}
|
||||
serverResponse.pipe(res);
|
||||
serverResponse.resume();
|
||||
});
|
||||
req.pipe(connector);
|
||||
req.resume();
|
||||
};
|
||||
|
||||
const httpServer = http.createServer((req, res) => {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
|
||||
if (process.env.NODE_ENV !== 'production' && !/^\/sockjs\/info\?cb=/.test(req.url || '')) {
|
||||
return proxy(req, res);
|
||||
|
||||
// res.writeHead(404);
|
||||
// return res.end();
|
||||
}
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
|
||||
res.end('{"websocket":true,"origins":["*:*"],"cookie_needed":false,"entropy":666}');
|
||||
});
|
||||
|
||||
httpServer.listen(port);
|
||||
|
||||
const wss = new WebSocket.Server({ server: httpServer });
|
||||
|
||||
wss.on('connection', (ws, req) => new Client(ws, req.url !== '/websocket', req));
|
||||
const { PORT = 4000 } = process.env;
|
||||
|
||||
export class DDPStreamer extends ServiceClass {
|
||||
protected name = 'streamer';
|
||||
|
||||
constructor() {
|
||||
private app?: polka.Polka;
|
||||
|
||||
private wss?: WebSocket.Server;
|
||||
|
||||
constructor(notifications: NotificationsModule) {
|
||||
super();
|
||||
|
||||
new ListenersModule(this, notifications);
|
||||
@ -90,15 +51,6 @@ export class DDPStreamer extends ServiceClass {
|
||||
});
|
||||
}
|
||||
|
||||
async started(): Promise<void> {
|
||||
// TODO this call creates a dependency to MeteorService, should it be a hard dependency? or can this call fail and be ignored?
|
||||
const versions = await MeteorService.getAutoUpdateClientVersions();
|
||||
|
||||
Object.keys(versions).forEach((key) => {
|
||||
Autoupdate.updateVersion(versions[key]);
|
||||
});
|
||||
}
|
||||
|
||||
async created(): Promise<void> {
|
||||
if (!this.context) {
|
||||
return;
|
||||
@ -147,13 +99,13 @@ export class DDPStreamer extends ServiceClass {
|
||||
const { userId, connection } = info;
|
||||
|
||||
Presence.newConnection(userId, connection.id, nodeID);
|
||||
api.broadcast('accounts.login', { userId, connection });
|
||||
this.api.broadcast('accounts.login', { userId, connection });
|
||||
});
|
||||
|
||||
server.on(DDP_EVENTS.LOGGEDOUT, (info) => {
|
||||
const { userId, connection } = info;
|
||||
|
||||
api.broadcast('accounts.logout', { userId, connection });
|
||||
this.api.broadcast('accounts.logout', { userId, connection });
|
||||
|
||||
if (!userId) {
|
||||
return;
|
||||
@ -164,7 +116,7 @@ export class DDPStreamer extends ServiceClass {
|
||||
server.on(DDP_EVENTS.DISCONNECTED, (info) => {
|
||||
const { userId, connection } = info;
|
||||
|
||||
api.broadcast('socket.disconnected', connection);
|
||||
this.api.broadcast('socket.disconnected', connection);
|
||||
|
||||
if (!userId) {
|
||||
return;
|
||||
@ -173,7 +125,45 @@ export class DDPStreamer extends ServiceClass {
|
||||
});
|
||||
|
||||
server.on(DDP_EVENTS.CONNECTED, ({ connection }) => {
|
||||
api.broadcast('socket.connected', connection);
|
||||
this.api.broadcast('socket.connected', connection);
|
||||
});
|
||||
}
|
||||
|
||||
async started(): Promise<void> {
|
||||
// TODO this call creates a dependency to MeteorService, should it be a hard dependency? or can this call fail and be ignored?
|
||||
const versions = await MeteorService.getAutoUpdateClientVersions();
|
||||
|
||||
Object.keys(versions).forEach((key) => {
|
||||
Autoupdate.updateVersion(versions[key]);
|
||||
});
|
||||
|
||||
this.app = polka()
|
||||
.use(proxy())
|
||||
.get('/health', async (_req, res) => {
|
||||
await this.api.nodeList();
|
||||
res.end('ok');
|
||||
})
|
||||
.get('*', function (_req, res) {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
|
||||
res.writeHead(200);
|
||||
|
||||
res.end(`{"websocket":true,"origins":["*:*"],"cookie_needed":false,"entropy":${crypto.randomBytes(4).readUInt32LE(0)},"ms":true}`);
|
||||
})
|
||||
.listen(PORT);
|
||||
|
||||
this.wss = new WebSocket.Server({ server: this.app.server });
|
||||
|
||||
this.wss.on('connection', (ws, req) => new Client(ws, req.url !== '/websocket', req));
|
||||
}
|
||||
|
||||
async stopped(): Promise<void> {
|
||||
this.wss?.clients.forEach(function (client) {
|
||||
client.terminate();
|
||||
});
|
||||
|
||||
this.app?.server?.close();
|
||||
this.wss?.close();
|
||||
}
|
||||
}
|
||||
|
||||
41
ee/apps/ddp-streamer/src/proxy.ts
Normal file
41
ee/apps/ddp-streamer/src/proxy.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import type { IncomingMessage, RequestOptions, ServerResponse } from 'http';
|
||||
import http from 'http';
|
||||
import url from 'url';
|
||||
|
||||
import type polka from 'polka';
|
||||
|
||||
const isProdEnv = process.env.NODE_ENV === 'production';
|
||||
|
||||
const skipProxyPaths = [/^\/sockjs\/info\?cb=/, /^\/health/];
|
||||
|
||||
export function proxy(): (req: IncomingMessage, res: ServerResponse, next: polka.Next) => void {
|
||||
if (isProdEnv) {
|
||||
return (_req, _res, next) => next();
|
||||
}
|
||||
|
||||
return (req, res, next) => {
|
||||
if (skipProxyPaths.some((regex) => regex.test(req.url || ''))) {
|
||||
return next();
|
||||
}
|
||||
|
||||
req.pause();
|
||||
|
||||
const options: RequestOptions = url.parse(req.url || '');
|
||||
options.headers = req.headers;
|
||||
options.method = req.method;
|
||||
options.agent = false;
|
||||
options.hostname = 'localhost';
|
||||
options.port = 3000;
|
||||
|
||||
const connector = http.request(options, function (serverResponse) {
|
||||
serverResponse.pause();
|
||||
if (serverResponse.statusCode) {
|
||||
res.writeHead(serverResponse.statusCode, serverResponse.headers);
|
||||
}
|
||||
serverResponse.pipe(res);
|
||||
serverResponse.resume();
|
||||
});
|
||||
req.pipe(connector);
|
||||
req.resume();
|
||||
};
|
||||
}
|
||||
@ -1,6 +1,29 @@
|
||||
import '../../../../apps/meteor/ee/server/startup/broker';
|
||||
import type { Document } from 'mongodb';
|
||||
|
||||
import { api } from '../../../../apps/meteor/server/sdk/api';
|
||||
import { DDPStreamer } from './DDPStreamer';
|
||||
import { broker } from '../../../../apps/meteor/ee/server/startup/broker';
|
||||
import { Collections, getCollection, getConnection } from '../../../../apps/meteor/ee/server/services/mongo';
|
||||
import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels';
|
||||
|
||||
api.registerService(new DDPStreamer());
|
||||
(async () => {
|
||||
const db = await getConnection();
|
||||
|
||||
const trash = await getCollection<Document>(Collections.Trash);
|
||||
|
||||
registerServiceModels(db, trash);
|
||||
|
||||
api.setBroker(broker);
|
||||
|
||||
// need to import service after models are registered
|
||||
const { NotificationsModule } = await import('../../../../apps/meteor/server/modules/notifications/notifications.module');
|
||||
const { DDPStreamer } = await import('./DDPStreamer');
|
||||
const { Stream } = await import('./Streamer');
|
||||
|
||||
const notifications = new NotificationsModule(Stream);
|
||||
|
||||
notifications.configure();
|
||||
|
||||
api.registerService(new DDPStreamer(notifications));
|
||||
|
||||
await api.start();
|
||||
})();
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
import type { Document } from 'mongodb';
|
||||
|
||||
import { NotificationsModule } from '../../../../apps/meteor/server/modules/notifications/notifications.module';
|
||||
import { Stream } from './Streamer';
|
||||
import { Collections, getCollection, getConnection } from '../../../../apps/meteor/ee/server/services/mongo';
|
||||
import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels';
|
||||
|
||||
export const notifications = new NotificationsModule(Stream);
|
||||
|
||||
getConnection().then(async (db) => {
|
||||
const trash = await getCollection<Document>(Collections.Trash);
|
||||
|
||||
registerServiceModels(db, trash);
|
||||
|
||||
notifications.configure();
|
||||
});
|
||||
@ -15,13 +15,16 @@
|
||||
],
|
||||
"author": "Rocket.Chat",
|
||||
"dependencies": {
|
||||
"@rocket.chat/core-typings": "workspace:^",
|
||||
"@rocket.chat/emitter": "next",
|
||||
"@rocket.chat/model-typings": "workspace:^",
|
||||
"@rocket.chat/models": "workspace:^",
|
||||
"@rocket.chat/presence": "workspace:^",
|
||||
"@rocket.chat/string-helpers": "next",
|
||||
"@types/node": "^14.18.21",
|
||||
"ejson": "^2.2.2",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"fibers": "^5.0.1",
|
||||
"fibers": "^5.0.3",
|
||||
"moleculer": "^0.14.21",
|
||||
"mongodb": "^4.3.1",
|
||||
"nats": "^2.4.0",
|
||||
|
||||
@ -1,28 +1,33 @@
|
||||
import type { Document } from 'mongodb';
|
||||
import polka from 'polka';
|
||||
|
||||
import '../../../../apps/meteor/ee/server/startup/broker';
|
||||
|
||||
import { api } from '../../../../apps/meteor/server/sdk/api';
|
||||
import { broker } from '../../../../apps/meteor/ee/server/startup/broker';
|
||||
import { Collections, getCollection, getConnection } from '../../../../apps/meteor/ee/server/services/mongo';
|
||||
import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels';
|
||||
|
||||
const PORT = process.env.PORT || 3031;
|
||||
|
||||
getConnection().then(async (db) => {
|
||||
(async () => {
|
||||
const db = await getConnection();
|
||||
|
||||
const trash = await getCollection<Document>(Collections.Trash);
|
||||
|
||||
registerServiceModels(db, trash);
|
||||
|
||||
api.setBroker(broker);
|
||||
|
||||
// need to import Presence service after models are registered
|
||||
const { Presence } = await import('@rocket.chat/presence');
|
||||
|
||||
api.registerService(new Presence());
|
||||
|
||||
await api.start();
|
||||
|
||||
polka()
|
||||
.get('/health', async function (_req, res) {
|
||||
await api.nodeList();
|
||||
res.end('ok');
|
||||
})
|
||||
.listen(PORT);
|
||||
});
|
||||
})();
|
||||
|
||||
@ -26,6 +26,6 @@
|
||||
"declarationMap": false
|
||||
},
|
||||
"files": ["./src/service.ts"],
|
||||
"include": ["../../../apps/meteor/definition"],
|
||||
"include": ["../../../apps/meteor/definition/externals/meteor"],
|
||||
"exclude": ["./dist"]
|
||||
}
|
||||
|
||||
16
ee/apps/stream-hub-service/.eslintrc
Normal file
16
ee/apps/stream-hub-service/.eslintrc
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": ["@rocket.chat/eslint-config"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.spec.js", "**/*.spec.jsx"],
|
||||
"env": {
|
||||
"jest": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"ignorePatterns": ["**/dist"],
|
||||
"plugins": ["jest"],
|
||||
"env": {
|
||||
"jest/globals": true
|
||||
}
|
||||
}
|
||||
34
ee/apps/stream-hub-service/Dockerfile
Normal file
34
ee/apps/stream-hub-service/Dockerfile
Normal file
@ -0,0 +1,34 @@
|
||||
FROM node:14.19.3-alpine
|
||||
|
||||
ARG SERVICE
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./packages/core-typings/package.json packages/core-typings/package.json
|
||||
COPY ./packages/core-typings/dist packages/core-typings/dist
|
||||
COPY ./packages/rest-typings/package.json packages/rest-typings/package.json
|
||||
COPY ./packages/rest-typings/dist packages/rest-typings/dist
|
||||
COPY ./packages/model-typings/package.json packages/model-typings/package.json
|
||||
COPY ./packages/model-typings/dist packages/model-typings/dist
|
||||
COPY ./packages/models/package.json packages/models/package.json
|
||||
COPY ./packages/models/dist packages/models/dist
|
||||
|
||||
COPY ./ee/apps/${SERVICE}/dist .
|
||||
|
||||
COPY ./package.json .
|
||||
COPY ./yarn.lock .
|
||||
COPY ./.yarnrc.yml .
|
||||
COPY ./.yarn/plugins .yarn/plugins
|
||||
COPY ./.yarn/releases .yarn/releases
|
||||
COPY ./ee/apps/${SERVICE}/package.json ee/apps/${SERVICE}/package.json
|
||||
|
||||
ENV NODE_ENV=production \
|
||||
PORT=3000
|
||||
|
||||
WORKDIR /app/ee/apps/${SERVICE}
|
||||
|
||||
RUN yarn workspaces focus --production
|
||||
|
||||
EXPOSE 3000 9458
|
||||
|
||||
CMD ["node", "src/service.js"]
|
||||
47
ee/apps/stream-hub-service/package.json
Normal file
47
ee/apps/stream-hub-service/package.json
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "@rocket.chat/stream-hub-service",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"description": "Rocket.Chat Stream Hub service",
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"ms": "MONGO_URL=${MONGO_URL:-mongodb://localhost:3001/meteor} ts-node --files src/service.ts",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"lint": "eslint src",
|
||||
"typecheck": "tsc --noEmit --skipLibCheck -p tsconfig.json"
|
||||
},
|
||||
"keywords": [
|
||||
"rocketchat"
|
||||
],
|
||||
"author": "Rocket.Chat",
|
||||
"dependencies": {
|
||||
"@rocket.chat/core-typings": "workspace:^",
|
||||
"@rocket.chat/emitter": "next",
|
||||
"@rocket.chat/model-typings": "workspace:^",
|
||||
"@rocket.chat/models": "workspace:^",
|
||||
"@rocket.chat/string-helpers": "next",
|
||||
"@types/node": "^14.18.21",
|
||||
"ejson": "^2.2.2",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"fibers": "^5.0.3",
|
||||
"mem": "^8.1.1",
|
||||
"moleculer": "^0.14.21",
|
||||
"mongodb": "^4.3.1",
|
||||
"nats": "^2.4.0",
|
||||
"pino": "^8.4.2",
|
||||
"polka": "^0.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rocket.chat/eslint-config": "workspace:^",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/eslint": "^8",
|
||||
"@types/polka": "^0.5.4",
|
||||
"eslint": "^8.21.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "~4.5.5"
|
||||
},
|
||||
"main": "./dist/ee/apps/stream-hub-service/src/service.js",
|
||||
"files": [
|
||||
"/dist"
|
||||
]
|
||||
}
|
||||
22
ee/apps/stream-hub-service/src/StreamHub.ts
Executable file
22
ee/apps/stream-hub-service/src/StreamHub.ts
Executable file
@ -0,0 +1,22 @@
|
||||
import type { Db } from 'mongodb';
|
||||
|
||||
import type { IServiceClass } from '../../../../apps/meteor/server/sdk/types/ServiceClass';
|
||||
import { ServiceClass } from '../../../../apps/meteor/server/sdk/types/ServiceClass';
|
||||
import { initWatchers } from '../../../../apps/meteor/server/modules/watchers/watchers.module';
|
||||
import { DatabaseWatcher } from '../../../../apps/meteor/server/database/DatabaseWatcher';
|
||||
|
||||
export class StreamHub extends ServiceClass implements IServiceClass {
|
||||
protected name = 'hub';
|
||||
|
||||
constructor(private db: Db) {
|
||||
super();
|
||||
}
|
||||
|
||||
async created(): Promise<void> {
|
||||
const watcher = new DatabaseWatcher({ db: this.db });
|
||||
|
||||
initWatchers(watcher, this.api.broadcast.bind(this.api));
|
||||
|
||||
watcher.watch();
|
||||
}
|
||||
}
|
||||
33
ee/apps/stream-hub-service/src/service.ts
Executable file
33
ee/apps/stream-hub-service/src/service.ts
Executable file
@ -0,0 +1,33 @@
|
||||
import type { Document } from 'mongodb';
|
||||
import polka from 'polka';
|
||||
|
||||
import { api } from '../../../../apps/meteor/server/sdk/api';
|
||||
import { broker } from '../../../../apps/meteor/ee/server/startup/broker';
|
||||
import { Collections, getCollection, getConnection } from '../../../../apps/meteor/ee/server/services/mongo';
|
||||
import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels';
|
||||
|
||||
const PORT = process.env.PORT || 3035;
|
||||
|
||||
(async () => {
|
||||
const db = await getConnection();
|
||||
|
||||
const trash = await getCollection<Document>(Collections.Trash);
|
||||
|
||||
registerServiceModels(db, trash);
|
||||
|
||||
api.setBroker(broker);
|
||||
|
||||
// need to import service after models are registered
|
||||
const { StreamHub } = await import('./StreamHub');
|
||||
|
||||
api.registerService(new StreamHub(db));
|
||||
|
||||
await api.start();
|
||||
|
||||
polka()
|
||||
.get('/health', async function (_req, res) {
|
||||
await api.nodeList();
|
||||
res.end('ok');
|
||||
})
|
||||
.listen(PORT);
|
||||
})();
|
||||
31
ee/apps/stream-hub-service/tsconfig.json
Normal file
31
ee/apps/stream-hub-service/tsconfig.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"target": "es2018",
|
||||
"lib": ["esnext", "dom"],
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"incremental": true,
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"strictPropertyInitialization": false,
|
||||
"strictFunctionTypes": false,
|
||||
|
||||
/* Additional Checks */
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": false,
|
||||
"noFallthroughCasesInSwitch": false,
|
||||
|
||||
/* Module Resolution Options */
|
||||
"outDir": "./dist",
|
||||
"importsNotUsedAsValues": "preserve",
|
||||
"declaration": false,
|
||||
"declarationMap": false
|
||||
},
|
||||
"files": ["./src/service.ts"],
|
||||
"include": ["../../../apps/meteor/definition/externals/meteor"],
|
||||
"exclude": ["./dist"]
|
||||
}
|
||||
@ -76,6 +76,7 @@ export interface IStats {
|
||||
};
|
||||
instanceCount: number;
|
||||
oplogEnabled: boolean;
|
||||
msEnabled: boolean;
|
||||
mongoVersion: string;
|
||||
mongoStorageEngine: string;
|
||||
pushQueue: number;
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
"@rocket.chat/apps-engine": "^1.32.0",
|
||||
"@rocket.chat/eslint-config": "workspace:^",
|
||||
"@rocket.chat/rest-typings": "workspace:^",
|
||||
"@rocket.chat/ui-contexts": "workspace:^",
|
||||
"@types/node": "^14.18.21",
|
||||
"babel-jest": "^29.0.3",
|
||||
"eslint": "^8.21.0",
|
||||
|
||||
119
yarn.lock
119
yarn.lock
@ -5462,6 +5462,38 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rocket.chat/account-service@workspace:ee/apps/account-service":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@rocket.chat/account-service@workspace:ee/apps/account-service"
|
||||
dependencies:
|
||||
"@rocket.chat/core-typings": "workspace:^"
|
||||
"@rocket.chat/emitter": next
|
||||
"@rocket.chat/eslint-config": "workspace:^"
|
||||
"@rocket.chat/model-typings": "workspace:^"
|
||||
"@rocket.chat/models": "workspace:^"
|
||||
"@rocket.chat/rest-typings": "workspace:^"
|
||||
"@rocket.chat/string-helpers": next
|
||||
"@types/bcrypt": ^5.0.0
|
||||
"@types/eslint": ^8
|
||||
"@types/node": ^14.18.21
|
||||
"@types/polka": ^0.5.4
|
||||
bcrypt: ^5.0.1
|
||||
ejson: ^2.2.2
|
||||
eslint: ^8.21.0
|
||||
eventemitter3: ^4.0.7
|
||||
fibers: ^5.0.3
|
||||
mem: ^8.1.1
|
||||
moleculer: ^0.14.21
|
||||
mongodb: ^4.3.1
|
||||
nats: ^2.4.0
|
||||
pino: ^8.4.2
|
||||
polka: ^0.5.2
|
||||
ts-node: ^10.9.1
|
||||
typescript: ~4.5.5
|
||||
uuid: ^9.0.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@rocket.chat/agenda@workspace:^, @rocket.chat/agenda@workspace:packages/agenda":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@rocket.chat/agenda@workspace:packages/agenda"
|
||||
@ -5542,6 +5574,35 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rocket.chat/authorization-service@workspace:ee/apps/authorization-service":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@rocket.chat/authorization-service@workspace:ee/apps/authorization-service"
|
||||
dependencies:
|
||||
"@rocket.chat/core-typings": "workspace:^"
|
||||
"@rocket.chat/emitter": next
|
||||
"@rocket.chat/eslint-config": "workspace:^"
|
||||
"@rocket.chat/model-typings": "workspace:^"
|
||||
"@rocket.chat/models": "workspace:^"
|
||||
"@rocket.chat/rest-typings": "workspace:^"
|
||||
"@rocket.chat/string-helpers": next
|
||||
"@types/eslint": ^8
|
||||
"@types/node": ^14.18.21
|
||||
"@types/polka": ^0.5.4
|
||||
ejson: ^2.2.2
|
||||
eslint: ^8.21.0
|
||||
eventemitter3: ^4.0.7
|
||||
fibers: ^5.0.3
|
||||
mem: ^8.1.1
|
||||
moleculer: ^0.14.21
|
||||
mongodb: ^4.3.1
|
||||
nats: ^2.4.0
|
||||
pino: ^8.4.2
|
||||
polka: ^0.5.2
|
||||
ts-node: ^10.9.1
|
||||
typescript: ~4.5.5
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@rocket.chat/cas-validate@workspace:^, @rocket.chat/cas-validate@workspace:packages/cas-validate":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@rocket.chat/cas-validate@workspace:packages/cas-validate"
|
||||
@ -5605,11 +5666,11 @@ __metadata:
|
||||
"@rocket.chat/models": "workspace:^"
|
||||
"@rocket.chat/rest-typings": "workspace:^"
|
||||
"@rocket.chat/string-helpers": next
|
||||
"@rocket.chat/ui-contexts": "workspace:^"
|
||||
"@types/ejson": ^2.2.0
|
||||
"@types/eslint": ^8
|
||||
"@types/meteor": 2.7.1
|
||||
"@types/node": ^14.18.21
|
||||
"@types/polka": ^0.5.4
|
||||
"@types/sharp": ^0.30.4
|
||||
"@types/uuid": ^8.3.4
|
||||
"@types/ws": ^8.5.3
|
||||
@ -5617,13 +5678,14 @@ __metadata:
|
||||
ejson: ^2.2.2
|
||||
eslint: ^8.22.0
|
||||
eventemitter3: ^4.0.7
|
||||
fibers: ^5.0.1
|
||||
fibers: ^5.0.3
|
||||
jaeger-client: ^3.19.0
|
||||
moleculer: ^0.14.21
|
||||
mongodb: ^4.3.1
|
||||
nats: ^2.4.0
|
||||
pino: ^7.11.0
|
||||
pino-pretty: ^7.6.1
|
||||
polka: ^0.5.2
|
||||
sharp: ^0.30.7
|
||||
ts-node: ^10.9.1
|
||||
typescript: ~4.5.5
|
||||
@ -6454,8 +6516,11 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@rocket.chat/presence-service@workspace:ee/apps/presence-service"
|
||||
dependencies:
|
||||
"@rocket.chat/core-typings": "workspace:^"
|
||||
"@rocket.chat/emitter": next
|
||||
"@rocket.chat/eslint-config": "workspace:^"
|
||||
"@rocket.chat/model-typings": "workspace:^"
|
||||
"@rocket.chat/models": "workspace:^"
|
||||
"@rocket.chat/presence": "workspace:^"
|
||||
"@rocket.chat/string-helpers": next
|
||||
"@types/eslint": ^8
|
||||
@ -6464,7 +6529,7 @@ __metadata:
|
||||
ejson: ^2.2.2
|
||||
eslint: ^8.21.0
|
||||
eventemitter3: ^4.0.7
|
||||
fibers: ^5.0.1
|
||||
fibers: ^5.0.3
|
||||
moleculer: ^0.14.21
|
||||
mongodb: ^4.3.1
|
||||
nats: ^2.4.0
|
||||
@ -6487,7 +6552,6 @@ __metadata:
|
||||
"@rocket.chat/eslint-config": "workspace:^"
|
||||
"@rocket.chat/models": "workspace:^"
|
||||
"@rocket.chat/rest-typings": "workspace:^"
|
||||
"@rocket.chat/ui-contexts": "workspace:^"
|
||||
"@types/node": ^14.18.21
|
||||
babel-jest: ^29.0.3
|
||||
eslint: ^8.21.0
|
||||
@ -6538,6 +6602,35 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rocket.chat/stream-hub-service@workspace:ee/apps/stream-hub-service":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@rocket.chat/stream-hub-service@workspace:ee/apps/stream-hub-service"
|
||||
dependencies:
|
||||
"@rocket.chat/core-typings": "workspace:^"
|
||||
"@rocket.chat/emitter": next
|
||||
"@rocket.chat/eslint-config": "workspace:^"
|
||||
"@rocket.chat/model-typings": "workspace:^"
|
||||
"@rocket.chat/models": "workspace:^"
|
||||
"@rocket.chat/string-helpers": next
|
||||
"@types/bcrypt": ^5.0.0
|
||||
"@types/eslint": ^8
|
||||
"@types/node": ^14.18.21
|
||||
"@types/polka": ^0.5.4
|
||||
ejson: ^2.2.2
|
||||
eslint: ^8.21.0
|
||||
eventemitter3: ^4.0.7
|
||||
fibers: ^5.0.3
|
||||
mem: ^8.1.1
|
||||
moleculer: ^0.14.21
|
||||
mongodb: ^4.3.1
|
||||
nats: ^2.4.0
|
||||
pino: ^8.4.2
|
||||
polka: ^0.5.2
|
||||
ts-node: ^10.9.1
|
||||
typescript: ~4.5.5
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@rocket.chat/string-helpers@npm:next":
|
||||
version: 0.31.19-dev.19
|
||||
resolution: "@rocket.chat/string-helpers@npm:0.31.19-dev.19"
|
||||
@ -18513,6 +18606,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fibers@npm:^5.0.3":
|
||||
version: 5.0.3
|
||||
resolution: "fibers@npm:5.0.3"
|
||||
dependencies:
|
||||
detect-libc: ^1.0.3
|
||||
checksum: d66c5e18a911aab3480b846e1c837e5c7cfacb27a2a5fe512919865eaecef33cdd4abc14d777191a6a93473dc52356d48549c91a2a7b8b3450544c44104b23f3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"figgy-pudding@npm:^3.5.1":
|
||||
version: 3.5.2
|
||||
resolution: "figgy-pudding@npm:3.5.2"
|
||||
@ -35808,6 +35910,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"uuid@npm:^9.0.0":
|
||||
version: 9.0.0
|
||||
resolution: "uuid@npm:9.0.0"
|
||||
bin:
|
||||
uuid: dist/bin/uuid
|
||||
checksum: 8dd2c83c43ddc7e1c71e36b60aea40030a6505139af6bee0f382ebcd1a56f6cd3028f7f06ffb07f8cf6ced320b76aea275284b224b002b289f89fe89c389b028
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"v8-compile-cache-lib@npm:^3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "v8-compile-cache-lib@npm:3.0.1"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user