sync
This commit is contained in:
parent
48f640b55d
commit
440a2fa01f
28 changed files with 1849 additions and 96 deletions
1
00-TERMS.md
Normal file
1
00-TERMS.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Hexagonal Architecture
|
||||||
28
00-archive/sources/all.md
Normal file
28
00-archive/sources/all.md
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
***linux***: {
|
||||||
|
*async-await kernel on rust*:
|
||||||
|
https://github.com/hexagonal-sun/moss-kernel
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
***ml*** {
|
||||||
|
*models*:
|
||||||
|
https://github.com/MoonshotAI/Kimi-K2
|
||||||
|
|
||||||
|
*db for ml*:
|
||||||
|
https://github.com/surrealdb/surrealdb
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
***rust*** {
|
||||||
|
*async/await runtime*:
|
||||||
|
https://github.com/tokio-rs/tokio
|
||||||
|
backend framework:
|
||||||
|
https://github.com/tokio-rs/axum
|
||||||
|
}
|
||||||
|
|
||||||
|
***utils*** {
|
||||||
|
*speech to text*:
|
||||||
|
https://github.com/kristoferlund/ostt
|
||||||
|
|
||||||
|
}
|
||||||
10
00-archive/Архив на зомби апокалипсис.md
Normal file
10
00-archive/Архив на зомби апокалипсис.md
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
# разделы:
|
||||||
|
|
||||||
|
## rust
|
||||||
|
## linux
|
||||||
|
## ml
|
||||||
|
## math
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
6
00-english/isac asimov.md
Normal file
6
00-english/isac asimov.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
stiffened - застыл
|
||||||
|
stuttered - заикался
|
||||||
|
wolfish leer - волчий оскал
|
||||||
|
nodded - кивнул
|
||||||
|
strangling cough -
|
||||||
|
quit cold – strike - забастовка
|
||||||
341
00-work/doc.md
Normal file
341
00-work/doc.md
Normal file
|
|
@ -0,0 +1,341 @@
|
||||||
|
# Понимание проекта Trails Server: Архитектурный разбор и гайд для контрибьютора
|
||||||
|
|
||||||
|
## 1. Философия архитектуры: Почему именно такая структура?
|
||||||
|
|
||||||
|
Проект следует принципам **Clean Architecture** (чистая архитектура) с четким разделением ответственности. Каждый crate — это слой с собственной причиной существования:
|
||||||
|
|
||||||
|
- **domain** изолирует бизнес-правила от деталей реализации (ORM, HTTP, БД)
|
||||||
|
- **entity** определяет data access objects (DAO), привязанные к SeaORM
|
||||||
|
- **infrastructure** содержит "грязные" детали: репозитории, внешние сервисы, шаблоны email
|
||||||
|
- **api-core** реализует бизнес-логику (use cases), зависящую только от domain
|
||||||
|
- **api** — чистый адаптер HTTP, преобразующий запросы/ответы, но не содержащий логики
|
||||||
|
- **bin** — entrypoint, который wire'ит все компоненты вместе
|
||||||
|
|
||||||
|
Эта структура позволяет:
|
||||||
|
- **Тестировать бизнес-логику без HTTP/БД** (mock'ая repository)
|
||||||
|
- **Менять технологии без рефакторинга** (например, заменить PostgreSQL на SQLite для тестов)
|
||||||
|
- **Разным бинарникам использовать одну логику** (admin и client)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Обзор ключевых концепций домена
|
||||||
|
|
||||||
|
### "Trail" — центральная сущность
|
||||||
|
На основе файлов миграций и структур, Trail — это контент-объект с жизненным циклом:
|
||||||
|
- **Статус**: `draft` → `public` (см. миграцию `m20250129_122341`)
|
||||||
|
- **Иерархия**: может быть root-элементом или child'ом (см. `m20241020_153507`)
|
||||||
|
- **Thread**: цепочка связанных trails (соединение через `thread_id`)
|
||||||
|
- **Content types**: поддержка множественных типов контента (`m20250618_120032`)
|
||||||
|
- **Аналитика**: история посещений (`trails_visit_history`)
|
||||||
|
- **Взаимодействие**: система лайков (`like.rs`)
|
||||||
|
|
||||||
|
### Abuse System
|
||||||
|
Модерация с многоуровневой системой репортов (degree 1/2) и причинами (`abuse_reason.rs`)
|
||||||
|
|
||||||
|
### Два API в одном сервисе
|
||||||
|
- **Client API** (`/client/*`): user-facing endpoints для создания trails, авторизации, профилей
|
||||||
|
- **Admin API** (`/admin/*`): administrative endpoints для модерации, статистики, управления пользователями
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Путешествие запроса: От TCP-сокета до БД
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ bin::client │
|
||||||
|
│ └── tokio::main() │
|
||||||
|
│ └── axum::Server::bind() │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ api::router::client::create_router() │
|
||||||
|
│ └── routes like POST /trails │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ api::endpoints::client::trails::create_trail() │
|
||||||
|
│ ├── Extractors (JWT, FormData) │
|
||||||
|
│ └── Валидация через validator crate │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ api_core::trails::TrailsService::create() │
|
||||||
|
│ └── Бизнес-правила: проверка прав, валидация статуса │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ infrastructure::repositories::trail::TrailRepository │
|
||||||
|
│ └── sea_orm::Entity::insert() (database abstraction) │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ entity::trail::ActiveModel → PostgreSQL │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ключевой принцип**: `api` crate **не знает** о существовании БД. Он работает с абстракциями (`api-core` traits).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Глубокое погружение в crates
|
||||||
|
|
||||||
|
### `entity` — Data Access Layer
|
||||||
|
- **Содержит**: SeaORM entity definitions, связанные 1:1 с таблицами БД
|
||||||
|
- **Важно**: Все поля `pub`, но это нормально — это DTO для ORM
|
||||||
|
- **Пример**: `user.rs` содержит `#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]`
|
||||||
|
- **Использование**: Никогда не используйте `entity` в бизнес-логике напрямую. Всегда маппайте в `domain`
|
||||||
|
|
||||||
|
### `domain` — Language of the Business
|
||||||
|
- **request/response**: DTO для API (разделены на `client`/`admin`)
|
||||||
|
- **common**: Переиспользуемые типы (пагинация, токены, abuse)
|
||||||
|
- **Валидация**: Derive-метки `#[derive(Validate)]` — central source of truth
|
||||||
|
- **Пример**: `domain::client::trails::CreateTrailRequest` с валидацией полей
|
||||||
|
|
||||||
|
### `infrastructure` — "Грязные детали"
|
||||||
|
**Репозитории** (`repositories/`):
|
||||||
|
```rust
|
||||||
|
// Паттерн: каждая сущность имеет trait + impl
|
||||||
|
#[async_trait]
|
||||||
|
pub trait TrailRepository: Send + Sync {
|
||||||
|
async fn create(&self, trail: domain::trails::NewTrail) -> Result<TrailId>;
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Реализация зависит от `sea_orm`, но домен ничего не знает об этом.
|
||||||
|
|
||||||
|
**Services** (`services/`):
|
||||||
|
- `auth.rs`: JWT encoding/decoding, bcrypt/argon2
|
||||||
|
- `mail.rs`: Askama templates (type-safe email HTML)
|
||||||
|
- `storage/`: Абстракция над файловым хранилищем через OpenDAL с **стратегиями**:
|
||||||
|
- `single`: простое хранение
|
||||||
|
- `mirror`: дублирование в несколько бакетов
|
||||||
|
- `backup`: асинхронный бэкап в cold storage
|
||||||
|
|
||||||
|
**Forms** (`forms/`): Type-safe multipart form handling (`axum_typed_multipart`)
|
||||||
|
|
||||||
|
### `api-core` — Use Cases Orchestrator
|
||||||
|
Содержит **stateless services**, которые координируют репозитории:
|
||||||
|
```rust
|
||||||
|
pub struct TrailService {
|
||||||
|
trail_repo: Arc<dyn TrailRepository>,
|
||||||
|
user_repo: Arc<dyn UserRepository>,
|
||||||
|
storage: Arc<dyn StorageService>,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Здесь проверяются сложные инварианты: права доступа, консистентность данных, транзакционность.
|
||||||
|
|
||||||
|
### `api` — HTTP Adapter
|
||||||
|
**Extractors** (`extractors/`):
|
||||||
|
- `jwt.rs`: Извлечение claims из `Authorization: Bearer`
|
||||||
|
- `api_key_extractor.rs`: API key auth
|
||||||
|
- `multitype_form_extractor.rs`: Объединяет JSON и multipart
|
||||||
|
|
||||||
|
**Middleware** (`middleware/`):
|
||||||
|
- `jwt.rs`: Проверка JWT
|
||||||
|
- `api_key_middleware.rs`: Rate limiting + API key validation
|
||||||
|
|
||||||
|
**Router**: Разделение на `admin.rs` и `client.rs` для безопасности
|
||||||
|
|
||||||
|
### `bin` — Composition Root
|
||||||
|
Инициализация зависимостей (DI container вручную):
|
||||||
|
```rust
|
||||||
|
let db_pool = infrastructure::db::connect().await?;
|
||||||
|
let trail_repo = Arc::new(TrailRepositoryImpl::new(db_pool.clone()));
|
||||||
|
let trail_service = Arc::new(TrailService::new(trail_repo, ...));
|
||||||
|
let router = api::router::client::create_router(trail_service, ...);
|
||||||
|
```
|
||||||
|
**Важно**: Никакой логики, только сборка компонентов.
|
||||||
|
|
||||||
|
### `migrate` — Database Schema as Code
|
||||||
|
SeaORM миграции с timestamp-именами (`m20240910_074808`). Каждая миграция — отдельный модуль, проверяющий `sea-orm-migration::MigrationTrait`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Как начать изучение: Практический план
|
||||||
|
|
||||||
|
### Шаг 1: Запуск и отладка
|
||||||
|
```bash
|
||||||
|
# 1. Понять переменные окружения (см. .env.example или docker-compose.yml)
|
||||||
|
DATABASE_URL=postgres://...
|
||||||
|
|
||||||
|
# 2. Запустить миграции
|
||||||
|
cargo run --bin migrate
|
||||||
|
|
||||||
|
# 3. Запустить клиентский API
|
||||||
|
cargo run --bin client
|
||||||
|
|
||||||
|
# 4. Проверить health endpoint
|
||||||
|
curl http://localhost:3000/client/ping
|
||||||
|
|
||||||
|
# 5. Запустить тестовый сценарий
|
||||||
|
hurl examples/positive/register_positive.hurl
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 2: Изучение через тесты
|
||||||
|
**Hurl-тесты** (`examples/positive/`) — лучшая документация:
|
||||||
|
```hurl
|
||||||
|
# examples/positive/user_login.hurl
|
||||||
|
POST http://localhost:3000/client/auth/login
|
||||||
|
{
|
||||||
|
"username": "testuser",
|
||||||
|
"password": "testpass123"
|
||||||
|
}
|
||||||
|
HTTP 200
|
||||||
|
[Captures]
|
||||||
|
access_token: jsonpath "$.access_token"
|
||||||
|
```
|
||||||
|
Это показывает **real-world usage** без изучения кода.
|
||||||
|
|
||||||
|
### Шаг 3: Трейсинг одной фичи
|
||||||
|
Выберите простой endpoint и пройдитесь по нему:
|
||||||
|
1. **GET /client/profiles/:id** → `api::endpoints::client::profiles.rs`
|
||||||
|
2. Смотрите, как используется `ProfileService`
|
||||||
|
3. Изучите `ProfileRepository` в `infrastructure`
|
||||||
|
4. Проверьте `domain::client::profiles::responses::ProfileResponse`
|
||||||
|
|
||||||
|
### Шаг 4: Изучение доменной модели
|
||||||
|
Прочитайте **все миграции по порядку** (`crates/migrate/src/`). Это история эволюции бизнес-требований:
|
||||||
|
- Сначала был User и Trail
|
||||||
|
- Потом добавили лайки, треды, статусы
|
||||||
|
- Затем abuse system
|
||||||
|
- Потом множественные content types
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Паттерны и конвенции
|
||||||
|
|
||||||
|
### Обработка ошибок
|
||||||
|
Все crates используют `thiserror` для domain errors и `anyhow` для верхнего уровня:
|
||||||
|
```rust
|
||||||
|
// domain ошибка
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum TrailError {
|
||||||
|
#[error("Trail not found")]
|
||||||
|
NotFound,
|
||||||
|
#[error("Permission denied")]
|
||||||
|
Forbidden,
|
||||||
|
}
|
||||||
|
|
||||||
|
// api уровень
|
||||||
|
pub type ApiResult<T> = Result<T, (StatusCode, String)>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Валидация
|
||||||
|
**Один источник правды**: Все валидации в `domain` DTO:
|
||||||
|
```rust
|
||||||
|
#[derive(Validate)]
|
||||||
|
pub struct CreateTrailRequest {
|
||||||
|
#[validate(length(min = 3, max = 200))]
|
||||||
|
pub title: String,
|
||||||
|
#[validate(custom = "validate_content_type")]
|
||||||
|
pub content_type: String,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Фича-флаги через миграции
|
||||||
|
Статусы, фичи и права добавляются через миграции, а не конфиги. Это делает состояние БД источником правды.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Где что менять: GPS для контрибьютора
|
||||||
|
|
||||||
|
### Добавить новое поле в Trail
|
||||||
|
1. **Миграция**: `crates/migrate/src/m2025xxxx_add_field.rs`
|
||||||
|
2. **Entity**: Добавить поле в `crates/entity/src/trail.rs`
|
||||||
|
3. **Domain**: Обновить `domain::trails::NewTrail` и Response
|
||||||
|
4. **Repository**: Изменить маппинг в `infrastructure::repositories::trail`
|
||||||
|
5. **Form**: Обновить `infrastructure::forms::forms_trail.rs` (если поле editable)
|
||||||
|
6. **Tests**: Добавить hurl-тест в `examples/positive/`
|
||||||
|
|
||||||
|
### Добавить новый endpoint
|
||||||
|
1. **Domain**: Создать Request/Response DTO
|
||||||
|
2. **API-core**: Создать метод в Service
|
||||||
|
3. **API**: Добавить endpoint в `api::endpoints::client` или `admin`
|
||||||
|
4. **Router**: Подключить в `api::router`
|
||||||
|
5. **Hurl**: Создать тест в `examples/`
|
||||||
|
6. **Utoipa**: Добавить `#[utoipa::path]` для Swagger-документации
|
||||||
|
|
||||||
|
### Исправить баг в бизнес-логике
|
||||||
|
**Ищите в `api-core`**, а не в `api`. Например, логика перехода Draft→Public в `TrailService::update_status()`.
|
||||||
|
|
||||||
|
### Поменять storage backend
|
||||||
|
Измените инициализацию в `infrastructure::services::storage_service.rs` или добавьте новый driver в `storage/drivers/`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Инструменты для вашего стека (NixOS + Helix)
|
||||||
|
|
||||||
|
### NixOS-specific
|
||||||
|
- **Postgres**: `services.postgresql.enable = true;` в configuration.nix
|
||||||
|
- **Rust toolchain**: Используйте `rust-overlay` или `fenix` для точной версии 1.92.0
|
||||||
|
- **Дев-окружение**: Создайте `shell.nix` с зависимостями: `postgresql`, `hurl`, `docker` (for tests)
|
||||||
|
|
||||||
|
### Helix editor workflow
|
||||||
|
```toml
|
||||||
|
# .helix/languages.toml
|
||||||
|
[language-server.rust-analyzer]
|
||||||
|
config = {
|
||||||
|
cargo = { features = "all" }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Jump to definition**: `gd` перейдет от `domain` к `entity` через trait
|
||||||
|
- **Global search**: `Space + /` ищите по `examples/` чтобы найти использование endpoint
|
||||||
|
- **LSP go-to-impl**: Посмотреть реализацию repository
|
||||||
|
|
||||||
|
### Быстрый поиск по коду
|
||||||
|
```bash
|
||||||
|
# Найти все использования TrailService
|
||||||
|
rg "TrailService" --type rust
|
||||||
|
|
||||||
|
# Найти все endpoints с /trails
|
||||||
|
rg "trails" crates/api/src/router/
|
||||||
|
|
||||||
|
# Найти миграции за конкретную дату
|
||||||
|
ls crates/migrate/src | rg "2025-02"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Риски и особенности для контрибьютора
|
||||||
|
|
||||||
|
### Version pinning
|
||||||
|
- `tokio = "1,<1.48"` — **исключительно важно**. Это ограничение может быть связано с `sea-orm` или `axum` совместимостью. Не обновляйте без тестов.
|
||||||
|
- `sea-orm = "=1.1.17"` — точная версия, возможно, из-за багов в 1.2+
|
||||||
|
|
||||||
|
### Async runtime
|
||||||
|
Везде используется `tokio::main`, но `sea-orm` использует `native-tls`, а `sqlx` — `rustls`. Это может вызвать конфликты SSL-библиотек в NixOS. Следите за линковкой.
|
||||||
|
|
||||||
|
### OpenDAL features
|
||||||
|
`opendal` собран без default features, только с memory и fs. Если понадобится S3, нужно расширить features в `infrastructure/Cargo.toml`.
|
||||||
|
|
||||||
|
### Migration dependencies
|
||||||
|
Миграции зависят от `entity` crate. Если меняете entity, **всегда пересобирайте миграции**:
|
||||||
|
```bash
|
||||||
|
cargo clean -p migrate && cargo build -p migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Чеклист перед PR
|
||||||
|
|
||||||
|
- [ ] Hurl-тесты проходят: `cargo test --test hurl_test`
|
||||||
|
- [ ] Clippy чист: `cargo clippy --all-targets --all-features`
|
||||||
|
- [ ] Форматирование: `cargo fmt --all`
|
||||||
|
- [ ] Миграции откатываются: `cargo run --bin migrate down`
|
||||||
|
- [ ] OpenAPI спецификация генерируется (если меняли endpoints)
|
||||||
|
- [ ] Логирование добавлено: `tracing::info!/`debug!` на важных шагах
|
||||||
|
- [ ] Обработка ошибок: Domain ошибки преобразованы в правильные HTTP статусы
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Финальный совет**: Начните с **трех файлов**:
|
||||||
|
1. `crates/migrate/src/lib.rs` — поймете эволюцию домена
|
||||||
|
2. `crates/api/src/router/client.rs` — увидите все endpoint'ы
|
||||||
|
3. `crates/api-core/src/trails.rs` — поймете бизнес-логику
|
||||||
|
|
||||||
|
Это даст вам mental model проекта за 30 минут. Удачи в контрибуции!
|
||||||
253
00-work/issues.md
Normal file
253
00-work/issues.md
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
почему в качестве валидации email - регулярка, когда можно использовать общепринятую функцию
|
||||||
|
|
||||||
|
tls конфликт
|
||||||
|
```
|
||||||
|
sea-orm: runtime-tokio-native-tls
|
||||||
|
sqlx: runtime-tokio-rustls
|
||||||
|
```
|
||||||
|
|
||||||
|
Причина долгого исполнения тестов: компиляция сервера каждый раз.
|
||||||
|
```rust
|
||||||
|
Command::new("cargo")
|
||||||
|
.args(["r", "--bin", "client"])
|
||||||
|
```
|
||||||
|
1. решение: предскомпилировать сервер, и переиспользовать.
|
||||||
|
2. Распараллелить тесты (может быть сложно)
|
||||||
|
3. **Проблема**: `DELETE FROM table;` — **медленная** операция (проверяет constraints, пишет в WAL). **TRUNCATE** — мгновенно.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
async fn clear_db(db: &DbConn) -> Result<(), DbErr> {
|
||||||
|
db.execute_unprepared("
|
||||||
|
TRUNCATE abuse, abuse_reason, admin, \"like\", mail, refreshtoken,
|
||||||
|
thread, trail, trails_visit_history, \"user\"
|
||||||
|
RESTART IDENTITY CASCADE;
|
||||||
|
").await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## 1. **Токио
|
||||||
|
|
||||||
|
```toml
|
||||||
|
tokio = { version = "1,<1.48", features = ["full"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. **SeaORM**
|
||||||
|
|
||||||
|
```toml
|
||||||
|
sea-orm = { version = "=1.1.17", ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. **SQLx + SeaORM**
|
||||||
|
|
||||||
|
**Почему это плохо**:
|
||||||
|
- `sea-orm` использует `sqlx` под капотом, но с другими runtime features (`native-tls` vs `rustls`)
|
||||||
|
- У вас **два пула соединений** с БД (один от SeaORM, второй от sqlx)
|
||||||
|
- Миграции на SeaORM, а тесты могут использовать sqlx напрямую → **schema drift** (расхождение схемы)
|
||||||
|
|
||||||
|
**Конкретный риск**: В `tests/Cargo.toml` вы подключаете оба. Если напишете тест, который использует `sqlx::query!` и `sea_orm::Entity::insert`, у вас будет **race condition** на уровне транзакций. PostgreSQL не увидит изменения из одного пула в другом до коммита, и ваши тесты будут flaky (нестабильными).
|
||||||
|
|
||||||
|
**Техдолг**: Вы платите за **complexity tax** (налог на сложность) дважды: за абстракцию ORM и за прямой доступ.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. **Storage Service: Premature abstraction, которая убьет вас**
|
||||||
|
|
||||||
|
Ваша абстракция хранилища с `mirror`, `backup`, `single` стратегиями — это **over-engineering без причины**.
|
||||||
|
|
||||||
|
**Почему это ошибка**:
|
||||||
|
- У вас нет SLA на durability (надежность хранения)
|
||||||
|
- У вас нет метрик на RTO/RPO (Recovery Time/Point Objectives)
|
||||||
|
- У вас нет **idempotency** в storage operations
|
||||||
|
|
||||||
|
**Конкретный баг**: В `mirror.rs` если первый write успешен, а второй — нет, у вас **silent data corruption** (тихая порча данных). У вас нет **transaction log** для отката первого write. Это **eventual consistency without reconciliation** (согласование в конечном счете без сверки).
|
||||||
|
|
||||||
|
**Техдолг**: Вы построили **distributed system** там, где был достаточен `std::fs::write`. Через год, когда storage заполнится, вы не поймете, почему `mirror` стратегия оставляет мусор в старых бакетах — потому что нет garbage collection.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. **DTO Hell: Вы создаете 3-4 структуры на каждую сущность**
|
||||||
|
|
||||||
|
Посмотрите на `domain/src/client/profiles/requests.rs` и `responses.rs`. У вас:
|
||||||
|
- `CreateUserRequest`
|
||||||
|
- `UpdateUserRequest`
|
||||||
|
- `PublicUserResponse`
|
||||||
|
- `PrivateUserResponse`
|
||||||
|
- `UserResponse`
|
||||||
|
|
||||||
|
**Это не separation of concerns, это duplication of concerns**. Каждое изменение поля `username` — это изменение в 5 файлах и 2 миграциях.
|
||||||
|
|
||||||
|
**Что вы потеряли**: **Single source of truth**. У вас нет `User` entity из домена, который бы гарантировал инварианты. Вместо этого вы полагаетесь на валидацию в каждом DTO отдельно.
|
||||||
|
|
||||||
|
**Техдолг**: **Schema spread**. Когда добавите поле `last_login_at`, вы забудете обновить один из Response DTO, и клиент получит panicking deserialization.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. **Abuse System: Security theater (имитация безопасности)**
|
||||||
|
|
||||||
|
У вас есть `CreateAbuseRequest`, `abuse.rs`, `abuse_reason.rs`, но нет **rate limiting** на создание abuse-репортов. Я могу написать скрипт, который заспамит 1M репортов на один trail и **сложит вашу БД**.
|
||||||
|
|
||||||
|
**Что не так**:
|
||||||
|
- Нет `abuse_reports_per_user_per_hour` констрейнта
|
||||||
|
- Нет `abuse_score` алгоритма (например, если 10 репортов от разных юзеров — автоматический бан)
|
||||||
|
- Нет `abuse_report_fingerprint` (я могу создавать репорты с разных аккаунтов, но с одного IP)
|
||||||
|
|
||||||
|
**Техдолг**: У вас есть **data model для безопасности, но нет enforcement**. Это как замок на двери без стен.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. **Transactions: Невидимый кошмар**
|
||||||
|
|
||||||
|
В `repository` трейтах у вас методы типа `create_trail`, `toggle_trail_like` — каждый **своя собственная транзакция**.
|
||||||
|
|
||||||
|
**Почему это баг**: Если в use-case нужно создать trail, создать thread, поставить лайк — у вас **три отдельные транзакции**. Если последняя упадет, первые две останутся в БД. У вас **inconsistent state** без `ROLLBACK`.
|
||||||
|
|
||||||
|
**Где посмотреть**: `api-core` должен принимать `&DatabaseTransaction` и выполнять все операции в ней. Сейчас у вас **autocommit hell** — каждый `repo.insert()` — это `COMMIT`.
|
||||||
|
|
||||||
|
**Техдолг**: У вас нет **transactional boundaries** на уровне use-case. Это значит, ваши данные **не консистентны** в границах бизнес-операции.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. **API Keys Middleware: Тёмная материя без тестов**
|
||||||
|
|
||||||
|
В `api/src/middleware/api_key_middleware.rs` (который вы не показали, но он подключен) — вероятно, нет **integration tests**.
|
||||||
|
|
||||||
|
**Что это значит**: Вы не знаете, работает ли rate limiting. Я могу brute-force API keys со скоростью 10k req/s, и ваша middleware может **не выдержать нагрузку**.
|
||||||
|
|
||||||
|
**Техдолг**: У вас **нет нагрузочных тестов** на middleware. Когда ваше приложение ляжет от DDoS, вы не поймете, виноват ли `axum`, `tower`, или ваш кастомный код.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. **Thread Model: Race condition при append**
|
||||||
|
|
||||||
|
`append_trail_to_thread` — это **concurrency nightmare**.
|
||||||
|
|
||||||
|
**Сценарий**:
|
||||||
|
1. User A читает thread (id=1, last_trail_id=5)
|
||||||
|
2. User B читает thread (id=1, last_trail_id=5)
|
||||||
|
3. Оба одновременно `append_trail_to_thread` с parent_id=5
|
||||||
|
4. У вас два trail'а с parent_id=5, но thread не знает, кто "победил"
|
||||||
|
|
||||||
|
У вас нет **optimistic locking** (`version` поле) или `SELECT FOR UPDATE`. Это **lost update problem** на уровне приложения.
|
||||||
|
|
||||||
|
**Техдолг**: У вас **нет SERIALIZABLE isolation** для критичных операций. Ваши треды могут разветвляться в невалидные структуры.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. **Observability: Вы слепы в production**
|
||||||
|
|
||||||
|
У вас есть `tracing`, но нет:
|
||||||
|
- **Metrics** (`prometheus`, `metrics-rs`)
|
||||||
|
- **Structured logging** (JSON logs для ELK/Loki)
|
||||||
|
- **Distributed tracing** (OpenTelemetry для Jaeger/Tempo)
|
||||||
|
|
||||||
|
**Что это значит**: Когда в продакшене упадет запрос, вы увидите:
|
||||||
|
```
|
||||||
|
INFO: request started
|
||||||
|
INFO: request failed
|
||||||
|
```
|
||||||
|
|
||||||
|
Но не увидите: `user_id`, `trace_id`, `duration_ms`, `db_query_count`. Вы **не сможете отладить performance regression** после вашего PR.
|
||||||
|
|
||||||
|
**Техдолг**: У вас **нет production-grade observability**. Это значит, вы **не сможете понять, что сломали**, когда добавите ML-фичу.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. **Release Profile: Thin LTO — это медленный production**
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[profile.release]
|
||||||
|
lto = "thin"
|
||||||
|
```
|
||||||
|
|
||||||
|
Для вашего уровня performance-чувствительности (RTX 3050, 24GB RAM) это **сознательный выбор скорости компиляции в ущерб runtime**.
|
||||||
|
|
||||||
|
**Что это значит**: Ваш бинарник будет на 15-30% медленнее, чем мог бы быть. Для "тропиночного" приложения это не критично, но если вы планируете **batch ML inference** на GPU, вы **теряете performance** из-за неинлайненных вызовов.
|
||||||
|
|
||||||
|
**Техдолг**: У вас **нет performance budget**. Вы не знаете, сколько RPS может выдержать ваш сервер, потому что не тестировали под нагрузкой с `lto = "fat"`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. **Migrations: Append-only log без checksums**
|
||||||
|
|
||||||
|
Ваш `Migrator` — это **poor man's Flyway**. Но нет:
|
||||||
|
- **Checksum** каждой миграции (если кто-то изменит старую миграцию, вы не узнаете)
|
||||||
|
- **Baseline** миграции (невозможно подключиться к существующей БД)
|
||||||
|
- **Repair** mode (если миграция упала на production)
|
||||||
|
|
||||||
|
**Что это значит**: Если ваш DevOps-engineer случайно отредактирует `m20241005_112021_create_like.rs` на проде-сервере, `cargo run --bin migrate` **не заметит изменения** и накатит кривую схему.
|
||||||
|
|
||||||
|
**Техдолг**: У вас **нет migration integrity checks**. Это значит, схема БД — **mutable history**, а не immutable log.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. **Dependencies: 150+ crate'ов, и вы не знаете, зачем**
|
||||||
|
|
||||||
|
Запустите `cargo tree | wc -l`. У вас больше 150 зависимостей для CRUD приложения.
|
||||||
|
|
||||||
|
**Конкретные лишние**:
|
||||||
|
- `rust-argon2` **и** `bcrypt` — два password hashing алгоритма. Выбери один.
|
||||||
|
- `lazy_static` **и** `once_cell` — дублирование (once_cell в std с 1.70)
|
||||||
|
- `utoipa` **и** `utoipa-swagger-ui` — генерируете OpenAPI, но нет тестов, что спецификация валидна
|
||||||
|
- `opendal` — вы используете только `memory` и `fs`, но это 50+ transitive dependencies
|
||||||
|
|
||||||
|
**Техдолг**: У вас **dependency bloat**. Каждый `cargo build` тянет мир, а security audit (cargo audit) будет плакать каждую неделю.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. **Test Coverage: Hurl — это не enough**
|
||||||
|
|
||||||
|
У вас 50+ hurl файлов, но **нет unit тестов** на `api-core`. Это значит, вы тестируете **только happy path** через HTTP.
|
||||||
|
|
||||||
|
**Что вы не тестируете**:
|
||||||
|
- **Error conditions**: Что если `TrailRepository::create` вернет `DbErr::ConnectionAcquire`?
|
||||||
|
- **Edge cases**: Что если `user_id` отрицательный? (SeaORM принимает i64, но должна валидировать)
|
||||||
|
- **Concurrency**: Что если 1000 потоков вызывают `toggle_trail_like` одновременно?
|
||||||
|
|
||||||
|
**Техдолг**: У вас **нет property-based testing** (proptest) и **нет fuzzing** (cargo-fuzz). Вы **не нашли race conditions и integer overflow bugs**, которые 100% есть в коде.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. **DTO Validation: Validate на уровне HTTP, а не домена**
|
||||||
|
|
||||||
|
Ваша валидация в `domain` DTO — это **security theater**. Почему?
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[derive(Validate)]
|
||||||
|
pub struct CreateTrailRequest {
|
||||||
|
#[validate(length(min = 3, max = 200))]
|
||||||
|
pub title: String,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Эта валидация **не вызывается автоматически**. Она вызывается в **extractor**. Если кто-то создаст `Trail` напрямую через `api-core`, минуя HTTP — валидация **пропущена**.
|
||||||
|
|
||||||
|
**Правильно**: Валидация должна быть в `NewTrail` **domain entity**, и она должна быть **infallible constructor** (конструктор, который не позволит создать невалидный объект).
|
||||||
|
|
||||||
|
**Техдолг**: У вас **type safety holes**. Невалидные данные могут просочиться в БД через прямой вызов api-core.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Финальная правда: Это хороший прототип, но не production system
|
||||||
|
|
||||||
|
Ваш проект — **MVP с правильной архитектурной формой, но без production-grade деталей**. Он выживет до первого серьезного инцидента:
|
||||||
|
|
||||||
|
- **До первого DDoS** — упадет, потому что нет rate limiting
|
||||||
|
- **До первого data corruption** — потеряете данные, потому что нет транзакций
|
||||||
|
- **До первого security breach** — скомпрометируете API keys, потому что нет audit log
|
||||||
|
- **До первого performance regression** — не поймете, что сломали, потому что нет метрик
|
||||||
|
|
||||||
|
**Что делать сейчас** (порядок приоритета):
|
||||||
|
|
||||||
|
1. **Удалите `sqlx` из workspace** — используйте только SeaORM
|
||||||
|
2. **Добавьте `TransactionTrait`** во все use-кейсы в `api-core`
|
||||||
|
3. **Напишите 10 unit тестов** на `TrailService` с `MockTrailRepository` — вы поймете, где у вас **logic gaps**
|
||||||
|
4. **Запустите `cargo audit`** и пофиксите critical уязвимости (их будет 5-10)
|
||||||
|
5. **Добавьте один rate limiter** в `api_key_middleware` — поймете, насколько ваш design **fragile**
|
||||||
|
|
||||||
|
Это не "у вас всё плохо". Это "у вас всё **правильно начато, но не закончено**".
|
||||||
69
00-work/onboarding.md
Normal file
69
00-work/onboarding.md
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
Скопировал .env из .env.example
|
||||||
|
|
||||||
|
выставил таким
|
||||||
|
API_CORS="http://localhost:3000,http://localhost:8000"
|
||||||
|
|
||||||
|
оставил каким есть:
|
||||||
|
DATABASE_URL="postgres://root:root@localhost/trails_db?sslmode=disable"
|
||||||
|
POSTGRES_USER="root"
|
||||||
|
POSTGRES_PASSWORD="root"
|
||||||
|
POSTGRES_PORT=5432
|
||||||
|
POSTGRES_DB="trails_db"
|
||||||
|
CLIENT_JWT_MAXAGE=60 # hours
|
||||||
|
ADMIN_JWT_MAXAGE=60 # hours
|
||||||
|
STORAGE_DOMAIN="https://static.thetrails.app"
|
||||||
|
STORAGE_FOLDER="./storage"
|
||||||
|
|
||||||
|
API_KEY
|
||||||
|
добавил туда чаще вывод из таких команд
|
||||||
|
|
||||||
|
openssl rand -base64 32
|
||||||
|
openssl rand -hex 32
|
||||||
|
|
||||||
|
PGADMIN_DEFAULT_EMAIL
|
||||||
|
PGADMIN_DEFAULT_PASSWORD
|
||||||
|
рандомный email, password
|
||||||
|
|
||||||
|
storage оставил таким какой он есть
|
||||||
|
|
||||||
|
# Поднимаем бд
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
docker compose ps # проверяем логи
|
||||||
|
|
||||||
|
docker compose logs -f postgres # тоже логи но бд
|
||||||
|
|
||||||
|
docker exec -it trails_postgres psql -U root -d trails_db # тестим бд
|
||||||
|
```
|
||||||
|
|
||||||
|
# Миграции:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run --bin migrate -- up
|
||||||
|
|
||||||
|
cargo run --bin migrate -- status
|
||||||
|
```
|
||||||
|
|
||||||
|
# storage
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir -p ./storage
|
||||||
|
```
|
||||||
|
|
||||||
|
# cargo run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run --bin client
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# hurl tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# поднимаем контейнер
|
||||||
|
docker run --rm ghcr.io/orange-opensource/hurl:latest --version
|
||||||
|
|
||||||
|
hurl --test examples/
|
||||||
|
```
|
||||||
|
|
||||||
3
00-work/upgrading.md
Normal file
3
00-work/upgrading.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
![[Pasted image 20251223184946.png]]
|
||||||
|
|
||||||
|
![[Pasted image 20251223185010.png]]
|
||||||
4
10-dictionary/dev_terms.md
Normal file
4
10-dictionary/dev_terms.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Hexagonal Architecture
|
||||||
|
(также "порты и адаптеры") с четкой экономией зависимостей: bin → api → api-core → domain ← infrastructure ← entity. Это не просто модное разделение — это оборона от технического долга.g
|
||||||
|
# domain
|
||||||
|
изолирует бизнес-правила от деталей реализации (ORM, HTTP, БД)
|
||||||
0
10-dictionary/linux-kernel_terms.md
Normal file
0
10-dictionary/linux-kernel_terms.md
Normal file
0
10-dictionary/linux-packages_terms.md
Normal file
0
10-dictionary/linux-packages_terms.md
Normal file
1
10-linux/20-open-wrt/Untitled.md
Normal file
1
10-linux/20-open-wrt/Untitled.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
134
10-linux/90-stories/arch-installing.md
Normal file
134
10-linux/90-stories/arch-installing.md
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
# cfdisk...
|
||||||
|
|
||||||
|
512M, EFI
|
||||||
|
остальное - Linux Filesystem
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkfs.fat -F 32 /dev/vda1
|
||||||
|
mkfs.ext4 /dev/vda2
|
||||||
|
|
||||||
|
mount /dev/vda2 /mnt
|
||||||
|
mkdir -p /mnt/boot
|
||||||
|
mount /dev/vda1 /mnt/boot
|
||||||
|
|
||||||
|
# проверяем монтирование
|
||||||
|
findmnt /mnt
|
||||||
|
findmnt /mnt/boot
|
||||||
|
|
||||||
|
# устанавливаем
|
||||||
|
pacstrap -K /mnt base linux linux-firmware sudo openssh
|
||||||
|
|
||||||
|
# если упало с ошибкой
|
||||||
|
mkdir -p /mnt/etc echo 'KEYMAP=us' > /mnt/etc/vconsole.conf
|
||||||
|
arch-chroot /mnt
|
||||||
|
mkinitcpio -P
|
||||||
|
exit
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mount | grep '/mnt'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Установка базовой системы
|
||||||
|
Дальше делаем базовую установку в `/mnt` через `pacstrap`, потом генерируем `fstab` и переходим в установленную систему через `arch-chroot` — это прям “хребет” официального Installation guide.[1]
|
||||||
|
|
||||||
|
Команды:
|
||||||
|
```bash
|
||||||
|
pacstrap -K /mnt base linux linux-firmware sudo openssh
|
||||||
|
genfstab -U /mnt >> /mnt/etc/fstab
|
||||||
|
arch-chroot /mnt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Настройка в chroot (минимум)
|
||||||
|
Внутри `arch-chroot` сделай базовые вещи: timezone/clock, locale, hostname, пароль root и (желательно) обычного пользователя — это следует общему порядку из Installation guide.[1]
|
||||||
|
|
||||||
|
Команды-шаблон:
|
||||||
|
```bash
|
||||||
|
ln -sf /usr/share/zoneinfo/Europe/Moscow /etc/localtime
|
||||||
|
hwclock --systohc
|
||||||
|
|
||||||
|
# locale
|
||||||
|
sed -i 's/^#\(en_US.UTF-8\)/\1/' /etc/locale.gen
|
||||||
|
locale-gen
|
||||||
|
printf "LANG=en_US.UTF-8\n" > /etc/locale.conf
|
||||||
|
|
||||||
|
# hostname + hosts
|
||||||
|
echo "archvm" > /etc/hostname
|
||||||
|
cat > /etc/hosts <<'EOF'
|
||||||
|
127.0.0.1 localhost
|
||||||
|
::1 localhost
|
||||||
|
127.0.1.1 archvm.localdomain archvm
|
||||||
|
EOF
|
||||||
|
|
||||||
|
passwd
|
||||||
|
useradd -m -G wheel -s /bin/bash rori
|
||||||
|
passwd rori
|
||||||
|
EDITOR=vi visudo # раскомментируй строку про %wheel ALL=(ALL:ALL) ALL
|
||||||
|
```
|
||||||
|
|
||||||
|
## Загрузчик + сеть + SSH + ребут
|
||||||
|
Раз у тебя UEFI и ESP смонтирован в `/boot`, можно ставить systemd-boot через `bootctl install`, а затем добавить загрузочную entry в `/boot/loader/entries/` — это ровно то, что описывает ArchWiki по systemd-boot. Чтобы после первого ребута продолжить работать по SSH, поставленный `openssh` нужно включить как сервис `sshd.service` на автозапуск.[3][4][5]
|
||||||
|
|
||||||
|
Команды (всё ещё внутри chroot):
|
||||||
|
```bash
|
||||||
|
bootctl install
|
||||||
|
|
||||||
|
ROOT_PARTUUID="$(blkid -s PARTUUID -o value /dev/vda2)"
|
||||||
|
cat > /boot/loader/entries/arch.conf <<EOF
|
||||||
|
title Arch Linux
|
||||||
|
linux /vmlinuz-linux
|
||||||
|
initrd /initramfs-linux.img
|
||||||
|
options root=PARTUUID=${ROOT_PARTUUID} rw
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Чтобы сеть поднялась после перезагрузки (самый простой вариант)
|
||||||
|
pacman -S --noconfirm networkmanager
|
||||||
|
systemctl enable NetworkManager
|
||||||
|
|
||||||
|
# SSH после ребута
|
||||||
|
systemctl enable sshd
|
||||||
|
```
|
||||||
|
|
||||||
|
Выход и перезагрузка:
|
||||||
|
```bash
|
||||||
|
exit
|
||||||
|
umount -R /mnt
|
||||||
|
reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
[1](https://wiki.archlinux.org/title/Installation_guide)
|
||||||
|
[2](https://wiki.archlinux.org/title/EFI_system_partition)
|
||||||
|
[3](https://wiki.archlinux.org/title/OpenSSH)
|
||||||
|
[4](https://wiki.archlinux.org/title/Systemd-boot)
|
||||||
|
[5](https://wiki.archlinux.org/title/Systemd-boot_(%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9))
|
||||||
|
[6](https://wiki.archlinux.org/title/Install_Arch_Linux_from_existing_Linux)
|
||||||
|
[7](https://wiki.archlinux.org/title/Installation_guide_(%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9))
|
||||||
|
[8](https://gist.github.com/mjkstra/96ce7a5689d753e7a6bdd92cdc169bae)
|
||||||
|
[9](https://linuxconfig.org/arch-linux-installation-easy-step-by-step-guide)
|
||||||
|
[10](https://www.tsunderechen.io/2020/05/archlinux-systemd-boot-installation/)
|
||||||
|
[11](https://dropvps.com/blog/arch-linux-enable-ssh-server/)
|
||||||
|
[12](https://habr.com/ru/articles/805671/)
|
||||||
|
[13](https://www.youtube.com/watch?v=FFXRFTrZ2Lk)
|
||||||
|
[14](https://gist.github.com/miguelmota/575af40ddb7f86b858a86a673cec3999)
|
||||||
|
[15](https://wiki.archlinux.org/title/Install_Arch_Linux_from_existing_Linux_(%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9))
|
||||||
|
[16](https://fhackts.wordpress.com/2016/09/09/installing-archlinux-the-efisystemd-boot-way/)
|
||||||
|
[17](https://bruhtus.github.io/posts/arch-installation-guide/)
|
||||||
|
[18](https://wiki.archlinux.org/title/OpenSSH_(%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9))
|
||||||
|
[19](https://www.youtube.com/watch?v=rUEnS1zj1DM)
|
||||||
|
[20](https://www.atlantic.net/dedicated-server-hosting/how-to-secure-ssh-server-on-arch-linux/)
|
||||||
|
[21](https://ru.scribd.com/document/601730020/Installation-guide-ArchWiki-Part-4)
|
||||||
|
|
||||||
|
|
||||||
|
# docker autostart
|
||||||
|
```
|
||||||
|
sudo systemctl enable --now docker.service
|
||||||
|
```
|
||||||
|
|
||||||
|
# base-devel
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo pacman -S --needed base-devel
|
||||||
|
```
|
||||||
303
10-linux/90-stories/gentoo-install.md
Normal file
303
10-linux/90-stories/gentoo-install.md
Normal file
|
|
@ -0,0 +1,303 @@
|
||||||
|
# 1) GPT + ESP + root
|
||||||
|
parted -a optimal /dev/vda mklabel gpt
|
||||||
|
parted /dev/vda mkpart ESP fat32 1MiB 513MiB
|
||||||
|
parted /dev/vda set 1 esp on
|
||||||
|
parted -a optimal /dev/vda mkpart rootfs ext4 513MiB 100%
|
||||||
|
|
||||||
|
# 2) На всякий случай перечитать таблицу (обычно хватает одного)
|
||||||
|
partprobe /dev/vda # перечитать partition table без ребута [web:137]
|
||||||
|
# (альтернатива, если вдруг /dev/vda2 не появился)
|
||||||
|
# partx -u /dev/vda && udevadm settle
|
||||||
|
|
||||||
|
# 3) ФС
|
||||||
|
mkfs.fat -F 32 /dev/vda1
|
||||||
|
mkfs.ext4 -L gentoo-root /dev/vda2
|
||||||
|
|
||||||
|
# 4) Монтирование
|
||||||
|
mount /dev/vda2 /mnt/gentoo
|
||||||
|
mkdir -p /mnt/gentoo/boot/efi
|
||||||
|
mount /dev/vda1 /mnt/gentoo/boot/efi
|
||||||
|
cd /mnt/gentoo
|
||||||
|
|
||||||
|
# 5) stage3
|
||||||
|
|
||||||
|
Да: **сначала нужно скачать stage3**, а уже потом выполнять `tar xpvf ...` (в каталоге `/mnt/gentoo`). Команда `tar ...` просто распаковывает tarball stage3 в будущий rootfs, после этого следующий шаг — подготовить `make.conf`, смонтировать `/proc /sys /dev /run` и зайти в chroot (там уже будет `emerge`).[1][2]
|
||||||
|
|
||||||
|
## Шаг 1 — скачать stage3 (OpenRC, headless)
|
||||||
|
Ты сейчас в `/mnt/gentoo`, это правильно. Дальше:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd /mnt/gentoo
|
||||||
|
wget https://distfiles.gentoo.org/releases/amd64/autobuilds/current-stage3-amd64-openrc/latest-stage3-amd64-openrc.txt
|
||||||
|
cat latest-stage3-amd64-openrc.txt
|
||||||
|
```
|
||||||
|
В этом файле будет имя актуального tarball’а (строка с `stage3-amd64-openrc-...tar.xz`).[1]
|
||||||
|
|
||||||
|
Скачать сам tarball (подставь имя из файла):
|
||||||
|
```sh
|
||||||
|
wget https://distfiles.gentoo.org/releases/amd64/autobuilds/current-stage3-amd64-openrc/<ИМЯ_ТАРБОЛЛА>.tar.xz
|
||||||
|
wget https://distfiles.gentoo.org/releases/amd64/autobuilds/current-stage3-amd64-openrc/<ИМЯ_ТАРБОЛЛА>.tar.xz.DIGESTS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Шаг 2 — распаковать stage3
|
||||||
|
Теперь да, выполняешь:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
tar xpvf stage3-*.tar.xz --xattrs-include='*.*' --numeric-owner
|
||||||
|
```
|
||||||
|
|
||||||
|
Эта команда — стандартный способ распаковки stage3 с сохранением xattrs и владельцев.[3][1]
|
||||||
|
|
||||||
|
## Шаг 3 — базовая подготовка до chroot
|
||||||
|
1) DNS в будущую систему:
|
||||||
|
```sh
|
||||||
|
cp -L /etc/resolv.conf /mnt/gentoo/etc/
|
||||||
|
```
|
||||||
|
|
||||||
|
2) (Опционально, но обычно делают сразу) `make.conf` пока хотя бы с `MAKEOPTS`:
|
||||||
|
```sh
|
||||||
|
nano -w /mnt/gentoo/etc/portage/make.conf
|
||||||
|
# например:
|
||||||
|
# MAKEOPTS="-j6"
|
||||||
|
```
|
||||||
|
`/etc/portage/make.conf` — основной файл глобальных настроек Portage.[4]
|
||||||
|
|
||||||
|
## Шаг 4 — смонтировать псевдо-ФС и зайти в chroot
|
||||||
|
```sh
|
||||||
|
mount -t proc /proc /mnt/gentoo/proc
|
||||||
|
mount --rbind /sys /mnt/gentoo/sys
|
||||||
|
mount --make-rslave /mnt/gentoo/sys
|
||||||
|
mount --rbind /dev /mnt/gentoo/dev
|
||||||
|
mount --make-rslave /mnt/gentoo/dev
|
||||||
|
mount --bind /run /mnt/gentoo/run
|
||||||
|
mount --make-slave /mnt/gentoo/run
|
||||||
|
|
||||||
|
chroot /mnt/gentoo /bin/bash
|
||||||
|
source /etc/profile
|
||||||
|
export PS1="(chroot) ${PS1}"
|
||||||
|
```
|
||||||
|
Такой набор монтирований — типовой для корректной работы внутри chroot (установка пакетов, udev-сокеты и т.д.).[2]
|
||||||
|
|
||||||
|
Скажи, на каком шаге остановишься (скачал/распаковал stage3 или уже вошёл в chroot) — и дальше дам точный минимальный набор команд: профиль, `emerge --sync`, базовые пакеты (dhcpcd+netifrc+openssh), kernel + GRUB под UEFI.
|
||||||
|
|
||||||
|
[1](https://distfiles.gentoo.org/releases/amd64/autobuilds/current-stage3-amd64-openrc/)
|
||||||
|
[2](https://emirror.d0a.io/wiki/wiki.gentoo.org/wiki/Handbook_AMD64/Installation/Base/de.html)
|
||||||
|
[3](https://www.reddit.com/r/Gentoo/comments/z3lmdq/first_time_installing_gentoo/)
|
||||||
|
[4](https://emirror.d0a.io/wiki/wiki.gentoo.org/wiki/etc/portage/make.html)
|
||||||
|
[5](https://distfiles.gentoo.org/releases/amd64/autobuilds/current-stage3-amd64-nomultilib-openrc/)
|
||||||
|
[6](https://gentoo.osuosl.org/releases/amd64/autobuilds/current-stage3-amd64-desktop-openrc/)
|
||||||
|
[7](https://mirrors.mit.edu/gentoo-distfiles/releases/amd64/autobuilds/current-stage3-amd64-openrc/)
|
||||||
|
[8](https://mirror.telepoint.bg/gentoo/releases/amd64/autobuilds/current-stage3-amd64-desktop-openrc/)
|
||||||
|
[9](https://emirror.d0a.io/wiki/wiki.gentoo.org/wiki/Handbook_AMD64/Installation/Base/ja.html)
|
||||||
|
[10](http://gentoo.osuosl.org/releases/amd64/autobuilds/)
|
||||||
|
[11](https://www.youtube.com/watch?v=HwY0IMfiiTA)
|
||||||
|
[12](http://www.mirrorservice.org/sites/distfiles.gentoo.org/releases/amd64/autobuilds/current-stage3-amd64-openrc/)
|
||||||
|
[13](https://www.reddit.com/r/linuxquestions/comments/16lublb/issue_w_gentoo_ab_halfway_through_installation/)
|
||||||
|
[14](https://www.reddit.com/r/Gentoo/comments/1acnqma/recover_from_gentoo_installation_mistake/)
|
||||||
|
[15](http://mirrors.lug.mtu.edu/gentoo/releases/amd64/autobuilds/)
|
||||||
|
[16](https://soso-cod3v.tistory.com/168)
|
||||||
|
[17](https://stephane-cheatsheets.readthedocs.io/en/latest/distros/gentoo-based/gentoo_installation/)
|
||||||
|
[18](https://mirror.kumi.systems/gentoo/releases/amd64/autobuilds/current-stage3-amd64-desktop-openrc/)
|
||||||
|
[19](https://www.reddit.com/r/linux/comments/ndvutp/tutorial_encrypted_gentoo_installation_guide/)
|
||||||
|
[20](https://www.scribd.com/document/86619356/Gentoo-Linux-AMD64-Handbook)
|
||||||
|
[21](http://ftp.riken.jp/Linux/gentoo/releases/amd64/autobuilds/)
|
||||||
|
|
||||||
|
Ты уже в chroot, значит дальше цель — чтобы система **первый раз загрузилась** и ты смог зайти по `ssh -p 2222 root@127.0.0.1`.
|
||||||
|
|
||||||
|
## Portage sync (репозиторий)
|
||||||
|
Сначала создай конфиг основного репозитория и синхронизируй дерево ebuild’ов:[1]
|
||||||
|
```sh
|
||||||
|
mkdir -p /etc/portage/repos.conf
|
||||||
|
cp /usr/share/portage/config/repos.conf /etc/portage/repos.conf/gentoo.conf
|
||||||
|
emerge --sync
|
||||||
|
```
|
||||||
|
(Если `cp` ругнётся, покажи вывод `ls -la /usr/share/portage/config/` — подберём правильный путь.)
|
||||||
|
|
||||||
|
## Минимальная “голова” системы
|
||||||
|
Дальше быстрые обязательные настройки (без них можно, но потом больнее дебажить):
|
||||||
|
```sh
|
||||||
|
echo "Europe/Moscow" > /etc/timezone
|
||||||
|
emerge --config sys-libs/timezone-data
|
||||||
|
|
||||||
|
echo "gentoo-vm" > /etc/hostname
|
||||||
|
|
||||||
|
# локали (пример)
|
||||||
|
nano -w /etc/locale.gen # раскомментируй en_US.UTF-8 UTF-8 (и при желании ru_RU.UTF-8)
|
||||||
|
locale-gen
|
||||||
|
eselect locale list
|
||||||
|
eselect locale set <номер>
|
||||||
|
env-update && source /etc/profile
|
||||||
|
```
|
||||||
|
|
||||||
|
## Сеть + SSH на OpenRC (под твой SLiRP NAT)
|
||||||
|
Поставь минимальный сетевой стек и SSH:
|
||||||
|
```sh
|
||||||
|
emerge --ask net-misc/netifrc net-misc/dhcpcd net-misc/openssh
|
||||||
|
```
|
||||||
|
|
||||||
|
Настрой DHCP на интерфейсе (имя смотри так: `ip link`, обычно будет `enp0s1`/`eth0`):
|
||||||
|
```sh
|
||||||
|
IF=enp0s1 # поменяй на своё
|
||||||
|
ln -s /etc/init.d/net.lo /etc/init.d/net.${IF}
|
||||||
|
echo "config_${IF}=\"dhcp\"" >> /etc/conf.d/net
|
||||||
|
rc-update add net.${IF} default
|
||||||
|
rc-update add sshd default
|
||||||
|
```
|
||||||
|
Формат `config_<iface>="dhcp"` — это штатная схема netifrc.[2]
|
||||||
|
Если вообще ничего не прописывать, netifrc часто и так поднимает DHCP на интерфейсах без явной конфигурации, но лучше зафиксировать явно, чтобы было предсказуемо.[3]
|
||||||
|
|
||||||
|
## Ядро + GRUB (UEFI)
|
||||||
|
Тут два пути:
|
||||||
|
|
||||||
|
1) Самый быстрый для “headless и без приключений”: поставить готовое ядро (`sys-kernel/gentoo-kernel-bin`) и дальше GRUB.
|
||||||
|
|
||||||
|
2) Если хочешь кастом/zen — это отдельный длинный шаг, лучше после первого успешного SSH.
|
||||||
|
|
||||||
|
GRUB ставится по handbook примерно так (у тебя ESP смонтирован в `/boot/efi`):[4]
|
||||||
|
```sh
|
||||||
|
emerge --ask sys-boot/grub sys-boot/efibootmgr
|
||||||
|
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Gentoo
|
||||||
|
|
||||||
|
emerge --ask sys-kernel/gentoo-kernel-bin
|
||||||
|
// возможно придется обновить конфиг: etc-update
|
||||||
|
|
||||||
|
grub-mkconfig -o /boot/grub/grub.cfg
|
||||||
|
```
|
||||||
|
|
||||||
|
## fstab (чтобы монтировалось после ребута)
|
||||||
|
Сгенерируй UUID и пропиши:
|
||||||
|
```sh
|
||||||
|
blkid
|
||||||
|
nano -w /etc/fstab
|
||||||
|
```
|
||||||
|
(Я вставил что то вроде этого)
|
||||||
|
```
|
||||||
|
# /etc/fstab: static file system information.
|
||||||
|
#
|
||||||
|
# See the manpage fstab(5) for more information.
|
||||||
|
#
|
||||||
|
# NOTE: The root filesystem should have a pass number of either 0 or 1.
|
||||||
|
# All other filesystems should have a pass number of 0 or greater than 1.
|
||||||
|
#
|
||||||
|
# NOTE: Even though we list ext4 as the type here, it will work with ext2/ext3
|
||||||
|
# filesystems. This just tells the kernel to use the ext4 driver.
|
||||||
|
#
|
||||||
|
# NOTE: You can use full paths to devices like /dev/sda3, but it is often
|
||||||
|
# more reliable to use filesystem labels or UUIDs. See your filesystem
|
||||||
|
# documentation for details on setting a label. To obtain the UUID, use
|
||||||
|
# the blkid(8) command.
|
||||||
|
|
||||||
|
# <fs> <mountpoint> <type> <opts> <dump> <pass>
|
||||||
|
|
||||||
|
#LABEL=boot /boot ext4 defaults 1 2
|
||||||
|
#UUID=58e72203-57d1-4497-81ad-97655bd56494 / xfs defaults 0 1
|
||||||
|
#LABEL=swap none swap sw 0 0
|
||||||
|
#/dev/cdrom /mnt/cdrom auto noauto,ro 0 0
|
||||||
|
UUID=3733ce80-bdb7-477d-a0bf-7a70122fa071 / ext4 defaults 0 1
|
||||||
|
UUID=66FA-9048 /boot/efi vfat umask=0077 0 2
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
а потом:
|
||||||
|
```
|
||||||
|
mount -a
|
||||||
|
findmnt /boot/efi
|
||||||
|
```
|
||||||
|
|
||||||
|
Минимально:
|
||||||
|
- root (`/dev/vda2`) → `/` ext4
|
||||||
|
- ESP (`/dev/vda1`) → `/boot/efi` vfat
|
||||||
|
|
||||||
|
## Финиш: пароль, выход, ребут
|
||||||
|
```sh
|
||||||
|
useradd -m -G wheel -s /bin/bash <user>
|
||||||
|
passwd <user>
|
||||||
|
|
||||||
|
emerge --ask app-admin/sudo
|
||||||
|
visudo
|
||||||
|
|
||||||
|
```
|
||||||
|
В `visudo` раскомментируй строку (или добавь), чтобы wheel мог sudo:
|
||||||
|
`%wheel ALL=(ALL:ALL) ALL`
|
||||||
|
```
|
||||||
|
|
||||||
|
passwd root
|
||||||
|
exit
|
||||||
|
umount -l /mnt/gentoo/dev{/shm,/pts,}
|
||||||
|
cd /
|
||||||
|
umount -R /mnt/gentoo
|
||||||
|
reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
Сейчас напиши вывод двух команд из chroot:
|
||||||
|
```sh
|
||||||
|
ls /sys/firmware/efi
|
||||||
|
ip link
|
||||||
|
```
|
||||||
|
По ним скажу точно: нужно ли отдельно монтировать `efivarfs`, и как именно называется твой сетевой интерфейс для `net.<iface>`.
|
||||||
|
|
||||||
|
[1](https://github.com/dudekmichal/gentoo/blob/master/readme.md)
|
||||||
|
[2](https://github.com/gentoo/netifrc/blob/master/doc/net.example.Linux.in)
|
||||||
|
[3](https://emirror.d0a.io/wiki/wiki.gentoo.org/wiki/Handbook_PPC/Full/Networking/zh-cn.html)
|
||||||
|
[4](https://emirror.d0a.io/wiki/wiki.gentoo.org/wiki/Handbook_AMD64/Installation/Bootloader/en.html)
|
||||||
|
[5](https://github.com/sergibarroso/gentoo_install)
|
||||||
|
[6](https://www.reddit.com/r/Gentoo/comments/17sztdp/how_to_speed_up_emerge_sync_synchronizing_with/)
|
||||||
|
[7](https://github.com/dudekmichal/gentoo)
|
||||||
|
[8](https://www.reddit.com/r/Gentoo/comments/9ua7lm/question_about_fully_amd64_installation/)
|
||||||
|
[9](https://blog.csdn.net/qq_40738478/article/details/129191358)
|
||||||
|
[10](https://www.reddit.com/r/Gentoo/comments/ffim09/installation_cannot_upgrade_portage_update_world/)
|
||||||
|
[11](https://gist.github.com/CDA0/4b1c2ee20cb68bdd4e72fb452fde9941)
|
||||||
|
[12](https://www.reddit.com/r/Gentoo/comments/ha2v6q/how_does_portage_and_its_config_files_work/)
|
||||||
|
[13](https://www.reddit.com/r/Gentoo/comments/1hj03zd/network_address_assigned_and_internet_connection/)
|
||||||
|
[14](https://www.reddit.com/r/Gentoo/comments/k7zpm3/grubinstall_error/)
|
||||||
|
[15](https://emirror.d0a.io/wiki/wiki.gentoo.org/wiki/etc/portage/repos.conf/it.html)
|
||||||
|
[16](https://wiki.archlinux.org/title/GRUB)
|
||||||
|
[17](https://www.reddit.com/r/Gentoo/comments/x0ke6i/problem_with_portage/)
|
||||||
|
[18](https://emirror.d0a.io/wiki/wiki.gentoo.org/wiki/Netifrc.html)
|
||||||
|
[19](https://www.reddit.com/r/Gentoo/comments/dfqah8/grub_efi_error_handbook/)
|
||||||
|
[20](https://emirror.d0a.io/wiki/wiki.gentoo.org/wiki/Handbook_AMD64/Full/Installation.html)
|
||||||
|
|
||||||
|
# timezone
|
||||||
|
|
||||||
|
`echo "Europe/Moscow" | sudo tee /etc/timezone`[](https://www.semirocket.science/noteblog/2023/03/gentoo-how-to-change-time-zone/)-
|
||||||
|
|
||||||
|
- `sudo emerge --config sys-libs/timezone-data`
|
||||||
|
отключить hwlock:
|
||||||
|
|
||||||
|
/etc/conf.d/hwlock:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Set CLOCK to "UTC" if your Hardware Clock is set to UTC (also known as
|
||||||
|
# Greenwich Mean Time). If that clock is set to the local time, then
|
||||||
|
# set CLOCK to "local". Note that if you dual boot with Windows, then
|
||||||
|
# you should set it to "local".
|
||||||
|
clock="UTC"
|
||||||
|
|
||||||
|
# If you want the hwclock script to set the system time (software clock)
|
||||||
|
# to match the current hardware clock during bootup, leave this
|
||||||
|
# commented out.
|
||||||
|
# However, you can set this to "NO" if you are running a modern kernel
|
||||||
|
# and using NTP to synchronize your system clock.
|
||||||
|
clock_hctosys="NO"
|
||||||
|
|
||||||
|
# If you do not want to set the hardware clock to the current system
|
||||||
|
# time (software clock) during shutdown, set this to no.
|
||||||
|
#clock_systohc="NO"
|
||||||
|
|
||||||
|
# If you wish to pass any other arguments to hwclock during bootup,
|
||||||
|
# you may do so here. Alpha users may wish to use --arc or --srm here.
|
||||||
|
clock_args=""
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# docker в группу (чтобы не писать sudo)
|
||||||
|
|
||||||
|
`sudo usermod -aG docker $USER`
|
||||||
|
|
||||||
|
# как я hurl качал (или keywords amd64 и ~amd64)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /etc/portage/package.accept_keywords
|
||||||
|
echo "net-misc/hurl ~amd64" | sudo tee /etc/portage/package.accept_keywords/hurl
|
||||||
|
sudo emerge -av net-misc/hurl
|
||||||
|
```
|
||||||
83
10-linux/90-stories/min env.md
Normal file
83
10-linux/90-stories/min env.md
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
strong need before first load:
|
||||||
|
|
||||||
|
Niri
|
||||||
|
OpenRC
|
||||||
|
neofetch
|
||||||
|
rustup
|
||||||
|
git
|
||||||
|
wget
|
||||||
|
curl
|
||||||
|
uutils
|
||||||
|
sudo-rs
|
||||||
|
fd
|
||||||
|
rg
|
||||||
|
iwd?
|
||||||
|
network-manager
|
||||||
|
dnsmasq
|
||||||
|
ffmpeg
|
||||||
|
openssl
|
||||||
|
Alacrity
|
||||||
|
helix-editor
|
||||||
|
Firefox-bin
|
||||||
|
chromium (бинкэш)
|
||||||
|
Gentoo Portage
|
||||||
|
keepassxc
|
||||||
|
llvm?
|
||||||
|
nautilus
|
||||||
|
p7zip
|
||||||
|
mkvtoolnix-cli
|
||||||
|
python3
|
||||||
|
pip
|
||||||
|
less
|
||||||
|
btrfs-progs
|
||||||
|
cmake
|
||||||
|
gcc
|
||||||
|
gdb
|
||||||
|
|
||||||
|
|
||||||
|
after:
|
||||||
|
gcovr
|
||||||
|
obsidian
|
||||||
|
uv
|
||||||
|
exercism
|
||||||
|
valgrind
|
||||||
|
cppcheck
|
||||||
|
check
|
||||||
|
doxygen
|
||||||
|
yt-dlp
|
||||||
|
qrencode
|
||||||
|
jq
|
||||||
|
zoxide
|
||||||
|
htop
|
||||||
|
dust
|
||||||
|
atuin
|
||||||
|
fish
|
||||||
|
starship
|
||||||
|
jj
|
||||||
|
procs
|
||||||
|
eza
|
||||||
|
ripgrep
|
||||||
|
just
|
||||||
|
telegram-desktop
|
||||||
|
qbittorrent
|
||||||
|
rocketchat-desktop
|
||||||
|
postgresql
|
||||||
|
qemu_full
|
||||||
|
docker
|
||||||
|
android-tools
|
||||||
|
tor
|
||||||
|
tor-browser
|
||||||
|
mandoc
|
||||||
|
vim
|
||||||
|
nano
|
||||||
|
vlc
|
||||||
|
audacity
|
||||||
|
obs-studio
|
||||||
|
imv
|
||||||
|
yazi
|
||||||
|
imagemagick
|
||||||
|
pandoc
|
||||||
|
libreoffice-bin
|
||||||
|
unixtools.netstat
|
||||||
|
lm_sensors
|
||||||
|
upower?
|
||||||
97
10-linux/90-stories/как я переименовывал кривой label.md
Normal file
97
10-linux/90-stories/как я переименовывал кривой label.md
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
Ок — если хочешь “пересоздать заново и забыть старое”, то план такой: **сначала вынести `home/qemu`**, потом **размонтировать**, сделать **mkfs.btrfs -f с новым label**, заново создать subvol’ы и обновить NixOS-конфиг под новый label+пути. `mkfs.btrfs -f` реально **уничтожит** старую ФС и всё на ней.[1]
|
||||||
|
|
||||||
|
## 1) Скопировать данные в безопасное место
|
||||||
|
Команда, которую ты написал, ок **если ты реально находишься в `/mnt/gentoo-root`** (как в примере), и `~/temp` существует:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/temp
|
||||||
|
cp -a /mnt/gentoo-root/home/qemu ~/temp/
|
||||||
|
```
|
||||||
|
|
||||||
|
Рекомендую `cp -a` вместо `cp -r`, чтобы сохранить права/времена/симлинки.
|
||||||
|
Проверь, что копия на месте: `ls -la ~/temp/qemu`.
|
||||||
|
|
||||||
|
## 2) Размонтировать старые точки
|
||||||
|
Нужно отмонтировать все subvol-ы с этого диска (иначе `mkfs` не даст, или ты рискуешь).
|
||||||
|
В NixOS:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo umount /mnt/gentoo-root/images
|
||||||
|
sudo umount /mnt/gentoo-root/distfiles
|
||||||
|
sudo umount /mnt/gentoo-root/home
|
||||||
|
sudo umount /mnt/gentoo-root
|
||||||
|
```
|
||||||
|
|
||||||
|
Если где-то “busy”, покажет кто держит: `sudo lsof +f -- /mnt/gentoo-root` (или `fuser -mv`).
|
||||||
|
|
||||||
|
## 3) Пересоздать btrfs с новым label
|
||||||
|
Теперь форматируем раздел (у тебя это `/dev/nvme0n1p2` по `lsblk`), и задаём новый label, например `nvme-950`. Опция `-L/--label` задаёт label файловой системы.[1]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkfs.btrfs -f -L nvme-950 /dev/nvme0n1p2
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4) Создать subvol’ы заново (правильно — на top-level id=5)
|
||||||
|
Чтобы создать subvol на “корне” btrfs, монтируем top-level (`subvolid=5`), создаём subvol, и размонтируем.[2][3]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /mnt/tmp_nvme950
|
||||||
|
sudo mount -o subvolid=5 /dev/disk/by-label/nvme-950 /mnt/tmp_nvme950
|
||||||
|
|
||||||
|
sudo btrfs subvolume create /mnt/tmp_nvme950/@
|
||||||
|
sudo btrfs subvolume create /mnt/tmp_nvme950/@home
|
||||||
|
sudo btrfs subvolume create /mnt/tmp_nvme950/@distfiles
|
||||||
|
sudo btrfs subvolume create /mnt/tmp_nvme950/@images
|
||||||
|
|
||||||
|
sudo umount /mnt/tmp_nvme950
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5) Обновить NixOS-конфиг
|
||||||
|
Теперь можно:
|
||||||
|
- переименовать mountpoint’ы на `/mnt/nvme-950/...`
|
||||||
|
- и device сменить на `/dev/disk/by-label/nvme-950`
|
||||||
|
|
||||||
|
То есть в твоём файле:
|
||||||
|
- все `"/mnt/gentoo-root"` → `"/mnt/nvme-950"`
|
||||||
|
- все `by-label/gentoo_root` → `by-label/nvme-950`
|
||||||
|
- в `tmpfiles.rules` тоже заменить пути.
|
||||||
|
|
||||||
|
После этого:
|
||||||
|
```bash
|
||||||
|
sudo nixos-rebuild switch
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6) Вернуть qemu обратно
|
||||||
|
Когда новый диск смонтировался:
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /mnt/nvme-950/home
|
||||||
|
sudo cp -a ~/temp/qemu /mnt/nvme-950/home/
|
||||||
|
sudo chown -R wave:users /mnt/nvme-950/home/qemu
|
||||||
|
```
|
||||||
|
|
||||||
|
Если это btrfs и ты хочешь отключить CoW для qcow2 (как раньше делал), делай `chattr +C` на каталог **до** создания/копирования новых qcow2. (На уже существующие файлы это не всегда помогает.)
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
Перед тем как форматировать: подтверди, что правильный раздел — это **точно** `/dev/nvme0n1p2` (953.9G диск → 952.9G partition), а не второй NVMe на 1.8T. Самый надёжный чек: `ls -l /dev/disk/by-id/ | rg nvme0n1p2` и/или `blkid /dev/nvme0n1p2`.
|
||||||
|
|
||||||
|
[1](https://man7.org/linux/man-pages/man8/mkfs.btrfs.8.html)
|
||||||
|
[2](https://discussion.fedoraproject.org/t/btrfs-subvol-created-but-does-not-need-mounting/127943/4)
|
||||||
|
[3](https://forum.endeavouros.com/t/want-to-create-btrfs-top-level-5-subvolume-in-an-already-installed-system/43712)
|
||||||
|
[4](https://www.reddit.com/r/linuxquestions/comments/oj64ti/how_to_change_filesystem_label_using_commandline/)
|
||||||
|
[5](https://www.cheat-sheets.org/project/tldr/command/mkfs.btrfs/os/linux/)
|
||||||
|
[6](https://btrfs.readthedocs.io/en/latest/Administration.html)
|
||||||
|
[7](https://gitlab.denx.de/denx/eldk/-/blob/4ebd9bbbeb0dd52a5f346266129eeae9b3c5941a/meta/recipes-devtools/btrfs-tools/btrfs-tools/upstream-tmp/0011-Add-the-btrfs-filesystem-label-command.patch)
|
||||||
|
[8](https://www.reddit.com/r/NixOS/comments/1lhkffb/creating_a_new_btrfs_subvolume_on_existingrunning/)
|
||||||
|
[9](https://man.archlinux.org/man/mkfs.btrfs.8.en)
|
||||||
|
[10](https://github.com/nix-community/disko/blob/master/example/btrfs-subvolumes.nix)
|
||||||
|
[11](https://www.mankier.com/8/mkfs.btrfs)
|
||||||
|
[12](https://fedoramagazine.org/working-with-btrfs-subvolumes/)
|
||||||
|
[13](https://wiki.nixos.org/wiki/Btrfs)
|
||||||
|
[14](https://man7.org/linux/man-pages/man8/btrfs-property.8.html)
|
||||||
|
[15](https://bbs.archlinux.org/viewtopic.php?id=276843)
|
||||||
|
[16](https://mt-caret.github.io/blog/posts/2020-06-29-optin-state.html)
|
||||||
|
[17](https://man.archlinux.org/man/btrfs.8.en)
|
||||||
|
[18](https://btrfs.readthedocs.io/en/latest/btrfs-subvolume.html)
|
||||||
|
[19](https://discourse.nixos.org/t/persistent-btrfs-subvolume-mounting/30021)
|
||||||
|
[20](https://forum.mxlinux.org/viewtopic.php?t=79038)
|
||||||
57
10-linux/90-stories/сценарий монтирования и chroot.md
Normal file
57
10-linux/90-stories/сценарий монтирования и chroot.md
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
Почти: после загрузки livecd сначала нужно **смонтировать разделы твоей установленной системы**, а уже потом — `proc/sys/dev/run` и `chroot`. Иначе ты будешь “чрутьнуться в пустоту” или в старое состояние.[1]
|
||||||
|
|
||||||
|
## Правильный порядок
|
||||||
|
1) Смонтировать root и ESP:
|
||||||
|
```sh
|
||||||
|
mount /dev/vda2 /mnt/gentoo
|
||||||
|
mkdir -p /mnt/gentoo/boot/efi
|
||||||
|
mount /dev/vda1 /mnt/gentoo/boot/efi
|
||||||
|
```
|
||||||
|
|
||||||
|
2) Потом уже то, что ты написал (proc/sys/dev/run) и chroot:
|
||||||
|
```sh
|
||||||
|
mount -t proc /proc /mnt/gentoo/proc
|
||||||
|
mount --rbind /sys /mnt/gentoo/sys
|
||||||
|
mount --make-rslave /mnt/gentoo/sys
|
||||||
|
mount --rbind /dev /mnt/gentoo/dev
|
||||||
|
mount --make-rslave /mnt/gentoo/dev
|
||||||
|
mount --bind /run /mnt/gentoo/run
|
||||||
|
mount --make-slave /mnt/gentoo/run
|
||||||
|
|
||||||
|
chroot /mnt/gentoo /bin/bash
|
||||||
|
source /etc/profile
|
||||||
|
export PS1="(chroot) ${PS1}"
|
||||||
|
```
|
||||||
|
|
||||||
|
# размонтировать
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exit
|
||||||
|
umount -l /mnt/gentoo/dev{/shm,/pts,}
|
||||||
|
cd /
|
||||||
|
umount -R /mnt/gentoo
|
||||||
|
reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
`--rbind` + `--make-rslave` как раз нужны, чтобы внутри chroot корректно “виделись” устройства и динамические mount’ы, но изменения не утекали обратно в хостовую систему.[2][3]
|
||||||
|
|
||||||
|
[1](https://www.simplified.guide/gentoo/build-chroot-environment)
|
||||||
|
[2](https://emirror.d0a.io/wiki/wiki.gentoo.org/wiki/Handbook_Alpha/Installation/Base.html)
|
||||||
|
[3](https://www.reddit.com/r/linuxquestions/comments/czgk8u/what_is_the_purpose_of_bind_mounting_rbind/)
|
||||||
|
[4](https://www.reddit.com/r/Gentoo/comments/14ou6z8/trying_to_chroot_into_install_but_nothing_exists/)
|
||||||
|
[5](https://wiki.archlinux.org/title/Chroot)
|
||||||
|
[6](https://groups.google.com/g/linux.gentoo.user/c/hADFN2EHjmY)
|
||||||
|
[7](https://www.funtoo.org/Talk:Install/Chroot)
|
||||||
|
[8](https://gist.github.com/zunkree/c28ccef99df972e4af75a97616bab2ff)
|
||||||
|
[9](https://www.reddit.com/r/Gentoo/comments/wge8w5/cant_chroot_into_gentoo/)
|
||||||
|
[10](https://gist.github.com/gekola/7bd15bfd80f94a257a76093f218cbb43)
|
||||||
|
[11](https://emirror.d0a.io/wiki/wiki.gentoo.org/wiki/Handbook_AMD64/Installation/Base/ja.html)
|
||||||
|
[12](https://www.linux.org.ru/forum/general/13908919)
|
||||||
|
[13](https://www.youtube.com/watch?v=oXJrevOr7Pc)
|
||||||
|
[14](https://grandekos.com/index.php?static_page=Memorize%2FLinux+things%2Fchroot+some+directory)
|
||||||
|
[15](https://www.linux.org.ru/forum/general/13611252)
|
||||||
|
[16](https://www.reddit.com/r/Gentoo/comments/w2qlel/what_would_happen_if_i_dont_use_mount_makerslave/)
|
||||||
|
[17](https://www.reddit.com/r/Gentoo/comments/xgr9b6/error_during_installation/)
|
||||||
|
[18](https://www.reddit.com/r/Gentoo/comments/143wq54/regaining_access_to_install_via_chroot/)
|
||||||
|
[19](https://seekstar.github.io/2022/04/27/%E5%9C%A8chroot%E7%8E%AF%E5%A2%83%E4%B8%AD%E6%8C%82%E8%BD%BDdev-proc-sys/)
|
||||||
|
[20](https://github.com/sorah/gentoo-build/blob/master/scripts/prepare-chroot.sh)
|
||||||
131
20-dev/00-rust/10-no_std/Untitled.md
Normal file
131
20-dev/00-rust/10-no_std/Untitled.md
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
Ты задал отличный вопрос, который вскрывает один из самых интересных нюансов системного программирования.
|
||||||
|
|
||||||
|
Действительно, **Moss kernel** (и некоторые другие проекты вроде *Hubris* от Oxide) используют `async/await` прямо внутри ядра. Это не противоречие, а смена подхода к тому, что такое «многозадачность ядра».
|
||||||
|
|
||||||
|
Давай разберем, почему они так сделали и почему это отличается от того `async`, который мы используем в вебе.
|
||||||
|
|
||||||
|
### 1. Ядро — это и есть «самый большой асинхронный рантайм»
|
||||||
|
Подумай, что делает ядро ОС:
|
||||||
|
1. Ждет нажатия клавиши (прерывание).
|
||||||
|
2. Ждет, пока диск дочитает сектор (DMA).
|
||||||
|
3. Ждет пакета от сетевой карты.
|
||||||
|
|
||||||
|
Традиционные ядра (Linux, Windows) решают это через сложную систему прерываний, колбэков, workqueues и конечных автоматов, написанных вручную на C. Это сложно читать и легко ошибиться.
|
||||||
|
|
||||||
|
**В чем идея Moss:**
|
||||||
|
Вместо того чтобы писать эти конечные автоматы (state machines) вручную, они используют Rust-компилятор.
|
||||||
|
* `async fn read_disk()` в ядре Moss — это не запуск Tokio. Это способ сказать компилятору: «сгенерируй для меня state machine, которая приостановится, пока контроллер диска не пришлет прерывание».
|
||||||
|
* Это позволяет писать драйверы так, будто код выполняется линейно, хотя на самом деле он разбит на микро-кусочки, ожидающие железа.
|
||||||
|
|
||||||
|
### 2. Async в ядре != Async в вебе
|
||||||
|
В Moss нет «тяжелого» рантайма типа Tokio.
|
||||||
|
* **В вебе (Tokio):** Рантайм использует потоки ОС (threads), work-stealing, и сложные очереди, чтобы утилизировать все ядра CPU.
|
||||||
|
* **В ядре (Moss/no_std):** `Executor` — это часть планировщика ядра. Он очень примитивный.
|
||||||
|
* Когда драйвер делает `.await`, он просто говорит планировщику: «Не буди меня, пока не сработает вот это прерывание».
|
||||||
|
* Это **Cooperative Multitasking** (кооперативная многозадачность) внутри ядра. Это *дешевле*, чем создавать отдельный поток ядра (kernel thread) для каждого драйвера.
|
||||||
|
|
||||||
|
### 3. Почему это круто (Best Practices & Philosophy)
|
||||||
|
Использование `async` в ядре решает проблему **Complexity Management**.
|
||||||
|
* **Без async:** Ты пишешь обработчик прерывания, который ставит флаг, потом таймер, который проверяет флаг, потом функцию, которая доделывает работу... Код размазан по файлу.
|
||||||
|
* **С async:** Ты пишешь:
|
||||||
|
```rust
|
||||||
|
// Псевдокод драйвера в Moss
|
||||||
|
async fn handle_packet() {
|
||||||
|
let header = nic.wait_for_header().await; // Ждем железо
|
||||||
|
let data = nic.dma_read(header.len).await; // Ждем DMA
|
||||||
|
process(data);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Логика линейна. Компилятор сам нарежет это на состояния. Это безопаснее и понятнее.
|
||||||
|
|
||||||
|
### Резюме для менти
|
||||||
|
То, что Moss написан на async — это **не про производительность** (как в Nginx/Tokio), а про **эргономику кода драйверов**.
|
||||||
|
|
||||||
|
* Они используют `async/await` как синтаксический сахар для управления сложными конечными автоматами железа.
|
||||||
|
* Для твоих задач (Backend/Systems Tools) правило остается прежним: если ты не пишешь ядро или встроенное ПО для микроконтроллера (Embedded Rust), выбирай инструмент по задаче (I/O -> Async, CPU -> Sync).
|
||||||
|
|
||||||
|
В ядре они *вынуждены* быть асинхронными, потому что железо асинхронно по своей природе. Rust просто дал им удобный синтаксис для этого.
|
||||||
|
|
||||||
|
[1](https://github.com/hexagonal-sun/moss-kernel)
|
||||||
|
[2](https://www.reddit.com/r/rust/comments/1p2dhex/moss_a_rust_linuxcompatible_kernel_in_about_26000/)
|
||||||
|
[3](https://news.ycombinator.com/item?id=46012803)
|
||||||
|
[4](https://www.alm.com/press_release/alm-intelligence-updates-verdictsearch/?s-news-13792569-2025-11-28-moss-a-rust-based-linux-compatible-kernel-developed-in-26000-lines-of-code)
|
||||||
|
[5](https://prohoster.info/en/blog/novosti-interneta/proekt-moss-razvivaet-linux-sovmestimoe-yadro-na-yazyke-rust)
|
||||||
|
[6](https://rust-lang.github.io/async-book/01_getting_started/02_why_async.html)
|
||||||
|
[7](https://www.reddit.com/r/rust/comments/130hcbc/how_does_async_rust_work/)
|
||||||
|
[8](https://news.ycombinator.com/item?id=20738606)
|
||||||
|
[9](https://www.youtube.com/watch?v=RVFI1NRhhCg)
|
||||||
|
[10](https://www.wyeworks.com/blog/2025/02/25/async-rust-when-to-use-it-when-to-avoid-it/)
|
||||||
|
[11](https://os.phil-opp.com/async-await/)
|
||||||
|
[12](https://kobzol.github.io/rust/2025/01/15/async-rust-is-about-concurrency.html)
|
||||||
|
[13](https://www.osnews.com/story/143918/moss-a-linux-compatible-kernel-written-in-rust/)
|
||||||
|
[14](https://news.ycombinator.com/item?id=46077481)
|
||||||
|
[15](https://www.reddit.com/r/rust/comments/1ax32ot/how_is_asynchronous_programming_implemented_under/)
|
||||||
|
[16](https://www.reddit.com/r/rust/comments/1gvmtok/asyncawait_is_real_and_can_hurt_you/)
|
||||||
|
[17](https://www.facebook.com/nixcraft/posts/moss-is-a-unix-like-linux-compatible-kernel-written-in-rust-and-aarch64-assembly/1272741511551520/)
|
||||||
|
[18](https://without.boats/blog/why-async-rust/)
|
||||||
|
[19](https://news.ycombinator.com/item?id=22729461)
|
||||||
|
[20](https://www.reddit.com/r/rust/comments/ec1bnu/what_are_the_common_use_cases_of_async_await/)
|
||||||
|
|
||||||
|
Это отличный вопрос, который показывает, что ты начинаешь мыслить архитектурно.
|
||||||
|
|
||||||
|
**Короткий ответ: В Redox OS изначально не было `async/await`, но сейчас он внедряется — но иначе, чем в Moss.**
|
||||||
|
|
||||||
|
В Moss `async/await` — это **основа ядра** (монолитный подход к драйверам внутри ядра). В Redox OS — это **инструмент для драйверов в userspace** и оптимизации системных вызовов (через `io_uring`).
|
||||||
|
|
||||||
|
Вот подробности, почему так и в чем разница:
|
||||||
|
|
||||||
|
### 1. Архитектура диктует инструменты
|
||||||
|
* **Moss Kernel:** Это (в основном) монолитный подход в плане исполнения драйверов. Драйверы работают в пространстве ядра. Чтобы они не блокировали ядро, используется кооперативная многозадачность через `async`.
|
||||||
|
* **Redox OS:** Это **микроядро**.
|
||||||
|
* Драйверы — это обычные процессы (userspace daemons).
|
||||||
|
* Файловая система — это процесс.
|
||||||
|
* Сетевой стек — это процесс.
|
||||||
|
* Ядро очень маленькое, его задача — перекладывать сообщения (Message Passing) между процессами.
|
||||||
|
|
||||||
|
### 2. Как работает Redox (Схемы vs Async)
|
||||||
|
В Redox все построено на концепции **URL Schemes** (`file:`, `tcp:`, `http:`).
|
||||||
|
Изначально Redox использовал блокирующие системные вызовы. Процесс делает `read()`, ядро блокирует его, посылает сообщение драйверу, ждет ответа.
|
||||||
|
|
||||||
|
**Почему это стало проблемой?**
|
||||||
|
В userspace-драйверах это неудобно. Если драйвер сетевой карты обрабатывает 1000 пакетов, ему нужно либо 1000 потоков (дорого), либо сложный event loop (сложно).
|
||||||
|
|
||||||
|
**Что изменилось (2024-2025):**
|
||||||
|
Redox активно внедряет поддержку асинхронности, вдохновляясь Linux `io_uring`.
|
||||||
|
1. **Userspace Drivers:** Драйверы в Redox начинают переписывать на `async/await`, чтобы они могли эффективно обрабатывать очереди сообщений от ядра, не плодя потоки.
|
||||||
|
2. **Kernel Interface:** Ядро учится принимать "пачки" запросов асинхронно, чтобы уменьшить оверхед на переключение контекста (syscall overhead), который в микроядрах всегда высок.
|
||||||
|
|
||||||
|
### 3. Сравнение подходов (Mental Model)
|
||||||
|
|
||||||
|
| Характеристика | Moss Kernel | Redox OS |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| **Где живет Async?** | Внутри ядра (Kernel Space). | В драйверах (User Space) + интерфейс ядра. |
|
||||||
|
| **Роль Async** | Замена прерываниям и сложным State Machines железа. | Оптимизация Message Passing между процессами. |
|
||||||
|
| **Аналогия** | Как JavaScript в браузере (Single thread, non-blocking). | Как микросервисы (gRPC), которые общаются асинхронно. |
|
||||||
|
|
||||||
|
### Вывод для тебя
|
||||||
|
Redox не *написан* на async так тотально, как Moss. Redox — это про изоляцию. Но он *использует* async там, где это нужно для производительности I/O.
|
||||||
|
|
||||||
|
Если ты хочешь поковырять **"Async в ядре"** — смотри Moss или **Embassy** (для embedded).
|
||||||
|
Если хочешь понять **"Async в системной архитектуре"** (как построить ОС из микросервисов) — смотри Redox.
|
||||||
|
|
||||||
|
[1](https://www.redox-os.org/news/io_uring-3/)
|
||||||
|
[2](https://www.reddit.com/r/rust/comments/1ax32ot/how_is_asynchronous_programming_implemented_under/)
|
||||||
|
[3](https://users.rust-lang.org/t/state-of-async-await-unrestrained-cooperation-is-not-cooperative/131119)
|
||||||
|
[4](https://news.ycombinator.com/item?id=22729461)
|
||||||
|
[5](https://news.ycombinator.com/item?id=22727985)
|
||||||
|
[6](https://www.redox-os.org/news/io_uring-5/)
|
||||||
|
[7](http://wiki.osdev.org/Message_Passing)
|
||||||
|
[8](https://os.phil-opp.com/async-await/)
|
||||||
|
[9](https://www.webpronews.com/redox-os-roadmap-rust-driven-speed-boosts-to-challenge-linux/)
|
||||||
|
[10](https://docs.redoxengine.com/basics/data-exchange-with-redox/process-async-traffic-with-child-queues/)
|
||||||
|
[11](https://www.youtube.com/watch?v=0HwrZp9CBD4)
|
||||||
|
[12](https://stackoverflow.com/questions/7931537/whats-the-difference-between-asynchronous-non-blocking-event-base-architectu)
|
||||||
|
[13](https://www.osnews.com/story/29463/a-complete-rewrite-of-the-redox-kernel/)
|
||||||
|
[14](https://github.com/redox-os/drivers)
|
||||||
|
[15](https://news.ycombinator.com/item?id=21657744)
|
||||||
|
[16](https://news.ycombinator.com/item?id=18274235)
|
||||||
|
[17](https://www.redox-os.org/news/this-week-in-redox-25/)
|
||||||
|
[18](https://www.redox-os.org/news/io_uring-4/)
|
||||||
|
[19](https://github.com/bendudson/EuraliOS)
|
||||||
|
[20](https://www.reddit.com/r/rust/comments/128048k/asyncawait_in_action_rustbased_kernel_with/)
|
||||||
|
|
@ -21,6 +21,10 @@ methods:
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.saturating_sub()
|
.saturating_sub()
|
||||||
.try_fold()
|
.try_fold()
|
||||||
|
.append() // по &mut ссылке
|
||||||
|
.extend() // ownership (removes obj in parantheses)
|
||||||
|
.flatten()
|
||||||
|
.then_some()
|
||||||
|
|
||||||
|
|
||||||
crates:
|
crates:
|
||||||
|
|
@ -33,8 +37,8 @@ traits:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|Префикс|Что делает (Технический смысл)|Пример|Стоимость (Cost)|
|
| Префикс | Что делает (Технический смысл) | Пример | Стоимость (Cost) |
|
||||||
|---|---|---|---|
|
| ------- | ----------------------------------------------------------------------------------- | -------------------- | ------------------------------------- |
|
||||||
|as_|View / Borrow. Бесплатное преобразование типа, работающее с ссылкой.|as_bytes()|Нулевая (Zero-cost)|
|
| as_ | View / Borrow. Бесплатное преобразование типа, работающее с ссылкой. | as_bytes() | Нулевая (Zero-cost) |
|
||||||
|to_|Clone / Allocate. Создает новую копию данных (тяжелая операция).|to_string(),to_vec()|Аллокация памяти|
|
| to_ | Clone / Allocate. Создает новую копию данных (тяжелая операция). | to_string(),to_vec() | Аллокация памяти |
|
||||||
|into_|Consume. Потребляет (съедает) переменную. После этого старая переменная недоступна.|into_iter()|Обычно дешево (перемещение указателя)|
|
| into_ | Consume. Потребляет (съедает) переменную. После этого старая переменная недоступна. | into_iter() | Обычно дешево (перемещение указателя) |
|
||||||
|
|
|
||||||
4
20-dev/00-rust/20-dictionary/books.md
Normal file
4
20-dev/00-rust/20-dictionary/books.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
Beej's Guide to Network Programming.
|
||||||
|
High Performance Browser Networking
|
||||||
|
Rust in Action
|
||||||
|
TCP/IP Illustrated, Vol. 1: The Protocols
|
||||||
75
20-dev/00-rust/50-rust-gdb/cheat-sheet.md
Normal file
75
20-dev/00-rust/50-rust-gdb/cheat-sheet.md
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
Конечно. Вот сжатая шпаргалка для **rust-gdb** (или чистого GDB), оптимизированная под твой стиль работы (терминал, эффективность, понимание процессов).
|
||||||
|
|
||||||
|
### 🚀 Запуск и База
|
||||||
|
```bash
|
||||||
|
rust-gdb target/debug/my_app # Запуск с отладочными символами
|
||||||
|
rust-gdb --args target/debug/my_app arg1 arg2 # Запуск с аргументами
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🎮 Управление (Control Flow)
|
||||||
|
| Команда | Сокр. | Описание |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `run` | `r` | Запустить программу с начала |
|
||||||
|
| `continue` | `c` | Продолжить выполнение до следующей точки |
|
||||||
|
| `next` | `n` | **Шагнуть через** (выполнить строку, не заходя в функции) |
|
||||||
|
| `step` | `s` | **Шагнуть внутрь** (зайти внутрь функции на этой строке) |
|
||||||
|
| `finish` | `fin` | Выполнить функцию до конца и вернуться на уровень выше |
|
||||||
|
| `quit` | `q` | Выйти из GDB |
|
||||||
|
|
||||||
|
### 🛑 Точки останова (Breakpoints)
|
||||||
|
| Команда | Описание |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `break main` | Остановиться на входе в `main` |
|
||||||
|
| `break 42` | Остановиться на строке 42 текущего файла |
|
||||||
|
| `break utils.rs:15` | Остановиться в файле `utils.rs` на строке 15 |
|
||||||
|
| `break is_prime if n == 13` | **Условный брейкпоинт** (остановиться, только если n = 13) |
|
||||||
|
| `info break` | Показать список всех точек |
|
||||||
|
| `delete 1` | Удалить точку останова номер 1 |
|
||||||
|
| `clear` | Удалить точку на текущей строке |
|
||||||
|
|
||||||
|
### 🕵️ Инспекция данных
|
||||||
|
| Команда | Сокр. | Описание |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `print x` | `p` | Вывести значение переменной `x` |
|
||||||
|
| `print/x x` | `p/x` | Вывести в **HEX** (шестнадцатеричном) виде |
|
||||||
|
| `print/t x` | `p/t` | Вывести в **BIN** (двоичном) виде (полезно для битмасок!) |
|
||||||
|
| `display x` | | Автоматически выводить `x` при каждом шаге |
|
||||||
|
| `undisplay 1` | | Перестать следить (по ID из `info display`) |
|
||||||
|
| `info locals` | | Показать все локальные переменные в текущем скоупе |
|
||||||
|
| `ptype x` | | Показать точный тип переменной `x` |
|
||||||
|
|
||||||
|
### 🖥 Визуальный режим (TUI)
|
||||||
|
Это то, что делает GDB похожим на IDE в терминале.
|
||||||
|
* **`Ctrl + X`, `A`** (или команда `layout src`) — Включить TUI (код + консоль).
|
||||||
|
* **`Ctrl + X`, `2`** — Добавить окно ассемблера (код + asm + консоль).
|
||||||
|
* **`Ctrl + L`** — Перерисовать экран (если поплыл вывод).
|
||||||
|
* **`Up`/`Down`** — Скролить код (пока фокус на коде).
|
||||||
|
* **`Ctrl + P` / `Ctrl + N`** — История команд (как в bash).
|
||||||
|
|
||||||
|
### 🛠 Продвинутые фишки
|
||||||
|
* **`backtrace` (`bt`)**: Показать стек вызовов (кто вызвал эту функцию). Критично при панике.
|
||||||
|
* **`frame 2` (`f 2`)**: Прыгнуть в стек-фрейм #2 (в функцию, которая вызвала текущую), чтобы посмотреть её переменные.
|
||||||
|
* **`set var x = 10`**: **Изменить** значение переменной прямо во время выполнения (для тестов "а что если").
|
||||||
|
* **`watch x`**: Остановиться, когда значение `x` **изменится** (hardware watchpoint). Супер для поиска багов, когда "кто-то портит память".
|
||||||
|
|
||||||
|
### 💡 Пример сессии
|
||||||
|
```gdb
|
||||||
|
# 1. Ставим брейк на функцию
|
||||||
|
b miller_rabin
|
||||||
|
# 2. Запускаем
|
||||||
|
r
|
||||||
|
# 3. Включаем красивый вид
|
||||||
|
layout src
|
||||||
|
# 4. Смотрим аргумент
|
||||||
|
p n
|
||||||
|
# 5. Идем по шагам
|
||||||
|
n
|
||||||
|
n
|
||||||
|
# 6. Заходим в mod_pow
|
||||||
|
s
|
||||||
|
# 7. Смотрим биты степени
|
||||||
|
p/t exp
|
||||||
|
```
|
||||||
|
|
||||||
|
$X \cdot X \cdot X \dots$
|
||||||
|
|
||||||
1
20-dev/terms.md
Normal file
1
20-dev/terms.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
**Hexagonal Architecture** (также "порты и адаптеры") с четкой экономией зависимостей: `bin → api → api-core → domain ← infrastructure ← entity`. Это не просто модное разделение — это оборона от **технического долга**.
|
||||||
77
30-money/pyshop_foreachpartners/Untitled.md
Normal file
77
30-money/pyshop_foreachpartners/Untitled.md
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
Сейчас задача — за 10 минут получить максимум ясности и не “подписаться” на бесконечный бесплатный онбординг. План можно вести как короткий сценарий разговора.
|
||||||
|
|
||||||
|
## Структура созвона (15–25 минут)
|
||||||
|
1) 1 минута: выравнивание ожиданий.
|
||||||
|
2) 5–7 минут: выяснить проект и роль (что именно делать на Rust).
|
||||||
|
3) 5–7 минут: зафиксировать формат “пилота” (объём/срок/критерии готовности).
|
||||||
|
4) 3–5 минут: деньги и юридика (самозанятость/ГПХ, оплата, акты).
|
||||||
|
5) 1–2 минуты: следующий шаг и кто принимает решение.
|
||||||
|
|
||||||
|
Такой список вопросов “что делать/как оформляем/как платим” — нормальная практика для кандидата и советуется в гайдах по вопросам к работодателю.[1][2]
|
||||||
|
|
||||||
|
## Открывающая фраза (чтобы уверенно рулить)
|
||||||
|
“Хочу быстро понять: какая первая задача под Rust, как вы видите онбординг, и чтобы не было недопонимания — на каких условиях стартуем (ГПХ/самозанятость, схема оплаты).”
|
||||||
|
|
||||||
|
## Блок 1: про проект (7 вопросов)
|
||||||
|
- Какой продукт/клиенты и что именно делает команда (1–2 предложения)?
|
||||||
|
- Почему открыли роль Rust-разработчика именно сейчас?[3]
|
||||||
|
- Какие типовые задачи на первые 2–4 недели?
|
||||||
|
- Какой стек вокруг Rust (DB, очередь, deployment, observability)?
|
||||||
|
- Кто мой непосредственный техлид/ревьюер и как проходит code review?[4]
|
||||||
|
- Какие артефакты ожидаются: сервис, библиотека, интеграция, CLI?
|
||||||
|
- Есть ли уже репозиторий/CI/процессы или это “с нуля”?
|
||||||
|
|
||||||
|
## Блок 2: “пилот” (самое важное)
|
||||||
|
Скажи прямо, но спокойно:
|
||||||
|
|
||||||
|
“Предлагаю начать с пилота: одна небольшая задача с фиксированным объёмом на 1 неделю (или 10 часов), с критериями готовности (DoD), по итогам — продолжаем.”
|
||||||
|
|
||||||
|
И вопросы:
|
||||||
|
- Какая одна задача может быть пилотом?
|
||||||
|
- Какой DoD/критерии приёмки для этой задачи? (например: PR смержен, тесты зелёные, дока есть)
|
||||||
|
- Кто принимает результат: тимлид или PM?
|
||||||
|
- Где будет выполняться работа: в их репозитории или можно в отдельном?
|
||||||
|
|
||||||
|
## Блок 3: деньги и оформление (3–5 минут)
|
||||||
|
Тут важно не “просить”, а “фиксировать условия”. Вопросы:
|
||||||
|
- Оформление: ГПХ с физлицом или договор с самозанятым (НПД)?
|
||||||
|
- Оплата: ставка/час или фикс за этап? и **когда** платёж (после акта/раз в месяц/по этапам)?
|
||||||
|
- Можно ли аванс/разбивка 50/50 на первый этап (это резко снижает риск неоплаты)?[5]
|
||||||
|
- Когда готовы назвать вилку (прямо сегодня или после разговора с тимлидом)?
|
||||||
|
|
||||||
|
Если они опять уходят от оплаты:
|
||||||
|
“Ок, тогда давайте зафиксируем, что до согласования ставки я делаю только ознакомление/мини‑тест до N часов, без прод‑задач.”
|
||||||
|
|
||||||
|
## Красные флаги прямо на созвоне
|
||||||
|
- “Дадим реальные таски, а про деньги потом” (без срока/условий).
|
||||||
|
- “Онбординг” дольше недели без явной схемы оплаты.
|
||||||
|
- Нет владельца приёмки результата (кто подписывает акт/говорит Done).
|
||||||
|
|
||||||
|
## Финал: закрепи следующий шаг
|
||||||
|
“Договорились: сегодня вы присылаете (1) описание пилотной задачи/DoD, (2) формат договора (НПД/ГПХ), (3) вилку или схему оплаты. Послезавтра на созвоне с тимлидом утверждаем пилот и стартуем.”
|
||||||
|
|
||||||
|
Если хочешь, напиши одним сообщением: сколько времени созвон и что они уже обещали (онбординг сколько дней, какие задачи, удалёнка/график). Под это можно подстроить формулировки “пилота” максимально конкретно.
|
||||||
|
|
||||||
|
[1](https://hh.ru/article/25614)
|
||||||
|
[2](https://aspro.cloud/pm/docs/provedenie-sobesedovaniya-s-soiskatelem/)
|
||||||
|
[3](https://habr.com/ru/articles/655631/)
|
||||||
|
[4](https://selecty.ru/blog/questions-to-the-employer)
|
||||||
|
[5](https://recruitment.by/rus/news/payment-under-contractor-agreement/)
|
||||||
|
[6](https://moscow.mba/journal/kak-provodit-sobesedovanie-s-kandidatom-na-rabotu-sekrety-uspeshnogo-podbora-personala)
|
||||||
|
[7](https://www.freshdoc.ru/dogovor/dogovory_podryada/na_okazanie_uslug_programmista/)
|
||||||
|
[8](https://stewardshipexcellence.com.au/wp-content/uploads/2024/03/PSCoE-Practice-Note-Checklist-for-Pilot-Projects.pdf)
|
||||||
|
[9](https://www.gazprombank.ru/pro-finance/lifestyle/kakie-voprosy-zadat-na-sobesedovanii/)
|
||||||
|
[10](https://evaluation.treasury.gov.au/sites/evaluation.treasury.gov.au/files/2025-07/guide-evaluating-pilot-programs.pdf)
|
||||||
|
[11](https://skillbox.ru/media/management/7-tipov-voprosov-iz-za-kotoryh-vas-mogut-ne-vzyat-na-rabotu/)
|
||||||
|
[12](https://advokat-poluektov.ru/publish/spory-po-dogovoram-podryada-nekotorye-nyuansy-kotorye-nado-uchityvat/)
|
||||||
|
[13](https://www.sfmta.com/es/media/30919/download?inline)
|
||||||
|
[14](https://konsol.pro/blog/sotrudnichestva-po-dogovoru-gph)
|
||||||
|
[15](https://workspace.ru/docs/software-development-contract-instructions-for-drafting-and-ready-made-template/)
|
||||||
|
[16](https://www.teamwork.com/blog/pilot-project-example/)
|
||||||
|
[17](https://potok.io/blog/hr-howto/questions-to-applicant-at-interview/)
|
||||||
|
[18](https://habr.com/ru/articles/922998/)
|
||||||
|
[19](https://www.unisys.com/blog-post/six-questions-to-help-set-up-an-effective-pilot-and-system-rollout/)
|
||||||
|
[20](https://tproger.ru/articles/50-voprosov-rabotodatelju-na-sobesedovanii-v-it)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
7
30-money/pyshop_foreachpartners/issues.md
Normal file
7
30-money/pyshop_foreachpartners/issues.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
backdoor admin
|
||||||
|
черновик
|
||||||
|
аватарка при логауте
|
||||||
|
|
||||||
|
|
||||||
|
2021 -> 2024
|
||||||
|
|
@ -2,30 +2,36 @@
|
||||||
|
|
||||||
пример кода, близкого к эталонному:
|
пример кода, близкого к эталонному:
|
||||||
```rust
|
```rust
|
||||||
pub fn nth(n: u32) -> u32 {
|
fn main() {
|
||||||
let mut num = 1;
|
let n: u64 = std::env::args().nth(1)
|
||||||
for _ in 0..=n {
|
.and_then(|s| s.parse().ok())
|
||||||
loop {
|
.unwrap_or(35);
|
||||||
num += 1;
|
|
||||||
if miller_rabin(num as u64) {
|
println!("{} -> {}", n, if is_prime(n) { "prime" } else { "composite" });
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
num
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn miller_rabin(n: u64) -> bool {
|
pub fn is_prime(n: u64) -> bool {
|
||||||
const HINT: &[u64] = &[2];
|
if n < 2 { return false; }
|
||||||
|
if n == 2 || n == 3 { return true; }
|
||||||
|
if n % 2 == 0 { return false; }
|
||||||
|
|
||||||
// we have a strict upper bound, so we can just use the witness
|
let s = (n - 1).trailing_zeros();
|
||||||
// table of Pomerance, Selfridge & Wagstaff and Jeaschke to be as
|
let d = (n - 1) >> s;
|
||||||
// efficient as possible, without having to fall back to
|
|
||||||
// randomness. Additional limits from Feitsma and Galway complete
|
let witnesses = get_witnesses(n);
|
||||||
// the entire range of `u64`. See also:
|
|
||||||
// https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test#Testing_against_small_sets_of_bases
|
for &a in witnesses {
|
||||||
|
if !miller_rabin_test(a, d, n, s) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// O(1) lookup based on input size
|
||||||
|
fn get_witnesses(n: u64) -> &'static [u64] {
|
||||||
const WITNESSES: &[(u64, &[u64])] = &[
|
const WITNESSES: &[(u64, &[u64])] = &[
|
||||||
(2_046, HINT),
|
(2_046, &[2]),
|
||||||
(1_373_652, &[2, 3]),
|
(1_373_652, &[2, 3]),
|
||||||
(9_080_190, &[31, 73]),
|
(9_080_190, &[31, 73]),
|
||||||
(25_326_000, &[2, 3, 5]),
|
(25_326_000, &[2, 3, 5]),
|
||||||
|
|
@ -35,88 +41,50 @@ fn miller_rabin(n: u64) -> bool {
|
||||||
(3_474_749_660_382, &[2, 3, 5, 7, 11, 13]),
|
(3_474_749_660_382, &[2, 3, 5, 7, 11, 13]),
|
||||||
(341_550_071_728_320, &[2, 3, 5, 7, 11, 13, 17]),
|
(341_550_071_728_320, &[2, 3, 5, 7, 11, 13, 17]),
|
||||||
(3_825_123_056_546_413_050, &[2, 3, 5, 7, 11, 13, 17, 19, 23]),
|
(3_825_123_056_546_413_050, &[2, 3, 5, 7, 11, 13, 17, 19, 23]),
|
||||||
(std::u64::MAX, &[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]),
|
(u64::MAX, &[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]),
|
||||||
];
|
];
|
||||||
|
|
||||||
if n % 2 == 0 {
|
WITNESSES.iter()
|
||||||
return n == 2;
|
.find(|(limit, _)| *limit >= n)
|
||||||
}
|
.map(|(_, w)| *w)
|
||||||
if n == 1 {
|
.unwrap()
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut d = n - 1;
|
|
||||||
let mut s = 0;
|
|
||||||
while d % 2 == 0 {
|
|
||||||
d /= 2;
|
|
||||||
s += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
let witnesses = WITNESSES
|
|
||||||
.iter()
|
|
||||||
.find(|&&(hi, _)| hi >= n)
|
|
||||||
.map(|&(_, wtnss)| wtnss)
|
|
||||||
.unwrap();
|
|
||||||
'next_witness: for &a in witnesses.iter() {
|
|
||||||
let mut power = mod_exp(a, d, n);
|
|
||||||
assert!(power < n);
|
|
||||||
if power == 1 || power == n - 1 {
|
|
||||||
continue 'next_witness;
|
|
||||||
}
|
|
||||||
|
|
||||||
for _r in 0..s {
|
|
||||||
power = mod_sqr(power, n);
|
|
||||||
assert!(power < n);
|
|
||||||
if power == 1 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if power == n - 1 {
|
|
||||||
continue 'next_witness;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mod_mul_(a: u64, b: u64, m: u64) -> u64 {
|
fn miller_rabin_test(a: u64, d: u64, n: u64, s: u32) -> bool {
|
||||||
(u128::from(a) * u128::from(b) % u128::from(m)) as u64
|
let mut x = mod_pow(a, d, n);
|
||||||
|
if x == 1 || x == n - 1 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 1..s {
|
||||||
|
x = mod_mul(x, x, n);
|
||||||
|
if x == n - 1 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === Math Core ===
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn mod_mul(a: u64, b: u64, m: u64) -> u64 {
|
fn mod_mul(a: u64, b: u64, m: u64) -> u64 {
|
||||||
match a.checked_mul(b) {
|
((a as u128 * b as u128) % m as u128) as u64
|
||||||
Some(r) => {
|
|
||||||
if r >= m {
|
|
||||||
r % m
|
|
||||||
} else {
|
|
||||||
r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => mod_mul_(a, b, m),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mod_sqr(a: u64, m: u64) -> u64 {
|
fn mod_pow(mut base: u64, mut exp: u64, m: u64) -> u64 {
|
||||||
if a < (1 << 32) {
|
let mut res = 1;
|
||||||
let r = a * a;
|
base %= m;
|
||||||
if r >= m { r % m } else { r }
|
while exp > 0 {
|
||||||
} else {
|
if exp % 2 == 1 {
|
||||||
mod_mul_(a, a, m)
|
res = mod_mul(res, base, m);
|
||||||
|
}
|
||||||
|
base = mod_mul(base, base, m);
|
||||||
|
exp /= 2;
|
||||||
}
|
}
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mod_exp(mut x: u64, mut d: u64, n: u64) -> u64 {
|
|
||||||
let mut ret: u64 = 1;
|
|
||||||
while d != 0 {
|
|
||||||
if d % 2 == 1 {
|
|
||||||
ret = mod_mul(ret, x, n)
|
|
||||||
}
|
|
||||||
d /= 2;
|
|
||||||
x = mod_sqr(x, n);
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -153,7 +121,9 @@ let mut s = 0;
|
||||||
while d % 2 == 0 { d /= 2; s += 1 }
|
while d % 2 == 0 { d /= 2; s += 1 }
|
||||||
```
|
```
|
||||||
|
|
||||||
**Математика:** Каждое простое удовлетворяет **малой теореме Ферма**: \(a^{n-1} \equiv 1 \pmod n\).
|
**Математика:** Каждое простое удовлетворяет **малой теореме Ферма**:
|
||||||
|
\(a^{n-1} \equiv 1 \pmod n\).
|
||||||
|
|
||||||
|
|
||||||
Но вместо прямого вычисления \(a^{n-1}\) мы раскладываем показатель:
|
Но вместо прямого вычисления \(a^{n-1}\) мы раскладываем показатель:
|
||||||
\[ n-1 = d \cdot 2^s \]
|
\[ n-1 = d \cdot 2^s \]
|
||||||
|
|
@ -360,3 +330,96 @@ fn fermat_test(n: u64, a: u64) -> bool {
|
||||||
[8](https://journalpro.ru/articles/malaya-teorema-ferma-i-ee-primenenie-v-kriptosistemakh/)
|
[8](https://journalpro.ru/articles/malaya-teorema-ferma-i-ee-primenenie-v-kriptosistemakh/)
|
||||||
[9](https://www.reddit.com/r/math/comments/2v6db7/intuition_for_fermats_little_theorem/)
|
[9](https://www.reddit.com/r/math/comments/2v6db7/intuition_for_fermats_little_theorem/)
|
||||||
[10](https://math.fandom.com/ru/wiki/%D0%9C%D0%B0%D0%BB%D0%B0%D1%8F_%D1%82%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%A4%D0%B5%D1%80%D0%BC%D0%B0)
|
[10](https://math.fandom.com/ru/wiki/%D0%9C%D0%B0%D0%BB%D0%B0%D1%8F_%D1%82%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%A4%D0%B5%D1%80%D0%BC%D0%B0)
|
||||||
|
|
||||||
|
Ты нащупал самую суть! Это называется **"Binary Exponentiation" (Бинарное возведение в степень)**.
|
||||||
|
|
||||||
|
Твоя догадка: _"мы ищем минимальную степень... домножаем... переиспользуем"_ — абсолютно верна.
|
||||||
|
|
||||||
|
Давай разложим это на механику "Сборщика" и "Генератора".
|
||||||
|
|
||||||
|
## 1. Генератор: Как мы переиспользуем `base`?
|
||||||
|
|
||||||
|
Посмотри на строку:
|
||||||
|
|
||||||
|
rust
|
||||||
|
|
||||||
|
`base = mod_mul(base, base, m);`
|
||||||
|
|
||||||
|
В каждой итерации цикла эта строка делает одно и то же: **возводит текущую базу в квадрат**.
|
||||||
|
|
||||||
|
Представь, что на входе `base` — это число $X$.
|
||||||
|
|
||||||
|
- **Итерация 1:** `base` становится $X^2$.
|
||||||
|
|
||||||
|
- **Итерация 2:** Мы берем этот $X^2$ и умножаем сам на себя: $(X^2) \cdot (X^2) = X^4$.
|
||||||
|
|
||||||
|
- **Итерация 3:** Берем $X^4$ и умножаем сам на себя: $(X^4) \cdot (X^4) = X^8$.
|
||||||
|
|
||||||
|
- **Итерация 4:** ... $X^{16}$.
|
||||||
|
|
||||||
|
|
||||||
|
**Вот оно, переиспользование!** Вместо того чтобы умножать $X \cdot X \cdot X \dots$ (линейно), мы прыгаем по степеням двойки: 1, 2, 4, 8, 16, 32...
|
||||||
|
Мы **генерируем** эти "кирпичики" ($X^1, X^2, X^4, X^8$) на лету.
|
||||||
|
|
||||||
|
## 2. Сборщик: Как мы собираем результат `res`?
|
||||||
|
|
||||||
|
Теперь посмотри на условие:
|
||||||
|
|
||||||
|
rust
|
||||||
|
|
||||||
|
`if exp % 2 == 1 { res = mod_mul(res, base, m); }`
|
||||||
|
|
||||||
|
Здесь мы решаем: **нужен нам этот кирпичик или нет?**
|
||||||
|
|
||||||
|
Допустим, нам нужно вычислить $3^{13}$.
|
||||||
|
Двоичное представление 13 — это `1101`.
|
||||||
|
Это значит:
|
||||||
|
13=1⋅8+1⋅4+0⋅2+1⋅113 = 1 \cdot 8 + 1 \cdot 4 + 0 \cdot 2 + 1 \cdot 113=1⋅8+1⋅4+0⋅2+1⋅1
|
||||||
|
То есть:
|
||||||
|
313=38⋅34⋅(пропускаем 32)⋅313^{13} = 3^8 \cdot 3^4 \cdot (\text{пропускаем } 3^2) \cdot 3^1313=38⋅34⋅(пропускаем 32)⋅31
|
||||||
|
|
||||||
|
Мы просто берем только те степени, где в двоичном коде стоит **1**.
|
||||||
|
|
||||||
|
## Визуализация процесса (Трассировка)
|
||||||
|
|
||||||
|
Давай пройдем этот цикл для $3^{13} \pmod{100}$.
|
||||||
|
Изначально: `res = 1`, `base = 3`. `exp = 13` (`1101` bin).
|
||||||
|
|
||||||
|
| Итерация | exp (dec) | exp (bin) | Бит (четность) | Действие с `res` (Сборщик) | Состояние `res` | Действие с `base` (Генератор) | Новое `base` |
|
||||||
|
| -------- | --------- | --------- | -------------- | -------------------------- | ---------------------------------- | ----------------------------- | ------------------- |
|
||||||
|
| **1** | 13 | `...1101` | **1 (нечет)** | Нужен! Умножаем на $3^1$ | $1 \cdot 3 = \mathbf{3}$ | $3 \cdot 3$ (квадратируем) | $3^2 = 9$ |
|
||||||
|
| **2** | 6 | `...0110` | **0 (чет)** | Не нужен. Пропускаем. | $\mathbf{3}$ | $9 \cdot 9$ (квадратируем) | $3^4 = 81$ |
|
||||||
|
| **3** | 3 | `...0011` | **1 (нечет)** | Нужен! Умножаем на $3^4$ | $3 \cdot 81 = 243 \to \mathbf{43}$ | $81 \cdot 81 \to 61$ | $3^8 = 61$ |
|
||||||
|
| **4** | 1 | `...0001` | **1 (нечет)** | Нужен! Умножаем на $3^8$ | $43 \cdot 61 \to \mathbf{23}$ | $61 \cdot 61 \dots$ | $3^{16}$ (не важно) |
|
||||||
|
|
||||||
|
**Итог:** Мы получили 23.
|
||||||
|
Проверка: $3^{13} = 1,594,323$.
|
||||||
|
$1,594,323 \pmod{100} = 23$. **Сошлось!**
|
||||||
|
|
||||||
|
## Доказательство формулы остатков
|
||||||
|
|
||||||
|
Ты спрашиваешь: _"Как мы доказываем, что взятие остатка на каждом шаге не ломает математику?"_
|
||||||
|
|
||||||
|
Это фундаментальное свойство модульной арифметики:
|
||||||
|
**Остаток произведения равен произведению остатков.**
|
||||||
|
|
||||||
|
(A⋅B)(modM)=((A(modM))⋅(B(modM)))(modM)(A \cdot B) \pmod M = ((A \pmod M) \cdot (B \pmod M)) \pmod M(A⋅B)(modM)=((A(modM))⋅(B(modM)))(modM)
|
||||||
|
|
||||||
|
**Пример на пальцах:**
|
||||||
|
Пусть $M=10$ (мы смотрим только на последнюю цифру).
|
||||||
|
Мы хотим узнать последнюю цифру $12 \cdot 13$.
|
||||||
|
|
||||||
|
1. **По-честному:** $12 \cdot 13 = 156$. Последняя цифра **6**.
|
||||||
|
|
||||||
|
2. **По-модульному:**
|
||||||
|
|
||||||
|
- От 12 берем остаток $\to 2$.
|
||||||
|
|
||||||
|
- От 13 берем остаток $\to 3$.
|
||||||
|
|
||||||
|
- Умножаем остатки: $2 \cdot 3 = 6$.
|
||||||
|
|
||||||
|
- Результат тот же: **6**.
|
||||||
|
|
||||||
|
|
||||||
|
Именно это правило разрешает нам делать `base %= m` и `mod_mul` на каждом шаге. Мы просто отбрасываем "лишние круги", которые никак не влияют на конечный остаток. Без этого правила вся современная криптография (RSA, Diffie-Hellman) просто не работала бы на компьютерах.
|
||||||
|
|
|
||||||
1
ENTRY-NAVIGATOR.md
Normal file
1
ENTRY-NAVIGATOR.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[[00-TERMS]]
|
||||||
BIN
Pasted image 20251223184946.png
Normal file
BIN
Pasted image 20251223184946.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 521 KiB |
BIN
Pasted image 20251223185010.png
Normal file
BIN
Pasted image 20251223185010.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 379 KiB |
Loading…
Add table
Add a link
Reference in a new issue