Skip to content

Commit 71bfc68

Browse files
authored
Merge branch 'main' into sentry-masking
2 parents bd4f5a1 + 07074e7 commit 71bfc68

File tree

205 files changed

+9648
-7141
lines changed

Some content is hidden

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

205 files changed

+9648
-7141
lines changed

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ GITHUB_CLIENT_ID=""
5050
GITHUB_CLIENT_SECRET=""
5151
BEEHIIVE_API_KEY=""
5252
BEEHIIVE_PUBLICATION_ID=""
53+
LISTMONK_DOMAIN=""
54+
LISTMONK_USER=""
55+
LISTMONK_API_KEY=""
56+
LISTMONK_LIST_ID=""
5357
THREADS_APP_ID=""
5458
THREADS_APP_SECRET=""
5559
FACEBOOK_APP_ID=""

.github/workflows/build-pr.yml

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
---
2+
name: Build
3+
4+
on:
5+
pull_request_target:
6+
7+
jobs:
8+
build:
9+
runs-on: ubuntu-latest
10+
11+
environment:
12+
name: build-pr
13+
14+
strategy:
15+
matrix:
16+
node-version: ['20.17.0']
17+
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
18+
19+
steps:
20+
- uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 0
23+
ref: ${{ github.event.pull_request.head.sha }}
24+
25+
- name: Use Node.js ${{ matrix.node-version }}
26+
uses: actions/setup-node@v4
27+
with:
28+
node-version: ${{ matrix.node-version }}
29+
30+
- name: Install pnpm
31+
uses: pnpm/action-setup@v2
32+
with:
33+
version: 8
34+
run_install: false
35+
36+
- name: Get pnpm store directory
37+
shell: bash
38+
run: |
39+
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
40+
41+
- name: Setup pnpm cache
42+
uses: actions/cache@v4
43+
with:
44+
path: |
45+
${{ env.STORE_PATH }}
46+
${{ github.workspace }}/.next/cache
47+
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
48+
restore-keys: |
49+
${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}-
50+
51+
- name: Install dependencies
52+
run: pnpm install
53+
54+
- name: Build
55+
run: pnpm run build
56+
57+
- name: Get Commit SHA (short)
58+
id: get_version
59+
run: |
60+
# Get the short 8-character commit SHA
61+
VERSION=$(git rev-parse --short=8 HEAD)
62+
echo "Commit SHA is $VERSION"
63+
echo "tag=$VERSION" >> $GITHUB_OUTPUT
64+
65+
- name: SonarQube Analysis (Pull Request)
66+
uses: SonarSource/sonarqube-scan-action@v6
67+
env:
68+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
69+
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
70+
with:
71+
args: >
72+
-Dsonar.projectVersion=${{ steps.get_version.outputs.tag }}
73+
-Dsonar.pullrequest.key=${{ github.event.pull_request.number }}
74+
-Dsonar.pullrequest.branch=${{ github.event.pull_request.head.ref }}
75+
-Dsonar.pullrequest.base=${{ github.event.pull_request.base.ref }}

.github/workflows/build.yaml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ name: Build
33

44
on:
55
push:
6-
pull_request:
76

87
jobs:
98
build:
109
runs-on: ubuntu-latest
10+
1111

1212
strategy:
1313
matrix:
@@ -16,6 +16,9 @@ jobs:
1616

1717
steps:
1818
- uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
1922
- name: Use Node.js ${{ matrix.node-version }}
2023
uses: actions/setup-node@v4
2124
with:
@@ -47,3 +50,20 @@ jobs:
4750

4851
- name: Build
4952
run: pnpm run build
53+
54+
- name: Get Commit SHA (short)
55+
id: get_version
56+
run: |
57+
# Get the short 8-character commit SHA
58+
VERSION=$(git rev-parse --short=8 HEAD)
59+
echo "Commit SHA is $VERSION"
60+
echo "tag=$VERSION" >> $GITHUB_OUTPUT
61+
62+
- name: SonarQube Analysis (Branch)
63+
uses: SonarSource/sonarqube-scan-action@v6
64+
env:
65+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
66+
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
67+
with:
68+
args: >
69+
-Dsonar.projectVersion=${{ steps.get_version.outputs.tag }}

.github/workflows/pr-docker-build.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,16 @@ permissions: write-all
99
jobs:
1010
build-and-publish:
1111
runs-on: ubuntu-latest
12-
12+
13+
environment:
14+
name: build-pr
15+
1316
steps:
1417
- name: Checkout code
1518
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
ref: ${{ github.event.pull_request.head.sha }}
1622

1723
- name: Log in to GitHub Container Registry
1824
uses: docker/login-action@v3

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<p align="center">
2-
<a href="https://affiliate.postiz.com">
3-
<img src="https://github.com/user-attachments/assets/af9f47b3-e20c-402b-bd11-02f39248d738" />
2+
<a href="https://github.com/growchief/growchief">
3+
<img alt="automate" src="https://github.com/user-attachments/assets/d760188d-8d56-4b05-a6c1-c57e67ef25cd" />
44
</a>
55
</p>
66

@@ -19,6 +19,21 @@
1919
</a>
2020
</p>
2121

22+
<div align="center">
23+
<a href="https://sonarqube.ennogelhaus.de/dashboard?id=gitroomhq_postiz-app_bd4cd369-af44-4d19-903b-c4bdb0b66022">
24+
<img src="https://sonarqube.ennogelhaus.de/api/project_badges/measure?project=gitroomhq_postiz-app_bd4cd369-af44-4d19-903b-c4bdb0b66022&metric=alert_status&token=sqb_4ef7409d8d3bb84ff51d945f5a62bc0df93895a9" alt="Quality Gate Status" />
25+
</a>
26+
<a href="https://sonarqube.ennogelhaus.de/dashboard?id=gitroomhq_postiz-app_bd4cd369-af44-4d19-903b-c4bdb0b66022">
27+
<img src="https://sonarqube.ennogelhaus.de/api/project_badges/measure?project=gitroomhq_postiz-app_bd4cd369-af44-4d19-903b-c4bdb0b66022&metric=software_quality_maintainability_rating&token=sqb_4ef7409d8d3bb84ff51d945f5a62bc0df93895a9" alt="Maintainability Rating" />
28+
</a>
29+
<a href="https://sonarqube.ennogelhaus.de/dashboard?id=gitroomhq_postiz-app_bd4cd369-af44-4d19-903b-c4bdb0b66022">
30+
<img src="https://sonarqube.ennogelhaus.de/api/project_badges/measure?project=gitroomhq_postiz-app_bd4cd369-af44-4d19-903b-c4bdb0b66022&metric=software_quality_reliability_rating&token=sqb_4ef7409d8d3bb84ff51d945f5a62bc0df93895a9" alt="Reliability Rating" />
31+
</a>
32+
<a href="https://sonarqube.ennogelhaus.de/dashboard?id=gitroomhq_postiz-app_bd4cd369-af44-4d19-903b-c4bdb0b66022">
33+
<img src="https://sonarqube.ennogelhaus.de/api/project_badges/measure?project=gitroomhq_postiz-app_bd4cd369-af44-4d19-903b-c4bdb0b66022&metric=software_quality_security_rating&token=sqb_4ef7409d8d3bb84ff51d945f5a62bc0df93895a9" alt="Security Rating" />
34+
</a>
35+
</div>
36+
2237
<div align="center">
2338
<strong>
2439
<h2>Your ultimate AI social media scheduling tool</h2><br />

apps/backend/src/api/routes/auth.controller.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export class AuthController {
4141
async register(
4242
@Req() req: Request,
4343
@Body() body: CreateOrgUserDto,
44-
@Res({ passthrough: true }) response: Response,
44+
@Res({ passthrough: false }) response: Response,
4545
@RealIP() ip: string,
4646
@UserAgent() userAgent: string
4747
) {
@@ -114,7 +114,7 @@ export class AuthController {
114114
async login(
115115
@Req() req: Request,
116116
@Body() body: LoginUserDto,
117-
@Res({ passthrough: true }) response: Response,
117+
@Res({ passthrough: false }) response: Response,
118118
@RealIP() ip: string,
119119
@UserAgent() userAgent: string
120120
) {
@@ -204,11 +204,11 @@ export class AuthController {
204204
@Post('/activate')
205205
async activate(
206206
@Body('code') code: string,
207-
@Res({ passthrough: true }) response: Response
207+
@Res({ passthrough: false }) response: Response
208208
) {
209209
const activate = await this._authService.activate(code);
210210
if (!activate) {
211-
return response.status(200).send({ can: false });
211+
return response.status(200).json({ can: false });
212212
}
213213

214214
response.cookie('auth', activate, {
@@ -228,16 +228,18 @@ export class AuthController {
228228
}
229229

230230
response.header('onboarding', 'true');
231-
return response.status(200).send({ can: true });
231+
232+
return response.status(200).json({ can: true });
232233
}
233234

234235
@Post('/oauth/:provider/exists')
235236
async oauthExists(
236237
@Body('code') code: string,
237238
@Param('provider') provider: string,
238-
@Res({ passthrough: true }) response: Response
239+
@Res({ passthrough: false }) response: Response
239240
) {
240241
const { jwt, token } = await this._authService.checkExists(provider, code);
242+
241243
if (token) {
242244
return response.json({ token });
243245
}

apps/backend/src/api/routes/integrations.controller.ts

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
Controller,
44
Delete,
55
Get,
6+
HttpException,
67
Param,
78
Post,
89
Put,
@@ -261,24 +262,33 @@ export class IntegrationsController {
261262
throw new Error('Invalid integration');
262263
}
263264

265+
let newList: any[] | { none: true } = [];
266+
try {
267+
newList = (await this.functionIntegration(org, body)) || [];
268+
} catch (err) {
269+
console.log(err);
270+
}
271+
272+
if (!Array.isArray(newList) && newList?.none) {
273+
return newList;
274+
}
275+
264276
const list = await this._integrationService.getMentions(
265277
getIntegration.providerIdentifier,
266278
body?.data?.query
267279
);
268280

269-
let newList = [];
270-
try {
271-
newList = await this.functionIntegration(org, body);
272-
} catch (err) {}
273-
274-
if (newList.length) {
281+
if (Array.isArray(newList) && newList.length) {
275282
await this._integrationService.insertMentions(
276283
getIntegration.providerIdentifier,
277-
newList.map((p: any) => ({
278-
name: p.label,
279-
username: p.id,
280-
image: p.image,
281-
})).filter((f: any) => f.name)
284+
newList
285+
.map((p: any) => ({
286+
name: p.label || '',
287+
username: p.id || '',
288+
image: p.image || '',
289+
doNotCache: p.doNotCache || false,
290+
}))
291+
.filter((f: any) => f.name && !f.doNotCache)
282292
);
283293
}
284294

@@ -289,10 +299,10 @@ export class IntegrationsController {
289299
image: p.image,
290300
label: p.name,
291301
})),
292-
...newList,
302+
...(newList as any[]),
293303
],
294304
(p) => p.id
295-
).filter(f => f.label && f.image && f.id);
305+
).filter((f) => f.label && f.id);
296306
}
297307

298308
@Post('/function')
@@ -478,6 +488,18 @@ export class IntegrationsController {
478488
validName = `Channel_${String(id).slice(0, 8)}`;
479489
}
480490
}
491+
492+
if (
493+
process.env.STRIPE_PUBLISHABLE_KEY &&
494+
org.isTrailing &&
495+
(await this._integrationService.checkPreviousConnections(
496+
org.id,
497+
String(id)
498+
))
499+
) {
500+
throw new HttpException('', 412);
501+
}
502+
481503
return this._integrationService.createOrUpdateIntegration(
482504
additionalSettings,
483505
!!integrationProvider.oneTimeToken,

apps/backend/src/api/routes/public.controller.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
import { Body, Controller, Get, Param, Post, Req, Res } from '@nestjs/common';
1+
import {
2+
Body,
3+
Controller,
4+
Get,
5+
Param,
6+
Post,
7+
Query,
8+
Req,
9+
Res,
10+
StreamableFile,
11+
} from '@nestjs/common';
212
import { ApiTags } from '@nestjs/swagger';
313
import { AgenciesService } from '@gitroom/nestjs-libraries/database/prisma/agencies/agencies.service';
414
import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service';
@@ -11,6 +21,10 @@ import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
1121
import { getCookieUrlFromDomain } from '@gitroom/helpers/subdomain/subdomain.management';
1222
import { AgentGraphInsertService } from '@gitroom/nestjs-libraries/agent/agent.graph.insert.service';
1323
import { Nowpayments } from '@gitroom/nestjs-libraries/crypto/nowpayments';
24+
import { Readable, pipeline } from 'stream';
25+
import { promisify } from 'util';
26+
27+
const pump = promisify(pipeline);
1428

1529
@ApiTags('Public')
1630
@Controller('/public')
@@ -136,4 +150,46 @@ export class PublicController {
136150
console.log('cryptoPost', body, path);
137151
return this._nowpayments.processPayment(path, body);
138152
}
153+
154+
@Get('/stream')
155+
async streamFile(
156+
@Query('url') url: string,
157+
@Res() res: Response,
158+
@Req() req: Request
159+
) {
160+
if (!url.endsWith('mp4')) {
161+
return res.status(400).send('Invalid video URL');
162+
}
163+
164+
const ac = new AbortController();
165+
const onClose = () => ac.abort();
166+
req.on('aborted', onClose);
167+
res.on('close', onClose);
168+
169+
const r = await fetch(url, { signal: ac.signal });
170+
171+
if (!r.ok && r.status !== 206) {
172+
res.status(r.status);
173+
throw new Error(`Upstream error: ${r.statusText}`);
174+
}
175+
176+
const type = r.headers.get('content-type') ?? 'application/octet-stream';
177+
res.setHeader('Content-Type', type);
178+
179+
const contentRange = r.headers.get('content-range');
180+
if (contentRange) res.setHeader('Content-Range', contentRange);
181+
182+
const len = r.headers.get('content-length');
183+
if (len) res.setHeader('Content-Length', len);
184+
185+
const acceptRanges = r.headers.get('accept-ranges') ?? 'bytes';
186+
res.setHeader('Accept-Ranges', acceptRanges);
187+
188+
if (r.status === 206) res.status(206); // Partial Content for range responses
189+
190+
try {
191+
await pump(Readable.fromWeb(r.body as any), res);
192+
} catch (err) {
193+
}
194+
}
139195
}

0 commit comments

Comments
 (0)