Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ REMNAWAVE_PANEL_URL=https://remnawave:3000
### Replace with own API Token. Create in Remnawave Dashboard → Remnawave Settings → API Tokens section
REMNAWAVE_API_TOKEN=

META_TITLE="Subscription page"
META_DESCRIPTION="Subscription page description"


# Serve at custom root path, for example, this value can be: CUSTOM_SUB_PREFIX=sub
# Do not place / at the start/end
CUSTOM_SUB_PREFIX=
Expand Down
20 changes: 10 additions & 10 deletions backend/package-lock.json

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

6 changes: 3 additions & 3 deletions backend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@remnawave/subscription-page",
"version": "7.0.3",
"version": "7.0.4",
"description": "Remnawave Subscription Page",
"private": false,
"type": "commonjs",
Expand Down Expand Up @@ -36,8 +36,8 @@
"@nestjs/jwt": "^11.0.2",
"@nestjs/platform-express": "11.1.9",
"@nestjs/serve-static": "5.0.4",
"@remnawave/backend-contract": "2.4.0",
"@remnawave/subscription-page-types": "0.2.7",
"@remnawave/backend-contract": "2.4.1",
"@remnawave/subscription-page-types": "0.3.3",
"axios": "^1.13.2",
"class-transformer": "^0.5.1",
"compression": "^1.8.1",
Expand Down
17 changes: 3 additions & 14 deletions backend/src/common/config/app-config/config.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,18 @@ export const configSchema = z
.min(1, REQUIRED_REMNAWAVE_API_TOKEN_MESSAGE),

SUBPAGE_CONFIG_UUID: z.string().default('00000000-0000-0000-0000-000000000000'),

MARZBAN_LEGACY_LINK_ENABLED: z
.string()
.default('false')
.transform((val) => val === 'true'),
MARZBAN_LEGACY_SECRET_KEY: z.optional(z.string()),

MARZBAN_LEGACY_SUBSCRIPTION_VALID_FROM: z.optional(z.string()),

CUSTOM_SUB_PREFIX: z.optional(z.string()),

CADDY_AUTH_API_TOKEN: z.optional(z.string()),

META_TITLE: z.string(),
META_DESCRIPTION: z.string(),

CLOUDFLARE_ZERO_TRUST_CLIENT_ID: z.optional(z.string()),
CLOUDFLARE_ZERO_TRUST_CLIENT_SECRET: z.optional(z.string()),

SUBSCRIPTION_UI_DISPLAY_RAW_KEYS: z
MARZBAN_LEGACY_LINK_ENABLED: z
.string()
.default('false')
.transform((val) => val === 'true'),
MARZBAN_LEGACY_SECRET_KEY: z.optional(z.string()),
MARZBAN_LEGACY_SUBSCRIPTION_VALID_FROM: z.optional(z.string()),
})
.superRefine((data, ctx) => {
if (
Expand Down
2 changes: 1 addition & 1 deletion backend/src/modules/root/root.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import {
REQUEST_TEMPLATE_TYPE_VALUES,
TRequestTemplateTypeKeys,
} from '@remnawave/backend-contract';
import { APP_CONFIG_ROUTE_WO_LEADING_PATH } from '@remnawave/subscription-page-types';

import { GetJWTPayload } from '@common/decorators/get-jwt-payload';
import { ClientIp } from '@common/decorators/get-ip';
import { IJwtPayload } from '@common/constants';

import { SubpageConfigService } from './subpage-config.service';
import { RootService } from './root.service';
import { APP_CONFIG_ROUTE_WO_LEADING_PATH } from '@remnawave/subscription-page-types';

@Controller()
export class RootController {
Expand Down
18 changes: 7 additions & 11 deletions backend/src/modules/root/root.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export class RootService {

private readonly isMarzbanLegacyLinkEnabled: boolean;
private readonly marzbanSecretKey?: string;
private readonly subscriptionUiDisplayRawKeys: boolean;

constructor(
private readonly configService: ConfigService,
Expand All @@ -34,9 +33,6 @@ export class RootService {
'MARZBAN_LEGACY_LINK_ENABLED',
);
this.marzbanSecretKey = this.configService.get<string>('MARZBAN_LEGACY_SECRET_KEY');
this.subscriptionUiDisplayRawKeys = this.configService.getOrThrow<boolean>(
'SUBSCRIPTION_UI_DISPLAY_RAW_KEYS',
);
}

public async serveSubscriptionPage(
Expand Down Expand Up @@ -203,9 +199,13 @@ export class RootService {
return;
}

const baseSettings = this.subpageConfigService.getBaseSettings(
subpageConfig.subpageConfigUuid,
);

const subscriptionData = subscriptionDataResponse.response;

if (!this.subscriptionUiDisplayRawKeys) {
if (!baseSettings.showConnectionKeys) {
subscriptionData.response.links = [];
subscriptionData.response.ssConfLinks = {};
}
Expand All @@ -217,12 +217,8 @@ export class RootService {
});

res.render('index', {
metaTitle: this.configService
.getOrThrow<string>('META_TITLE')
.replace(/^"|"$/g, ''),
metaDescription: this.configService
.getOrThrow<string>('META_DESCRIPTION')
.replace(/^"|"$/g, ''),
metaTitle: baseSettings.metaTitle,
metaDescription: baseSettings.metaDescription,
panelData: Buffer.from(JSON.stringify(subscriptionData)).toString('base64'),
});
} catch (error) {
Expand Down
25 changes: 24 additions & 1 deletion backend/src/modules/root/subpage-config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Logger } from '@nestjs/common';
import {
SubscriptionPageRawConfigSchema,
TSubscriptionPageRawConfig,
SUBPAGE_DEFAULT_CONFIG_UUID,
} from '@remnawave/subscription-page-types';

import { decryptUuid, encryptUuid } from '@common/utils/crypt-utils';
Expand Down Expand Up @@ -107,7 +108,7 @@ export class SubpageConfigService implements OnApplicationBootstrap {
public getEncryptedSubpageConfigUuid(subpageConfigUuidFromRemnawave: string | null): string {
let uuidToEncrypt: string;

const isDefaultUuid = this.subpageConfigUuid === '00000000-0000-0000-0000-000000000000';
const isDefaultUuid = this.subpageConfigUuid === SUBPAGE_DEFAULT_CONFIG_UUID;

if (isDefaultUuid && subpageConfigUuidFromRemnawave) {
uuidToEncrypt = subpageConfigUuidFromRemnawave;
Expand All @@ -117,4 +118,26 @@ export class SubpageConfigService implements OnApplicationBootstrap {

return encryptUuid(uuidToEncrypt, this.internalJwtSecret);
}

public getBaseSettings(
subpageConfigUuid: string | null,
): TSubscriptionPageRawConfig['baseSettings'] {
const subpageConfig = this.subpageConfigMap.get(
subpageConfigUuid || SUBPAGE_DEFAULT_CONFIG_UUID,
);

if (!subpageConfig) {
return {
metaTitle: 'Subscription Page',
metaDescription: 'Subscription Page',
showConnectionKeys: false,
};
}

return {
metaTitle: subpageConfig.baseSettings.metaTitle,
metaDescription: subpageConfig.baseSettings.metaDescription,
showConnectionKeys: subpageConfig.baseSettings.showConnectionKeys,
};
}
}
20 changes: 10 additions & 10 deletions frontend/package-lock.json

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

6 changes: 3 additions & 3 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@remnawave/subscription-page",
"private": false,
"type": "module",
"version": "7.0.3",
"version": "7.0.4",
"license": "AGPL-3.0-only",
"author": "REMNAWAVE <github.com/remnawave>",
"homepage": "https://github.com/remnawave",
Expand Down Expand Up @@ -34,8 +34,8 @@
"@mantine/modals": "8.3.10",
"@mantine/notifications": "8.3.10",
"@mantine/nprogress": "8.3.10",
"@remnawave/backend-contract": "2.4.0",
"@remnawave/subscription-page-types": "0.2.7",
"@remnawave/backend-contract": "2.4.1",
"@remnawave/subscription-page-types": "0.3.3",
"@tabler/icons-react": "^3.35.0",
"clsx": "^2.1.1",
"color-hash": "^2.0.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
TSubscriptionPagePlatformKey
} from '@remnawave/subscription-page-types'
import { Box, Button, ButtonVariant, Card, Group, NativeSelect, Stack, Title } from '@mantine/core'
import { notifications } from '@mantine/notifications'
import { IconStar } from '@tabler/icons-react'
import { useClipboard } from '@mantine/hooks'
import { useState } from 'react'

import { constructSubscriptionUrl } from '@shared/utils/construct-subscription-url'
Expand Down Expand Up @@ -33,6 +35,7 @@ export const InstallationGuideConnector = (props: IProps) => {
const { t, currentLang, baseTranslations } = useTranslation()

const { platforms, svgLibrary } = useAppConfig()
const { copy } = useClipboard({ timeout: 2_000 })
const subscription = useSubscription()

const [selectedAppIndex, setSelectedAppIndex] = useState(0)
Expand Down Expand Up @@ -69,14 +72,39 @@ export const InstallationGuideConnector = (props: IProps) => {
)

const handleButtonClick = (button: TSubscriptionPageButtonConfig) => {
if (button.type === 'subscriptionLink') {
const formattedUrl = TemplateEngine.formatWithMetaInfo(button.link, {
let formattedUrl: string | undefined

if (button.type === 'subscriptionLink' || button.type === 'copyButton') {
formattedUrl = TemplateEngine.formatWithMetaInfo(button.link, {
username: subscription.user.username,
subscriptionUrl
})
window.open(formattedUrl, '_blank')
} else if (button.type === 'external') {
window.open(button.link, '_blank')
}

switch (button.type) {
case 'copyButton': {
if (!formattedUrl) return

copy(formattedUrl)
notifications.show({
title: t(baseTranslations.linkCopied),
message: t(baseTranslations.linkCopiedToClipboard),
color: 'cyan'
})
break
}
case 'external': {
window.open(button.link, '_blank')
break
}
case 'subscriptionLink': {
if (!formattedUrl) return

window.open(formattedUrl, '_blank')
break
}
default:
break
}
}

Expand Down