Database and api route ready
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
target
|
||||
@@ -82,7 +82,7 @@
|
||||
|
||||
<params>
|
||||
<!-- default url can be overridden by app data -->
|
||||
<param name="gateway-url" value="http://www.freeswitch.org/api/index.cgi" />
|
||||
<param name="gateway-url" value="http://127.0.0.1:3000/api/route_did" />
|
||||
|
||||
<!-- set this to provide authentication credentials to the server -->
|
||||
<!--<param name="gateway-credentials" value="muser:mypass"/>-->
|
||||
|
||||
@@ -45,5 +45,10 @@
|
||||
</condition>
|
||||
</extension>
|
||||
|
||||
<extension name="did-route">
|
||||
<condition field="destination_number" expression="^8598989002$">
|
||||
<action application="httapi" data="{method=POST}" />
|
||||
</condition>
|
||||
</extension>
|
||||
</context>
|
||||
</include>
|
||||
|
||||
2
did_router/.env
Normal file
2
did_router/.env
Normal file
@@ -0,0 +1,2 @@
|
||||
DATABASE_URL=postgres://freeswitch:T5NyU2NwQb5DD9oV@localhost/did_router
|
||||
|
||||
175
did_router/Cargo.lock
generated
175
did_router/Cargo.lock
generated
@@ -257,6 +257,12 @@ dependencies = [
|
||||
"alloc-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.11.1"
|
||||
@@ -338,6 +344,41 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.8"
|
||||
@@ -375,6 +416,60 @@ name = "did_router"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"diesel",
|
||||
"diesel-derive-enum",
|
||||
"dotenvy",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"xml-builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel"
|
||||
version = "2.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4ae09a41a4b89f94ec1e053623da8340d996bc32c6517d325a9daad9b239358"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"diesel_derives",
|
||||
"downcast-rs",
|
||||
"itoa",
|
||||
"pq-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel-derive-enum"
|
||||
version = "3.0.0-beta.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50a8c082045d01debc8589f8a0db9f2855a37c99c9b031325c856b5b98e1625f"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel_derives"
|
||||
version = "2.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47618bf0fac06bb670c036e48404c26a865e6a71af4114dfd97dfe89936e404e"
|
||||
dependencies = [
|
||||
"diesel_table_macro_syntax",
|
||||
"dsl_auto_type",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel_table_macro_syntax"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe2444076b48641147115697648dc743c2c00b61adade0f01ce67133c7babe8c"
|
||||
dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -398,6 +493,38 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||
|
||||
[[package]]
|
||||
name = "downcast-rs"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc"
|
||||
|
||||
[[package]]
|
||||
name = "dsl_auto_type"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd122633e4bef06db27737f21d3738fb89c8f6d5360d6d9d7635dda142a7757e"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"either",
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
@@ -537,6 +664,18 @@ version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.12"
|
||||
@@ -641,6 +780,12 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.1.0"
|
||||
@@ -855,6 +1000,17 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pq-sys"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "574ddd6a267294433f140b02a726b0640c43cf7c6f717084684aaa3b285aba61"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
@@ -986,6 +1142,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1104,6 +1261,12 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.117"
|
||||
@@ -1270,6 +1433,12 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
@@ -1391,6 +1560,12 @@ version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
|
||||
|
||||
[[package]]
|
||||
name = "xml-builder"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3d5951f276a574fce1cf86598f6143778be7ac5585c241f5e39dbdb0371d8b4"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.1"
|
||||
|
||||
@@ -5,3 +5,9 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
actix-web = "4"
|
||||
xml-builder = "*"
|
||||
diesel = { version = "2.3", features = ["postgres"] }
|
||||
dotenvy = "0.15"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = "1.0.149"
|
||||
diesel-derive-enum = { version = "3.0.0-beta.1", features = ["postgres"] }
|
||||
|
||||
9
did_router/diesel.toml
Normal file
9
did_router/diesel.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
# For documentation on how to configure this file,
|
||||
# see https://diesel.rs/guides/configuring-diesel-cli
|
||||
|
||||
[print_schema]
|
||||
file = "src/schema.rs"
|
||||
custom_type_derives = ["diesel::query_builder::QueryId", "Clone"]
|
||||
|
||||
[migrations_directory]
|
||||
dir = "migrations"
|
||||
0
did_router/migrations/.diesel_lock
Normal file
0
did_router/migrations/.diesel_lock
Normal file
0
did_router/migrations/.keep
Normal file
0
did_router/migrations/.keep
Normal file
@@ -0,0 +1,6 @@
|
||||
-- This file was automatically created by Diesel to setup helper functions
|
||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||
-- changes will be added to existing projects as new migrations.
|
||||
|
||||
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
|
||||
DROP FUNCTION IF EXISTS diesel_set_updated_at();
|
||||
@@ -0,0 +1,36 @@
|
||||
-- This file was automatically created by Diesel to setup helper functions
|
||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||
-- changes will be added to existing projects as new migrations.
|
||||
|
||||
|
||||
|
||||
|
||||
-- Sets up a trigger for the given table to automatically set a column called
|
||||
-- `updated_at` whenever the row is modified (unless `updated_at` was included
|
||||
-- in the modified columns)
|
||||
--
|
||||
-- # Example
|
||||
--
|
||||
-- ```sql
|
||||
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
|
||||
--
|
||||
-- SELECT diesel_manage_updated_at('users');
|
||||
-- ```
|
||||
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
|
||||
BEGIN
|
||||
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
|
||||
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
|
||||
BEGIN
|
||||
IF (
|
||||
NEW IS DISTINCT FROM OLD AND
|
||||
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
|
||||
) THEN
|
||||
NEW.updated_at := current_timestamp;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
DROP TABLE dids;
|
||||
DROP TYPE did_target_type;
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Your SQL goes here
|
||||
CREATE TYPE did_target_type AS ENUM ('URL', 'MOH', 'EXTERNAL_NUMBER', 'NO_SERVICE', 'NIGHT_MODE', 'CUSTOME_MESSAGE');
|
||||
CREATE TABLE dids (
|
||||
id SERIAL PRIMARY KEY,
|
||||
did VARCHAR(64) NOT NULL,
|
||||
target_type DID_TARGET_TYPE NOT NULL DEFAULT 'NO_SERVICE',
|
||||
target VARCHAR(512),
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE
|
||||
)
|
||||
82
did_router/src/database.rs
Normal file
82
did_router/src/database.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
use diesel::prelude::*;
|
||||
use diesel::pg::PgConnection;
|
||||
use diesel::result::Error;
|
||||
use dotenvy::dotenv;
|
||||
use std::env;
|
||||
use serde::{Serialize};
|
||||
use diesel_derive_enum::DbEnum;
|
||||
|
||||
|
||||
#[derive(Debug, PartialEq, DbEnum, Serialize)]
|
||||
#[db_enum(existing_type_path = "crate::schema::sql_types::DidTargetType")]
|
||||
pub enum DidTargetType {
|
||||
#[db_enum(rename ="URL")]
|
||||
Url,
|
||||
#[db_enum(rename ="MOH")]
|
||||
Moh,
|
||||
#[db_enum(rename ="EXTERNAL_NUMBER")]
|
||||
ExternalNumber,
|
||||
#[db_enum(rename ="NO_SERVICE")]
|
||||
NoService,
|
||||
#[db_enum(rename ="NIGHT_MODE")]
|
||||
NightMode,
|
||||
#[db_enum(rename ="CUSTOME_MESSAGE")]
|
||||
CustomMessage
|
||||
}
|
||||
|
||||
#[derive(Queryable, Selectable, Serialize)]
|
||||
#[diesel(table_name = crate::schema::dids)]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct Did {
|
||||
pub id: i32,
|
||||
pub did: String,
|
||||
pub target_type: DidTargetType,
|
||||
pub target: Option<String>,
|
||||
pub active: bool
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = crate::schema::dids)]
|
||||
pub struct NewDid<'a> {
|
||||
pub did: &'a str,
|
||||
pub target_type: DidTargetType,
|
||||
pub target: Option<&'a str>,
|
||||
pub active: bool
|
||||
}
|
||||
|
||||
pub fn connect() -> PgConnection {
|
||||
dotenv().ok();
|
||||
|
||||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
PgConnection::establish(&database_url)
|
||||
.unwrap_or_else(|_| panic!("Error connecting to database"))
|
||||
}
|
||||
|
||||
pub fn add_did() -> Result<Did, Error> {
|
||||
use crate::schema::dids;
|
||||
|
||||
let mut conn = connect();
|
||||
let new_did = NewDid {
|
||||
did: "123456",
|
||||
target_type: DidTargetType::Url,
|
||||
target: Some("123456"),
|
||||
active: true
|
||||
};
|
||||
|
||||
let did = diesel::insert_into(dids::table)
|
||||
.values(&new_did)
|
||||
.returning(Did::as_returning())
|
||||
.get_result(&mut conn)?;
|
||||
|
||||
Ok(did)
|
||||
}
|
||||
|
||||
pub fn list_did() ->Result<Vec<Did>, Error> {
|
||||
use crate::schema::dids::dsl::*;
|
||||
let mut conn = connect();
|
||||
|
||||
let res = dids
|
||||
.load(&mut conn)?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
@@ -1,28 +1,86 @@
|
||||
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
|
||||
pub mod database;
|
||||
pub mod schema;
|
||||
|
||||
#[get("/")]
|
||||
async fn hello() -> impl Responder {
|
||||
HttpResponse::Ok().body("Hello world!")
|
||||
use xml_builder::{XMLBuilder, XMLElement, XMLVersion};
|
||||
use actix_web::{get, post, web, App,
|
||||
HttpResponse,
|
||||
HttpServer,
|
||||
http::header::ContentType,
|
||||
Responder,
|
||||
};
|
||||
|
||||
use database::{ add_did,
|
||||
list_did
|
||||
};
|
||||
|
||||
async fn route_did() -> impl Responder {
|
||||
let mut xml = XMLBuilder::new()
|
||||
.version(XMLVersion::XML1_1)
|
||||
.encoding("UTF-8".into())
|
||||
.build();
|
||||
|
||||
let mut doc = XMLElement::new("document");
|
||||
doc.add_attribute("type", "xml/freeswitch-httapi");
|
||||
|
||||
let params = XMLElement::new("params");
|
||||
let mut work = XMLElement::new("work");
|
||||
|
||||
let mut playback = XMLElement::new("playback");
|
||||
playback.add_attribute("name", "exten");
|
||||
playback.add_attribute("file", "ivr/ivr-welcome_to_freeswitch.wav");
|
||||
work.add_child(playback).unwrap();
|
||||
|
||||
doc.add_child(params).unwrap();
|
||||
doc.add_child(work).unwrap();
|
||||
|
||||
xml.set_root_element(doc);
|
||||
|
||||
let mut writer: Vec<u8> = Vec::new();
|
||||
xml.generate(&mut writer).unwrap();
|
||||
|
||||
HttpResponse::Ok()
|
||||
.content_type(ContentType::xml())
|
||||
.body(writer)
|
||||
}
|
||||
|
||||
#[post("/echo")]
|
||||
async fn echo(req_body: String) -> impl Responder {
|
||||
HttpResponse::Ok().body(req_body)
|
||||
async fn did_post() -> impl Responder {
|
||||
add_did().unwrap();
|
||||
HttpResponse::Ok().body("DID added.")
|
||||
}
|
||||
|
||||
async fn manual_hello() -> impl Responder {
|
||||
HttpResponse::Ok().body("Hey there!")
|
||||
async fn did_index() -> impl Responder {
|
||||
let dids = list_did().unwrap();
|
||||
HttpResponse::Ok().json(dids)
|
||||
}
|
||||
|
||||
fn api_config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::resource("/fs")
|
||||
.route(web::get().to(did_index))
|
||||
.route(web::post().to(did_post))
|
||||
.route(web::patch().to(|| async { HttpResponse::Ok().body("did patch") }))
|
||||
.route(web::delete().to(|| async { HttpResponse::Ok().body("did delete") }))
|
||||
);
|
||||
cfg.service(
|
||||
web::resource("/fs/{id}")
|
||||
.route(web::get().to(|| async { HttpResponse::Ok().body("did get")}))
|
||||
.route(web::patch().to(|| async { HttpResponse::Ok().body("did patch")}))
|
||||
.route(web::delete().to(|| async { HttpResponse::Ok().body("did delete")}))
|
||||
);
|
||||
cfg.service(
|
||||
web::resource("/route_did")
|
||||
.route(web::post().to(route_did))
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
HttpServer::new(|| {
|
||||
App::new()
|
||||
.service(hello)
|
||||
.service(echo)
|
||||
.route("/hey", web::get().to(manual_hello))
|
||||
.service(
|
||||
web::scope("/api").configure(api_config))
|
||||
})
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.bind(("0.0.0.0", 3000))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
|
||||
22
did_router/src/schema.rs
Normal file
22
did_router/src/schema.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
// @generated automatically by Diesel CLI.
|
||||
|
||||
pub mod sql_types {
|
||||
#[derive(diesel::query_builder::QueryId, Clone, diesel::sql_types::SqlType)]
|
||||
#[diesel(postgres_type(name = "did_target_type"))]
|
||||
pub struct DidTargetType;
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
use diesel::sql_types::*;
|
||||
use super::sql_types::DidTargetType;
|
||||
|
||||
dids (id) {
|
||||
id -> Int4,
|
||||
#[max_length = 64]
|
||||
did -> Varchar,
|
||||
target_type -> DidTargetType,
|
||||
#[max_length = 512]
|
||||
target -> Nullable<Varchar>,
|
||||
active -> Bool,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user