Merge pull request #770 from gitroomhq/feat/multi-language
Added multi-language option
This commit is contained in:
commit
90424ba1ac
|
|
@ -56,4 +56,5 @@ Thumbs.db
|
|||
|
||||
# ignore Secrets folder
|
||||
.secrets/
|
||||
libraries/plugins/src/plugins.ts
|
||||
libraries/plugins/src/plugins.ts
|
||||
i18n.cache
|
||||
|
|
|
|||
|
|
@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
|
|||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
- Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
- The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
|
@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
|
|||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ As a general rule;
|
|||
## Types of Contributions
|
||||
|
||||
Contributions can include:
|
||||
|
||||
- **Code improvements:** Fixing bugs or adding new features.
|
||||
- **Documentation updates:** Enhancing clarity or adding missing information.
|
||||
- **Feature requests:** Suggesting new capabilities or integrations.
|
||||
|
|
@ -39,17 +40,15 @@ This project follows a Fork/Feature Branch/Pull Request model. If you're not fam
|
|||
```bash
|
||||
git checkout -b feature/your-feature-name
|
||||
```
|
||||
6. **Make your changes**: Implement the changes you wish to contribute.
|
||||
7. **Push your changes**: Upload your changes to your fork.
|
||||
4. **Make your changes**: Implement the changes you wish to contribute.
|
||||
5. **Push your changes**: Upload your changes to your fork.
|
||||
```bash
|
||||
git push -u origin feature/your-feature-name
|
||||
```
|
||||
9. **Create a pull request**: Propose your changes **to the main branch**.
|
||||
|
||||
6. **Create a pull request**: Propose your changes **to the main branch**.
|
||||
|
||||
# Need Help?
|
||||
|
||||
Again, do check the [developer guide](https://docs.postiz.com/developer-guide). Much of what you probably need to know is in there.
|
||||
|
||||
If you encounter any issues, please visit our [support page](https://docs.postiz.com/support) or check the community forums. Your contributions help make Postiz better!
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/Apache-2.0">
|
||||
<img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" alt="License">
|
||||
|
|
@ -28,7 +27,6 @@
|
|||
Postiz offers everything you need to manage your social media posts,<br />build an audience, capture leads, and grow your business.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex" align="center">
|
||||
<br />
|
||||
<img alt="Instagram" src="https://postiz.com/svgs/socials/Instagram.svg" width="32">
|
||||
|
|
@ -77,7 +75,7 @@
|
|||
## ✨ Features
|
||||
|
||||
|  |  |
|
||||
|--------------------------------|--------------------------------|
|
||||
| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
|  |  |
|
||||
|
||||
# Intro
|
||||
|
|
@ -98,9 +96,11 @@
|
|||
- Resend (email notifications)
|
||||
|
||||
## Quick Start
|
||||
|
||||
To have the project up and running, please follow the [Quick Start Guide](https://docs.postiz.com/quickstart)
|
||||
|
||||
## Invest in the Postiz Coin :)
|
||||
|
||||
DMsTbeCfX1crgAse5tver98KAMarPWeP3d6U3Gmmpump
|
||||
|
||||
# License
|
||||
|
|
@ -112,5 +112,3 @@ This repository's source code is available under the [AGPL-3.0 license](LICENSE)
|
|||
<p align="center">
|
||||
<a href="https://www.g2.com/products/postiz/take_survey" target="blank"><img alt="g2" src="https://github.com/user-attachments/assets/892cb74c-0b49-4589-b2f5-fbdbf7a98f66" /></a>
|
||||
</p>
|
||||
|
||||
|
||||
|
|
|
|||
18
SECURITY.md
18
SECURITY.md
|
|
@ -8,14 +8,14 @@ The Postiz app is committed to ensuring the security and integrity of our users'
|
|||
|
||||
If you discover a security vulnerability in the Postiz app, please report it to us privately via email to one of the maintainers:
|
||||
|
||||
* @nevo-david
|
||||
* @egelhaus ([email](mailto:gelhausenno@outlook.de))
|
||||
- @nevo-david
|
||||
- @egelhaus ([email](mailto:gelhausenno@outlook.de))
|
||||
|
||||
When reporting a security vulnerability, please provide as much detail as possible, including:
|
||||
|
||||
* A clear description of the vulnerability
|
||||
* Steps to reproduce the vulnerability
|
||||
* Any relevant code or configuration files
|
||||
- A clear description of the vulnerability
|
||||
- Steps to reproduce the vulnerability
|
||||
- Any relevant code or configuration files
|
||||
|
||||
## Supported Versions
|
||||
|
||||
|
|
@ -31,10 +31,10 @@ We will not publicly disclose security vulnerabilities until a patch or fix is a
|
|||
|
||||
We take security vulnerabilities seriously and will respond promptly to reports of vulnerabilities. Our response process includes:
|
||||
|
||||
* Investigating the report and verifying the vulnerability.
|
||||
* Developing a patch or fix for the vulnerability.
|
||||
* Releasing the patch or fix as soon as possible.
|
||||
* Notifying users of the vulnerability and the patch or fix.
|
||||
- Investigating the report and verifying the vulnerability.
|
||||
- Developing a patch or fix for the vulnerability.
|
||||
- Releasing the patch or fix as soon as possible.
|
||||
- Notifying users of the vulnerability and the patch or fix.
|
||||
|
||||
## Template Attribution
|
||||
|
||||
|
|
|
|||
|
|
@ -11,4 +11,4 @@
|
|||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ const authenticatedController = [
|
|||
TrackService,
|
||||
ShortLinkService,
|
||||
Nowpayments,
|
||||
McpService
|
||||
McpService,
|
||||
],
|
||||
get exports() {
|
||||
return [...this.imports, ...this.providers];
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integ
|
|||
export class AnalyticsController {
|
||||
constructor(
|
||||
private _starsService: StarsService,
|
||||
private _integrationService: IntegrationService,
|
||||
private _integrationService: IntegrationService
|
||||
) {}
|
||||
@Get('/')
|
||||
async getStars(@GetOrgFromRequest() org: Organization) {
|
||||
|
|
|
|||
|
|
@ -13,9 +13,12 @@ export class CopilotController {
|
|||
constructor(private _subscriptionService: SubscriptionService) {}
|
||||
@Post('/chat')
|
||||
chat(@Req() req: Request, @Res() res: Response) {
|
||||
if (process.env.OPENAI_API_KEY === undefined || process.env.OPENAI_API_KEY === '') {
|
||||
if (
|
||||
process.env.OPENAI_API_KEY === undefined ||
|
||||
process.env.OPENAI_API_KEY === ''
|
||||
) {
|
||||
Logger.warn('OpenAI API key not set, chat functionality will not work');
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const copilotRuntimeHandler = copilotRuntimeNestEndpoint({
|
||||
|
|
|
|||
|
|
@ -87,35 +87,34 @@ export class IntegrationsController {
|
|||
async getIntegrationList(@GetOrgFromRequest() org: Organization) {
|
||||
return {
|
||||
integrations: await Promise.all(
|
||||
(await this._integrationService.getIntegrationsList(org.id)).map(
|
||||
async (p) => {
|
||||
const findIntegration =
|
||||
this._integrationManager.getSocialIntegration(
|
||||
p.providerIdentifier
|
||||
);
|
||||
return {
|
||||
name: p.name,
|
||||
id: p.id,
|
||||
internalId: p.internalId,
|
||||
disabled: p.disabled,
|
||||
picture: p.picture || '/no-picture.jpg',
|
||||
identifier: p.providerIdentifier,
|
||||
inBetweenSteps: p.inBetweenSteps,
|
||||
refreshNeeded: p.refreshNeeded,
|
||||
isCustomFields: !!findIntegration.customFields,
|
||||
...(findIntegration.customFields
|
||||
? { customFields: await findIntegration.customFields() }
|
||||
: {}),
|
||||
display: p.profile,
|
||||
type: p.type,
|
||||
time: JSON.parse(p.postingTimes),
|
||||
changeProfilePicture: !!findIntegration?.changeProfilePicture,
|
||||
changeNickName: !!findIntegration?.changeNickname,
|
||||
customer: p.customer,
|
||||
additionalSettings: p.additionalSettings || '[]',
|
||||
};
|
||||
}
|
||||
)
|
||||
(
|
||||
await this._integrationService.getIntegrationsList(org.id)
|
||||
).map(async (p) => {
|
||||
const findIntegration = this._integrationManager.getSocialIntegration(
|
||||
p.providerIdentifier
|
||||
);
|
||||
return {
|
||||
name: p.name,
|
||||
id: p.id,
|
||||
internalId: p.internalId,
|
||||
disabled: p.disabled,
|
||||
picture: p.picture || '/no-picture.jpg',
|
||||
identifier: p.providerIdentifier,
|
||||
inBetweenSteps: p.inBetweenSteps,
|
||||
refreshNeeded: p.refreshNeeded,
|
||||
isCustomFields: !!findIntegration.customFields,
|
||||
...(findIntegration.customFields
|
||||
? { customFields: await findIntegration.customFields() }
|
||||
: {}),
|
||||
display: p.profile,
|
||||
type: p.type,
|
||||
time: JSON.parse(p.postingTimes),
|
||||
changeProfilePicture: !!findIntegration?.changeProfilePicture,
|
||||
changeNickName: !!findIntegration?.changeNickname,
|
||||
customer: p.customer,
|
||||
additionalSettings: p.additionalSettings || '[]',
|
||||
};
|
||||
})
|
||||
),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
import { Body, Controller, HttpException, Param, Post, Sse } from '@nestjs/common';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
HttpException,
|
||||
Param,
|
||||
Post,
|
||||
Sse,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { McpService } from '@gitroom/nestjs-libraries/mcp/mcp.service';
|
||||
import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service';
|
||||
|
|
|
|||
|
|
@ -26,7 +26,12 @@ export class MessagesController {
|
|||
@Param('groupId') groupId: string,
|
||||
@Param('page') page: string
|
||||
) {
|
||||
return this._messagesService.getMessages(user.id, organization.id, groupId, +page);
|
||||
return this._messagesService.getMessages(
|
||||
user.id,
|
||||
organization.id,
|
||||
groupId,
|
||||
+page
|
||||
);
|
||||
}
|
||||
@Post('/:groupId')
|
||||
createMessage(
|
||||
|
|
@ -35,6 +40,11 @@ export class MessagesController {
|
|||
@Param('groupId') groupId: string,
|
||||
@Body() message: AddMessageDto
|
||||
) {
|
||||
return this._messagesService.createMessage(user.id, organization.id, groupId, message);
|
||||
return this._messagesService.createMessage(
|
||||
user.id,
|
||||
organization.id,
|
||||
groupId,
|
||||
message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.req
|
|||
import { Organization, User } from '@prisma/client';
|
||||
import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request';
|
||||
import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service';
|
||||
import {ApiTags} from "@nestjs/swagger";
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
@ApiTags('Notifications')
|
||||
@Controller('/notifications')
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import {
|
|||
Sections,
|
||||
} from '@gitroom/backend/services/auth/permissions/permissions.service';
|
||||
import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service';
|
||||
import {AddTeamMemberDto} from "@gitroom/nestjs-libraries/dtos/settings/add.team.member.dto";
|
||||
import {ApiTags} from "@nestjs/swagger";
|
||||
import { AddTeamMemberDto } from '@gitroom/nestjs-libraries/dtos/settings/add.team.member.dto';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
@ApiTags('Settings')
|
||||
@Controller('/settings')
|
||||
|
|
@ -105,25 +105,34 @@ export class SettingsController {
|
|||
}
|
||||
|
||||
@Get('/team')
|
||||
@CheckPolicies([AuthorizationActions.Create, Sections.TEAM_MEMBERS], [AuthorizationActions.Create, Sections.ADMIN])
|
||||
@CheckPolicies(
|
||||
[AuthorizationActions.Create, Sections.TEAM_MEMBERS],
|
||||
[AuthorizationActions.Create, Sections.ADMIN]
|
||||
)
|
||||
async getTeam(@GetOrgFromRequest() org: Organization) {
|
||||
return this._organizationService.getTeam(org.id);
|
||||
}
|
||||
|
||||
@Post('/team')
|
||||
@CheckPolicies([AuthorizationActions.Create, Sections.TEAM_MEMBERS], [AuthorizationActions.Create, Sections.ADMIN])
|
||||
@CheckPolicies(
|
||||
[AuthorizationActions.Create, Sections.TEAM_MEMBERS],
|
||||
[AuthorizationActions.Create, Sections.ADMIN]
|
||||
)
|
||||
async inviteTeamMember(
|
||||
@GetOrgFromRequest() org: Organization,
|
||||
@Body() body: AddTeamMemberDto,
|
||||
@GetOrgFromRequest() org: Organization,
|
||||
@Body() body: AddTeamMemberDto
|
||||
) {
|
||||
return this._organizationService.inviteTeamMember(org.id, body);
|
||||
}
|
||||
|
||||
@Delete('/team/:id')
|
||||
@CheckPolicies([AuthorizationActions.Create, Sections.TEAM_MEMBERS], [AuthorizationActions.Create, Sections.ADMIN])
|
||||
@CheckPolicies(
|
||||
[AuthorizationActions.Create, Sections.TEAM_MEMBERS],
|
||||
[AuthorizationActions.Create, Sections.ADMIN]
|
||||
)
|
||||
deleteTeamMember(
|
||||
@GetOrgFromRequest() org: Organization,
|
||||
@Param('id') id: string
|
||||
@GetOrgFromRequest() org: Organization,
|
||||
@Param('id') id: string
|
||||
) {
|
||||
return this._organizationService.deleteTeamMember(org, id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,8 +62,7 @@ export class UsersController {
|
|||
// @ts-ignore
|
||||
totalChannels: !process.env.STRIPE_PUBLISHABLE_KEY ? 10000 : organization?.subscription?.totalChannels || pricing.FREE.channel,
|
||||
// @ts-ignore
|
||||
tier: organization?.subscription?.subscriptionTier ||
|
||||
(!process.env.STRIPE_PUBLISHABLE_KEY ? 'ULTIMATE' : 'FREE'),
|
||||
tier: organization?.subscription?.subscriptionTier || (!process.env.STRIPE_PUBLISHABLE_KEY ? 'ULTIMATE' : 'FREE'),
|
||||
// @ts-ignore
|
||||
role: organization?.users[0]?.role,
|
||||
// @ts-ignore
|
||||
|
|
@ -72,7 +71,7 @@ export class UsersController {
|
|||
impersonate: !!impersonate,
|
||||
allowTrial: organization?.allowTrial,
|
||||
// @ts-ignore
|
||||
publicApi: organization?.users[0]?.role === 'SUPERADMIN' || organization?.users[0]?.role === 'ADMIN' ? organization?.apiKey : '',
|
||||
publicApi: organization?.users[0]?.role === 'SUPERADMIN' || organization?.users[0]?.role === 'ADMIN' ? organization?.apiKey : '',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,16 +11,10 @@ import { CodesService } from '@gitroom/nestjs-libraries/services/codes.service';
|
|||
import { PublicIntegrationsController } from '@gitroom/backend/public-api/routes/v1/public.integrations.controller';
|
||||
import { PublicAuthMiddleware } from '@gitroom/backend/services/auth/public.auth.middleware';
|
||||
|
||||
const authenticatedController = [
|
||||
PublicIntegrationsController
|
||||
];
|
||||
const authenticatedController = [PublicIntegrationsController];
|
||||
@Module({
|
||||
imports: [
|
||||
UploadModule,
|
||||
],
|
||||
controllers: [
|
||||
...authenticatedController,
|
||||
],
|
||||
imports: [UploadModule],
|
||||
controllers: [...authenticatedController],
|
||||
providers: [
|
||||
AuthService,
|
||||
StripeService,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,14 @@
|
|||
import {
|
||||
Body, Controller, Delete, Get, HttpException, Param, Post, Query, UploadedFile, UseInterceptors
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
HttpException,
|
||||
Param,
|
||||
Post,
|
||||
Query,
|
||||
UploadedFile,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import {SetMetadata} from "@nestjs/common";
|
||||
import {AuthorizationActions, Sections} from "@gitroom/backend/services/auth/permissions/permissions.service";
|
||||
import { SetMetadata } from '@nestjs/common';
|
||||
import {
|
||||
AuthorizationActions,
|
||||
Sections,
|
||||
} from '@gitroom/backend/services/auth/permissions/permissions.service';
|
||||
|
||||
export const CHECK_POLICIES_KEY = 'check_policy';
|
||||
export type AbilityPolicy = [AuthorizationActions, Sections];
|
||||
export const CheckPolicies = (...handlers: AbilityPolicy[]) => SetMetadata(CHECK_POLICIES_KEY, handlers);
|
||||
export const CheckPolicies = (...handlers: AbilityPolicy[]) =>
|
||||
SetMetadata(CHECK_POLICIES_KEY, handlers);
|
||||
|
|
|
|||
|
|
@ -1,29 +1,37 @@
|
|||
import {CanActivate, ExecutionContext, Injectable} from "@nestjs/common";
|
||||
import {Reflector} from "@nestjs/core";
|
||||
import {AppAbility, PermissionsService} from "@gitroom/backend/services/auth/permissions/permissions.service";
|
||||
import {AbilityPolicy, CHECK_POLICIES_KEY} from "@gitroom/backend/services/auth/permissions/permissions.ability";
|
||||
import {Organization} from "@prisma/client";
|
||||
import {SubscriptionException} from "@gitroom/backend/services/auth/permissions/subscription.exception";
|
||||
import {Request} from "express";
|
||||
|
||||
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import {
|
||||
AppAbility,
|
||||
PermissionsService,
|
||||
} from '@gitroom/backend/services/auth/permissions/permissions.service';
|
||||
import {
|
||||
AbilityPolicy,
|
||||
CHECK_POLICIES_KEY,
|
||||
} from '@gitroom/backend/services/auth/permissions/permissions.ability';
|
||||
import { Organization } from '@prisma/client';
|
||||
import { SubscriptionException } from '@gitroom/backend/services/auth/permissions/subscription.exception';
|
||||
import { Request } from 'express';
|
||||
|
||||
@Injectable()
|
||||
export class PoliciesGuard implements CanActivate {
|
||||
constructor(
|
||||
private _reflector: Reflector,
|
||||
private _authorizationService: PermissionsService,
|
||||
private _authorizationService: PermissionsService
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const request: Request = context.switchToHttp().getRequest();
|
||||
if (request.path.indexOf('/auth') > -1 || request.path.indexOf('/stripe') > -1) {
|
||||
if (
|
||||
request.path.indexOf('/auth') > -1 ||
|
||||
request.path.indexOf('/stripe') > -1
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const policyHandlers =
|
||||
this._reflector.get<AbilityPolicy[]>(
|
||||
CHECK_POLICIES_KEY,
|
||||
context.getHandler(),
|
||||
context.getHandler()
|
||||
) || [];
|
||||
|
||||
if (!policyHandlers || !policyHandlers.length) {
|
||||
|
|
@ -32,19 +40,19 @@ export class PoliciesGuard implements CanActivate {
|
|||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
const { org } : {org: Organization} = request;
|
||||
const { org }: { org: Organization } = request;
|
||||
|
||||
// @ts-ignore
|
||||
const ability = await this._authorizationService.check(org.id, org.createdAt, org.users[0].role, policyHandlers);
|
||||
|
||||
const item = policyHandlers.find((handler) =>
|
||||
!this.execPolicyHandler(handler, ability),
|
||||
const item = policyHandlers.find(
|
||||
(handler) => !this.execPolicyHandler(handler, ability)
|
||||
);
|
||||
|
||||
if (item) {
|
||||
throw new SubscriptionException({
|
||||
section: item[1],
|
||||
action: item[0]
|
||||
action: item[0],
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ describe('PermissionsService', () => {
|
|||
image_generation_count: 50,
|
||||
public_api: true,
|
||||
webhooks: 10,
|
||||
autoPost: true // Added the missing property
|
||||
autoPost: true, // Added the missing property
|
||||
};
|
||||
|
||||
const baseIntegration = {
|
||||
|
|
@ -101,7 +101,6 @@ describe('PermissionsService', () => {
|
|||
|
||||
describe('check()', () => {
|
||||
describe('Verification Bypass (64)', () => {
|
||||
|
||||
it('Bypass for Empty List', async () => {
|
||||
// Setup: STRIPE_PUBLISHABLE_KEY exists and requestedPermission is empty
|
||||
|
||||
|
|
@ -114,16 +113,18 @@ describe('PermissionsService', () => {
|
|||
);
|
||||
|
||||
// Verification: not requested, no authorization
|
||||
expect(result.cannot(AuthorizationActions.Create, Sections.CHANNEL)).toBe(true);
|
||||
expect(
|
||||
result.cannot(AuthorizationActions.Create, Sections.CHANNEL)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('Bypass for Missing Stripe', async () => {
|
||||
// Setup: STRIPE_PUBLISHABLE_KEY does not exist
|
||||
process.env.STRIPE_PUBLISHABLE_KEY = undefined;
|
||||
// Necessary mock to avoid undefined filter error
|
||||
jest.spyOn(mockIntegrationService, 'getIntegrationsList').mockResolvedValue([
|
||||
{ ...baseIntegration, refreshNeeded: false }
|
||||
]);
|
||||
jest
|
||||
.spyOn(mockIntegrationService, 'getIntegrationsList')
|
||||
.mockResolvedValue([{ ...baseIntegration, refreshNeeded: false }]);
|
||||
// Mock of getPackageOptions (even if not used due to bypass)
|
||||
jest.spyOn(service, 'getPackageOptions').mockResolvedValue({
|
||||
subscription: baseSubscription,
|
||||
|
|
@ -132,7 +133,7 @@ describe('PermissionsService', () => {
|
|||
// List of requested permissions
|
||||
const requestedPermissions: Array<[AuthorizationActions, Sections]> = [
|
||||
[AuthorizationActions.Read, Sections.CHANNEL],
|
||||
[AuthorizationActions.Create, Sections.AI]
|
||||
[AuthorizationActions.Create, Sections.AI],
|
||||
];
|
||||
// Execution: call the check method
|
||||
const result = await service.check(
|
||||
|
|
@ -142,7 +143,9 @@ describe('PermissionsService', () => {
|
|||
requestedPermissions
|
||||
);
|
||||
// Verification: should allow all requested actions due to the absence of the Stripe key
|
||||
expect(result.can(AuthorizationActions.Read, Sections.CHANNEL)).toBe(true);
|
||||
expect(result.can(AuthorizationActions.Read, Sections.CHANNEL)).toBe(
|
||||
true
|
||||
);
|
||||
expect(result.can(AuthorizationActions.Create, Sections.AI)).toBe(true);
|
||||
});
|
||||
|
||||
|
|
@ -150,7 +153,7 @@ describe('PermissionsService', () => {
|
|||
// List of requested permissions
|
||||
const requestedPermissions: Array<[AuthorizationActions, Sections]> = [
|
||||
[AuthorizationActions.Read, Sections.CHANNEL],
|
||||
[AuthorizationActions.Create, Sections.AI]
|
||||
[AuthorizationActions.Create, Sections.AI],
|
||||
];
|
||||
// Mock of getPackageOptions to force a scenario without permissions
|
||||
jest.spyOn(service, 'getPackageOptions').mockResolvedValue({
|
||||
|
|
@ -158,13 +161,13 @@ describe('PermissionsService', () => {
|
|||
options: {
|
||||
...baseOptions,
|
||||
channel: 0,
|
||||
ai: false
|
||||
ai: false,
|
||||
},
|
||||
});
|
||||
// Mock of getIntegrationsList for the channel scenario
|
||||
jest.spyOn(mockIntegrationService, 'getIntegrationsList').mockResolvedValue([
|
||||
{ ...baseIntegration, refreshNeeded: false }
|
||||
]);
|
||||
jest
|
||||
.spyOn(mockIntegrationService, 'getIntegrationsList')
|
||||
.mockResolvedValue([{ ...baseIntegration, refreshNeeded: false }]);
|
||||
// Execution: call the check method
|
||||
const result = await service.check(
|
||||
'mock-org-id',
|
||||
|
|
@ -173,8 +176,12 @@ describe('PermissionsService', () => {
|
|||
requestedPermissions
|
||||
);
|
||||
// Verification: should not allow the requested actions as there is no bypass
|
||||
expect(result.can(AuthorizationActions.Read, Sections.CHANNEL)).toBe(false);
|
||||
expect(result.can(AuthorizationActions.Create, Sections.AI)).toBe(false);
|
||||
expect(result.can(AuthorizationActions.Read, Sections.CHANNEL)).toBe(
|
||||
false
|
||||
);
|
||||
expect(result.can(AuthorizationActions.Create, Sections.AI)).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -187,15 +194,17 @@ describe('PermissionsService', () => {
|
|||
});
|
||||
|
||||
// Mock of getIntegrationsList to set existing channels
|
||||
jest.spyOn(mockIntegrationService, 'getIntegrationsList').mockResolvedValue([
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
]);
|
||||
jest
|
||||
.spyOn(mockIntegrationService, 'getIntegrationsList')
|
||||
.mockResolvedValue([
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
]);
|
||||
|
||||
// List of requested permissions
|
||||
const requestedPermissions: Array<[AuthorizationActions, Sections]> = [
|
||||
[AuthorizationActions.Create, Sections.CHANNEL]
|
||||
[AuthorizationActions.Create, Sections.CHANNEL],
|
||||
];
|
||||
|
||||
// Execution: call the check method
|
||||
|
|
@ -206,7 +215,9 @@ describe('PermissionsService', () => {
|
|||
requestedPermissions
|
||||
);
|
||||
// Verification: should allow the requested action
|
||||
expect(result.can(AuthorizationActions.Create, Sections.CHANNEL)).toBe(true);
|
||||
expect(result.can(AuthorizationActions.Create, Sections.CHANNEL)).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it('Channel With Option Limit', async () => {
|
||||
|
|
@ -216,14 +227,16 @@ describe('PermissionsService', () => {
|
|||
options: { ...baseOptions, channel: 10 },
|
||||
});
|
||||
// Mock of getIntegrationsList to set existing channels
|
||||
jest.spyOn(mockIntegrationService, 'getIntegrationsList').mockResolvedValue([
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
]);
|
||||
jest
|
||||
.spyOn(mockIntegrationService, 'getIntegrationsList')
|
||||
.mockResolvedValue([
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
]);
|
||||
// List of requested permissions
|
||||
const requestedPermissions: Array<[AuthorizationActions, Sections]> = [
|
||||
[AuthorizationActions.Create, Sections.CHANNEL]
|
||||
[AuthorizationActions.Create, Sections.CHANNEL],
|
||||
];
|
||||
// Execution: call the check method
|
||||
const result = await service.check(
|
||||
|
|
@ -233,7 +246,9 @@ describe('PermissionsService', () => {
|
|||
requestedPermissions
|
||||
);
|
||||
// Verification: should allow the requested action
|
||||
expect(result.can(AuthorizationActions.Create, Sections.CHANNEL)).toBe(true);
|
||||
expect(result.can(AuthorizationActions.Create, Sections.CHANNEL)).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it('Channel With Subscription Limit', async () => {
|
||||
|
|
@ -243,15 +258,17 @@ describe('PermissionsService', () => {
|
|||
options: { ...baseOptions, channel: 3 },
|
||||
});
|
||||
// Mock of getIntegrationsList to set existing channels
|
||||
jest.spyOn(mockIntegrationService, 'getIntegrationsList').mockResolvedValue([
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
]);
|
||||
jest
|
||||
.spyOn(mockIntegrationService, 'getIntegrationsList')
|
||||
.mockResolvedValue([
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
]);
|
||||
|
||||
// List of requested permissions
|
||||
const requestedPermissions: Array<[AuthorizationActions, Sections]> = [
|
||||
[AuthorizationActions.Create, Sections.CHANNEL]
|
||||
[AuthorizationActions.Create, Sections.CHANNEL],
|
||||
];
|
||||
// Execution: call the check method
|
||||
const result = await service.check(
|
||||
|
|
@ -261,7 +278,9 @@ describe('PermissionsService', () => {
|
|||
requestedPermissions
|
||||
);
|
||||
// Verification: should allow the requested action
|
||||
expect(result.can(AuthorizationActions.Create, Sections.CHANNEL)).toBe(true);
|
||||
expect(result.can(AuthorizationActions.Create, Sections.CHANNEL)).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
it('Channel Without Available Limits', async () => {
|
||||
// Mock of getPackageOptions to set channel limits
|
||||
|
|
@ -270,14 +289,16 @@ describe('PermissionsService', () => {
|
|||
options: { ...baseOptions, channel: 3 },
|
||||
});
|
||||
// Mock of getIntegrationsList to set existing channels
|
||||
jest.spyOn(mockIntegrationService, 'getIntegrationsList').mockResolvedValue([
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
]);
|
||||
jest
|
||||
.spyOn(mockIntegrationService, 'getIntegrationsList')
|
||||
.mockResolvedValue([
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
]);
|
||||
// List of requested permissions
|
||||
const requestedPermissions: Array<[AuthorizationActions, Sections]> = [
|
||||
[AuthorizationActions.Create, Sections.CHANNEL]
|
||||
[AuthorizationActions.Create, Sections.CHANNEL],
|
||||
];
|
||||
// Execution: call the check method
|
||||
const result = await service.check(
|
||||
|
|
@ -287,7 +308,9 @@ describe('PermissionsService', () => {
|
|||
requestedPermissions
|
||||
);
|
||||
// Verification: should not allow the requested action
|
||||
expect(result.can(AuthorizationActions.Create, Sections.CHANNEL)).toBe(false);
|
||||
expect(result.can(AuthorizationActions.Create, Sections.CHANNEL)).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
it('Section Different from Channel', async () => {
|
||||
// Mock of getPackageOptions to set channel limits
|
||||
|
|
@ -296,14 +319,16 @@ describe('PermissionsService', () => {
|
|||
options: { ...baseOptions, channel: 10 },
|
||||
});
|
||||
// Mock of getIntegrationsList to set existing channels
|
||||
jest.spyOn(mockIntegrationService, 'getIntegrationsList').mockResolvedValue([
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
]);
|
||||
jest
|
||||
.spyOn(mockIntegrationService, 'getIntegrationsList')
|
||||
.mockResolvedValue([
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
{ ...baseIntegration, refreshNeeded: false },
|
||||
]);
|
||||
// List of requested permissions
|
||||
const requestedPermissions: Array<[AuthorizationActions, Sections]> = [
|
||||
[AuthorizationActions.Create, Sections.AI] // Requesting permission for AI instead of CHANNEL
|
||||
[AuthorizationActions.Create, Sections.AI], // Requesting permission for AI instead of CHANNEL
|
||||
];
|
||||
// Execution: call the check method
|
||||
const result = await service.check(
|
||||
|
|
@ -313,7 +338,9 @@ describe('PermissionsService', () => {
|
|||
requestedPermissions
|
||||
);
|
||||
// Verification: should not allow the requested action in CHANNEL
|
||||
expect(result.can(AuthorizationActions.Create, Sections.CHANNEL)).toBe(false);
|
||||
expect(result.can(AuthorizationActions.Create, Sections.CHANNEL)).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('Monthly Posts Permission (97/110)', () => {
|
||||
|
|
@ -324,15 +351,17 @@ describe('PermissionsService', () => {
|
|||
options: { ...baseOptions, posts_per_month: 100 },
|
||||
});
|
||||
// Mock of getSubscription
|
||||
jest.spyOn(mockSubscriptionService, 'getSubscription').mockResolvedValue({
|
||||
...baseSubscription,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
jest
|
||||
.spyOn(mockSubscriptionService, 'getSubscription')
|
||||
.mockResolvedValue({
|
||||
...baseSubscription,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
// Mock of countPostsFromDay to return quantity within the limit
|
||||
jest.spyOn(mockPostsService, 'countPostsFromDay').mockResolvedValue(50);
|
||||
// List of requested permissions
|
||||
const requestedPermissions: Array<[AuthorizationActions, Sections]> = [
|
||||
[AuthorizationActions.Create, Sections.POSTS_PER_MONTH]
|
||||
[AuthorizationActions.Create, Sections.POSTS_PER_MONTH],
|
||||
];
|
||||
// Execution: call the check method
|
||||
const result = await service.check(
|
||||
|
|
@ -342,7 +371,9 @@ describe('PermissionsService', () => {
|
|||
requestedPermissions
|
||||
);
|
||||
// Verification: should allow the requested action
|
||||
expect(result.can(AuthorizationActions.Create, Sections.POSTS_PER_MONTH)).toBe(true);
|
||||
expect(
|
||||
result.can(AuthorizationActions.Create, Sections.POSTS_PER_MONTH)
|
||||
).toBe(true);
|
||||
});
|
||||
it('Posts Exceed Limit', async () => {
|
||||
// Mock of getPackageOptions to set post limits
|
||||
|
|
@ -351,16 +382,20 @@ describe('PermissionsService', () => {
|
|||
options: { ...baseOptions, posts_per_month: 100 },
|
||||
});
|
||||
// Mock of getSubscription
|
||||
jest.spyOn(mockSubscriptionService, 'getSubscription').mockResolvedValue({
|
||||
...baseSubscription,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
jest
|
||||
.spyOn(mockSubscriptionService, 'getSubscription')
|
||||
.mockResolvedValue({
|
||||
...baseSubscription,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
// Mock of countPostsFromDay to return quantity above the limit
|
||||
jest.spyOn(mockPostsService, 'countPostsFromDay').mockResolvedValue(150);
|
||||
jest
|
||||
.spyOn(mockPostsService, 'countPostsFromDay')
|
||||
.mockResolvedValue(150);
|
||||
|
||||
// List of requested permissions
|
||||
const requestedPermissions: Array<[AuthorizationActions, Sections]> = [
|
||||
[AuthorizationActions.Create, Sections.POSTS_PER_MONTH]
|
||||
[AuthorizationActions.Create, Sections.POSTS_PER_MONTH],
|
||||
];
|
||||
// Execution: call the check method
|
||||
const result = await service.check(
|
||||
|
|
@ -370,7 +405,9 @@ describe('PermissionsService', () => {
|
|||
requestedPermissions
|
||||
);
|
||||
// Verification: should not allow the requested action
|
||||
expect(result.can(AuthorizationActions.Create, Sections.POSTS_PER_MONTH)).toBe(false);
|
||||
expect(
|
||||
result.can(AuthorizationActions.Create, Sections.POSTS_PER_MONTH)
|
||||
).toBe(false);
|
||||
});
|
||||
it('Section Different with Posts Within Limit', async () => {
|
||||
// Mock of getPackageOptions to set post limits
|
||||
|
|
@ -379,15 +416,17 @@ describe('PermissionsService', () => {
|
|||
options: { ...baseOptions, posts_per_month: 100 },
|
||||
});
|
||||
// Mock of getSubscription
|
||||
jest.spyOn(mockSubscriptionService, 'getSubscription').mockResolvedValue({
|
||||
...baseSubscription,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
jest
|
||||
.spyOn(mockSubscriptionService, 'getSubscription')
|
||||
.mockResolvedValue({
|
||||
...baseSubscription,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
// Mock of countPostsFromDay to return quantity within the limit
|
||||
jest.spyOn(mockPostsService, 'countPostsFromDay').mockResolvedValue(50);
|
||||
// List of requested permissions
|
||||
const requestedPermissions: Array<[AuthorizationActions, Sections]> = [
|
||||
[AuthorizationActions.Create, Sections.AI] // Requesting permission for AI instead of POSTS_PER_MONTH
|
||||
[AuthorizationActions.Create, Sections.AI], // Requesting permission for AI instead of POSTS_PER_MONTH
|
||||
];
|
||||
// Execution: call the check method
|
||||
const result = await service.check(
|
||||
|
|
@ -397,8 +436,10 @@ describe('PermissionsService', () => {
|
|||
requestedPermissions
|
||||
);
|
||||
// Verification: should not allow the requested action in POSTS_PER_MONTH
|
||||
expect(result.can(AuthorizationActions.Create, Sections.POSTS_PER_MONTH)).toBe(false);
|
||||
expect(
|
||||
result.can(AuthorizationActions.Create, Sections.POSTS_PER_MONTH)
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export class PermissionsService {
|
|||
private _subscriptionService: SubscriptionService,
|
||||
private _postsService: PostsService,
|
||||
private _integrationService: IntegrationService,
|
||||
private _webhooksService: WebhooksService,
|
||||
private _webhooksService: WebhooksService
|
||||
) {}
|
||||
async getPackageOptions(orgId: string) {
|
||||
const subscription =
|
||||
|
|
@ -85,7 +85,7 @@ export class PermissionsService {
|
|||
if (section === Sections.CHANNEL) {
|
||||
const totalChannels = (
|
||||
await this._integrationService.getIntegrationsList(orgId)
|
||||
).filter(f => !f.refreshNeeded).length;
|
||||
).filter((f) => !f.refreshNeeded).length;
|
||||
|
||||
if (
|
||||
(options.channel && options.channel > totalChannels) ||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
export interface ProvidersInterface {
|
||||
generateLink(query?: any): Promise<string> | string;
|
||||
getToken(code: string): Promise<string>;
|
||||
getUser(providerToken: string): Promise<{email: string, id: string}> | false;
|
||||
}
|
||||
generateLink(query?: any): Promise<string> | string;
|
||||
getToken(code: string): Promise<string>;
|
||||
getUser(
|
||||
providerToken: string
|
||||
): Promise<{ email: string; id: string }> | false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export class FarcasterProvider implements ProvidersInterface {
|
|||
|
||||
async getToken(code: string) {
|
||||
const data = JSON.parse(Buffer.from(code, 'base64').toString());
|
||||
const status = await client.lookupSigner({signerUuid: data.signer_uuid});
|
||||
const status = await client.lookupSigner({ signerUuid: data.signer_uuid });
|
||||
if (status.status === 'approved') {
|
||||
return data.signer_uuid;
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ export class FarcasterProvider implements ProvidersInterface {
|
|||
}
|
||||
|
||||
async getUser(providerToken: string) {
|
||||
const status = await client.lookupSigner({signerUuid: providerToken});
|
||||
const status = await client.lookupSigner({ signerUuid: providerToken });
|
||||
if (status.status !== 'approved') {
|
||||
return {
|
||||
id: '',
|
||||
|
|
@ -29,7 +29,6 @@ export class FarcasterProvider implements ProvidersInterface {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
// const { client, oauth2 } = clientAndYoutube();
|
||||
// client.setCredentials({ access_token: providerToken });
|
||||
// const user = oauth2(client);
|
||||
|
|
|
|||
|
|
@ -17,16 +17,18 @@ export class PublicAuthMiddleware implements NestMiddleware {
|
|||
const org = await this._organizationService.getOrgByApiKey(auth);
|
||||
if (!org) {
|
||||
res.status(HttpStatus.UNAUTHORIZED).json({ msg: 'Invalid API key' });
|
||||
return ;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!!process.env.STRIPE_SECRET_KEY && !org.subscription) {
|
||||
res.status(HttpStatus.UNAUTHORIZED).json({ msg: 'No subscription found' });
|
||||
return ;
|
||||
res
|
||||
.status(HttpStatus.UNAUTHORIZED)
|
||||
.json({ msg: 'No subscription found' });
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
req.org = {...org, users: [{users: {role: 'SUPERADMIN'}}]};
|
||||
req.org = { ...org, users: [{ users: { role: 'SUPERADMIN' } }] };
|
||||
} catch (err) {
|
||||
throw new HttpForbiddenException();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,4 +10,4 @@
|
|||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,12 +9,7 @@ import { AgentRun } from './tasks/agent.run';
|
|||
import { AgentModule } from '@gitroom/nestjs-libraries/agent/agent.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ExternalCommandModule,
|
||||
DatabaseModule,
|
||||
BullMqModule,
|
||||
AgentModule,
|
||||
],
|
||||
imports: [ExternalCommandModule, DatabaseModule, BullMqModule, AgentModule],
|
||||
controllers: [],
|
||||
providers: [CheckStars, RefreshTokens, ConfigurationTask, AgentRun],
|
||||
get exports() {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,16 @@
|
|||
import { NestFactory } from '@nestjs/core';
|
||||
import {CommandModule} from "./command.module";
|
||||
import {CommandService} from "nestjs-command";
|
||||
import { CommandModule } from './command.module';
|
||||
import { CommandService } from 'nestjs-command';
|
||||
|
||||
async function bootstrap() {
|
||||
// some comment again
|
||||
const app = await NestFactory.createApplicationContext(CommandModule, {
|
||||
logger: ['error']
|
||||
logger: ['error'],
|
||||
});
|
||||
|
||||
try {
|
||||
await app
|
||||
.select(CommandModule)
|
||||
.get(CommandService)
|
||||
.exec();
|
||||
await app.close()
|
||||
await app.select(CommandModule).get(CommandService).exec();
|
||||
await app.close();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
await app.close();
|
||||
|
|
|
|||
|
|
@ -10,21 +10,23 @@ export class ConfigurationTask {
|
|||
})
|
||||
create() {
|
||||
const checker = new ConfigurationChecker();
|
||||
checker.readEnvFromProcess()
|
||||
checker.check()
|
||||
checker.readEnvFromProcess();
|
||||
checker.check();
|
||||
|
||||
if (checker.hasIssues()) {
|
||||
for (const issue of checker.getIssues()) {
|
||||
console.warn("Configuration issue:", issue)
|
||||
console.warn('Configuration issue:', issue);
|
||||
}
|
||||
|
||||
console.error("Configuration check complete, issues: ", checker.getIssuesCount())
|
||||
console.error(
|
||||
'Configuration check complete, issues: ',
|
||||
checker.getIssuesCount()
|
||||
);
|
||||
} else {
|
||||
console.log("Configuration check complete, no issues found.")
|
||||
console.log('Configuration check complete, no issues found.');
|
||||
}
|
||||
|
||||
console.log("Press Ctrl+C to exit.");
|
||||
return true
|
||||
console.log('Press Ctrl+C to exit.');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,4 +11,4 @@
|
|||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { NestFactory } from '@nestjs/core';
|
||||
import {CronModule} from "./cron.module";
|
||||
import { CronModule } from './cron.module';
|
||||
|
||||
async function bootstrap() {
|
||||
// some comment again
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/cl
|
|||
|
||||
@Injectable()
|
||||
export class SyncTrending {
|
||||
constructor(
|
||||
private _workerServiceProducer: BullMqClient
|
||||
) {}
|
||||
constructor(private _workerServiceProducer: BullMqClient) {}
|
||||
@Cron('0 * * * *')
|
||||
async syncTrending() {
|
||||
this._workerServiceProducer.emit('sync_trending', {}).subscribe();
|
||||
|
|
|
|||
|
|
@ -3,39 +3,46 @@ import { resolve } from 'path';
|
|||
import type { PluginOption } from 'vite';
|
||||
|
||||
// plugin to remove dev icons from prod build
|
||||
export function stripDevIcons (isDev: boolean) {
|
||||
if (isDev) return null
|
||||
export function stripDevIcons(isDev: boolean) {
|
||||
if (isDev) return null;
|
||||
|
||||
return {
|
||||
name: 'strip-dev-icons',
|
||||
resolveId (source: string) {
|
||||
return source === 'virtual-module' ? source : null
|
||||
resolveId(source: string) {
|
||||
return source === 'virtual-module' ? source : null;
|
||||
},
|
||||
renderStart (outputOptions: any, inputOptions: any) {
|
||||
const outDir = outputOptions.dir
|
||||
fs.rm(resolve(outDir, 'dev-icon-32.png'), () => console.log(`Deleted dev-icon-32.png from prod build`))
|
||||
fs.rm(resolve(outDir, 'dev-icon-128.png'), () => console.log(`Deleted dev-icon-128.png from prod build`))
|
||||
}
|
||||
}
|
||||
renderStart(outputOptions: any, inputOptions: any) {
|
||||
const outDir = outputOptions.dir;
|
||||
fs.rm(resolve(outDir, 'dev-icon-32.png'), () =>
|
||||
console.log(`Deleted dev-icon-32.png from prod build`)
|
||||
);
|
||||
fs.rm(resolve(outDir, 'dev-icon-128.png'), () =>
|
||||
console.log(`Deleted dev-icon-128.png from prod build`)
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// plugin to support i18n
|
||||
export function crxI18n (options: { localize: boolean, src: string }): PluginOption {
|
||||
if (!options.localize) return null
|
||||
// plugin to support i18n
|
||||
export function crxI18n(options: {
|
||||
localize: boolean;
|
||||
src: string;
|
||||
}): PluginOption {
|
||||
if (!options.localize) return null;
|
||||
|
||||
const getJsonFiles = (dir: string): Array<string> => {
|
||||
const files = fs.readdirSync(dir, {recursive: true}) as string[]
|
||||
return files.filter(file => !!file && file.endsWith('.json'))
|
||||
}
|
||||
const entry = resolve(__dirname, options.src)
|
||||
const localeFiles = getJsonFiles(entry)
|
||||
const files = localeFiles.map(file => {
|
||||
const files = fs.readdirSync(dir, { recursive: true }) as string[];
|
||||
return files.filter((file) => !!file && file.endsWith('.json'));
|
||||
};
|
||||
const entry = resolve(__dirname, options.src);
|
||||
const localeFiles = getJsonFiles(entry);
|
||||
const files = localeFiles.map((file) => {
|
||||
return {
|
||||
id: '',
|
||||
fileName: file,
|
||||
source: fs.readFileSync(resolve(entry, file))
|
||||
}
|
||||
})
|
||||
source: fs.readFileSync(resolve(entry, file)),
|
||||
};
|
||||
});
|
||||
return {
|
||||
name: 'crx-i18n',
|
||||
enforce: 'pre',
|
||||
|
|
@ -43,14 +50,14 @@ export function crxI18n (options: { localize: boolean, src: string }): PluginOpt
|
|||
order: 'post',
|
||||
handler() {
|
||||
files.forEach((file) => {
|
||||
const refId = this.emitFile({
|
||||
type: 'asset',
|
||||
source: file.source,
|
||||
fileName: '_locales/'+file.fileName
|
||||
})
|
||||
file.id = refId
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const refId = this.emitFile({
|
||||
type: 'asset',
|
||||
source: file.source,
|
||||
fileName: '_locales/' + file.fileName,
|
||||
});
|
||||
file.id = refId;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,7 @@
|
|||
},
|
||||
"web_accessible_resources": [
|
||||
{
|
||||
"resources": [
|
||||
"contentStyle.css",
|
||||
"dev-icon-128.png",
|
||||
"dev-icon-32.png"
|
||||
],
|
||||
"resources": ["contentStyle.css", "dev-icon-128.png", "dev-icon-32.png"],
|
||||
"matches": []
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@
|
|||
"manifest.dev.json"
|
||||
],
|
||||
"ext": "tsx,css,html,ts,json",
|
||||
"ignore": [
|
||||
"src/**/*.spec.ts"
|
||||
],
|
||||
"ignore": ["src/**/*.spec.ts"],
|
||||
"exec": "vite build --config vite.config.chrome.ts --mode development"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@
|
|||
"manifest.dev.json"
|
||||
],
|
||||
"ext": "tsx,css,html,ts,json",
|
||||
"ignore": [
|
||||
"src/**/*.spec.ts"
|
||||
],
|
||||
"ignore": ["src/**/*.spec.ts"],
|
||||
"exec": "vite build --config vite.config.firefox.ts --mode development"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
@tailwind utilities;
|
||||
|
||||
@theme {
|
||||
--animate-spin-slow: spin 20s linear infinite;
|
||||
--animate-spin-slow: spin 20s linear infinite;
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,4 +7,4 @@
|
|||
"message": "description in src/locales/en/messages.json",
|
||||
"description": "Extension description"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,27 @@
|
|||
import { fetchRequestUtil } from "@gitroom/extension/utils/request.util";
|
||||
import { fetchRequestUtil } from '@gitroom/extension/utils/request.util';
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV === "development";
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
|
||||
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
|
||||
if (request.action === "makeHttpRequest") {
|
||||
if (request.action === 'makeHttpRequest') {
|
||||
fetchRequestUtil(request).then((response) => {
|
||||
sendResponse(response);
|
||||
});
|
||||
}
|
||||
|
||||
if (request.action === "loadStorage") {
|
||||
chrome.storage.local.get([request.key],
|
||||
function (storage) {
|
||||
sendResponse(storage[request.key]);
|
||||
},
|
||||
);
|
||||
if (request.action === 'loadStorage') {
|
||||
chrome.storage.local.get([request.key], function (storage) {
|
||||
sendResponse(storage[request.key]);
|
||||
});
|
||||
}
|
||||
|
||||
if (request.action === "saveStorage") {
|
||||
chrome.storage.local.set(
|
||||
{ [request.key]: request.value },
|
||||
function () {
|
||||
sendResponse({ success: true });
|
||||
}
|
||||
);
|
||||
if (request.action === 'saveStorage') {
|
||||
chrome.storage.local.set({ [request.key]: request.value }, function () {
|
||||
sendResponse({ success: true });
|
||||
});
|
||||
}
|
||||
|
||||
if (request.action === "loadCookie") {
|
||||
if (request.action === 'loadCookie') {
|
||||
chrome.cookies.get(
|
||||
{
|
||||
url: import.meta.env?.FRONTEND_URL || process?.env?.FRONTEND_URL,
|
||||
|
|
@ -34,7 +29,7 @@ chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
|
|||
},
|
||||
function (cookies) {
|
||||
sendResponse(cookies?.value);
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ export const ActionComponent: FC<{
|
|||
|
||||
return (
|
||||
<div className="g-wrapper" style={{ position: 'relative' }}>
|
||||
<div className="absolute left-0 top-0 z-[9999] w-full h-full" />
|
||||
<div className="absolute start-0 top-0 z-[9999] w-full h-full" />
|
||||
{modal && (
|
||||
<Comp
|
||||
platform={provider.identifier}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { createRoot } from "react-dom/client";
|
||||
import "./style.css";
|
||||
import { MainContent } from "@gitroom/extension/pages/content/main.content";
|
||||
const div = document.createElement("div");
|
||||
div.id = "__root";
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import './style.css';
|
||||
import { MainContent } from '@gitroom/extension/pages/content/main.content';
|
||||
const div = document.createElement('div');
|
||||
div.id = '__root';
|
||||
document.body.appendChild(div);
|
||||
|
||||
const rootContainer = document.querySelector("#__root");
|
||||
const rootContainer = document.querySelector('#__root');
|
||||
if (!rootContainer) throw new Error("Can't find Content root element");
|
||||
const root = createRoot(rootContainer);
|
||||
root.render(<MainContent />);
|
||||
|
|
|
|||
|
|
@ -157,7 +157,12 @@ export const MainContentInner: FC = (props) => {
|
|||
actionType={actionEl.actionType}
|
||||
provider={provider}
|
||||
wrap={true}
|
||||
selector={stringToABC(provider.element.split(',').map(z => z.trim()).find(p => actionEl.element.matches(p)) || '')}
|
||||
selector={stringToABC(
|
||||
provider.element
|
||||
.split(',')
|
||||
.map((z) => z.trim())
|
||||
.find((p) => actionEl.element.matches(p)) || ''
|
||||
)}
|
||||
/>,
|
||||
actionEl.element
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -3,25 +3,25 @@
|
|||
@tailwind utilities;
|
||||
|
||||
.my-wrapper {
|
||||
left: 0 !important;
|
||||
top: 0 !important;
|
||||
position: fixed !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
z-index: 999999 !important;
|
||||
display: flex !important;
|
||||
justify-content: center !important;
|
||||
background: rgba(0, 0, 0, 0.5) !important;
|
||||
left: 0 !important;
|
||||
top: 0 !important;
|
||||
position: fixed !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
z-index: 999999 !important;
|
||||
display: flex !important;
|
||||
justify-content: center !important;
|
||||
background: rgba(0, 0, 0, 0.5) !important;
|
||||
}
|
||||
|
||||
.my-wrapper > div {
|
||||
background: white !important;
|
||||
width: 600px !important;
|
||||
height: 300px !important;
|
||||
border-radius: 10px !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
justify-items: center !important;
|
||||
margin-top: 100px !important;
|
||||
color: black !important;
|
||||
}
|
||||
background: white !important;
|
||||
width: 600px !important;
|
||||
height: 300px !important;
|
||||
border-radius: 10px !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
justify-items: center !important;
|
||||
margin-top: 100px !important;
|
||||
color: black !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Options</title>
|
||||
</head>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Options</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="__root"></div>
|
||||
<script type="module" src="./index.tsx"></script>
|
||||
</body>
|
||||
<body>
|
||||
<div id="__root"></div>
|
||||
<script type="module" src="./index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import '@gitroom/extension/pages/options/index.css';
|
||||
import Options from "@gitroom/extension/pages/options/Options";
|
||||
import Options from '@gitroom/extension/pages/options/Options';
|
||||
|
||||
function init() {
|
||||
const rootContainer = document.querySelector("#__root");
|
||||
const rootContainer = document.querySelector('#__root');
|
||||
if (!rootContainer) throw new Error("Can't find Options root element");
|
||||
const root = createRoot(rootContainer);
|
||||
root.render(<Options />);
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ body {
|
|||
|
||||
.container {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Devtools Panel</title>
|
||||
</head>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Devtools Panel</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="__root"></div>
|
||||
<script type="module" src="./index.tsx"></script>
|
||||
</body>
|
||||
<body>
|
||||
<div id="__root"></div>
|
||||
<script type="module" src="./index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import '@pages/panel/index.css';
|
|||
import '@assets/styles/tailwind.css';
|
||||
|
||||
function init() {
|
||||
const rootContainer = document.querySelector("#__root");
|
||||
const rootContainer = document.querySelector('#__root');
|
||||
if (!rootContainer) throw new Error("Can't find Panel root element");
|
||||
const root = createRoot(rootContainer);
|
||||
root.render(<Panel />);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { FC, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { ProviderList } from "@gitroom/extension/providers/provider.list";
|
||||
import { fetchCookie } from "@gitroom/extension/utils/load.cookie";
|
||||
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { ProviderList } from '@gitroom/extension/providers/provider.list';
|
||||
import { fetchCookie } from '@gitroom/extension/utils/load.cookie';
|
||||
|
||||
export const PopupContainerContainer: FC = () => {
|
||||
const [url, setUrl] = useState<string | null>(null);
|
||||
|
|
@ -11,7 +11,9 @@ export const PopupContainerContainer: FC = () => {
|
|||
}, []);
|
||||
|
||||
if (!url) {
|
||||
return <div className="text-4xl">This website is not supported by Postiz</div>;
|
||||
return (
|
||||
<div className="text-4xl">This website is not supported by Postiz</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <PopupContainer url={url} />;
|
||||
|
|
@ -54,7 +56,9 @@ export const PopupContainer: FC<{ url: string }> = (props) => {
|
|||
}
|
||||
|
||||
if (!provider) {
|
||||
return <div className="text-4xl">This website is not supported by Postiz</div>;
|
||||
return (
|
||||
<div className="text-4xl">This website is not supported by Postiz</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoggedIn) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Popup</title>
|
||||
</head>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Popup</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="__root"></div>
|
||||
<script type="module" src="./index.tsx"></script>
|
||||
</body>
|
||||
<body>
|
||||
<div id="__root"></div>
|
||||
<script type="module" src="./index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ import React from 'react';
|
|||
import { createRoot } from 'react-dom/client';
|
||||
import './index.css';
|
||||
import '@gitroom/extension/assets/styles/tailwind.css';
|
||||
import Popup from "@gitroom/extension/pages/popup/Popup";
|
||||
import Popup from '@gitroom/extension/pages/popup/Popup';
|
||||
|
||||
function init() {
|
||||
const rootContainer = document.querySelector("#__root");
|
||||
const rootContainer = document.querySelector('#__root');
|
||||
if (!rootContainer) throw new Error("Can't find Popup root element");
|
||||
const root = createRoot(rootContainer);
|
||||
root.render(<Popup />);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { ProviderInterface } from "@gitroom/extension/providers/provider.interface";
|
||||
import { ProviderInterface } from '@gitroom/extension/providers/provider.interface';
|
||||
|
||||
export class LinkedinProvider implements ProviderInterface {
|
||||
identifier = "linkedin";
|
||||
baseUrl = "https://www.linkedin.com";
|
||||
identifier = 'linkedin';
|
||||
baseUrl = 'https://www.linkedin.com';
|
||||
element = `.share-box-feed-entry__closed-share-box`;
|
||||
attachTo = `[role="main"]`;
|
||||
style = "light" as "light";
|
||||
style = 'light' as 'light';
|
||||
findIdentifier = (element: HTMLElement) => {
|
||||
return element.closest('[data-urn]').getAttribute("data-urn");
|
||||
return element.closest('[data-urn]').getAttribute('data-urn');
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export class XProvider implements ProviderInterface {
|
|||
baseUrl = 'https://x.com';
|
||||
element = `[data-testid="primaryColumn"]:has([data-testid="toolBar"]) [data-testid="tweetTextarea_0_label"], [data-testid="SideNav_NewTweet_Button"]`;
|
||||
attachTo = `#react-root`;
|
||||
style = "dark" as "dark";
|
||||
style = 'dark' as 'dark';
|
||||
findIdentifier = (element: HTMLElement) => {
|
||||
return (
|
||||
Array.from(
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
export const fetchCookie = (cookieName: string) => {
|
||||
return chrome.runtime.sendMessage({
|
||||
action: "loadCookie",
|
||||
action: 'loadCookie',
|
||||
cookieName,
|
||||
});
|
||||
};
|
||||
|
||||
export const getCookie = async (
|
||||
cookies: chrome.cookies.Cookie[],
|
||||
cookie: string,
|
||||
cookie: string
|
||||
) => {
|
||||
// return cookies.find((c) => c.name === cookie).value;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
export const fetchStorage = (key: string) => {
|
||||
return chrome.runtime.sendMessage({
|
||||
action: "loadStorage",
|
||||
action: 'loadStorage',
|
||||
key,
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
const isDev = process.env.NODE_ENV === "development";
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
export const sendRequest = (
|
||||
auth: string,
|
||||
url: string,
|
||||
method: "GET" | "POST",
|
||||
body?: string,
|
||||
method: 'GET' | 'POST',
|
||||
body?: string
|
||||
) => {
|
||||
return chrome.runtime.sendMessage({
|
||||
action: "makeHttpRequest",
|
||||
action: 'makeHttpRequest',
|
||||
url,
|
||||
method,
|
||||
body,
|
||||
|
|
@ -17,16 +17,17 @@ export const sendRequest = (
|
|||
export const fetchRequestUtil = async (request: any) => {
|
||||
return (
|
||||
await fetch(
|
||||
(import.meta.env?.FRONTEND_URL || process?.env?.FRONTEND_URL) + request.url,
|
||||
(import.meta.env?.FRONTEND_URL || process?.env?.FRONTEND_URL) +
|
||||
request.url,
|
||||
{
|
||||
method: request.method || "GET",
|
||||
method: request.method || 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: request.auth,
|
||||
// Add any auth headers here if needed
|
||||
},
|
||||
...(request.body ? { body: request.body } : {}),
|
||||
},
|
||||
}
|
||||
)
|
||||
).json();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
export const saveStorage = (key: string, value: any) => {
|
||||
return chrome.runtime.sendMessage({
|
||||
action: "saveStorage",
|
||||
action: 'saveStorage',
|
||||
key,
|
||||
value,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src",
|
||||
|
|
@ -23,5 +23,5 @@
|
|||
"vite.config.base.ts",
|
||||
"vite.config.chrome.ts",
|
||||
"vite.config.firefox.ts"
|
||||
],
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import react from "@vitejs/plugin-react";
|
||||
import { resolve } from "path";
|
||||
import { ManifestV3Export } from "@crxjs/vite-plugin";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import { defineConfig, BuildOptions } from "vite";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
import { stripDevIcons, crxI18n } from "./custom-vite-plugins";
|
||||
import manifest from "./manifest.json";
|
||||
import devManifest from "./manifest.dev.json";
|
||||
import pkg from "./package.json";
|
||||
import { ProviderList } from "./src/providers/provider.list";
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { resolve } from 'path';
|
||||
import { ManifestV3Export } from '@crxjs/vite-plugin';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import { defineConfig, BuildOptions } from 'vite';
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
import { stripDevIcons, crxI18n } from './custom-vite-plugins';
|
||||
import manifest from './manifest.json';
|
||||
import devManifest from './manifest.dev.json';
|
||||
import pkg from './package.json';
|
||||
import { ProviderList } from './src/providers/provider.list';
|
||||
|
||||
const isDev = process.env.NODE_ENV === "development";
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
// set this flag to true, if you want localization support
|
||||
const localize = false;
|
||||
|
||||
|
|
@ -20,17 +20,15 @@ const { matches, ...rest } = manifest?.content_scripts?.[0] || {};
|
|||
export const baseManifest = {
|
||||
...manifest,
|
||||
host_permissions: [
|
||||
...ProviderList.map((p) => p.baseUrl + "/"),
|
||||
...ProviderList.map((p) => p.baseUrl + '/'),
|
||||
import.meta.env?.FRONTEND_URL || process?.env?.FRONTEND_URL + '/*',
|
||||
],
|
||||
permissions: [...(manifest.permissions || [])],
|
||||
content_scripts: [
|
||||
{
|
||||
matches: ProviderList.reduce(
|
||||
(all, p) => [...all, p.baseUrl + "/*"],
|
||||
[
|
||||
import.meta.env?.FRONTEND_URL || process?.env?.FRONTEND_URL + '/*',
|
||||
],
|
||||
(all, p) => [...all, p.baseUrl + '/*'],
|
||||
[import.meta.env?.FRONTEND_URL || process?.env?.FRONTEND_URL + '/*']
|
||||
),
|
||||
...rest,
|
||||
},
|
||||
|
|
@ -39,9 +37,9 @@ export const baseManifest = {
|
|||
...merge,
|
||||
...(localize
|
||||
? {
|
||||
name: "__MSG_extName__",
|
||||
description: "__MSG_extDescription__",
|
||||
default_locale: "en",
|
||||
name: '__MSG_extName__',
|
||||
description: '__MSG_extDescription__',
|
||||
default_locale: 'en',
|
||||
}
|
||||
: {}),
|
||||
} as ManifestV3Export;
|
||||
|
|
@ -52,13 +50,13 @@ export const baseBuildOptions: BuildOptions = {
|
|||
};
|
||||
|
||||
export default defineConfig({
|
||||
envPrefix: ["NEXT_PUBLIC_", "FRONTEND_URL"],
|
||||
envPrefix: ['NEXT_PUBLIC_', 'FRONTEND_URL'],
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
tsconfigPaths(),
|
||||
react(),
|
||||
stripDevIcons(isDev),
|
||||
crxI18n({ localize, src: "./src/locales" }),
|
||||
crxI18n({ localize, src: './src/locales' }),
|
||||
],
|
||||
publicDir: resolve(__dirname, "public"),
|
||||
publicDir: resolve(__dirname, 'public'),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { resolve } from "path";
|
||||
import { mergeConfig, defineConfig } from "vite";
|
||||
import { crx, ManifestV3Export } from "@crxjs/vite-plugin";
|
||||
import baseConfig, { baseManifest, baseBuildOptions } from "./vite.config.base";
|
||||
import hotReloadExtension from "hot-reload-extension-vite";
|
||||
import { resolve } from 'path';
|
||||
import { mergeConfig, defineConfig } from 'vite';
|
||||
import { crx, ManifestV3Export } from '@crxjs/vite-plugin';
|
||||
import baseConfig, { baseManifest, baseBuildOptions } from './vite.config.base';
|
||||
import hotReloadExtension from 'hot-reload-extension-vite';
|
||||
|
||||
const outDir = resolve(__dirname, "dist");
|
||||
const isDev = process.env.NODE_ENV === "development";
|
||||
const outDir = resolve(__dirname, 'dist');
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
|
||||
export default mergeConfig(
|
||||
baseConfig,
|
||||
|
|
@ -15,11 +15,11 @@ export default mergeConfig(
|
|||
manifest: {
|
||||
...baseManifest,
|
||||
background: {
|
||||
service_worker: "src/pages/background/index.ts",
|
||||
type: "module",
|
||||
service_worker: 'src/pages/background/index.ts',
|
||||
type: 'module',
|
||||
},
|
||||
} as ManifestV3Export,
|
||||
browser: "chrome",
|
||||
browser: 'chrome',
|
||||
contentScripts: {
|
||||
injectCss: true,
|
||||
},
|
||||
|
|
@ -28,7 +28,7 @@ export default mergeConfig(
|
|||
? [
|
||||
hotReloadExtension({
|
||||
log: true,
|
||||
backgroundPath: "src/pages/background/index.ts",
|
||||
backgroundPath: 'src/pages/background/index.ts',
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
|
|
@ -40,13 +40,13 @@ export default mergeConfig(
|
|||
? {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
entryFileNames: "assets/[name].js",
|
||||
chunkFileNames: "assets/[name].js",
|
||||
assetFileNames: "assets/[name][extname]",
|
||||
entryFileNames: 'assets/[name].js',
|
||||
chunkFileNames: 'assets/[name].js',
|
||||
assetFileNames: 'assets/[name][extname]',
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { resolve } from 'path';
|
||||
import { mergeConfig, defineConfig } from 'vite';
|
||||
import { crx, ManifestV3Export } from '@crxjs/vite-plugin';
|
||||
import baseConfig, { baseManifest, baseBuildOptions } from './vite.config.base'
|
||||
import baseConfig, { baseManifest, baseBuildOptions } from './vite.config.base';
|
||||
|
||||
const outDir = resolve(__dirname, 'dist_firefox');
|
||||
|
||||
|
|
@ -13,19 +13,19 @@ export default mergeConfig(
|
|||
manifest: {
|
||||
...baseManifest,
|
||||
background: {
|
||||
scripts: [ 'src/pages/background/index.ts' ]
|
||||
scripts: ['src/pages/background/index.ts'],
|
||||
},
|
||||
} as ManifestV3Export,
|
||||
browser: 'firefox',
|
||||
contentScripts: {
|
||||
injectCss: true,
|
||||
}
|
||||
})
|
||||
},
|
||||
}),
|
||||
],
|
||||
build: {
|
||||
...baseBuildOptions,
|
||||
outDir
|
||||
outDir,
|
||||
},
|
||||
publicDir: resolve(__dirname, 'public'),
|
||||
})
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
// @ts-check
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
experimental: {
|
||||
|
|
@ -41,5 +40,4 @@ const nextConfig = {
|
|||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,6 +1,5 @@
|
|||
import { ReactNode } from 'react';
|
||||
import { PreviewWrapper } from '@gitroom/frontend/components/preview/preview.wrapper';
|
||||
|
||||
export default async function AppLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<div className="bg-[#000000] min-h-screen">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import { internalFetch } from '@gitroom/helpers/utils/internal.fetch';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import { Metadata } from 'next';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
import Image from 'next/image';
|
||||
|
|
@ -11,30 +9,32 @@ import dayjs from 'dayjs';
|
|||
import utc from 'dayjs/plugin/utc';
|
||||
import { VideoOrImage } from '@gitroom/react/helpers/video.or.image';
|
||||
import { CopyClient } from '@gitroom/frontend/components/preview/copy.client';
|
||||
|
||||
import { getT } from '@gitroom/react/translation/get.translation.service.backend';
|
||||
dayjs.extend(utc);
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Preview`,
|
||||
description: '',
|
||||
};
|
||||
|
||||
export default async function Auth({
|
||||
params: { id },
|
||||
searchParams,
|
||||
}: {
|
||||
params: { id: string };
|
||||
searchParams?: { share?: string };
|
||||
params: {
|
||||
id: string;
|
||||
};
|
||||
searchParams?: {
|
||||
share?: string;
|
||||
};
|
||||
}) {
|
||||
const post = await (await internalFetch(`/public/posts/${id}`)).json();
|
||||
|
||||
const t = await getT();
|
||||
if (!post.length) {
|
||||
return (
|
||||
<div className="text-white fixed left-0 top-0 w-full h-full flex justify-center items-center text-[20px]">
|
||||
Post not found
|
||||
<div className="text-white fixed start-0 top-0 w-full h-full flex justify-center items-center text-[20px]">
|
||||
{t('post_not_found', 'Post not found')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mx-auto w-full max-w-[1346px] py-3 text-white">
|
||||
|
|
@ -91,7 +91,7 @@ export default async function Auth({
|
|||
</div>
|
||||
)}
|
||||
<div className="flex-1">
|
||||
Publication Date:{' '}
|
||||
{t('publication_date', 'Publication Date:')}
|
||||
{dayjs
|
||||
.utc(post[0].createdAt)
|
||||
.local()
|
||||
|
|
@ -119,7 +119,7 @@ export default async function Auth({
|
|||
src={post[0].integration.picture}
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute -right-[5px] -bottom-[5px] w-[30px] h-[30px] z-[20]">
|
||||
<div className="absolute -end-[5px] -bottom-[5px] w-[30px] h-[30px] z-[20]">
|
||||
<img
|
||||
className="w-full h-full bg-black aspect-square rounded-full border-tableBorder"
|
||||
alt={post[0].integration.providerIdentifier}
|
||||
|
|
@ -140,7 +140,9 @@ export default async function Auth({
|
|||
<div className="flex flex-col gap-[20px]">
|
||||
<div
|
||||
className="text-sm whitespace-pre-wrap"
|
||||
dangerouslySetInnerHTML={{ __html: p.content }}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: p.content,
|
||||
}}
|
||||
/>
|
||||
<div className="flex w-full gap-[10px]">
|
||||
{JSON.parse(p?.image || '[]').map((p: any) => (
|
||||
|
|
@ -148,7 +150,11 @@ export default async function Auth({
|
|||
key={p.name}
|
||||
className="flex-1 rounded-[10px] max-h-[500px] overflow-hidden"
|
||||
>
|
||||
<VideoOrImage isContain={true} src={p.path} autoplay={true} />
|
||||
<VideoOrImage
|
||||
isContain={true}
|
||||
src={p.path}
|
||||
autoplay={true}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import { AnalyticsComponent } from '@gitroom/frontend/components/analytics/analytics.component';
|
||||
import { Metadata } from 'next';
|
||||
import { PlatformAnalytics } from '@gitroom/frontend/components/platform-analytics/platform.analytics';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Analytics`,
|
||||
description: '',
|
||||
};
|
||||
|
||||
export default async function Index() {
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
import { LifetimeDeal } from '@gitroom/frontend/components/billing/lifetime.deal';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import { Metadata } from 'next';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Lifetime deal`,
|
||||
description: '',
|
||||
};
|
||||
|
||||
export default async function Page() {
|
||||
return <LifetimeDeal />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import { BillingComponent } from '@gitroom/frontend/components/billing/billing.component';
|
||||
import { Metadata } from 'next';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Billing`,
|
||||
description: '',
|
||||
};
|
||||
|
||||
export default async function Page() {
|
||||
return <BillingComponent />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
import {Metadata} from "next";
|
||||
|
||||
import { Metadata } from 'next';
|
||||
import { getT } from '@gitroom/react/translation/get.translation.service.backend';
|
||||
export const metadata: Metadata = {
|
||||
title: 'Error',
|
||||
description: '',
|
||||
}
|
||||
|
||||
};
|
||||
export default async function Page() {
|
||||
return (
|
||||
<div>We are experiencing some difficulty, try to refresh the page</div>
|
||||
)
|
||||
}
|
||||
const t = await getT();
|
||||
return (
|
||||
<div>
|
||||
{t(
|
||||
'we_are_experiencing_some_difficulty_try_to_refresh_the_page',
|
||||
'We are experiencing some difficulty, try to refresh the page'
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
import { HttpStatusCode } from 'axios';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import { internalFetch } from '@gitroom/helpers/utils/internal.fetch';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { Redirect } from '@gitroom/frontend/components/layout/redirect';
|
||||
|
||||
import { getT } from '@gitroom/react/translation/get.translation.service.backend';
|
||||
export default async function Page({
|
||||
params: { provider },
|
||||
searchParams,
|
||||
}: {
|
||||
params: { provider: string };
|
||||
params: {
|
||||
provider: string;
|
||||
};
|
||||
searchParams: any;
|
||||
}) {
|
||||
const t = await getT();
|
||||
if (provider === 'x') {
|
||||
searchParams = {
|
||||
...searchParams,
|
||||
|
|
@ -21,25 +22,21 @@ export default async function Page({
|
|||
refresh: searchParams.refresh || '',
|
||||
};
|
||||
}
|
||||
|
||||
if (provider === 'vk') {
|
||||
searchParams = {
|
||||
...searchParams,
|
||||
state: searchParams.state || '',
|
||||
code: searchParams.code + '&&&&' + searchParams.device_id
|
||||
code: searchParams.code + '&&&&' + searchParams.device_id,
|
||||
};
|
||||
}
|
||||
|
||||
const data = await internalFetch(`/integrations/social/${provider}/connect`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(searchParams),
|
||||
});
|
||||
|
||||
if (data.status === HttpStatusCode.NotAcceptable) {
|
||||
const { msg } = await data.json();
|
||||
return redirect(`/launches?msg=${msg}`);
|
||||
}
|
||||
|
||||
if (
|
||||
data.status !== HttpStatusCode.Ok &&
|
||||
data.status !== HttpStatusCode.Created
|
||||
|
|
@ -47,20 +44,17 @@ export default async function Page({
|
|||
return (
|
||||
<>
|
||||
<div className="mt-[50px] text-[50px]">
|
||||
Could not add provider.
|
||||
{t('could_not_add_provider', 'Could not add provider.')}
|
||||
<br />
|
||||
You are being redirected back
|
||||
{t('you_are_being_redirected_back', 'You are being redirected back')}
|
||||
</div>
|
||||
<Redirect url="/launches" delay={3000} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const { inBetweenSteps, id } = await data.json();
|
||||
|
||||
if (inBetweenSteps && !searchParams.refresh) {
|
||||
return redirect(`/launches?added=${provider}&continue=${id}`);
|
||||
}
|
||||
|
||||
return redirect(`/launches?added=${provider}&msg=Channel Updated`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { IntegrationRedirectComponent } from '@gitroom/frontend/components/launches/integration.redirect.component';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export default async function Page({
|
||||
params: { provider },
|
||||
searchParams,
|
||||
}: {
|
||||
params: { provider: string };
|
||||
params: {
|
||||
provider: string;
|
||||
};
|
||||
searchParams: any;
|
||||
}) {
|
||||
return <IntegrationRedirectComponent />;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
import { ReactNode } from 'react';
|
||||
|
||||
import { getT } from '@gitroom/react/translation/get.translation.service.backend';
|
||||
export default async function IntegrationLayout({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const t = await getT();
|
||||
|
||||
return (
|
||||
<div className="text-6xl text-center mt-[50px]">
|
||||
Adding channel, Redirecting You{children}
|
||||
{t('adding_channel_redirecting_you', 'Adding channel, Redirecting You')}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,11 @@
|
|||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import {LaunchesComponent} from "@gitroom/frontend/components/launches/launches.component";
|
||||
import {Metadata} from "next";
|
||||
import { LaunchesComponent } from '@gitroom/frontend/components/launches/launches.component';
|
||||
import { Metadata } from 'next';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz Calendar' : 'Gitroom Launches'}`,
|
||||
description: '',
|
||||
}
|
||||
|
||||
};
|
||||
export default async function Index() {
|
||||
return (
|
||||
<LaunchesComponent />
|
||||
);
|
||||
return <LaunchesComponent />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import {LayoutSettings} from "@gitroom/frontend/components/layout/layout.settings";
|
||||
|
||||
export default async function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<LayoutSettings>
|
||||
{children}
|
||||
</LayoutSettings>
|
||||
);
|
||||
import { LayoutSettings } from '@gitroom/frontend/components/layout/layout.settings';
|
||||
export default async function Layout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return <LayoutSettings>{children}</LayoutSettings>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { Buyer } from '@gitroom/frontend/components/marketplace/buyer';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
import { Metadata } from 'next';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Marketplace`,
|
||||
description: '',
|
||||
|
|
@ -11,7 +9,9 @@ export const metadata: Metadata = {
|
|||
export default async function Index({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { code: string };
|
||||
searchParams: {
|
||||
code: string;
|
||||
};
|
||||
}) {
|
||||
return <Buyer />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { BuyerSeller } from '@gitroom/frontend/components/marketplace/buyer.seller';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
<BuyerSeller />
|
||||
{children}
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
|
||||
export const dynamic = 'force-dynamic';
|
||||
import { Metadata } from 'next';
|
||||
import { cookies } from 'next/headers';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Marketplace`,
|
||||
description: '',
|
||||
|
|
@ -12,8 +10,12 @@ export const metadata: Metadata = {
|
|||
export default async function Index({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { code: string };
|
||||
searchParams: {
|
||||
code: string;
|
||||
};
|
||||
}) {
|
||||
const currentCookie = cookies()?.get('marketplace')?.value;
|
||||
return redirect(currentCookie === 'buyer' ? '/marketplace/buyer' : '/marketplace/seller');
|
||||
return redirect(
|
||||
currentCookie === 'buyer' ? '/marketplace/buyer' : '/marketplace/seller'
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { Seller } from '@gitroom/frontend/components/marketplace/seller';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
import { Metadata } from 'next';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Marketplace`,
|
||||
description: '',
|
||||
|
|
@ -11,7 +9,9 @@ export const metadata: Metadata = {
|
|||
export default async function Index({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { code: string };
|
||||
searchParams: {
|
||||
code: string;
|
||||
};
|
||||
}) {
|
||||
return <Seller />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
import { Messages } from '@gitroom/frontend/components/messages/messages';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import { Metadata } from 'next';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Messages`,
|
||||
description: '',
|
||||
};
|
||||
|
||||
export default async function Index() {
|
||||
return <Messages />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { Layout } from '@gitroom/frontend/components/messages/layout';
|
||||
export const dynamic = 'force-dynamic';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export default async function LayoutWrapper({children}: {children: ReactNode}) {
|
||||
return (
|
||||
<Layout renderChildren={children} />
|
||||
);
|
||||
export default async function LayoutWrapper({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return <Layout renderChildren={children} />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,24 @@
|
|||
import { getT } from '@gitroom/react/translation/get.translation.service.backend';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import {Metadata} from "next";
|
||||
import { Metadata } from 'next';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Messages`,
|
||||
description: '',
|
||||
}
|
||||
|
||||
};
|
||||
export default async function Index() {
|
||||
const t = await getT();
|
||||
|
||||
return (
|
||||
<div className="bg-customColor3 h-[951px] flex flex-col rounded-[4px] border border-customColor6">
|
||||
<div className="bg-customColor8 h-[64px]" />
|
||||
<div className="flex-1 flex justify-center items-center text-[20px]">
|
||||
Select a conversation and chat away.
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-customColor3 h-[951px] flex flex-col rounded-[4px] border border-customColor6">
|
||||
<div className="bg-customColor8 h-[64px]" />
|
||||
<div className="flex-1 flex justify-center items-center text-[20px]">
|
||||
{t(
|
||||
'select_a_conversation_and_chat_away',
|
||||
'Select a conversation and chat away.'
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
import { Plugs } from '@gitroom/frontend/components/plugs/plugs';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import { Metadata } from 'next';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Plugs`,
|
||||
description: '',
|
||||
};
|
||||
|
||||
export default async function Index() {
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { SettingsPopup } from '@gitroom/frontend/components/layout/settings.component';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import { Metadata } from 'next';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Settings`,
|
||||
description: '',
|
||||
|
|
@ -12,7 +9,9 @@ export const metadata: Metadata = {
|
|||
export default async function Index({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { code: string };
|
||||
searchParams: {
|
||||
code: string;
|
||||
};
|
||||
}) {
|
||||
return <SettingsPopup />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,18 +2,15 @@ import { NextRequest, NextResponse } from 'next/server';
|
|||
import { createReadStream, statSync } from 'fs';
|
||||
// @ts-ignore
|
||||
import mime from 'mime';
|
||||
|
||||
async function* nodeStreamToIterator(stream: any) {
|
||||
for await (const chunk of stream) {
|
||||
yield chunk;
|
||||
}
|
||||
}
|
||||
|
||||
function iteratorToStream(iterator: any) {
|
||||
return new ReadableStream({
|
||||
async pull(controller) {
|
||||
const { value, done } = await iterator.next();
|
||||
|
||||
if (done) {
|
||||
controller.close();
|
||||
} else {
|
||||
|
|
@ -22,25 +19,29 @@ function iteratorToStream(iterator: any) {
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
export const GET = (
|
||||
request: NextRequest,
|
||||
context: { params: { path: string[] } }
|
||||
context: {
|
||||
params: {
|
||||
path: string[];
|
||||
};
|
||||
}
|
||||
) => {
|
||||
const filePath =
|
||||
process.env.UPLOAD_DIRECTORY + '/' + context.params.path.join('/');
|
||||
const response = createReadStream(filePath);
|
||||
|
||||
const fileStats = statSync(filePath);
|
||||
const contentType = mime.getType(filePath) || 'application/octet-stream';
|
||||
|
||||
const iterator = nodeStreamToIterator(response);
|
||||
const webStream = iteratorToStream(iterator);
|
||||
return new Response(webStream, {
|
||||
headers: {
|
||||
'Content-Type': contentType, // Set the appropriate content-type header
|
||||
'Content-Length': fileStats.size.toString(), // Set the content-length header
|
||||
'Last-Modified': fileStats.mtime.toUTCString(), // Set the last-modified header
|
||||
'Content-Type': contentType,
|
||||
// Set the appropriate content-type header
|
||||
'Content-Length': fileStats.size.toString(),
|
||||
// Set the content-length header
|
||||
'Last-Modified': fileStats.mtime.toUTCString(),
|
||||
// Set the last-modified header
|
||||
'Cache-Control': 'public, max-age=31536000, immutable', // Example cache-control header
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import { Metadata } from 'next';
|
||||
import { AfterActivate } from '@gitroom/frontend/components/auth/after.activate';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} - Activate your account`,
|
||||
title: `${
|
||||
isGeneralServerSide() ? 'Postiz' : 'Gitroom'
|
||||
} - Activate your account`,
|
||||
description: '',
|
||||
};
|
||||
|
||||
export default async function Auth() {
|
||||
return <AfterActivate />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import {Metadata} from "next";
|
||||
import { Metadata } from 'next';
|
||||
import { Activate } from '@gitroom/frontend/components/auth/activate';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} - Activate your account`,
|
||||
title: `${
|
||||
isGeneralServerSide() ? 'Postiz' : 'Gitroom'
|
||||
} - Activate your account`,
|
||||
description: '',
|
||||
};
|
||||
|
||||
export default async function Auth() {
|
||||
return (
|
||||
<Activate />
|
||||
);
|
||||
return <Activate />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import { ForgotReturn } from '@gitroom/frontend/components/auth/forgot-return';
|
||||
import { Metadata } from 'next';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Forgot Password`,
|
||||
description: '',
|
||||
};
|
||||
export default async function Auth(params: { params: { token: string } }) {
|
||||
export default async function Auth(params: {
|
||||
params: {
|
||||
token: string;
|
||||
};
|
||||
}) {
|
||||
return <ForgotReturn token={params.params.token} />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,11 @@
|
|||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import {Forgot} from "@gitroom/frontend/components/auth/forgot";
|
||||
import {Metadata} from "next";
|
||||
import { Forgot } from '@gitroom/frontend/components/auth/forgot';
|
||||
import { Metadata } from 'next';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Forgot Password`,
|
||||
description: '',
|
||||
};
|
||||
|
||||
export default async function Auth() {
|
||||
return (
|
||||
<Forgot />
|
||||
);
|
||||
return <Forgot />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,27 @@
|
|||
export const dynamic = 'force-dynamic';
|
||||
import { getT } from '@gitroom/react/translation/get.translation.service.backend';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
import { ReactNode } from 'react';
|
||||
import Image from 'next/image';
|
||||
import clsx from 'clsx';
|
||||
import loadDynamic from 'next/dynamic';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
const ReturnUrlComponent = loadDynamic(() => import('./return.url.component'));
|
||||
|
||||
export default async function AuthLayout({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const t = await getT();
|
||||
|
||||
return (
|
||||
<div className="dark !bg-black">
|
||||
<div className="dark !bg-black lbox">
|
||||
<ReturnUrlComponent />
|
||||
<div className="absolute left-0 top-0 z-[0] h-[100vh] w-[100vw] overflow-hidden bg-loginBg bg-contain bg-no-repeat bg-left-top" />
|
||||
<div className="absolute start-0 top-0 z-[0] h-[100vh] w-[100vw] overflow-hidden bg-loginBg bg-contain bg-no-repeat bg-left-top" />
|
||||
<div className="relative z-[1] px-3 lg:pr-[100px] xs:mt-[70px] flex justify-center lg:justify-end items-center h-[100vh] w-[100vw] overflow-hidden">
|
||||
<div className="w-full max-w-lg h-[614px] flex flex-col bg-loginBox bg-no-repeat bg-contain">
|
||||
<div className="w-full relative">
|
||||
<div className="custom:fixed custom:text-left custom:left-[20px] custom:justify-start custom:top-[20px] absolute -top-[100px] text-textColor justify-center items-center w-full flex gap-[10px]">
|
||||
<div className="custom:fixed custom:text-start custom:left-[20px] custom:justify-start custom:top-[20px] absolute -top-[100px] text-textColor justify-center items-center w-full flex gap-[10px]">
|
||||
<Image
|
||||
src={isGeneralServerSide() ? '/postiz.svg' : '/logo.svg'}
|
||||
width={55}
|
||||
|
|
@ -27,7 +29,9 @@ export default async function AuthLayout({
|
|||
alt="Logo"
|
||||
/>
|
||||
<div
|
||||
className={clsx(!isGeneralServerSide() ? 'mt-[12px]' : 'min-w-[80px]')}
|
||||
className={clsx(
|
||||
!isGeneralServerSide() ? 'mt-[12px]' : 'min-w-[80px]'
|
||||
)}
|
||||
>
|
||||
{isGeneralServerSide() ? (
|
||||
<svg
|
||||
|
|
@ -55,12 +59,12 @@ export default async function AuthLayout({
|
|||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<div className="text-[40px]">Gitroom</div>
|
||||
<div className="text-[40px]">{t('gitroom', 'Gitroom')}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-[32px] w-full h-[660px] text-textColor">
|
||||
<div className="p-[32px] w-full h-[660px] text-textColor rbox">
|
||||
{children}
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col">
|
||||
|
|
@ -68,11 +72,11 @@ export default async function AuthLayout({
|
|||
<div className="absolute top-0 bg-gradient-to-t from-customColor9 w-[1px] translate-x-[22px] h-full" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="absolute right-0 bg-gradient-to-l from-customColor9 h-[1px] translate-y-[60px] w-full" />
|
||||
<div className="absolute end-0 bg-gradient-to-l from-customColor9 h-[1px] translate-y-[60px] w-full" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute top-0 bg-gradient-to-t from-customColor9 w-[1px] -translate-x-[22px] h-full" />
|
||||
<div className="absolute right-0 bg-gradient-to-l from-customColor9 h-[1px] -translate-y-[22px] w-full" />
|
||||
<div className="absolute end-0 bg-gradient-to-l from-customColor9 h-[1px] -translate-y-[22px] w-full" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,11 @@
|
|||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import {Login} from "@gitroom/frontend/components/auth/login";
|
||||
import {Metadata} from "next";
|
||||
import { Login } from '@gitroom/frontend/components/auth/login';
|
||||
import { Metadata } from 'next';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Login`,
|
||||
description: '',
|
||||
};
|
||||
|
||||
export default async function Auth() {
|
||||
return (
|
||||
<Login />
|
||||
);
|
||||
return <Login />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
import { internalFetch } from '@gitroom/helpers/utils/internal.fetch';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
import { Register } from '@gitroom/frontend/components/auth/register';
|
||||
import { Metadata } from 'next';
|
||||
import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { getT } from '@gitroom/react/translation/get.translation.service.backend';
|
||||
export const metadata: Metadata = {
|
||||
title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Register`,
|
||||
description: '',
|
||||
};
|
||||
|
||||
export default async function Auth() {
|
||||
const t = await getT();
|
||||
|
||||
if (process.env.DISABLE_REGISTRATION) {
|
||||
const canRegister = (
|
||||
await (await internalFetch('/auth/can-register')).json()
|
||||
|
|
@ -20,13 +19,14 @@ export default async function Auth() {
|
|||
if (!canRegister) {
|
||||
return (
|
||||
<div className="text-center">
|
||||
Registration is disabled
|
||||
{t('registration_is_disabled', 'Registration is disabled')}
|
||||
<br />
|
||||
<Link className="underline hover:font-bold" href="/auth/login">Login instead</Link>
|
||||
<Link className="underline hover:font-bold" href="/auth/login">
|
||||
{t('login_instead', 'Login instead')}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return <Register />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { FC, useCallback, useEffect } from 'react';
|
||||
|
||||
const ReturnUrlComponent: FC = () => {
|
||||
const params = useSearchParams();
|
||||
const url = params.get('returnUrl');
|
||||
|
|
@ -10,18 +10,15 @@ const ReturnUrlComponent: FC = () => {
|
|||
localStorage.setItem('returnUrl', url!);
|
||||
}
|
||||
}, [url]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
export const useReturnUrl = () => {
|
||||
return {
|
||||
getAndClear: useCallback(() => {
|
||||
const data = localStorage.getItem('returnUrl');
|
||||
localStorage.removeItem('returnUrl');
|
||||
return data;
|
||||
}, [])
|
||||
}
|
||||
}
|
||||
|
||||
export default ReturnUrlComponent;
|
||||
}, []),
|
||||
};
|
||||
};
|
||||
export default ReturnUrlComponent;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ export const dynamic = 'force-dynamic';
|
|||
import '../global.scss';
|
||||
import 'react-tooltip/dist/react-tooltip.css';
|
||||
import '@copilotkit/react-ui/styles.css';
|
||||
|
||||
import LayoutContext from '@gitroom/frontend/components/layout/layout.context';
|
||||
import { ReactNode } from 'react';
|
||||
import { Chakra_Petch } from 'next/font/google';
|
||||
|
|
@ -15,16 +14,21 @@ import { PHProvider } from '@gitroom/react/helpers/posthog';
|
|||
import UtmSaver from '@gitroom/helpers/utils/utm.saver';
|
||||
import { ToltScript } from '@gitroom/frontend/components/layout/tolt.script';
|
||||
import { FacebookComponent } from '@gitroom/frontend/components/layout/facebook.component';
|
||||
import { headers } from 'next/headers';
|
||||
import { headerName } from '@gitroom/react/translation/i18n.config';
|
||||
import { HtmlComponent } from '@gitroom/frontend/components/layout/html.component';
|
||||
|
||||
const chakra = Chakra_Petch({ weight: '400', subsets: ['latin'] });
|
||||
|
||||
const chakra = Chakra_Petch({
|
||||
weight: '400',
|
||||
subsets: ['latin'],
|
||||
});
|
||||
export default async function AppLayout({ children }: { children: ReactNode }) {
|
||||
const allHeaders = headers();
|
||||
const Plausible = !!process.env.STRIPE_PUBLISHABLE_KEY
|
||||
? PlausibleProvider
|
||||
: Fragment;
|
||||
|
||||
return (
|
||||
<html className={interClass}>
|
||||
<HtmlComponent className={interClass}>
|
||||
<head>
|
||||
<link rel="icon" href="/favicon.ico" sizes="any" />
|
||||
</head>
|
||||
|
|
@ -49,6 +53,7 @@ export default async function AppLayout({ children }: { children: ReactNode }) {
|
|||
neynarClientId={process.env.NEYNAR_CLIENT_ID!}
|
||||
isSecured={!process.env.NOT_SECURED}
|
||||
disableImageCompression={!!process.env.DISABLE_IMAGE_COMPRESSION}
|
||||
language={allHeaders.get(headerName)}
|
||||
>
|
||||
<ToltScript />
|
||||
<FacebookComponent />
|
||||
|
|
@ -67,6 +72,6 @@ export default async function AppLayout({ children }: { children: ReactNode }) {
|
|||
</Plausible>
|
||||
</VariableContextComponent>
|
||||
</body>
|
||||
</html>
|
||||
</HtmlComponent>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ export const dynamic = 'force-dynamic';
|
|||
import '../global.scss';
|
||||
import 'react-tooltip/dist/react-tooltip.css';
|
||||
import '@copilotkit/react-ui/styles.css';
|
||||
|
||||
import LayoutContext from '@gitroom/frontend/components/layout/layout.context';
|
||||
import { ReactNode } from 'react';
|
||||
import { Chakra_Petch } from 'next/font/google';
|
||||
|
|
@ -12,9 +11,10 @@ import clsx from 'clsx';
|
|||
import { VariableContextComponent } from '@gitroom/react/helpers/variable.context';
|
||||
import { Fragment } from 'react';
|
||||
import UtmSaver from '@gitroom/helpers/utils/utm.saver';
|
||||
|
||||
const chakra = Chakra_Petch({ weight: '400', subsets: ['latin'] });
|
||||
|
||||
const chakra = Chakra_Petch({
|
||||
weight: '400',
|
||||
subsets: ['latin'],
|
||||
});
|
||||
export default async function AppLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<html className={interClass}>
|
||||
|
|
@ -23,6 +23,7 @@ export default async function AppLayout({ children }: { children: ReactNode }) {
|
|||
</head>
|
||||
<body className={clsx(chakra.className, 'dark text-primary !bg-primary')}>
|
||||
<VariableContextComponent
|
||||
language="en"
|
||||
storageProvider={
|
||||
process.env.STORAGE_PROVIDER! as 'local' | 'cloudflare'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import { StandaloneModal } from '@gitroom/frontend/components/standalone-modal/standalone.modal';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
export default async function Modal() {
|
||||
return (
|
||||
<div className="text-textColor">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { ReactNode } from 'react';
|
||||
import { AppLayout } from '@gitroom/frontend/components/launches/layout.standalone';
|
||||
|
||||
export default async function AppLayoutIn({
|
||||
children,
|
||||
}: {
|
||||
|
|
|
|||
|
|
@ -99,10 +99,10 @@
|
|||
--color-custom17: #000;
|
||||
--color-custom18: #000;
|
||||
--color-custom19: #f97066;
|
||||
--color-custom20: #F5F5F5;
|
||||
--color-custom20: #f5f5f5;
|
||||
--color-custom21: #506490;
|
||||
--color-custom22: #b91c1c;
|
||||
--color-custom23: #F5F5F5;
|
||||
--color-custom23: #f5f5f5;
|
||||
--color-custom24: #eaff00;
|
||||
--color-custom25: #2e3336;
|
||||
--color-custom26: #1d9bf0;
|
||||
|
|
@ -110,7 +110,7 @@
|
|||
--color-custom28: #b69dec;
|
||||
--color-custom29: #291259;
|
||||
--color-custom30: #efefef;
|
||||
--color-custom31: #E0E0E0;
|
||||
--color-custom31: #e0e0e0;
|
||||
--color-custom32: #181818;
|
||||
--color-custom33: #f2f2f2;
|
||||
--color-custom34: #334155;
|
||||
|
|
|
|||
|
|
@ -465,4 +465,16 @@ div div .set-font-family {
|
|||
|
||||
.hideCopilot .copilotKitPopup {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
html[dir='rtl'] .rbox {
|
||||
direction: rtl !important;
|
||||
}
|
||||
|
||||
html[dir='rtl'] .lbox {
|
||||
direction: ltr !important;
|
||||
}
|
||||
|
||||
html[dir='rtl'] [dir='ltr'] {
|
||||
direction: rtl !important;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -6,14 +6,11 @@ import { StarsTableComponent } from '@gitroom/frontend/components/analytics/star
|
|||
import useSWR from 'swr';
|
||||
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
|
||||
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
|
||||
|
||||
export const AnalyticsComponent: FC = () => {
|
||||
const fetch = useFetch();
|
||||
|
||||
const load = useCallback(async (path: string) => {
|
||||
return await (await fetch(path)).json();
|
||||
}, []);
|
||||
|
||||
const { isLoading: isLoadingAnalytics, data: analytics } = useSWR(
|
||||
'/analytics',
|
||||
load
|
||||
|
|
@ -22,11 +19,9 @@ export const AnalyticsComponent: FC = () => {
|
|||
'/analytics/trending',
|
||||
load
|
||||
);
|
||||
|
||||
if (isLoadingAnalytics || isLoadingTrending) {
|
||||
return <LoadingComponent />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-[24px] flex-1">
|
||||
<div className="flex flex-col gap-[24px] flex-1">
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
'use client';
|
||||
|
||||
import { FC, useEffect, useMemo, useRef } from 'react';
|
||||
import DrawChart from 'chart.js/auto';
|
||||
import { TotalList } from '@gitroom/frontend/components/analytics/stars.and.forks.interface';
|
||||
import { chunk } from 'lodash';
|
||||
|
||||
function mergeDataPoints(data: TotalList[], numPoints: number): TotalList[] {
|
||||
const res = chunk(data, Math.ceil(data.length / numPoints));
|
||||
return res.map((row) => {
|
||||
|
|
@ -13,13 +13,13 @@ function mergeDataPoints(data: TotalList[], numPoints: number): TotalList[] {
|
|||
};
|
||||
});
|
||||
}
|
||||
|
||||
export const ChartSocial: FC<{ data: TotalList[] }> = (props) => {
|
||||
export const ChartSocial: FC<{
|
||||
data: TotalList[];
|
||||
}> = (props) => {
|
||||
const { data } = props;
|
||||
const list = useMemo(() => {
|
||||
return mergeDataPoints(data, 7);
|
||||
}, [data]);
|
||||
|
||||
const ref = useRef<any>(null);
|
||||
const chart = useRef<null | DrawChart>(null);
|
||||
useEffect(() => {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue