mirror of
https://github.com/remnawave/panel.git
synced 2026-04-20 22:51:56 +00:00
-
This commit is contained in:
96
.github/workflows/build-docs.yml
vendored
Normal file
96
.github/workflows/build-docs.yml
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
name: Build docs and landing
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build-landing:
|
||||
name: Build landing
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout landing repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: remnawave/landing
|
||||
token: ${{ secrets.LANDING_REPO_TOKEN }}
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22.18.0'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build landing
|
||||
run: npm run start:build
|
||||
|
||||
- name: Upload landing artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: landing-build
|
||||
path: landing/
|
||||
|
||||
build-docs:
|
||||
name: Build documentation
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout docs repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22.18.0'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build documentation
|
||||
run: npm run build
|
||||
|
||||
- name: Upload docs artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: docs-build
|
||||
path: build/
|
||||
|
||||
build-docker:
|
||||
name: Build and push Docker image
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [build-landing, build-docs]
|
||||
steps:
|
||||
- name: Checkout docs repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download landing artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: landing-build
|
||||
path: landing/
|
||||
|
||||
- name: Download docs artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: docs-build
|
||||
path: build/
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: |
|
||||
remnawave/internal-docs:latest
|
||||
platforms: linux/amd64
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -160,4 +160,5 @@ yarn-error.log*
|
||||
|
||||
.wip/
|
||||
|
||||
samples/
|
||||
samples/
|
||||
docker-compose.local.yml
|
||||
47
Caddyfile
47
Caddyfile
@@ -29,22 +29,47 @@
|
||||
redir /blog/learn /docs/learn/quick-start permanent
|
||||
redir /blog/learn/quick-start /docs/learn/quick-start permanent
|
||||
redir /docs/install/reverse-proxies/ /docs/install/reverse-proxies/ permanent
|
||||
redir /docs/ /docs/overview/introduction permanent
|
||||
redir /apps /docs/clients permanent
|
||||
redir /client /docs/clients permanent
|
||||
redir /donate /docs/donate permanent
|
||||
redir /blog/learn /docs/learn/quick-start permanent
|
||||
redir /blog/learn/quick-start /docs/learn/quick-start permanent
|
||||
|
||||
root * /app/build
|
||||
encode gzip
|
||||
file_server
|
||||
try_files {path} /index.html
|
||||
handle / {
|
||||
root * /app/landing
|
||||
encode gzip
|
||||
file_server
|
||||
try_files {path} /index.html
|
||||
|
||||
header {
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "DENY"
|
||||
X-XSS-Protection "1; mode=block"
|
||||
header {
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "DENY"
|
||||
X-XSS-Protection "1; mode=block"
|
||||
}
|
||||
}
|
||||
|
||||
handle_path /landing-assets/* {
|
||||
root * /app/landing/landing-assets
|
||||
encode gzip
|
||||
file_server
|
||||
try_files {path} /index.html
|
||||
|
||||
header {
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "DENY"
|
||||
X-XSS-Protection "1; mode=block"
|
||||
}
|
||||
}
|
||||
|
||||
handle {
|
||||
root * /app/docs
|
||||
encode gzip
|
||||
file_server
|
||||
try_files {path} /index.html
|
||||
|
||||
header {
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "DENY"
|
||||
X-XSS-Protection "1; mode=block"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
57
Dockerfile
57
Dockerfile
@@ -1,67 +1,14 @@
|
||||
|
||||
# # docker build -t docusaurus-starter .
|
||||
# # docker run -d -p 80:80 --name docusaurus-starter-app docusaurus-starter
|
||||
|
||||
# ARG NODE_VERSION=22.17.0
|
||||
# ARG NGINX_VERSION=1.27.3
|
||||
# ARG APP_PORT=80
|
||||
# ARG IMAGE_NAME=remnawave-docs
|
||||
|
||||
# FROM node:${NODE_VERSION}-alpine as builder
|
||||
|
||||
# WORKDIR /usr/src/app
|
||||
|
||||
# COPY .npmrc ./
|
||||
# COPY package*.json ./
|
||||
|
||||
# RUN npm install
|
||||
|
||||
# COPY . .
|
||||
|
||||
# RUN npm run build
|
||||
|
||||
# # ---
|
||||
|
||||
# FROM nginx:${NGINX_VERSION}-alpine
|
||||
|
||||
# LABEL name=${IMAGE_NAME}
|
||||
|
||||
# WORKDIR /usr/src/app
|
||||
|
||||
# #COPY --from=builder /usr/src/app/nginx.conf /usr/share/nginx/
|
||||
# COPY --from=builder /usr/src/app/build /usr/share/nginx/html/
|
||||
|
||||
# EXPOSE ${APP_PORT}
|
||||
|
||||
# CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
|
||||
ARG NODE_VERSION=22.17.0
|
||||
ARG CADDY_VERSION=2.10
|
||||
ARG IMAGE_NAME=remnawave-docs
|
||||
|
||||
FROM node:${NODE_VERSION}-alpine as builder
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY .npmrc ./
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm ci
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm run build
|
||||
|
||||
# ---
|
||||
|
||||
FROM caddy:${CADDY_VERSION}-alpine
|
||||
|
||||
LABEL name=${IMAGE_NAME}
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=builder /usr/src/app/build /app/build
|
||||
COPY build /app/docs
|
||||
COPY landing /app/landing
|
||||
|
||||
COPY Caddyfile /etc/caddy/Caddyfile
|
||||
|
||||
|
||||
@@ -176,7 +176,8 @@ const config: Config = {
|
||||
title: 'Remnawave',
|
||||
logo: {
|
||||
alt: 'Remnawave Logo',
|
||||
src: 'img/logo.svg'
|
||||
src: 'img/logo.svg',
|
||||
href: 'https://docs.rw'
|
||||
},
|
||||
items: [
|
||||
{
|
||||
|
||||
343
package-lock.json
generated
343
package-lock.json
generated
@@ -12,13 +12,10 @@
|
||||
"@docusaurus/plugin-client-redirects": "^3.9.2",
|
||||
"@docusaurus/preset-classic": "^3.9.2",
|
||||
"@docusaurus/theme-mermaid": "^3.9.2",
|
||||
"@mantine/core": "^8.3.6",
|
||||
"@mantine/hooks": "^8.3.6",
|
||||
"@mdx-js/react": "^3.1.1",
|
||||
"@scalar/docusaurus": "^0.7.21",
|
||||
"@tabler/icons-react": "^3.35.0",
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^12.23.24",
|
||||
"prism-react-renderer": "^2.4.1",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
@@ -4383,59 +4380,6 @@
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
|
||||
"integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
|
||||
"integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.7.3",
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/react": {
|
||||
"version": "0.27.16",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.16.tgz",
|
||||
"integrity": "sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/react-dom": "^2.1.6",
|
||||
"@floating-ui/utils": "^0.2.10",
|
||||
"tabbable": "^6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17.0.0",
|
||||
"react-dom": ">=17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/react-dom": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz",
|
||||
"integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.7.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
|
||||
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@hapi/hoek": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
||||
@@ -4776,34 +4720,6 @@
|
||||
"integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@mantine/core": {
|
||||
"version": "8.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@mantine/core/-/core-8.3.6.tgz",
|
||||
"integrity": "sha512-paTl+0x+O/QtgMtqVJaG8maD8sfiOdgPmLOyG485FmeGZ1L3KMdEkhxZtmdGlDFsLXhmMGQ57ducT90bvhXX5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "^0.27.16",
|
||||
"clsx": "^2.1.1",
|
||||
"react-number-format": "^5.4.4",
|
||||
"react-remove-scroll": "^2.7.1",
|
||||
"react-textarea-autosize": "8.5.9",
|
||||
"type-fest": "^4.41.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@mantine/hooks": "8.3.6",
|
||||
"react": "^18.x || ^19.x",
|
||||
"react-dom": "^18.x || ^19.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@mantine/hooks": {
|
||||
"version": "8.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-8.3.6.tgz",
|
||||
"integrity": "sha512-liHfaWXHAkLjJy+Bkr29UsCwAoDQ/a64WrM67lksx8F0qqyjR5RQH8zVlhuOjdpQnwtlUkE/YiTvbJiPcoI0bw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^18.x || ^19.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@mdx-js/mdx": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz",
|
||||
@@ -9713,12 +9629,6 @@
|
||||
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-node-es": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-port": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz",
|
||||
@@ -11423,33 +11333,6 @@
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "12.23.24",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz",
|
||||
"integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-dom": "^12.23.23",
|
||||
"motion-utils": "^12.23.6",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
@@ -11570,15 +11453,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-nonce": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
|
||||
"integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/get-own-enumerable-property-symbols": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
|
||||
@@ -16249,21 +16123,6 @@
|
||||
"pathe": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "12.23.23",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz",
|
||||
"integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-utils": "^12.23.6"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "12.23.6",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
|
||||
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mrmime": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
||||
@@ -19190,63 +19049,6 @@
|
||||
"webpack": ">=4.41.1 || 5.x"
|
||||
}
|
||||
},
|
||||
"node_modules/react-number-format": {
|
||||
"version": "5.4.4",
|
||||
"resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.4.4.tgz",
|
||||
"integrity": "sha512-wOmoNZoOpvMminhifQYiYSTCLUDOiUbBunrMrMjA+dV52sY+vck1S4UhR6PkgnoCquvvMSeJjErXZ4qSaWCliA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-remove-scroll": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz",
|
||||
"integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-remove-scroll-bar": "^2.3.7",
|
||||
"react-style-singleton": "^2.2.3",
|
||||
"tslib": "^2.1.0",
|
||||
"use-callback-ref": "^1.3.3",
|
||||
"use-sidecar": "^1.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-remove-scroll-bar": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
|
||||
"integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-style-singleton": "^2.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "5.3.4",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz",
|
||||
@@ -19298,45 +19100,6 @@
|
||||
"react": ">=15"
|
||||
}
|
||||
},
|
||||
"node_modules/react-style-singleton": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
|
||||
"integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"get-nonce": "^1.0.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-textarea-autosize": {
|
||||
"version": "8.5.9",
|
||||
"resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz",
|
||||
"integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"use-composed-ref": "^1.3.0",
|
||||
"use-latest": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
@@ -21140,12 +20903,6 @@
|
||||
"react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tabbable": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.3.0.tgz",
|
||||
"integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tagged-tag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz",
|
||||
@@ -21485,18 +21242,6 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
|
||||
"integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
|
||||
"license": "(MIT OR CC0-1.0)",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
@@ -22102,94 +21847,6 @@
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/use-callback-ref": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
|
||||
"integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-composed-ref": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz",
|
||||
"integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-isomorphic-layout-effect": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz",
|
||||
"integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-latest": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz",
|
||||
"integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"use-isomorphic-layout-effect": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-sidecar": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
|
||||
"integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-node-es": "^1.1.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
|
||||
|
||||
@@ -19,13 +19,10 @@
|
||||
"@docusaurus/plugin-client-redirects": "^3.9.2",
|
||||
"@docusaurus/preset-classic": "^3.9.2",
|
||||
"@docusaurus/theme-mermaid": "^3.9.2",
|
||||
"@mantine/core": "^8.3.6",
|
||||
"@mantine/hooks": "^8.3.6",
|
||||
"@mdx-js/react": "^3.1.1",
|
||||
"@scalar/docusaurus": "^0.7.21",
|
||||
"@tabler/icons-react": "^3.35.0",
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^12.23.24",
|
||||
"prism-react-renderer": "^2.4.1",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
export const AnimatedBackground = () => {
|
||||
return (
|
||||
<div className="animated-background">
|
||||
<div className="blob-1" />
|
||||
<div className="blob-2" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export { AnimatedBackground } from './AnimatedBackground'
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
import { Card, Group, Stack, Text, ThemeIcon, Title } from '@mantine/core'
|
||||
import { motion } from 'framer-motion'
|
||||
import React from 'react'
|
||||
|
||||
interface FeatureCardProps {
|
||||
color?: string
|
||||
description: string
|
||||
icon: React.ReactNode
|
||||
title: string
|
||||
}
|
||||
|
||||
const cardVariants = {
|
||||
hidden: { opacity: 0, y: 30 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
type: 'spring' as const,
|
||||
stiffness: 80,
|
||||
damping: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const FeatureCard = ({ title, description, icon, color = 'cyan' }: FeatureCardProps) => {
|
||||
return (
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
style={{ height: '100%' }}
|
||||
variants={cardVariants}
|
||||
viewport={{ once: true }}
|
||||
whileHover={{
|
||||
scale: 1.03,
|
||||
y: -4,
|
||||
transition: { duration: 0.2 }
|
||||
}}
|
||||
whileInView="visible"
|
||||
>
|
||||
<Card
|
||||
p={{ base: 'md', sm: 'lg', md: 'xl' }}
|
||||
radius="lg"
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.02)',
|
||||
border: '1px solid rgba(255, 255, 255, 0.08)',
|
||||
backdropFilter: 'blur(10px)',
|
||||
height: '100%',
|
||||
transition: 'all 0.3s ease',
|
||||
position: 'relative',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
<Stack gap="md">
|
||||
<Group gap="md" wrap="nowrap">
|
||||
<ThemeIcon color={color} radius="md" size="xl" variant="outline">
|
||||
{icon}
|
||||
</ThemeIcon>
|
||||
</Group>
|
||||
<Title
|
||||
c="white"
|
||||
order={3}
|
||||
style={{
|
||||
fontSize: 'clamp(1.1rem, 3vw, 1.5rem)'
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Title>
|
||||
<Text c="dimmed" size="sm" style={{ lineHeight: 1.6 }}>
|
||||
{description}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Card>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export { FeatureCard } from './FeatureCard'
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
import { Box, Card, Stack, Text, ThemeIcon } from '@mantine/core'
|
||||
import { motion } from 'framer-motion'
|
||||
import React from 'react'
|
||||
|
||||
interface StatCardProps {
|
||||
color: 'blue' | 'cyan' | 'green' | 'violet'
|
||||
icon: React.ReactNode
|
||||
label: string
|
||||
value: number | string
|
||||
}
|
||||
|
||||
const gradients = {
|
||||
violet: {
|
||||
background:
|
||||
'linear-gradient(135deg, rgba(151, 117, 250, 0.1) 0%, rgba(132, 94, 247, 0.05) 100%)',
|
||||
border: 'rgba(151, 117, 250, 0.2)',
|
||||
radial: 'radial-gradient(circle, rgba(151, 117, 250, 0.15) 0%, transparent 70%)',
|
||||
text: 'linear-gradient(135deg, #9775fa 0%, #845ef7 100%)'
|
||||
},
|
||||
cyan: {
|
||||
background:
|
||||
'linear-gradient(135deg, rgba(34, 211, 238, 0.1) 0%, rgba(6, 182, 212, 0.05) 100%)',
|
||||
border: 'rgba(34, 211, 238, 0.2)',
|
||||
radial: 'radial-gradient(circle, rgba(34, 211, 238, 0.15) 0%, transparent 70%)',
|
||||
text: 'linear-gradient(135deg, #22d3ee 0%, #06b6d4 100%)'
|
||||
},
|
||||
blue: {
|
||||
background:
|
||||
'linear-gradient(135deg, rgba(34, 139, 230, 0.1) 0%, rgba(28, 126, 214, 0.05) 100%)',
|
||||
border: 'rgba(34, 139, 230, 0.2)',
|
||||
radial: 'radial-gradient(circle, rgba(34, 139, 230, 0.15) 0%, transparent 70%)',
|
||||
text: 'linear-gradient(135deg, #228be6 0%, #1c7ed6 100%)'
|
||||
},
|
||||
green: {
|
||||
background:
|
||||
'linear-gradient(135deg, rgba(64, 192, 87, 0.1) 0%, rgba(55, 178, 77, 0.05) 100%)',
|
||||
border: 'rgba(64, 192, 87, 0.2)',
|
||||
radial: 'radial-gradient(circle, rgba(64, 192, 87, 0.15) 0%, transparent 70%)',
|
||||
text: 'linear-gradient(135deg, #40c057 0%, #37b24d 100%)'
|
||||
}
|
||||
}
|
||||
|
||||
export const StatCard = ({ label, value, icon, color }: StatCardProps) => {
|
||||
const gradient = gradients[color]
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
transition={{
|
||||
type: 'spring' as const,
|
||||
stiffness: 300,
|
||||
damping: 25
|
||||
}}
|
||||
viewport={{ once: true }}
|
||||
whileHover={{
|
||||
scale: 1.03,
|
||||
transition: { duration: 0.2 }
|
||||
}}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
>
|
||||
<Card
|
||||
p={{ base: 'lg', sm: 'xl' }}
|
||||
radius="xl"
|
||||
style={{
|
||||
background: gradient.background,
|
||||
border: '1px solid',
|
||||
borderColor: gradient.border,
|
||||
backdropFilter: 'blur(10px)',
|
||||
position: 'relative',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '-50%',
|
||||
right: '-50%',
|
||||
width: '200%',
|
||||
height: '200%',
|
||||
background: gradient.radial,
|
||||
pointerEvents: 'none'
|
||||
}}
|
||||
/>
|
||||
<Stack align="center" gap="sm" style={{ position: 'relative', zIndex: 1 }}>
|
||||
<ThemeIcon color={color} radius="xl" size={72} variant="outline">
|
||||
{icon}
|
||||
</ThemeIcon>
|
||||
|
||||
<Text
|
||||
fw={700}
|
||||
style={{
|
||||
fontSize: 'clamp(2rem, 6vw, 3.5rem)',
|
||||
background: gradient.text,
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
backgroundClip: 'text'
|
||||
}}
|
||||
ta="center"
|
||||
>
|
||||
{value}
|
||||
</Text>
|
||||
|
||||
<Text c="dimmed" fw={600} size="md" ta="center">
|
||||
{label}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Card>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export { StatCard } from './StatCard'
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* work well for content-centric websites.
|
||||
*/
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Fira+Mono:wght@400;500;700&family=Montserrat:ital,wght@0,100..900;1,100..900&family=Unbounded:wght@200..900&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Fira+Mono:wght@400;500;700&family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
|
||||
|
||||
body {
|
||||
font-family: 'Montserrat', sans-serif !important;
|
||||
|
||||
@@ -1,485 +0,0 @@
|
||||
import {
|
||||
IconBox,
|
||||
IconBrandGithub,
|
||||
IconBrandTelegram,
|
||||
IconCloudDataConnection,
|
||||
IconDevices,
|
||||
IconHeart,
|
||||
IconRocket,
|
||||
IconServer,
|
||||
IconShieldCheck,
|
||||
IconUsers
|
||||
} from '@tabler/icons-react'
|
||||
import '@mantine/core/styles.css'
|
||||
import {
|
||||
Anchor,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
Container,
|
||||
Divider,
|
||||
Grid,
|
||||
Group,
|
||||
Image,
|
||||
MantineProvider,
|
||||
Stack,
|
||||
Text,
|
||||
ThemeIcon,
|
||||
Title
|
||||
} from '@mantine/core'
|
||||
import { TbArrowRightDashed, TbCloud, TbHeartFilled } from 'react-icons/tb'
|
||||
import { BiLogoTelegram } from 'react-icons/bi'
|
||||
import { SiNestjs } from 'react-icons/si'
|
||||
import { motion } from 'framer-motion'
|
||||
import Link from '@docusaurus/Link'
|
||||
import React, { JSX } from 'react'
|
||||
import Layout from '@theme/Layout'
|
||||
|
||||
import { AnimatedBackground } from '../components/AnimatedBackground'
|
||||
import { FeatureCard } from '../components/FeatureCard'
|
||||
import { theme } from '../theme/mantine-theme/theme'
|
||||
import { StatCard } from '../components/StatCard'
|
||||
|
||||
const containerVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.1,
|
||||
delayChildren: 0.2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const itemVariants = {
|
||||
hidden: { opacity: 0, y: 30 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
type: 'spring' as const,
|
||||
stiffness: 80,
|
||||
damping: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function HomePage() {
|
||||
return (
|
||||
<div className="landing-page-wrapper">
|
||||
<AnimatedBackground />
|
||||
<Container
|
||||
maw={1400}
|
||||
px={{ base: 'md', sm: 'lg', md: 'xl' }}
|
||||
py={{ base: 'xl', sm: '3rem', md: '4rem' }}
|
||||
style={{ position: 'relative', zIndex: 1 }}
|
||||
>
|
||||
<motion.div
|
||||
animate="visible"
|
||||
initial="hidden"
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 'var(--section-gap, 4rem)'
|
||||
}}
|
||||
variants={containerVariants}
|
||||
>
|
||||
<motion.div variants={itemVariants}>
|
||||
<Stack align="center" gap="xl">
|
||||
<Box ta="center">
|
||||
<Title
|
||||
c="white"
|
||||
ff="Unbounded"
|
||||
mb="md"
|
||||
order={1}
|
||||
style={{
|
||||
fontSize: 'clamp(2rem, 8vw, 3.5rem)',
|
||||
lineHeight: 1.2
|
||||
}}
|
||||
ta="center"
|
||||
>
|
||||
<Text c="cyan" component="span" fw="inherit" fz="inherit">
|
||||
Remna
|
||||
</Text>
|
||||
<Text c="white" component="span" fw="inherit" fz="inherit">
|
||||
wave
|
||||
</Text>
|
||||
</Title>
|
||||
|
||||
<Title
|
||||
c="white"
|
||||
ff="Unbounded"
|
||||
mb="md"
|
||||
order={2}
|
||||
style={{
|
||||
fontSize: 'clamp(1.5rem, 5vw, 2.5rem)',
|
||||
lineHeight: 1.3
|
||||
}}
|
||||
ta="center"
|
||||
>
|
||||
<Text
|
||||
component="span"
|
||||
fw="inherit"
|
||||
fz="inherit"
|
||||
style={{
|
||||
background:
|
||||
'linear-gradient(135deg, #22d3ee 0%, #06b6d4 100%)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
backgroundClip: 'text'
|
||||
}}
|
||||
>
|
||||
Proxy and user management
|
||||
</Text>{' '}
|
||||
<Text component="span" fw="inherit" fz="inherit">
|
||||
solution
|
||||
</Text>
|
||||
</Title>
|
||||
|
||||
<Text
|
||||
c="dimmed"
|
||||
ff="Unbounded"
|
||||
maw={800}
|
||||
mx="auto"
|
||||
px={{ base: 'sm', sm: 'sm' }}
|
||||
style={{
|
||||
lineHeight: 1.7,
|
||||
fontSize: 'clamp(0.95rem, 2.5vw, 1.25rem)'
|
||||
}}
|
||||
ta="center"
|
||||
>
|
||||
Built on top of{' '}
|
||||
<Text
|
||||
c="cyan"
|
||||
component="a"
|
||||
fw={600}
|
||||
fz="inherit"
|
||||
href="https://github.com/XTLS/Xray-core"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Xray Core
|
||||
</Text>
|
||||
, Remnawave provides rich functionality for user and proxy
|
||||
management.
|
||||
<br />
|
||||
Easily add users, nodes, configure Xray and much more with a
|
||||
feature-rich{' '}
|
||||
<Text c="cyan" component="span" fw={600} fz="inherit">
|
||||
REST API
|
||||
</Text>{' '}
|
||||
powered by{' '}
|
||||
<Text
|
||||
c="cyan"
|
||||
component="a"
|
||||
fw={600}
|
||||
fz="inherit"
|
||||
href="https://nestjs.com"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<SiNestjs size={20} style={{ verticalAlign: 'middle' }} />{' '}
|
||||
NestJS
|
||||
</Text>
|
||||
.
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</motion.div>
|
||||
|
||||
<Stack justify="center">
|
||||
<motion.div variants={itemVariants}>
|
||||
<Group justify="center" wrap="wrap">
|
||||
<Button
|
||||
component={Link}
|
||||
href="/docs/overview/quick-start"
|
||||
leftSection={<TbArrowRightDashed size={20} />}
|
||||
radius="md"
|
||||
size="md"
|
||||
td="none"
|
||||
>
|
||||
Get started
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
component={Link}
|
||||
href="https://try.tg"
|
||||
leftSection={<TbCloud size={20} />}
|
||||
radius="md"
|
||||
size="md"
|
||||
td="none"
|
||||
variant="light"
|
||||
>
|
||||
Try risk-free
|
||||
</Button>
|
||||
</Group>
|
||||
</motion.div>
|
||||
<motion.div variants={itemVariants}>
|
||||
<Group justify="center" wrap="wrap">
|
||||
<Button
|
||||
color="#0088cc"
|
||||
component="a"
|
||||
href="https://t.me/remnawave"
|
||||
leftSection={<BiLogoTelegram color="white" size={20} />}
|
||||
radius="md"
|
||||
size="md"
|
||||
td="none"
|
||||
variant="filled"
|
||||
>
|
||||
Join Community
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
color="red"
|
||||
component="a"
|
||||
href="/docs/donate"
|
||||
leftSection={<TbHeartFilled size={20} />}
|
||||
radius="md"
|
||||
size="md"
|
||||
td="none"
|
||||
variant="filled"
|
||||
>
|
||||
Sponsor
|
||||
</Button>
|
||||
</Group>
|
||||
</motion.div>
|
||||
</Stack>
|
||||
|
||||
<motion.div variants={itemVariants}>
|
||||
<Box
|
||||
px={{ base: 'xs', sm: 'md' }}
|
||||
style={{
|
||||
maxWidth: '1500px',
|
||||
margin: '0 auto'
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
alt="Remnawave Panel"
|
||||
radius="md"
|
||||
src="/pages/landing_page.webp"
|
||||
style={{
|
||||
display: 'block',
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
zIndex: 0
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</motion.div>
|
||||
|
||||
<motion.div variants={itemVariants}>
|
||||
<Grid gutter="xl" justify="center">
|
||||
<Grid.Col span={{ base: 12, sm: 6, lg: 3 }}>
|
||||
<StatCard
|
||||
color="violet"
|
||||
icon={<IconUsers size={32} />}
|
||||
label="Active community members"
|
||||
value="3K+"
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={{ base: 12, sm: 6, lg: 3 }}>
|
||||
<StatCard
|
||||
color="cyan"
|
||||
icon={<IconRocket size={32} />}
|
||||
label="DockerHub pulls"
|
||||
value="40K+"
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</motion.div>
|
||||
|
||||
<motion.div variants={itemVariants}>
|
||||
<Stack gap="xl">
|
||||
<Box ta="center">
|
||||
<Badge
|
||||
mb="md"
|
||||
size="lg"
|
||||
style={{
|
||||
background: 'rgba(34, 211, 238, 0.1)',
|
||||
border: '1px solid rgba(34, 211, 238, 0.3)',
|
||||
color: '#22d3ee'
|
||||
}}
|
||||
variant="dot"
|
||||
>
|
||||
KEY FEATURES
|
||||
</Badge>
|
||||
<Title
|
||||
c="white"
|
||||
order={2}
|
||||
px={{ base: 'sm', sm: 'md' }}
|
||||
style={{
|
||||
fontSize: 'clamp(1.5rem, 5vw, 2.5rem)'
|
||||
}}
|
||||
>
|
||||
Everything You Need
|
||||
</Title>
|
||||
</Box>
|
||||
|
||||
<Grid gutter="lg">
|
||||
<Grid.Col span={{ base: 12, md: 6, lg: 4 }}>
|
||||
<FeatureCard
|
||||
color="cyan"
|
||||
description="Connect as many nodes as you want, and manage them all in one place."
|
||||
icon={<IconServer size={24} />}
|
||||
title="Multiple nodes support"
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={{ base: 12, md: 6, lg: 4 }}>
|
||||
<FeatureCard
|
||||
color="violet"
|
||||
description="Create and manage users with flexible settings."
|
||||
icon={<IconUsers size={24} />}
|
||||
title="User Management"
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={{ base: 12, md: 6, lg: 4 }}>
|
||||
<FeatureCard
|
||||
color="blue"
|
||||
description="Full-featured REST API with comprehensive documentation for easy integration."
|
||||
icon={<IconCloudDataConnection size={24} />}
|
||||
title="REST API"
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={{ base: 12, md: 6, lg: 4 }}>
|
||||
<FeatureCard
|
||||
color="green"
|
||||
description="Support for all major protocols: VLESS, Trojan, Shadowsocks."
|
||||
icon={<IconBox size={24} />}
|
||||
title="Protocols support"
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={{ base: 12, md: 6, lg: 4 }}>
|
||||
<FeatureCard
|
||||
color="cyan"
|
||||
description="Remnawave providers for your users to connect to. Supports all major client applications."
|
||||
icon={<IconDevices size={24} />}
|
||||
title="Subscription Links"
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={{ base: 12, md: 6, lg: 4 }}>
|
||||
<FeatureCard
|
||||
color="violet"
|
||||
description="Auth with Passkeys, GitHub, and more. Connections to nodes are secured with mTLS."
|
||||
icon={<IconShieldCheck size={24} />}
|
||||
title="Security First"
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Stack>
|
||||
</motion.div>
|
||||
|
||||
<Container maw={800}>
|
||||
<motion.div animate="visible" initial="hidden" variants={itemVariants}>
|
||||
<Stack gap="lg" pb={0} py="xl">
|
||||
<Divider opacity="0.5" />
|
||||
<Group gap="xl" justify="center" wrap="wrap">
|
||||
<Anchor
|
||||
href="https://github.com/remnawave"
|
||||
rel="noopener noreferrer"
|
||||
style={{
|
||||
textDecoration: 'none'
|
||||
}}
|
||||
target="_blank"
|
||||
>
|
||||
<Group gap="xs">
|
||||
<ThemeIcon
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="lg"
|
||||
variant="light"
|
||||
>
|
||||
<IconBrandGithub size={18} />
|
||||
</ThemeIcon>
|
||||
<Text c="dimmed" fw={500} size="sm">
|
||||
GitHub
|
||||
</Text>
|
||||
</Group>
|
||||
</Anchor>
|
||||
|
||||
<Anchor
|
||||
href="https://t.me/remnawave"
|
||||
rel="noopener noreferrer"
|
||||
style={{
|
||||
textDecoration: 'none'
|
||||
}}
|
||||
target="_blank"
|
||||
>
|
||||
<Group gap="xs">
|
||||
<ThemeIcon
|
||||
color="blue"
|
||||
radius="md"
|
||||
size="lg"
|
||||
variant="light"
|
||||
>
|
||||
<IconBrandTelegram size={18} />
|
||||
</ThemeIcon>
|
||||
<Text c="dimmed" fw={500} size="sm">
|
||||
Telegram
|
||||
</Text>
|
||||
</Group>
|
||||
</Anchor>
|
||||
</Group>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Stack align="center" gap="xs" px="sm">
|
||||
<Group
|
||||
align="center"
|
||||
gap={6}
|
||||
justify="center"
|
||||
style={{ flexWrap: 'wrap' }}
|
||||
>
|
||||
<Text c="dimmed" ff="Unbounded" size="sm" ta="center">
|
||||
Created by{' '}
|
||||
<Anchor
|
||||
c="cyan"
|
||||
fw={600}
|
||||
href="https://github.com/kastov"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
td="none"
|
||||
>
|
||||
kastov
|
||||
</Anchor>{' '}
|
||||
and{' '}
|
||||
<Anchor
|
||||
c="cyan"
|
||||
fw={600}
|
||||
href="https://t.me/remnawave"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
td="none"
|
||||
>
|
||||
<IconHeart
|
||||
color="#ef4444"
|
||||
fill="#ef4444"
|
||||
size={20}
|
||||
style={{ verticalAlign: 'middle' }}
|
||||
/>{' '}
|
||||
community
|
||||
</Anchor>
|
||||
</Text>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</motion.div>
|
||||
</Container>
|
||||
</motion.div>
|
||||
</Container>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Home(): JSX.Element {
|
||||
return (
|
||||
<MantineProvider defaultColorScheme="dark" theme={theme}>
|
||||
<Layout
|
||||
description="Remnawave – user and proxy management solution"
|
||||
noFooter={true}
|
||||
wrapperClassName="landing-page-layout"
|
||||
>
|
||||
<HomePage />
|
||||
</Layout>
|
||||
</MantineProvider>
|
||||
)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export * from './theme'
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Badge } from '@mantine/core'
|
||||
|
||||
export default {
|
||||
Badge: Badge.extend({
|
||||
defaultProps: {
|
||||
radius: 'md',
|
||||
variant: 'outline'
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { ActionIcon, Button } from '@mantine/core'
|
||||
|
||||
export default {
|
||||
ActionIcon: ActionIcon.extend({
|
||||
defaultProps: {
|
||||
radius: 'lg',
|
||||
variant: 'outline'
|
||||
}
|
||||
}),
|
||||
Button: Button.extend({
|
||||
defaultProps: {
|
||||
radius: 'lg',
|
||||
variant: 'outline'
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
.root {
|
||||
background-color: var(--mantine-color-body);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { Card } from '@mantine/core'
|
||||
|
||||
import classes from './card.module.css'
|
||||
|
||||
export default {
|
||||
Card: Card.extend({
|
||||
classNames: classes,
|
||||
defaultProps: {
|
||||
radius: 'md',
|
||||
withBorder: true
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { DrawerOverlay } from '@mantine/core'
|
||||
|
||||
export default {
|
||||
DrawerOverlay: DrawerOverlay.extend({
|
||||
defaultProps: {
|
||||
backgroundOpacity: 0.5,
|
||||
blur: 2
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import loadingOverlay from './loading-overlay'
|
||||
import ringProgress from './ring-progress'
|
||||
import notification from './notification'
|
||||
import buttons from './buttons'
|
||||
import layouts from './layouts'
|
||||
import tooltip from './tooltip'
|
||||
import drawer from './drawer'
|
||||
import badge from './badge'
|
||||
import table from './table'
|
||||
import card from './card'
|
||||
import menu from './menu'
|
||||
|
||||
export default {
|
||||
...card,
|
||||
...badge,
|
||||
...buttons,
|
||||
...drawer,
|
||||
...loadingOverlay,
|
||||
...menu,
|
||||
...notification,
|
||||
...ringProgress,
|
||||
...table,
|
||||
...tooltip,
|
||||
...layouts
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Paper } from '@mantine/core'
|
||||
|
||||
export default {
|
||||
Paper: Paper.extend({
|
||||
defaultProps: {
|
||||
radius: 'lg'
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { LoadingOverlay } from '@mantine/core'
|
||||
|
||||
export default {
|
||||
LoadingOverlay: LoadingOverlay.extend({
|
||||
defaultProps: {
|
||||
zIndex: 1000,
|
||||
overlayProps: {
|
||||
radius: 'sm',
|
||||
blur: 4
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Menu } from '@mantine/core'
|
||||
|
||||
export default {
|
||||
Menu: Menu.extend({
|
||||
defaultProps: {
|
||||
shadow: 'md',
|
||||
withArrow: true,
|
||||
transitionProps: { transition: 'scale', duration: 200 }
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Notification } from '@mantine/core'
|
||||
|
||||
export default {
|
||||
Notification: Notification.extend({
|
||||
defaultProps: {
|
||||
radius: 'md'
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { RingProgress } from '@mantine/core'
|
||||
|
||||
export default {
|
||||
RingProgress: RingProgress.extend({
|
||||
defaultProps: {
|
||||
thickness: 6,
|
||||
roundCaps: true
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Table } from '@mantine/core'
|
||||
|
||||
export default {
|
||||
Table: Table.extend({
|
||||
defaultProps: {
|
||||
highlightOnHover: true
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { Tooltip } from '@mantine/core'
|
||||
|
||||
export default {
|
||||
Tooltip: Tooltip.extend({
|
||||
defaultProps: {
|
||||
radius: 'md',
|
||||
withArrow: true,
|
||||
transitionProps: { transition: 'scale-x', duration: 300 },
|
||||
arrowSize: 2,
|
||||
color: 'gray'
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
import { createTheme } from '@mantine/core'
|
||||
|
||||
import components from './overrides'
|
||||
|
||||
export const theme = createTheme({
|
||||
components,
|
||||
cursorType: 'pointer',
|
||||
fontFamily: 'Montserrat, sans-serif',
|
||||
fontFamilyMonospace: 'Fira Mono, monospace',
|
||||
breakpoints: {
|
||||
xs: '30em',
|
||||
sm: '40em',
|
||||
md: '48em',
|
||||
lg: '64em',
|
||||
xl: '80em',
|
||||
'2xl': '96em',
|
||||
'3xl': '120em',
|
||||
'4xl': '160em'
|
||||
},
|
||||
scale: 1,
|
||||
fontSmoothing: true,
|
||||
focusRing: 'never',
|
||||
white: '#ffffff',
|
||||
black: '#24292f',
|
||||
colors: {
|
||||
dark: [
|
||||
'#c9d1d9',
|
||||
'#b1bac4',
|
||||
'#8b949e',
|
||||
'#6e7681',
|
||||
'#484f58',
|
||||
'#30363d',
|
||||
'#21262d',
|
||||
'#161b22',
|
||||
'#0d1117',
|
||||
'#010409'
|
||||
],
|
||||
|
||||
blue: [
|
||||
'#ddf4ff',
|
||||
'#b6e3ff',
|
||||
'#80ccff',
|
||||
'#54aeff',
|
||||
'#218bff',
|
||||
'#0969da',
|
||||
'#0550ae',
|
||||
'#033d8b',
|
||||
'#0a3069',
|
||||
'#002155'
|
||||
],
|
||||
green: [
|
||||
'#dafbe1',
|
||||
'#aceebb',
|
||||
'#6fdd8b',
|
||||
'#4ac26b',
|
||||
'#2da44e',
|
||||
'#1a7f37',
|
||||
'#116329',
|
||||
'#044f1e',
|
||||
'#003d16',
|
||||
'#002d11'
|
||||
],
|
||||
yellow: [
|
||||
'#fff8c5',
|
||||
'#fae17d',
|
||||
'#eac54f',
|
||||
'#d4a72c',
|
||||
'#bf8700',
|
||||
'#9a6700',
|
||||
'#7d4e00',
|
||||
'#633c01',
|
||||
'#4d2d00',
|
||||
'#3b2300'
|
||||
],
|
||||
orange: [
|
||||
'#fff1e5',
|
||||
'#ffd8b5',
|
||||
'#ffb77c',
|
||||
'#fb8f44',
|
||||
'#e16f24',
|
||||
'#bc4c00',
|
||||
'#953800',
|
||||
'#762c00',
|
||||
'#5c2200',
|
||||
'#471700'
|
||||
]
|
||||
},
|
||||
primaryShade: 8,
|
||||
primaryColor: 'cyan',
|
||||
autoContrast: true,
|
||||
luminanceThreshold: 0.3,
|
||||
headings: {
|
||||
fontWeight: '600'
|
||||
},
|
||||
defaultRadius: 'md'
|
||||
})
|
||||
Reference in New Issue
Block a user