Moved to PSQL.
Build and Publish / Build and Publish Docker Image (push) Successful in 1m42s

This commit is contained in:
ab
2026-07-11 14:11:31 +03:00
parent 91ca486e64
commit 1bd3e17672
13 changed files with 483 additions and 748 deletions
+8 -8
View File
@@ -69,6 +69,12 @@ fn debug_enabled(config_name: &str) -> bool {
})
}
fn database_url() -> String {
std::env::var("WEB_PETTING_DATABASE_URL")
.or_else(|_| std::env::var("DATABASE_URL"))
.unwrap_or_else(|_| "postgresql://postgres:postgres@localhost:5432/web_petting".to_string())
}
impl Project for PettingProject {
fn cli_metadata(&self) -> CliMetadata {
cot::cli::metadata!()
@@ -77,11 +83,7 @@ impl Project for PettingProject {
fn config(&self, config_name: &str) -> cot::Result<ProjectConfig> {
Ok(ProjectConfig::builder()
.debug(debug_enabled(config_name))
.database(
DatabaseConfig::builder()
.url("sqlite://db.sqlite3?mode=rwc")
.build(),
)
.database(DatabaseConfig::builder().url(database_url()).build())
.middlewares(
MiddlewareConfig::builder()
.session(
@@ -121,8 +123,6 @@ impl Project for PettingProject {
fn main() -> impl Project {
let filter = tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info"));
let _ = tracing_subscriber::fmt()
.with_env_filter(filter)
.try_init();
let _ = tracing_subscriber::fmt().with_env_filter(filter).try_init();
PettingProject
}
+2 -14
View File
@@ -1,19 +1,7 @@
//! List of migrations for the current app.
//!
//! Generated by cot CLI 0.6.0 on 2026-04-29 10:36:47+00:00
//! Squashed for the PostgreSQL migration on 2026-07-11.
pub mod m_0001_initial;
pub mod m_0002_visit_schedule;
pub mod m_0003_visit_feedback;
pub mod m_0004_visit_public_notes;
pub mod m_0005_testimonials;
pub mod m_0006_user_telegram;
/// The list of migrations for current app.
pub const MIGRATIONS: &[&::cot::db::migrations::SyncDynMigration] = &[
&m_0001_initial::Migration,
&m_0002_visit_schedule::Migration,
&m_0003_visit_feedback::Migration,
&m_0004_visit_public_notes::Migration,
&m_0005_testimonials::Migration,
&m_0006_user_telegram::Migration,
];
pub const MIGRATIONS: &[&::cot::db::migrations::SyncDynMigration] = &[&m_0001_initial::Migration];
+376 -459
View File
@@ -1,7 +1,8 @@
//! Generated by cot CLI 0.6.0 on 2026-04-29 10:36:47+00:00
//! Initial PostgreSQL schema for the current data model.
#[derive(Debug, Copy, Clone)]
pub(super) struct Migration;
impl ::cot::db::migrations::Migration for Migration {
const APP_NAME: &'static str = "web-petting";
const MIGRATION_NAME: &'static str = "m_0001_initial";
@@ -9,479 +10,395 @@ impl ::cot::db::migrations::Migration for Migration {
const OPERATIONS: &'static [::cot::db::migrations::Operation] = &[
::cot::db::migrations::Operation::create_model()
.table_name(::cot::db::Identifier::new("web_petting__user"))
.fields(
&[
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("id"),
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::TYPE,
)
.auto()
.primary_key()
.set_null(
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("login"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE)
.unique(),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("password_hash"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("display_name"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<Option<String> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("status"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("created_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("updated_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE,
),
],
)
.fields(&[
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("id"),
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::TYPE,
)
.auto()
.primary_key()
.set_null(<cot::db::Auto<i64> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("login"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE)
.unique(),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("password_hash"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("display_name"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("telegram_chat_id"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("telegram_notifications"),
<Option<bool> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<bool> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("status"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("created_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("updated_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE),
])
.build(),
::cot::db::migrations::Operation::create_model()
.table_name(::cot::db::Identifier::new("web_petting__setting"))
.fields(
&[
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("id"),
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::TYPE,
)
.auto()
.primary_key()
.set_null(
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("key"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE)
.unique(),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("value"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("updated_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE,
),
],
)
.fields(&[
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("id"),
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::TYPE,
)
.auto()
.primary_key()
.set_null(<cot::db::Auto<i64> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("key"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE)
.unique(),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("value"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("updated_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE),
])
.build(),
::cot::db::migrations::Operation::create_model()
.table_name(::cot::db::Identifier::new("web_petting__client"))
.fields(
&[
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("id"),
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::TYPE,
)
.auto()
.primary_key()
.set_null(
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("name"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("phone"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<Option<String> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("email"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<Option<String> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("address"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<Option<String> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("notes"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<Option<String> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("media_token"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE)
.unique(),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("status"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("created_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("updated_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE,
),
],
)
.fields(&[
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("id"),
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::TYPE,
)
.auto()
.primary_key()
.set_null(<cot::db::Auto<i64> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("name"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("phone"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("email"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("address"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("notes"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("media_token"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE)
.unique(),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("color"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("status"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("created_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("updated_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE),
])
.build(),
::cot::db::migrations::Operation::create_model()
.table_name(::cot::db::Identifier::new("web_petting__testimonial"))
.fields(&[
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("id"),
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::TYPE,
)
.auto()
.primary_key()
.set_null(<cot::db::Auto<i64> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("text"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("author_note"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("image_path"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("status"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("sort_order"),
<i32 as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<i32 as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("created_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE),
])
.build(),
::cot::db::migrations::Operation::create_model()
.table_name(::cot::db::Identifier::new("web_petting__visit"))
.fields(
&[
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("id"),
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::TYPE,
)
.auto()
.primary_key()
.set_null(
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("client_id"),
<cot::db::ForeignKey<
crate::models::Client,
> as ::cot::db::DatabaseField>::TYPE,
)
.foreign_key(
<crate::models::Client as ::cot::db::Model>::TABLE_NAME,
<crate::models::Client as ::cot::db::Model>::PRIMARY_KEY_NAME,
::cot::db::ForeignKeyOnDeletePolicy::Restrict,
::cot::db::ForeignKeyOnUpdatePolicy::Restrict,
)
.set_null(
<cot::db::ForeignKey<
crate::models::Client,
> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("scheduled_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("duration_minutes"),
<Option<i32> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<i32> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("notes"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<Option<String> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("status"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("created_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("updated_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE,
),
],
)
.fields(&[
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("id"),
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::TYPE,
)
.auto()
.primary_key()
.set_null(<cot::db::Auto<i64> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("client_id"),
<cot::db::ForeignKey<crate::models::Client> as ::cot::db::DatabaseField>::TYPE,
)
.foreign_key(
<crate::models::Client as ::cot::db::Model>::TABLE_NAME,
<crate::models::Client as ::cot::db::Model>::PRIMARY_KEY_NAME,
::cot::db::ForeignKeyOnDeletePolicy::Restrict,
::cot::db::ForeignKeyOnUpdatePolicy::Restrict,
)
.set_null(
<cot::db::ForeignKey<crate::models::Client> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("user_id"),
<cot::db::ForeignKey<crate::models::User> as ::cot::db::DatabaseField>::TYPE,
)
.foreign_key(
<crate::models::User as ::cot::db::Model>::TABLE_NAME,
<crate::models::User as ::cot::db::Model>::PRIMARY_KEY_NAME,
::cot::db::ForeignKeyOnDeletePolicy::Restrict,
::cot::db::ForeignKeyOnUpdatePolicy::Restrict,
)
.set_null(
<cot::db::ForeignKey<crate::models::User> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("visit_date"),
<chrono::NaiveDate as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<chrono::NaiveDate as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("time_start"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("time_end"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("notes"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("public_notes"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("client_feedback"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("status"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("created_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("updated_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE),
])
.build(),
::cot::db::migrations::Operation::create_model()
.table_name(::cot::db::Identifier::new("web_petting__media"))
.fields(
&[
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("id"),
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::TYPE,
)
.auto()
.primary_key()
.set_null(
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("client_id"),
<cot::db::ForeignKey<
crate::models::Client,
> as ::cot::db::DatabaseField>::TYPE,
)
.foreign_key(
<crate::models::Client as ::cot::db::Model>::TABLE_NAME,
<crate::models::Client as ::cot::db::Model>::PRIMARY_KEY_NAME,
::cot::db::ForeignKeyOnDeletePolicy::Restrict,
::cot::db::ForeignKeyOnUpdatePolicy::Restrict,
)
.set_null(
<cot::db::ForeignKey<
crate::models::Client,
> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("visit_id"),
<Option<
cot::db::ForeignKey<crate::models::Visit>,
> as ::cot::db::DatabaseField>::TYPE,
)
.foreign_key(
<crate::models::Visit as ::cot::db::Model>::TABLE_NAME,
<crate::models::Visit as ::cot::db::Model>::PRIMARY_KEY_NAME,
::cot::db::ForeignKeyOnDeletePolicy::Restrict,
::cot::db::ForeignKeyOnUpdatePolicy::Restrict,
)
.set_null(
<Option<
cot::db::ForeignKey<crate::models::Visit>,
> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("file_path"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("file_type"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("caption"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<Option<String> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("status"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("created_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE,
),
],
)
.fields(&[
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("id"),
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::TYPE,
)
.auto()
.primary_key()
.set_null(<cot::db::Auto<i64> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("client_id"),
<cot::db::ForeignKey<crate::models::Client> as ::cot::db::DatabaseField>::TYPE,
)
.foreign_key(
<crate::models::Client as ::cot::db::Model>::TABLE_NAME,
<crate::models::Client as ::cot::db::Model>::PRIMARY_KEY_NAME,
::cot::db::ForeignKeyOnDeletePolicy::Restrict,
::cot::db::ForeignKeyOnUpdatePolicy::Restrict,
)
.set_null(
<cot::db::ForeignKey<crate::models::Client> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("visit_id"),
<Option<cot::db::ForeignKey<crate::models::Visit>> as ::cot::db::DatabaseField>::TYPE,
)
.foreign_key(
<crate::models::Visit as ::cot::db::Model>::TABLE_NAME,
<crate::models::Visit as ::cot::db::Model>::PRIMARY_KEY_NAME,
::cot::db::ForeignKeyOnDeletePolicy::Restrict,
::cot::db::ForeignKeyOnUpdatePolicy::Restrict,
)
.set_null(
<Option<cot::db::ForeignKey<crate::models::Visit>> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("file_path"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("file_type"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("caption"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("status"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("created_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE),
])
.build(),
::cot::db::migrations::Operation::create_model()
.table_name(::cot::db::Identifier::new("web_petting__lead"))
.fields(
&[
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("id"),
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::TYPE,
)
.auto()
.primary_key()
.set_null(
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("name"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("phone"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<Option<String> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("email"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<Option<String> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("comment"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<Option<String> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("status"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("client_id"),
<Option<
cot::db::ForeignKey<crate::models::Client>,
> as ::cot::db::DatabaseField>::TYPE,
)
.foreign_key(
<crate::models::Client as ::cot::db::Model>::TABLE_NAME,
<crate::models::Client as ::cot::db::Model>::PRIMARY_KEY_NAME,
::cot::db::ForeignKeyOnDeletePolicy::Restrict,
::cot::db::ForeignKeyOnUpdatePolicy::Restrict,
)
.set_null(
<Option<
cot::db::ForeignKey<crate::models::Client>,
> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("created_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("updated_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE,
),
],
)
.fields(&[
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("id"),
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::TYPE,
)
.auto()
.primary_key()
.set_null(<cot::db::Auto<i64> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("name"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("phone"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("email"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("comment"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("status"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("client_id"),
<Option<cot::db::ForeignKey<crate::models::Client>> as ::cot::db::DatabaseField>::TYPE,
)
.foreign_key(
<crate::models::Client as ::cot::db::Model>::TABLE_NAME,
<crate::models::Client as ::cot::db::Model>::PRIMARY_KEY_NAME,
::cot::db::ForeignKeyOnDeletePolicy::Restrict,
::cot::db::ForeignKeyOnUpdatePolicy::Restrict,
)
.set_null(
<Option<cot::db::ForeignKey<crate::models::Client>> as ::cot::db::DatabaseField>::NULLABLE,
),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("created_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("updated_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE),
])
.build(),
];
}
#[derive(::core::fmt::Debug)]
#[::cot::db::model(model_type = "migration")]
struct _Client {
#[model(primary_key)]
pub id: cot::db::Auto<i64>,
pub name: String,
pub phone: Option<String>,
pub email: Option<String>,
pub address: Option<String>,
pub notes: Option<String>,
/// Unique token for the public media page (client views photos/videos here).
#[model(unique)]
pub media_token: String,
/// active | archived
pub status: String,
pub created_at: chrono::NaiveDateTime,
pub updated_at: chrono::NaiveDateTime,
}
#[derive(::core::fmt::Debug)]
#[::cot::db::model(model_type = "migration")]
struct _Lead {
#[model(primary_key)]
pub id: cot::db::Auto<i64>,
pub name: String,
pub phone: Option<String>,
pub email: Option<String>,
pub comment: Option<String>,
/// new | in_progress | converted | rejected
pub status: String,
pub client_id: Option<cot::db::ForeignKey<crate::models::Client>>,
pub created_at: chrono::NaiveDateTime,
pub updated_at: chrono::NaiveDateTime,
}
#[derive(::core::fmt::Debug)]
#[::cot::db::model(model_type = "migration")]
struct _Media {
#[model(primary_key)]
pub id: cot::db::Auto<i64>,
pub client_id: cot::db::ForeignKey<crate::models::Client>,
pub visit_id: Option<cot::db::ForeignKey<crate::models::Visit>>,
pub file_path: String,
/// photo | video
pub file_type: String,
pub caption: Option<String>,
/// active | archived
pub status: String,
pub created_at: chrono::NaiveDateTime,
}
#[derive(::core::fmt::Debug)]
#[::cot::db::model(model_type = "migration")]
struct _Setting {
#[model(primary_key)]
pub id: cot::db::Auto<i64>,
#[model(unique)]
pub key: String,
pub value: String,
pub updated_at: chrono::NaiveDateTime,
}
#[derive(::core::fmt::Debug)]
#[::cot::db::model(model_type = "migration")]
struct _User {
#[model(primary_key)]
pub id: cot::db::Auto<i64>,
#[model(unique)]
pub login: String,
pub password_hash: String,
pub display_name: Option<String>,
/// active | archived
pub status: String,
pub created_at: chrono::NaiveDateTime,
pub updated_at: chrono::NaiveDateTime,
}
#[derive(::core::fmt::Debug)]
#[::cot::db::model(model_type = "migration")]
struct _Visit {
#[model(primary_key)]
pub id: cot::db::Auto<i64>,
pub client_id: cot::db::ForeignKey<crate::models::Client>,
pub scheduled_at: chrono::NaiveDateTime,
pub duration_minutes: Option<i32>,
pub notes: Option<String>,
/// scheduled | completed | cancelled
pub status: String,
pub created_at: chrono::NaiveDateTime,
pub updated_at: chrono::NaiveDateTime,
}
-90
View File
@@ -1,90 +0,0 @@
//! Migration: update Visit model for scheduling + add Client.color
//! Visit: Remove scheduled_at, duration_minutes; Add user_id, visit_date, time_start, time_end
//! Client: Add color
#[derive(Debug, Copy, Clone)]
pub(super) struct Migration;
impl ::cot::db::migrations::Migration for Migration {
const APP_NAME: &'static str = "web-petting";
const MIGRATION_NAME: &'static str = "m_0002_visit_schedule";
const DEPENDENCIES: &'static [::cot::db::migrations::MigrationDependency] =
&[::cot::db::migrations::MigrationDependency::migration(
"web-petting",
"m_0001_initial",
)];
const OPERATIONS: &'static [::cot::db::migrations::Operation] = &[
// Add color to client (nullable for existing rows)
::cot::db::migrations::Operation::add_field()
.table_name(::cot::db::Identifier::new("web_petting__client"))
.field(
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("color"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE)
)
.build(),
// Remove old visit fields
::cot::db::migrations::Operation::remove_field()
.table_name(::cot::db::Identifier::new("web_petting__visit"))
.field(::cot::db::migrations::Field::new(
::cot::db::Identifier::new("scheduled_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
))
.build(),
::cot::db::migrations::Operation::remove_field()
.table_name(::cot::db::Identifier::new("web_petting__visit"))
.field(::cot::db::migrations::Field::new(
::cot::db::Identifier::new("duration_minutes"),
<Option<i32> as ::cot::db::DatabaseField>::TYPE,
).set_null(<Option<i32> as ::cot::db::DatabaseField>::NULLABLE))
.build(),
// Add new fields
::cot::db::migrations::Operation::add_field()
.table_name(::cot::db::Identifier::new("web_petting__visit"))
.field(
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("user_id"),
<cot::db::ForeignKey<crate::models::User> as ::cot::db::DatabaseField>::TYPE,
)
.foreign_key(
<crate::models::User as ::cot::db::Model>::TABLE_NAME,
<crate::models::User as ::cot::db::Model>::PRIMARY_KEY_NAME,
::cot::db::ForeignKeyOnDeletePolicy::Restrict,
::cot::db::ForeignKeyOnUpdatePolicy::Restrict,
)
.set_null(<cot::db::ForeignKey<crate::models::User> as ::cot::db::DatabaseField>::NULLABLE)
)
.build(),
::cot::db::migrations::Operation::add_field()
.table_name(::cot::db::Identifier::new("web_petting__visit"))
.field(
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("visit_date"),
<chrono::NaiveDate as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<chrono::NaiveDate as ::cot::db::DatabaseField>::NULLABLE)
)
.build(),
::cot::db::migrations::Operation::add_field()
.table_name(::cot::db::Identifier::new("web_petting__visit"))
.field(
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("time_start"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE)
)
.build(),
::cot::db::migrations::Operation::add_field()
.table_name(::cot::db::Identifier::new("web_petting__visit"))
.field(
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("time_end"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE)
)
.build(),
];
}
-24
View File
@@ -1,24 +0,0 @@
//! Migration: add client_feedback to Visit
#[derive(Debug, Copy, Clone)]
pub(super) struct Migration;
impl ::cot::db::migrations::Migration for Migration {
const APP_NAME: &'static str = "web-petting";
const MIGRATION_NAME: &'static str = "m_0003_visit_feedback";
const DEPENDENCIES: &'static [::cot::db::migrations::MigrationDependency] =
&[::cot::db::migrations::MigrationDependency::migration(
"web-petting",
"m_0002_visit_schedule",
)];
const OPERATIONS: &'static [::cot::db::migrations::Operation] =
&[::cot::db::migrations::Operation::add_field()
.table_name(::cot::db::Identifier::new("web_petting__visit"))
.field(
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("client_feedback"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
)
.build()];
}
@@ -1,24 +0,0 @@
//! Migration: add public_notes to Visit
#[derive(Debug, Copy, Clone)]
pub(super) struct Migration;
impl ::cot::db::migrations::Migration for Migration {
const APP_NAME: &'static str = "web-petting";
const MIGRATION_NAME: &'static str = "m_0004_visit_public_notes";
const DEPENDENCIES: &'static [::cot::db::migrations::MigrationDependency] =
&[::cot::db::migrations::MigrationDependency::migration(
"web-petting",
"m_0003_visit_feedback",
)];
const OPERATIONS: &'static [::cot::db::migrations::Operation] =
&[::cot::db::migrations::Operation::add_field()
.table_name(::cot::db::Identifier::new("web_petting__visit"))
.field(
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("public_notes"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
)
.build()];
}
-56
View File
@@ -1,56 +0,0 @@
//! Migration: create Testimonial table
#[derive(Debug, Copy, Clone)]
pub(super) struct Migration;
impl ::cot::db::migrations::Migration for Migration {
const APP_NAME: &'static str = "web-petting";
const MIGRATION_NAME: &'static str = "m_0005_testimonials";
const DEPENDENCIES: &'static [::cot::db::migrations::MigrationDependency] =
&[::cot::db::migrations::MigrationDependency::migration(
"web-petting",
"m_0004_visit_public_notes",
)];
const OPERATIONS: &'static [::cot::db::migrations::Operation] =
&[::cot::db::migrations::Operation::create_model()
.table_name(::cot::db::Identifier::new("web_petting__testimonial"))
.fields(&[
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("id"),
<cot::db::Auto<i64> as ::cot::db::DatabaseField>::TYPE,
)
.auto()
.primary_key()
.set_null(<cot::db::Auto<i64> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("text"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("author_note"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("image_path"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("status"),
<String as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<String as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("sort_order"),
<i32 as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<i32 as ::cot::db::DatabaseField>::NULLABLE),
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("created_at"),
<chrono::NaiveDateTime as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<chrono::NaiveDateTime as ::cot::db::DatabaseField>::NULLABLE),
])
.build()];
}
-35
View File
@@ -1,35 +0,0 @@
//! Migration: add telegram_chat_id and telegram_notifications to User
#[derive(Debug, Copy, Clone)]
pub(super) struct Migration;
impl ::cot::db::migrations::Migration for Migration {
const APP_NAME: &'static str = "web-petting";
const MIGRATION_NAME: &'static str = "m_0006_user_telegram";
const DEPENDENCIES: &'static [::cot::db::migrations::MigrationDependency] =
&[::cot::db::migrations::MigrationDependency::migration(
"web-petting",
"m_0005_testimonials",
)];
const OPERATIONS: &'static [::cot::db::migrations::Operation] = &[
::cot::db::migrations::Operation::add_field()
.table_name(::cot::db::Identifier::new("web_petting__user"))
.field(
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("telegram_chat_id"),
<Option<String> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<String> as ::cot::db::DatabaseField>::NULLABLE),
)
.build(),
::cot::db::migrations::Operation::add_field()
.table_name(::cot::db::Identifier::new("web_petting__user"))
.field(
::cot::db::migrations::Field::new(
::cot::db::Identifier::new("telegram_notifications"),
<Option<bool> as ::cot::db::DatabaseField>::TYPE,
)
.set_null(<Option<bool> as ::cot::db::DatabaseField>::NULLABLE),
)
.build(),
];
}
+8 -14
View File
@@ -343,22 +343,14 @@ async fn submit_feedback(
serde_html_form::from_bytes(&bytes).map_err(|e| cot::Error::internal(e.to_string()))?;
if !crate::turnstile::verify(&db, form.cf_turnstile_response.as_deref()).await? {
return Redirect::new(format!(
"/client/{}?lang={}",
token_clone,
lang.code()
))
.into_response();
return Redirect::new(format!("/client/{}?lang={}", token_clone, lang.code()))
.into_response();
}
if let Some(mut visit) = query!(Visit, $id == visit_id).get(&db).await? {
if visit.status == "deleted" {
return Redirect::new(format!(
"/client/{}?lang={}",
token_clone,
lang.code()
))
.into_response();
return Redirect::new(format!("/client/{}?lang={}", token_clone, lang.code()))
.into_response();
}
if visit.client_id.primary_key().unwrap() == client_id {
visit.client_feedback = Some(form.feedback);
@@ -552,8 +544,10 @@ async fn sitemap_xml(_request: Request, db: Database) -> cot::Result<Response> {
domain = site_domain
);
let mut resp = Response::new(cot::Body::fixed(body.into_bytes()));
resp.headers_mut()
.insert("content-type", "application/xml; charset=utf-8".parse().unwrap());
resp.headers_mut().insert(
"content-type",
"application/xml; charset=utf-8".parse().unwrap(),
);
Ok(resp)
}