mirror of
https://github.com/house-of-vanity/OutFleet.git
synced 2025-10-26 10:09:08 +00:00
Improve CI and lint code
This commit is contained in:
75
.github/workflows/rust.yml
vendored
75
.github/workflows/rust.yml
vendored
@@ -6,11 +6,27 @@ on:
|
|||||||
- 'RUST'
|
- 'RUST'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker:
|
test-build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
rust-version: ${{ steps.vars.outputs.rust_version }}
|
||||||
|
sha-short: ${{ steps.vars.outputs.sha_short }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Rust toolchain cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/bin/
|
||||||
|
~/.cargo/registry/index/
|
||||||
|
~/.cargo/registry/cache/
|
||||||
|
~/.cargo/git/db/
|
||||||
|
target/
|
||||||
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-cargo-
|
||||||
|
|
||||||
- name: Extract version from Cargo.toml
|
- name: Extract version from Cargo.toml
|
||||||
id: extract_version
|
id: extract_version
|
||||||
run: |
|
run: |
|
||||||
@@ -19,22 +35,49 @@ jobs:
|
|||||||
echo "Extracted version: $VERSION"
|
echo "Extracted version: $VERSION"
|
||||||
|
|
||||||
- name: Set outputs
|
- name: Set outputs
|
||||||
|
id: vars
|
||||||
|
run: |
|
||||||
|
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||||
|
echo "rust_version=rs-${{ steps.extract_version.outputs.cargo_version }}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Test build (linux/amd64 only)
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: false
|
||||||
|
cache-from: |
|
||||||
|
type=registry,ref=ultradesu/outfleet:deps-cache
|
||||||
|
type=registry,ref=ultradesu/outfleet:builder-cache
|
||||||
|
build-args: |
|
||||||
|
GIT_COMMIT=${{ github.sha }}
|
||||||
|
GIT_COMMIT_SHORT=${{ steps.vars.outputs.sha_short }}
|
||||||
|
BUILD_DATE=$(date -u +'%Y-%m-%d %H:%M:%S UTC')
|
||||||
|
BRANCH_NAME=${GITHUB_REF#refs/heads/}
|
||||||
|
CARGO_VERSION=${{ steps.extract_version.outputs.cargo_version }}
|
||||||
|
|
||||||
|
docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: test-build
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set build variables
|
||||||
id: vars
|
id: vars
|
||||||
run: |
|
run: |
|
||||||
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||||
echo "sha_full=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
echo "sha_full=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||||
echo "build_date=$(date -u +'%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_OUTPUT
|
echo "build_date=$(date -u +'%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_OUTPUT
|
||||||
echo "branch_name=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT
|
echo "branch_name=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT
|
||||||
echo "rust_version=rs-${{ steps.extract_version.outputs.cargo_version }}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Check outputs
|
- name: Extract version from Cargo.toml
|
||||||
|
id: extract_version
|
||||||
run: |
|
run: |
|
||||||
echo "Short SHA: ${{ steps.vars.outputs.sha_short }}"
|
VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
|
||||||
echo "Full SHA: ${{ steps.vars.outputs.sha_full }}"
|
echo "cargo_version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
echo "Build Date: ${{ steps.vars.outputs.build_date }}"
|
|
||||||
echo "Branch: ${{ steps.vars.outputs.branch_name }}"
|
|
||||||
echo "Cargo Version: ${{ steps.extract_version.outputs.cargo_version }}"
|
|
||||||
echo "Rust Version Tag: ${{ steps.vars.outputs.rust_version }}"
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
@@ -54,8 +97,14 @@ jobs:
|
|||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
cache-from: type=registry,ref=ultradesu/outfleet:rust-buildcache
|
cache-from: |
|
||||||
cache-to: type=registry,ref=ultradesu/outfleet:rust-buildcache,mode=max
|
type=registry,ref=ultradesu/outfleet:deps-cache
|
||||||
|
type=registry,ref=ultradesu/outfleet:builder-cache
|
||||||
|
type=registry,ref=ultradesu/outfleet:rust-buildcache
|
||||||
|
cache-to: |
|
||||||
|
type=registry,ref=ultradesu/outfleet:deps-cache,mode=max
|
||||||
|
type=registry,ref=ultradesu/outfleet:builder-cache,mode=max
|
||||||
|
type=registry,ref=ultradesu/outfleet:rust-buildcache,mode=max
|
||||||
build-args: |
|
build-args: |
|
||||||
GIT_COMMIT=${{ steps.vars.outputs.sha_full }}
|
GIT_COMMIT=${{ steps.vars.outputs.sha_full }}
|
||||||
GIT_COMMIT_SHORT=${{ steps.vars.outputs.sha_short }}
|
GIT_COMMIT_SHORT=${{ steps.vars.outputs.sha_short }}
|
||||||
@@ -63,6 +112,6 @@ jobs:
|
|||||||
BRANCH_NAME=${{ steps.vars.outputs.branch_name }}
|
BRANCH_NAME=${{ steps.vars.outputs.branch_name }}
|
||||||
CARGO_VERSION=${{ steps.extract_version.outputs.cargo_version }}
|
CARGO_VERSION=${{ steps.extract_version.outputs.cargo_version }}
|
||||||
tags: |
|
tags: |
|
||||||
ultradesu/outfleet:${{ steps.vars.outputs.rust_version }}
|
ultradesu/outfleet:rs-${{ steps.extract_version.outputs.cargo_version }}
|
||||||
ultradesu/outfleet:${{ steps.vars.outputs.rust_version }}-${{ steps.vars.outputs.sha_short }}
|
ultradesu/outfleet:rs-${{ steps.extract_version.outputs.cargo_version }}-${{ steps.vars.outputs.sha_short }}
|
||||||
ultradesu/outfleet:rust-latest
|
ultradesu/outfleet:rust-latest
|
||||||
57
Dockerfile
57
Dockerfile
@@ -1,5 +1,29 @@
|
|||||||
|
# Cargo dependencies stage
|
||||||
|
FROM rust:1.75-slim as deps
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install system dependencies needed for building
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
protobuf-compiler \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Copy only dependency specification files
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
|
||||||
|
# Create dummy source to build dependencies
|
||||||
|
RUN mkdir -p src && \
|
||||||
|
echo "fn main() {}" > src/main.rs && \
|
||||||
|
echo "pub fn lib() {}" > src/lib.rs
|
||||||
|
|
||||||
|
# Build dependencies - this layer will be cached unless Cargo.toml changes
|
||||||
|
RUN cargo build --release && \
|
||||||
|
rm -rf src target/release/deps/xray_admin* target/release/xray-admin*
|
||||||
|
|
||||||
# Build stage
|
# Build stage
|
||||||
FROM rust:latest as builder
|
FROM deps as builder
|
||||||
|
|
||||||
# Build arguments
|
# Build arguments
|
||||||
ARG GIT_COMMIT="development"
|
ARG GIT_COMMIT="development"
|
||||||
@@ -15,27 +39,15 @@ ENV BUILD_DATE=${BUILD_DATE}
|
|||||||
ENV BRANCH_NAME=${BRANCH_NAME}
|
ENV BRANCH_NAME=${BRANCH_NAME}
|
||||||
ENV CARGO_VERSION=${CARGO_VERSION}
|
ENV CARGO_VERSION=${CARGO_VERSION}
|
||||||
|
|
||||||
WORKDIR /app
|
# Copy actual source code
|
||||||
|
|
||||||
# Install system dependencies
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
pkg-config \
|
|
||||||
libssl-dev \
|
|
||||||
protobuf-compiler \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Copy dependency files
|
|
||||||
COPY Cargo.toml Cargo.lock ./
|
|
||||||
|
|
||||||
# Copy source code
|
|
||||||
COPY src ./src
|
COPY src ./src
|
||||||
COPY static ./static
|
COPY static ./static
|
||||||
|
|
||||||
# Build the application
|
# Build the application (dependencies are already compiled)
|
||||||
RUN cargo build --release
|
RUN cargo build --release
|
||||||
|
|
||||||
# Runtime stage
|
# Runtime stage - minimal Debian image
|
||||||
FROM ubuntu:24.04
|
FROM debian:bookworm-slim as runtime
|
||||||
|
|
||||||
# Build arguments (needed for runtime stage)
|
# Build arguments (needed for runtime stage)
|
||||||
ARG GIT_COMMIT="development"
|
ARG GIT_COMMIT="development"
|
||||||
@@ -53,11 +65,13 @@ ENV CARGO_VERSION=${CARGO_VERSION}
|
|||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Install runtime dependencies
|
# Install minimal runtime dependencies
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
libssl3 \
|
libssl3 \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
libprotobuf32 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& apt-get clean
|
||||||
|
|
||||||
# Copy the binary from builder
|
# Copy the binary from builder
|
||||||
COPY --from=builder /app/target/release/xray-admin /app/xray-admin
|
COPY --from=builder /app/target/release/xray-admin /app/xray-admin
|
||||||
@@ -68,6 +82,11 @@ COPY --from=builder /app/static ./static
|
|||||||
# Copy config file
|
# Copy config file
|
||||||
COPY config.docker.toml ./config.toml
|
COPY config.docker.toml ./config.toml
|
||||||
|
|
||||||
|
# Create non-root user for security
|
||||||
|
RUN groupadd -r outfleet && useradd -r -g outfleet -s /bin/false outfleet
|
||||||
|
RUN chown -R outfleet:outfleet /app
|
||||||
|
USER outfleet
|
||||||
|
|
||||||
EXPOSE 8081
|
EXPOSE 8081
|
||||||
|
|
||||||
CMD ["/app/xray-admin", "--host", "0.0.0.0"]
|
CMD ["/app/xray-admin", "--host", "0.0.0.0"]
|
||||||
|
|||||||
@@ -11,13 +11,7 @@ pub mod user_request;
|
|||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use super::certificate::Entity as Certificate;
|
pub use super::certificate::Entity as Certificate;
|
||||||
pub use super::dns_provider::Entity as DnsProvider;
|
|
||||||
pub use super::inbound_template::Entity as InboundTemplate;
|
pub use super::inbound_template::Entity as InboundTemplate;
|
||||||
pub use super::inbound_users::Entity as InboundUsers;
|
|
||||||
pub use super::server::Entity as Server;
|
pub use super::server::Entity as Server;
|
||||||
pub use super::server_inbound::Entity as ServerInbound;
|
pub use super::server_inbound::Entity as ServerInbound;
|
||||||
pub use super::telegram_config::Entity as TelegramConfig;
|
|
||||||
pub use super::user::Entity as User;
|
|
||||||
pub use super::user_access::Entity as UserAccess;
|
|
||||||
pub use super::user_request::Entity as UserRequest;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ pub mod server;
|
|||||||
pub mod server_inbound;
|
pub mod server_inbound;
|
||||||
pub mod telegram_config;
|
pub mod telegram_config;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod user_access;
|
|
||||||
pub mod user_request;
|
pub mod user_request;
|
||||||
|
|
||||||
pub use certificate::CertificateRepository;
|
pub use certificate::CertificateRepository;
|
||||||
@@ -17,5 +16,4 @@ pub use server::ServerRepository;
|
|||||||
pub use server_inbound::ServerInboundRepository;
|
pub use server_inbound::ServerInboundRepository;
|
||||||
pub use telegram_config::TelegramConfigRepository;
|
pub use telegram_config::TelegramConfigRepository;
|
||||||
pub use user::UserRepository;
|
pub use user::UserRepository;
|
||||||
pub use user_access::UserAccessRepository;
|
|
||||||
pub use user_request::UserRequestRepository;
|
pub use user_request::UserRequestRepository;
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ impl ServerInboundRepository {
|
|||||||
&self,
|
&self,
|
||||||
server_id: Uuid,
|
server_id: Uuid,
|
||||||
) -> Result<Vec<server_inbound::ServerInboundResponse>> {
|
) -> Result<Vec<server_inbound::ServerInboundResponse>> {
|
||||||
use crate::database::entities::{certificate, inbound_template};
|
|
||||||
|
|
||||||
let inbounds = ServerInbound::find()
|
let inbounds = ServerInbound::find()
|
||||||
.filter(server_inbound::Column::ServerId.eq(server_id))
|
.filter(server_inbound::Column::ServerId.eq(server_id))
|
||||||
@@ -193,7 +192,7 @@ impl ServerInboundRepository {
|
|||||||
Ok(inbound.update(&self.db).await?)
|
Ok(inbound.update(&self.db).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn find_by_user_id(&self, user_id: Uuid) -> Result<Vec<server_inbound::Model>> {
|
pub async fn find_by_user_id(&self, _user_id: Uuid) -> Result<Vec<server_inbound::Model>> {
|
||||||
// This would need a join with user_access table
|
// This would need a join with user_access table
|
||||||
// For now, returning empty vec as placeholder
|
// For now, returning empty vec as placeholder
|
||||||
// TODO: Implement proper join query
|
// TODO: Implement proper join query
|
||||||
|
|||||||
@@ -1,137 +0,0 @@
|
|||||||
use anyhow::Result;
|
|
||||||
use sea_orm::*;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
use crate::database::entities::user_access::{
|
|
||||||
self, ActiveModel, CreateUserAccessDto, Entity as UserAccess, Model, UpdateUserAccessDto,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct UserAccessRepository {
|
|
||||||
db: DatabaseConnection,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UserAccessRepository {
|
|
||||||
pub fn new(db: DatabaseConnection) -> Self {
|
|
||||||
Self { db }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find all user access records
|
|
||||||
pub async fn find_all(&self) -> Result<Vec<Model>> {
|
|
||||||
let records = UserAccess::find().all(&self.db).await?;
|
|
||||||
Ok(records)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find user access by ID
|
|
||||||
pub async fn find_by_id(&self, id: Uuid) -> Result<Option<Model>> {
|
|
||||||
let record = UserAccess::find_by_id(id).one(&self.db).await?;
|
|
||||||
Ok(record)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find user access by user ID
|
|
||||||
pub async fn find_by_user_id(&self, user_id: Uuid) -> Result<Vec<Model>> {
|
|
||||||
let records = UserAccess::find()
|
|
||||||
.filter(user_access::Column::UserId.eq(user_id))
|
|
||||||
.all(&self.db)
|
|
||||||
.await?;
|
|
||||||
Ok(records)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find user access by server and inbound
|
|
||||||
pub async fn find_by_server_inbound(
|
|
||||||
&self,
|
|
||||||
server_id: Uuid,
|
|
||||||
server_inbound_id: Uuid,
|
|
||||||
) -> Result<Vec<Model>> {
|
|
||||||
let records = UserAccess::find()
|
|
||||||
.filter(user_access::Column::ServerId.eq(server_id))
|
|
||||||
.filter(user_access::Column::ServerInboundId.eq(server_inbound_id))
|
|
||||||
.all(&self.db)
|
|
||||||
.await?;
|
|
||||||
Ok(records)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find active user access for specific user, server and inbound
|
|
||||||
pub async fn find_active_access(
|
|
||||||
&self,
|
|
||||||
user_id: Uuid,
|
|
||||||
server_id: Uuid,
|
|
||||||
server_inbound_id: Uuid,
|
|
||||||
) -> Result<Option<Model>> {
|
|
||||||
let record = UserAccess::find()
|
|
||||||
.filter(user_access::Column::UserId.eq(user_id))
|
|
||||||
.filter(user_access::Column::ServerId.eq(server_id))
|
|
||||||
.filter(user_access::Column::ServerInboundId.eq(server_inbound_id))
|
|
||||||
.filter(user_access::Column::IsActive.eq(true))
|
|
||||||
.one(&self.db)
|
|
||||||
.await?;
|
|
||||||
Ok(record)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create new user access
|
|
||||||
pub async fn create(&self, dto: CreateUserAccessDto) -> Result<Model> {
|
|
||||||
let active_model: ActiveModel = dto.into();
|
|
||||||
let model = active_model.insert(&self.db).await?;
|
|
||||||
Ok(model)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update user access
|
|
||||||
pub async fn update(&self, id: Uuid, dto: UpdateUserAccessDto) -> Result<Option<Model>> {
|
|
||||||
let existing = match self.find_by_id(id).await? {
|
|
||||||
Some(model) => model,
|
|
||||||
None => return Ok(None),
|
|
||||||
};
|
|
||||||
|
|
||||||
let active_model = existing.apply_update(dto);
|
|
||||||
let updated = active_model.update(&self.db).await?;
|
|
||||||
Ok(Some(updated))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete user access
|
|
||||||
pub async fn delete(&self, id: Uuid) -> Result<bool> {
|
|
||||||
let result = UserAccess::delete_by_id(id).exec(&self.db).await?;
|
|
||||||
Ok(result.rows_affected > 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enable user access (set is_active = true)
|
|
||||||
pub async fn enable(&self, id: Uuid) -> Result<Option<Model>> {
|
|
||||||
self.update(
|
|
||||||
id,
|
|
||||||
UpdateUserAccessDto {
|
|
||||||
is_active: Some(true),
|
|
||||||
level: None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disable user access (set is_active = false)
|
|
||||||
pub async fn disable(&self, id: Uuid) -> Result<Option<Model>> {
|
|
||||||
self.update(
|
|
||||||
id,
|
|
||||||
UpdateUserAccessDto {
|
|
||||||
is_active: Some(false),
|
|
||||||
level: None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get all active access for a user
|
|
||||||
pub async fn find_active_for_user(&self, user_id: Uuid) -> Result<Vec<Model>> {
|
|
||||||
let records = UserAccess::find()
|
|
||||||
.filter(user_access::Column::UserId.eq(user_id))
|
|
||||||
.filter(user_access::Column::IsActive.eq(true))
|
|
||||||
.all(&self.db)
|
|
||||||
.await?;
|
|
||||||
Ok(records)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove all access for a specific server inbound
|
|
||||||
pub async fn remove_all_for_inbound(&self, server_inbound_id: Uuid) -> Result<u64> {
|
|
||||||
let result = UserAccess::delete_many()
|
|
||||||
.filter(user_access::Column::ServerInboundId.eq(server_inbound_id))
|
|
||||||
.exec(&self.db)
|
|
||||||
.await?;
|
|
||||||
Ok(result.rows_affected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,7 +11,6 @@ use crate::services::acme::{AcmeError, CloudflareClient};
|
|||||||
pub struct AcmeClient {
|
pub struct AcmeClient {
|
||||||
cloudflare: CloudflareClient,
|
cloudflare: CloudflareClient,
|
||||||
account: Account,
|
account: Account,
|
||||||
directory_url: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AcmeClient {
|
impl AcmeClient {
|
||||||
@@ -43,7 +42,6 @@ impl AcmeClient {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
cloudflare,
|
cloudflare,
|
||||||
account,
|
account,
|
||||||
directory_url,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +83,7 @@ impl AcmeClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get challenge value and record ID first
|
// Get challenge value and record ID first
|
||||||
let (challenge_value, record_id) = {
|
let (_challenge_value, record_id) = {
|
||||||
// Find DNS challenge
|
// Find DNS challenge
|
||||||
let mut challenge = authz
|
let mut challenge = authz
|
||||||
.challenge(ChallengeType::Dns01)
|
.challenge(ChallengeType::Dns01)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ pub mod telegram;
|
|||||||
pub mod uri_generator;
|
pub mod uri_generator;
|
||||||
pub mod xray;
|
pub mod xray;
|
||||||
|
|
||||||
pub use certificates::CertificateService;
|
|
||||||
pub use tasks::TaskScheduler;
|
pub use tasks::TaskScheduler;
|
||||||
pub use telegram::TelegramService;
|
pub use telegram::TelegramService;
|
||||||
pub use uri_generator::UriGeneratorService;
|
pub use uri_generator::UriGeneratorService;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::database::entities::inbound_users;
|
|
||||||
use crate::database::repository::{
|
use crate::database::repository::{
|
||||||
CertificateRepository, InboundTemplateRepository, InboundUsersRepository,
|
CertificateRepository, InboundTemplateRepository, InboundUsersRepository,
|
||||||
ServerInboundRepository, ServerRepository, UserRepository,
|
ServerInboundRepository, ServerRepository, UserRepository,
|
||||||
@@ -8,7 +7,6 @@ use crate::services::events::SyncEvent;
|
|||||||
use crate::services::XrayService;
|
use crate::services::XrayService;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use sea_orm::{ColumnTrait, EntityTrait, JoinType, QueryFilter, RelationTrait};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ pub async fn run_polling(
|
|||||||
bot: Bot,
|
bot: Bot,
|
||||||
db: DatabaseManager,
|
db: DatabaseManager,
|
||||||
app_config: AppConfig,
|
app_config: AppConfig,
|
||||||
mut shutdown_rx: oneshot::Receiver<()>,
|
shutdown_rx: oneshot::Receiver<()>,
|
||||||
) {
|
) {
|
||||||
tracing::info!("Starting Telegram bot polling...");
|
tracing::info!("Starting Telegram bot polling...");
|
||||||
|
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ pub async fn handle_approve_request(
|
|||||||
};
|
};
|
||||||
|
|
||||||
match user_repo.create(dto).await {
|
match user_repo.create(dto).await {
|
||||||
Ok(new_user) => {
|
Ok(_new_user) => {
|
||||||
// Approve the request
|
// Approve the request
|
||||||
request_repo
|
request_repo
|
||||||
.approve(
|
.approve(
|
||||||
@@ -297,9 +297,9 @@ pub async fn handle_approve_request(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send main menu to the user instead of just notification
|
// Send main menu to the user instead of just notification
|
||||||
let user_lang = Language::from_telegram_code(Some(&request.get_language()));
|
let _user_lang = Language::from_telegram_code(Some(&request.get_language()));
|
||||||
let user_repo_for_user = UserRepository::new(db.connection());
|
let user_repo_for_user = UserRepository::new(db.connection());
|
||||||
let is_admin = false; // New users are not admins by default
|
let _is_admin = false; // New users are not admins by default
|
||||||
|
|
||||||
// Create a fake user object for language detection
|
// Create a fake user object for language detection
|
||||||
let fake_user = teloxide::types::User {
|
let fake_user = teloxide::types::User {
|
||||||
@@ -633,7 +633,7 @@ pub async fn handle_select_server_access(
|
|||||||
short_request_id: &str,
|
short_request_id: &str,
|
||||||
db: &DatabaseManager,
|
db: &DatabaseManager,
|
||||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let lang = Language::English; // Default admin language
|
let _lang = Language::English; // Default admin language
|
||||||
let _l10n = LocalizationService::new();
|
let _l10n = LocalizationService::new();
|
||||||
let chat_id = q
|
let chat_id = q
|
||||||
.message
|
.message
|
||||||
@@ -815,7 +815,7 @@ pub async fn handle_apply_server_access(
|
|||||||
short_request_id: &str,
|
short_request_id: &str,
|
||||||
db: &DatabaseManager,
|
db: &DatabaseManager,
|
||||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let lang = Language::English; // Default admin language
|
let _lang = Language::English; // Default admin language
|
||||||
let _l10n = LocalizationService::new();
|
let _l10n = LocalizationService::new();
|
||||||
let chat_id = q
|
let chat_id = q
|
||||||
.message
|
.message
|
||||||
@@ -1177,7 +1177,7 @@ pub async fn handle_user_details(
|
|||||||
|
|
||||||
// Build keyboard
|
// Build keyboard
|
||||||
let short_user_id = generate_short_user_id(&user_id.to_string());
|
let short_user_id = generate_short_user_id(&user_id.to_string());
|
||||||
let mut keyboard_buttons = vec![
|
let keyboard_buttons = vec![
|
||||||
vec![InlineKeyboardButton::callback(
|
vec![InlineKeyboardButton::callback(
|
||||||
l10n.get(lang.clone(), "manage_access"),
|
l10n.get(lang.clone(), "manage_access"),
|
||||||
format!("user_manage:{}", short_user_id),
|
format!("user_manage:{}", short_user_id),
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ pub async fn handle_command(
|
|||||||
msg: Message,
|
msg: Message,
|
||||||
cmd: Command,
|
cmd: Command,
|
||||||
db: DatabaseManager,
|
db: DatabaseManager,
|
||||||
app_config: AppConfig,
|
_app_config: AppConfig,
|
||||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let chat_id = msg.chat.id;
|
let chat_id = msg.chat.id;
|
||||||
let from = &msg.from.ok_or("No user info")?;
|
let from = &msg.from.ok_or("No user info")?;
|
||||||
|
|||||||
@@ -289,21 +289,3 @@ pub fn get_new_user_keyboard(lang: Language) -> InlineKeyboardMarkup {
|
|||||||
)]])
|
)]])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restore UUID from compact format (without dashes)
|
|
||||||
fn restore_uuid(compact: &str) -> Option<String> {
|
|
||||||
if compact.len() != 32 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert dashes at proper positions for UUID format
|
|
||||||
let uuid_str = format!(
|
|
||||||
"{}-{}-{}-{}-{}",
|
|
||||||
&compact[0..8],
|
|
||||||
&compact[8..12],
|
|
||||||
&compact[12..16],
|
|
||||||
&compact[16..20],
|
|
||||||
&compact[20..32]
|
|
||||||
);
|
|
||||||
|
|
||||||
Some(uuid_str)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use teloxide::{
|
|||||||
|
|
||||||
use super::super::localization::{Language, LocalizationService};
|
use super::super::localization::{Language, LocalizationService};
|
||||||
use super::types::{get_main_keyboard, get_new_user_keyboard, get_user_language};
|
use super::types::{get_main_keyboard, get_new_user_keyboard, get_user_language};
|
||||||
use crate::database::entities::user_request::{CreateUserRequestDto, RequestStatus};
|
use crate::database::entities::user_request::CreateUserRequestDto;
|
||||||
use crate::database::repository::{UserRepository, UserRequestRepository};
|
use crate::database::repository::{UserRepository, UserRequestRepository};
|
||||||
use crate::database::DatabaseManager;
|
use crate::database::DatabaseManager;
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ pub mod error;
|
|||||||
pub mod handlers;
|
pub mod handlers;
|
||||||
pub mod localization;
|
pub mod localization;
|
||||||
|
|
||||||
pub use error::TelegramError;
|
|
||||||
|
|
||||||
/// Main Telegram service that manages the bot lifecycle
|
/// Main Telegram service that manages the bot lifecycle
|
||||||
pub struct TelegramService {
|
pub struct TelegramService {
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ pub trait UriBuilder {
|
|||||||
|
|
||||||
/// Helper functions for URI building
|
/// Helper functions for URI building
|
||||||
pub mod utils {
|
pub mod utils {
|
||||||
use crate::services::uri_generator::error::UriGeneratorError;
|
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use base64::{engine::general_purpose, Engine as _};
|
use base64::{engine::general_purpose, Engine as _};
|
||||||
use serde_json::Value;
|
|
||||||
|
|
||||||
use super::{utils, UriBuilder};
|
use super::{utils, UriBuilder};
|
||||||
use crate::services::uri_generator::{error::UriGeneratorError, ClientConfigData};
|
use crate::services::uri_generator::{error::UriGeneratorError, ClientConfigData};
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use base64::{engine::general_purpose, Engine as _};
|
use base64::{engine::general_purpose, Engine as _};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use super::{utils, UriBuilder};
|
use super::{utils, UriBuilder};
|
||||||
use crate::services::uri_generator::{error::UriGeneratorError, ClientConfigData};
|
use crate::services::uri_generator::{error::UriGeneratorError, ClientConfigData};
|
||||||
@@ -144,97 +143,6 @@ impl VmessUriBuilder {
|
|||||||
Ok(format!("vmess://{}", encoded))
|
Ok(format!("vmess://{}", encoded))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build VMess URI in query parameter format (alternative)
|
|
||||||
fn build_query_param_uri(
|
|
||||||
&self,
|
|
||||||
config: &ClientConfigData,
|
|
||||||
) -> Result<String, UriGeneratorError> {
|
|
||||||
// Apply variable substitution to stream settings
|
|
||||||
let stream_settings = if !config.variable_values.is_null() {
|
|
||||||
apply_variables(&config.stream_settings, &config.variable_values)?
|
|
||||||
} else {
|
|
||||||
config.stream_settings.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut params = HashMap::new();
|
|
||||||
|
|
||||||
// VMess uses auto encryption
|
|
||||||
params.insert("encryption".to_string(), "auto".to_string());
|
|
||||||
|
|
||||||
// Determine security layer
|
|
||||||
let has_certificate = config.certificate_domain.is_some();
|
|
||||||
let security = utils::extract_security_type(&stream_settings, has_certificate);
|
|
||||||
if security != "none" {
|
|
||||||
params.insert("security".to_string(), security.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transport type
|
|
||||||
let transport_type = utils::extract_transport_type(&stream_settings);
|
|
||||||
if transport_type != "tcp" {
|
|
||||||
params.insert("type".to_string(), transport_type.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transport-specific parameters
|
|
||||||
match transport_type.as_str() {
|
|
||||||
"ws" => {
|
|
||||||
if let Some(path) = utils::extract_ws_path(&stream_settings) {
|
|
||||||
params.insert("path".to_string(), path);
|
|
||||||
}
|
|
||||||
if let Some(host) = utils::extract_ws_host(&stream_settings) {
|
|
||||||
params.insert("host".to_string(), host);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"grpc" => {
|
|
||||||
if let Some(service_name) = utils::extract_grpc_service_name(&stream_settings) {
|
|
||||||
params.insert("serviceName".to_string(), service_name);
|
|
||||||
}
|
|
||||||
params.insert("mode".to_string(), "gun".to_string());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TLS specific parameters
|
|
||||||
if security != "none" {
|
|
||||||
if let Some(sni) =
|
|
||||||
utils::extract_tls_sni(&stream_settings, config.certificate_domain.as_deref())
|
|
||||||
{
|
|
||||||
params.insert("sni".to_string(), sni);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(fp) = stream_settings
|
|
||||||
.get("tlsSettings")
|
|
||||||
.and_then(|tls| tls.get("fingerprint"))
|
|
||||||
.and_then(|fp| fp.as_str())
|
|
||||||
{
|
|
||||||
params.insert("fp".to_string(), fp.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the URI
|
|
||||||
let query_string = utils::build_query_string(¶ms);
|
|
||||||
let alias = utils::generate_alias(&config.server_name, &config.template_name);
|
|
||||||
|
|
||||||
let uri = if query_string.is_empty() {
|
|
||||||
format!(
|
|
||||||
"vmess://{}@{}:{}#{}",
|
|
||||||
config.xray_user_id,
|
|
||||||
config.hostname,
|
|
||||||
config.port,
|
|
||||||
utils::url_encode(&alias)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"vmess://{}@{}:{}?{}#{}",
|
|
||||||
config.xray_user_id,
|
|
||||||
config.hostname,
|
|
||||||
config.port,
|
|
||||||
query_string,
|
|
||||||
utils::url_encode(&alias)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(uri)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UriBuilder for VmessUriBuilder {
|
impl UriBuilder for VmessUriBuilder {
|
||||||
|
|||||||
@@ -107,7 +107,8 @@ impl UriGeneratorService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply variable substitution to JSON values
|
/// Apply variable substitution to JSON values (for testing)
|
||||||
|
#[cfg(test)]
|
||||||
pub fn apply_variable_substitution(
|
pub fn apply_variable_substitution(
|
||||||
&self,
|
&self,
|
||||||
template: &Value,
|
template: &Value,
|
||||||
@@ -132,6 +133,7 @@ impl UriGeneratorService {
|
|||||||
serde_json::from_str(&result)
|
serde_json::from_str(&result)
|
||||||
.map_err(|e| UriGeneratorError::VariableSubstitution(e.to_string()))
|
.map_err(|e| UriGeneratorError::VariableSubstitution(e.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for UriGeneratorService {
|
impl Default for UriGeneratorService {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ use xray_core::{
|
|||||||
common::protocol::User,
|
common::protocol::User,
|
||||||
common::serial::TypedMessage,
|
common::serial::TypedMessage,
|
||||||
core::InboundHandlerConfig,
|
core::InboundHandlerConfig,
|
||||||
prost_types,
|
|
||||||
proxy::shadowsocks::ServerConfig as ShadowsocksServerConfig,
|
proxy::shadowsocks::ServerConfig as ShadowsocksServerConfig,
|
||||||
proxy::shadowsocks::{Account as ShadowsocksAccount, CipherType},
|
proxy::shadowsocks::{Account as ShadowsocksAccount, CipherType},
|
||||||
proxy::trojan::Account as TrojanAccount,
|
proxy::trojan::Account as TrojanAccount,
|
||||||
@@ -24,23 +23,6 @@ use xray_core::{
|
|||||||
Client,
|
Client,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Convert PEM format to DER (x509) format
|
|
||||||
fn pem_to_der(pem_data: &str) -> Result<Vec<u8>> {
|
|
||||||
// Remove PEM headers and whitespace, then decode base64
|
|
||||||
let base64_data: String = pem_data
|
|
||||||
.lines()
|
|
||||||
.filter(|line| !line.starts_with("-----") && !line.trim().is_empty())
|
|
||||||
.map(|line| line.trim())
|
|
||||||
.collect::<Vec<&str>>()
|
|
||||||
.join("");
|
|
||||||
|
|
||||||
tracing::debug!("PEM to DER conversion: {} bytes", base64_data.len());
|
|
||||||
|
|
||||||
use base64::{engine::general_purpose, Engine as _};
|
|
||||||
general_purpose::STANDARD
|
|
||||||
.decode(&base64_data)
|
|
||||||
.map_err(|e| anyhow!("Failed to decode base64 PEM data: {}", e))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct InboundClient<'a> {
|
pub struct InboundClient<'a> {
|
||||||
endpoint: String,
|
endpoint: String,
|
||||||
@@ -364,7 +346,7 @@ impl<'a> InboundClient<'a> {
|
|||||||
/// Restart Xray with new configuration
|
/// Restart Xray with new configuration
|
||||||
pub async fn restart_with_config(
|
pub async fn restart_with_config(
|
||||||
&self,
|
&self,
|
||||||
config: &crate::services::xray::XrayConfig,
|
_config: &crate::services::xray::XrayConfig,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"Restarting Xray server at {} with new config",
|
"Restarting Xray server at {} with new config",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::collections::HashMap;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tokio::time::{timeout, Duration, Instant};
|
use tokio::time::{timeout, Duration, Instant};
|
||||||
use tracing::{error, warn};
|
use tracing::warn;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
@@ -307,7 +307,7 @@ impl XrayService {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use tokio::time::{sleep, Duration};
|
use tokio::time::Duration;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -378,7 +378,7 @@ mod tests {
|
|||||||
fn test_cached_connection_expiration() {
|
fn test_cached_connection_expiration() {
|
||||||
// Create a mock client for testing purposes
|
// Create a mock client for testing purposes
|
||||||
// In real tests, we would use a mock framework
|
// In real tests, we would use a mock framework
|
||||||
let now = Instant::now();
|
let _now = Instant::now();
|
||||||
|
|
||||||
// Test the expiration logic directly without creating an actual client
|
// Test the expiration logic directly without creating an actual client
|
||||||
let short_ttl = Duration::from_nanos(1);
|
let short_ttl = Duration::from_nanos(1);
|
||||||
|
|||||||
@@ -13,13 +13,12 @@ use xray_core::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub struct UserClient<'a> {
|
pub struct UserClient<'a> {
|
||||||
endpoint: String,
|
|
||||||
client: &'a Client,
|
client: &'a Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> UserClient<'a> {
|
impl<'a> UserClient<'a> {
|
||||||
pub fn new(endpoint: String, client: &'a Client) -> Self {
|
pub fn new(_endpoint: String, client: &'a Client) -> Self {
|
||||||
Self { endpoint, client }
|
Self { client }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add user to inbound (simple version that works)
|
/// Add user to inbound (simple version that works)
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ pub async fn get_user_configs(
|
|||||||
/// Get all URIs for all users of a specific inbound
|
/// Get all URIs for all users of a specific inbound
|
||||||
pub async fn get_inbound_configs(
|
pub async fn get_inbound_configs(
|
||||||
State(app_state): State<AppState>,
|
State(app_state): State<AppState>,
|
||||||
Path((server_id, inbound_id)): Path<(Uuid, Uuid)>,
|
Path((_server_id, inbound_id)): Path<(Uuid, Uuid)>,
|
||||||
) -> Result<Json<Vec<ClientConfigResponse>>, StatusCode> {
|
) -> Result<Json<Vec<ClientConfigResponse>>, StatusCode> {
|
||||||
let repo = InboundUsersRepository::new(app_state.db.connection().clone());
|
let repo = InboundUsersRepository::new(app_state.db.connection().clone());
|
||||||
let uri_service = UriGeneratorService::new();
|
let uri_service = UriGeneratorService::new();
|
||||||
|
|||||||
@@ -638,7 +638,7 @@ pub async fn remove_user_from_inbound(
|
|||||||
|
|
||||||
// Get inbound tag
|
// Get inbound tag
|
||||||
let template_repo = InboundTemplateRepository::new(app_state.db.connection().clone());
|
let template_repo = InboundTemplateRepository::new(app_state.db.connection().clone());
|
||||||
let template = match template_repo.find_by_id(inbound.template_id).await {
|
let _template = match template_repo.find_by_id(inbound.template_id).await {
|
||||||
Ok(Some(template)) => template,
|
Ok(Some(template)) => template,
|
||||||
Ok(None) => return Err(StatusCode::NOT_FOUND),
|
Ok(None) => return Err(StatusCode::NOT_FOUND),
|
||||||
Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR),
|
Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ pub struct TasksSummary {
|
|||||||
|
|
||||||
/// Get status of all scheduled tasks
|
/// Get status of all scheduled tasks
|
||||||
pub async fn get_tasks_status(
|
pub async fn get_tasks_status(
|
||||||
State(state): State<AppState>,
|
State(_state): State<AppState>,
|
||||||
) -> Result<Json<TasksStatusResponse>, StatusCode> {
|
) -> Result<Json<TasksStatusResponse>, StatusCode> {
|
||||||
// Get task status from the scheduler
|
// Get task status from the scheduler
|
||||||
// For now, we'll return a mock response since we need to expose the scheduler
|
// For now, we'll return a mock response since we need to expose the scheduler
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
database::entities::user_request::{CreateUserRequestDto, RequestStatus, UpdateUserRequestDto},
|
|
||||||
database::repository::UserRequestRepository,
|
database::repository::UserRequestRepository,
|
||||||
services::telegram::localization::{Language, LocalizationService},
|
services::telegram::localization::{Language, LocalizationService},
|
||||||
web::AppState,
|
web::AppState,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use tower_http::cors::CorsLayer;
|
|||||||
use tower_http::services::ServeDir;
|
use tower_http::services::ServeDir;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use crate::config::{AppConfig, WebConfig};
|
use crate::config::AppConfig;
|
||||||
use crate::database::DatabaseManager;
|
use crate::database::DatabaseManager;
|
||||||
use crate::services::{TelegramService, XrayService};
|
use crate::services::{TelegramService, XrayService};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use axum::{
|
use axum::{
|
||||||
routing::{delete, get, post, put},
|
routing::{get, post},
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user