Can upload files
This commit is contained in:
parent
84e1c57dc9
commit
61a4ea62c6
1
moneymgr_backend/Cargo.lock
generated
1
moneymgr_backend/Cargo.lock
generated
@ -2110,6 +2110,7 @@ dependencies = [
|
||||
"rust-s3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
]
|
||||
|
@ -29,3 +29,4 @@ lazy-regex = "3.4.1"
|
||||
jwt-simple = { version = "0.12.11", default-features = false, features = ["pure-rust"] }
|
||||
mime_guess = "2.0.5"
|
||||
rust-embed = { version = "8.6.0" }
|
||||
sha2 = "0.10.8"
|
||||
|
@ -1,6 +1,6 @@
|
||||
DROP TABLE IF EXISTS inbox;
|
||||
DROP TABLE IF EXISTS movements;
|
||||
DROP TABLE IF EXISTS accounts;
|
||||
DROP TABLE IF EXISTS attachments;
|
||||
DROP TABLE IF EXISTS files;
|
||||
DROP TABLE IF EXISTS tokens;
|
||||
DROP TABLE IF EXISTS users;
|
@ -9,29 +9,30 @@ CREATE TABLE users
|
||||
|
||||
CREATE TABLE tokens
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(150) NOT NULL,
|
||||
time_create BIGINT NOT NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
token_value VARCHAR(150) NOT NULL,
|
||||
time_used BIGINT NOT NULL,
|
||||
max_inactivity INTEGER NOT NULL,
|
||||
ip_net VARCHAR(50),
|
||||
read_only BOOLEAN NOT NULL DEFAULT true,
|
||||
right_account BOOLEAN NOT NULL DEFAULT false,
|
||||
right_movement BOOLEAN NOT NULL DEFAULT false,
|
||||
right_inbox BOOLEAN NOT NULL DEFAULT false,
|
||||
right_attachment BOOLEAN NOT NULL DEFAULT false,
|
||||
right_auth BOOLEAN NOT NULL DEFAULT false
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(150) NOT NULL,
|
||||
time_create BIGINT NOT NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
token_value VARCHAR(150) NOT NULL,
|
||||
time_used BIGINT NOT NULL,
|
||||
max_inactivity INTEGER NOT NULL,
|
||||
ip_net VARCHAR(50),
|
||||
read_only BOOLEAN NOT NULL DEFAULT true,
|
||||
right_account BOOLEAN NOT NULL DEFAULT false,
|
||||
right_movement BOOLEAN NOT NULL DEFAULT false,
|
||||
right_inbox BOOLEAN NOT NULL DEFAULT false,
|
||||
right_file BOOLEAN NOT NULL DEFAULT false,
|
||||
right_auth BOOLEAN NOT NULL DEFAULT false
|
||||
);
|
||||
|
||||
CREATE TABLE attachments
|
||||
CREATE TABLE files
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
time_create BIGINT NOT NULL,
|
||||
mime_type VARCHAR(150) NOT NULL,
|
||||
sha512 VARCHAR(130) NOT NULL,
|
||||
file_size INTEGER NOT NULL,
|
||||
file_name VARCHAR(150) NOT NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE SET NULL
|
||||
);
|
||||
|
||||
@ -47,23 +48,23 @@ CREATE TABLE accounts
|
||||
|
||||
CREATE TABLE movements
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
account_id INTEGER NOT NULL REFERENCES accounts ON DELETE CASCADE,
|
||||
time BIGINT NOT NULL,
|
||||
label VARCHAR(200) NOT NULL,
|
||||
attachment_id INT REFERENCES attachments ON DELETE SET NULL,
|
||||
amount REAL NOT NULL,
|
||||
checked BOOLEAN NOT NULL DEFAULT false,
|
||||
time_create BIGINT NOT NULL,
|
||||
time_update BIGINT NOT NULL
|
||||
id SERIAL PRIMARY KEY,
|
||||
account_id INTEGER NOT NULL REFERENCES accounts ON DELETE CASCADE,
|
||||
time BIGINT NOT NULL,
|
||||
label VARCHAR(200) NOT NULL,
|
||||
file_id INT REFERENCES files ON DELETE SET NULL,
|
||||
amount REAL NOT NULL,
|
||||
checked BOOLEAN NOT NULL DEFAULT false,
|
||||
time_create BIGINT NOT NULL,
|
||||
time_update BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE inbox
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
attachment_id INTEGER NOT NULL REFERENCES attachments ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
account_id INTEGER REFERENCES accounts ON DELETE CASCADE,
|
||||
time_create BIGINT NOT NULL,
|
||||
time_update BIGINT NOT NULL
|
||||
id SERIAL PRIMARY KEY,
|
||||
file_id INTEGER NOT NULL REFERENCES files ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
account_id INTEGER REFERENCES accounts ON DELETE CASCADE,
|
||||
time_create BIGINT NOT NULL,
|
||||
time_update BIGINT NOT NULL
|
||||
);
|
@ -18,3 +18,6 @@ pub mod sessions {
|
||||
/// Authenticated ID
|
||||
pub const USER_ID: &str = "uid";
|
||||
}
|
||||
|
||||
/// Maximum uploaded file size (15MB)
|
||||
pub const MAX_UPLOAD_FILE_SIZE: usize = 15 * 1024 * 1024;
|
||||
|
18
moneymgr_backend/src/controllers/files_controller.rs
Normal file
18
moneymgr_backend/src/controllers/files_controller.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use crate::controllers::HttpResult;
|
||||
use crate::extractors::auth_extractor::AuthExtractor;
|
||||
use crate::extractors::file_extractor::FileExtractor;
|
||||
use crate::services::files_service;
|
||||
use actix_web::HttpResponse;
|
||||
|
||||
/// Upload a new file
|
||||
pub async fn upload(auth: AuthExtractor, file: FileExtractor) -> HttpResult {
|
||||
let file = files_service::create_file_with_mimetype(
|
||||
auth.user_id(),
|
||||
&file.name(),
|
||||
&file.mime,
|
||||
&file.buff,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(file))
|
||||
}
|
@ -4,6 +4,7 @@ use std::error::Error;
|
||||
|
||||
pub mod accounts_controller;
|
||||
pub mod auth_controller;
|
||||
pub mod files_controller;
|
||||
pub mod server_controller;
|
||||
pub mod static_controller;
|
||||
pub mod tokens_controller;
|
||||
|
@ -59,7 +59,7 @@ pub async fn create(auth: AuthExtractor, req: web::Json<CreateTokenBody>) -> Htt
|
||||
right_account: req.right_account,
|
||||
right_movement: req.right_movement,
|
||||
right_inbox: req.right_inbox,
|
||||
right_attachment: req.right_attachment,
|
||||
right_file: req.right_attachment,
|
||||
right_auth: req.right_auth,
|
||||
})
|
||||
.await?;
|
||||
|
@ -144,7 +144,7 @@ impl FromRequest for AuthExtractor {
|
||||
let authorized = (uri.starts_with("/api/account") && token.right_account)
|
||||
|| (uri.starts_with("/api/movement") && token.right_movement)
|
||||
|| (uri.starts_with("/api/inbox") && token.right_inbox)
|
||||
|| (uri.starts_with("/api/attachment") && token.right_attachment)
|
||||
|| (uri.starts_with("/api/file") && token.right_file)
|
||||
|| (uri.starts_with("/api/auth/") && token.right_auth);
|
||||
|
||||
if !authorized {
|
||||
|
74
moneymgr_backend/src/extractors/file_extractor.rs
Normal file
74
moneymgr_backend/src/extractors/file_extractor.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use crate::constants;
|
||||
use actix_multipart::form::MultipartForm;
|
||||
use actix_multipart::form::tempfile::TempFile;
|
||||
use actix_web::dev::Payload;
|
||||
use actix_web::{Error, FromRequest, HttpRequest};
|
||||
use mime_guess::Mime;
|
||||
use std::io::Read;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, MultipartForm)]
|
||||
struct FileUploadForm {
|
||||
#[multipart(rename = "file")]
|
||||
file: TempFile,
|
||||
}
|
||||
|
||||
pub struct FileExtractor {
|
||||
pub buff: Vec<u8>,
|
||||
pub mime: Mime,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
impl FileExtractor {
|
||||
pub fn name(&self) -> String {
|
||||
match &self.name {
|
||||
None => {
|
||||
let ext = self.mime.suffix().map(|s| s.as_str()).unwrap_or("bin");
|
||||
format!("file.{ext}")
|
||||
}
|
||||
Some(f) => f.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRequest for FileExtractor {
|
||||
type Error = Error;
|
||||
type Future = futures_util::future::LocalBoxFuture<'static, Result<Self, Self::Error>>;
|
||||
|
||||
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
|
||||
let form = MultipartForm::<FileUploadForm>::from_request(req, payload);
|
||||
|
||||
Box::pin(async move {
|
||||
let mut form = form.await?;
|
||||
|
||||
if form.file.size > constants::MAX_UPLOAD_FILE_SIZE {
|
||||
return Err(actix_web::error::ErrorPayloadTooLarge(
|
||||
"Uploaded file is too large!",
|
||||
));
|
||||
}
|
||||
|
||||
let mut buff = Vec::with_capacity(form.file.size);
|
||||
form.file.file.read_to_end(&mut buff)?;
|
||||
|
||||
let mime = match form
|
||||
.file
|
||||
.content_type
|
||||
.clone()
|
||||
.or_else(|| Mime::from_str(form.file.file_name.as_deref().unwrap_or("")).ok())
|
||||
{
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return Err(actix_web::error::ErrorBadRequest(
|
||||
"Mimetype was not specified!!",
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
mime,
|
||||
buff,
|
||||
name: form.file.file_name.clone(),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
pub mod account_extractor;
|
||||
pub mod auth_extractor;
|
||||
pub mod file_extractor;
|
||||
pub mod money_session;
|
||||
|
@ -119,6 +119,8 @@ async fn main() -> std::io::Result<()> {
|
||||
"/api/account/{account_id}",
|
||||
web::delete().to(accounts_controller::delete),
|
||||
)
|
||||
// Files controller
|
||||
.route("/api/file", web::post().to(files_controller::upload))
|
||||
// Static assets
|
||||
.route("/", web::get().to(static_controller::root_index))
|
||||
.route(
|
||||
|
47
moneymgr_backend/src/models/files.rs
Normal file
47
moneymgr_backend/src/models/files.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use crate::models::users::UserID;
|
||||
use crate::schema::*;
|
||||
use diesel::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||
pub struct FileID(pub i32);
|
||||
|
||||
#[derive(Queryable, Debug, serde::Serialize)]
|
||||
pub struct File {
|
||||
id: i32,
|
||||
pub time_create: i64,
|
||||
pub mime_type: String,
|
||||
pub sha512: String,
|
||||
pub file_size: i32,
|
||||
pub file_name: String,
|
||||
user_id: i32,
|
||||
}
|
||||
|
||||
impl File {
|
||||
pub fn id(&self) -> FileID {
|
||||
FileID(self.id)
|
||||
}
|
||||
pub fn file_path(&self) -> String {
|
||||
format!("blob/{}/{}", self.user_id, self.sha512)
|
||||
}
|
||||
|
||||
pub fn user_id(&self) -> UserID {
|
||||
UserID(self.id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = files)]
|
||||
pub struct NewFile<'a> {
|
||||
pub time_create: i64,
|
||||
pub mime_type: &'a str,
|
||||
pub sha512: &'a str,
|
||||
pub file_size: i32,
|
||||
pub file_name: &'a str,
|
||||
pub user_id: i32,
|
||||
}
|
||||
|
||||
impl NewFile<'_> {
|
||||
pub fn file_path(&self) -> String {
|
||||
format!("blob/{}/{}", self.user_id, self.sha512)
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
pub mod accounts;
|
||||
pub mod files;
|
||||
pub mod tokens;
|
||||
pub mod users;
|
||||
|
@ -32,7 +32,7 @@ pub struct Token {
|
||||
pub right_account: bool,
|
||||
pub right_movement: bool,
|
||||
pub right_inbox: bool,
|
||||
pub right_attachment: bool,
|
||||
pub right_file: bool,
|
||||
pub right_auth: bool,
|
||||
}
|
||||
|
||||
@ -76,6 +76,6 @@ pub struct NewToken<'a> {
|
||||
pub right_account: bool,
|
||||
pub right_movement: bool,
|
||||
pub right_inbox: bool,
|
||||
pub right_attachment: bool,
|
||||
pub right_file: bool,
|
||||
pub right_auth: bool,
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ diesel::table! {
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
attachments (id) {
|
||||
files (id) {
|
||||
id -> Int4,
|
||||
time_create -> Int8,
|
||||
#[max_length = 150]
|
||||
@ -21,6 +21,8 @@ diesel::table! {
|
||||
#[max_length = 130]
|
||||
sha512 -> Varchar,
|
||||
file_size -> Int4,
|
||||
#[max_length = 150]
|
||||
file_name -> Varchar,
|
||||
user_id -> Int4,
|
||||
}
|
||||
}
|
||||
@ -28,7 +30,7 @@ diesel::table! {
|
||||
diesel::table! {
|
||||
inbox (id) {
|
||||
id -> Int4,
|
||||
attachment_id -> Int4,
|
||||
file_id -> Int4,
|
||||
user_id -> Int4,
|
||||
account_id -> Nullable<Int4>,
|
||||
time_create -> Int8,
|
||||
@ -43,7 +45,7 @@ diesel::table! {
|
||||
time -> Int8,
|
||||
#[max_length = 200]
|
||||
label -> Varchar,
|
||||
attachment_id -> Nullable<Int4>,
|
||||
file_id -> Nullable<Int4>,
|
||||
amount -> Float4,
|
||||
checked -> Bool,
|
||||
time_create -> Int8,
|
||||
@ -68,7 +70,7 @@ diesel::table! {
|
||||
right_account -> Bool,
|
||||
right_movement -> Bool,
|
||||
right_inbox -> Bool,
|
||||
right_attachment -> Bool,
|
||||
right_file -> Bool,
|
||||
right_auth -> Bool,
|
||||
}
|
||||
}
|
||||
@ -86,19 +88,12 @@ diesel::table! {
|
||||
}
|
||||
|
||||
diesel::joinable!(accounts -> users (user_id));
|
||||
diesel::joinable!(attachments -> users (user_id));
|
||||
diesel::joinable!(files -> users (user_id));
|
||||
diesel::joinable!(inbox -> accounts (account_id));
|
||||
diesel::joinable!(inbox -> attachments (attachment_id));
|
||||
diesel::joinable!(inbox -> files (file_id));
|
||||
diesel::joinable!(inbox -> users (user_id));
|
||||
diesel::joinable!(movements -> accounts (account_id));
|
||||
diesel::joinable!(movements -> attachments (attachment_id));
|
||||
diesel::joinable!(movements -> files (file_id));
|
||||
diesel::joinable!(tokens -> users (user_id));
|
||||
|
||||
diesel::allow_tables_to_appear_in_same_query!(
|
||||
accounts,
|
||||
attachments,
|
||||
inbox,
|
||||
movements,
|
||||
tokens,
|
||||
users,
|
||||
);
|
||||
diesel::allow_tables_to_appear_in_same_query!(accounts, files, inbox, movements, tokens, users,);
|
||||
|
125
moneymgr_backend/src/services/files_service.rs
Normal file
125
moneymgr_backend/src/services/files_service.rs
Normal file
@ -0,0 +1,125 @@
|
||||
use crate::connections::db_connection::db;
|
||||
use crate::connections::s3_connection;
|
||||
use crate::models::files::{File, FileID, NewFile};
|
||||
use crate::models::users::UserID;
|
||||
use crate::schema::files;
|
||||
use crate::utils::crypt_utils::sha512;
|
||||
use crate::utils::time_utils::time;
|
||||
use diesel::prelude::*;
|
||||
use mime_guess::Mime;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
enum FilesServiceError {
|
||||
#[error("UnknownMimeType!")]
|
||||
UnknownMimeType,
|
||||
}
|
||||
|
||||
pub async fn create_file_with_file_name(
|
||||
user_id: UserID,
|
||||
file_name: &str,
|
||||
bytes: &[u8],
|
||||
) -> anyhow::Result<File> {
|
||||
let mime = mime_guess::from_path(file_name)
|
||||
.first()
|
||||
.ok_or(FilesServiceError::UnknownMimeType)?;
|
||||
|
||||
create_file_with_mimetype(user_id, file_name, &mime, bytes).await
|
||||
}
|
||||
|
||||
pub async fn create_file_with_mimetype(
|
||||
user_id: UserID,
|
||||
file_name: &str,
|
||||
mime_type: &Mime,
|
||||
bytes: &[u8],
|
||||
) -> anyhow::Result<File> {
|
||||
let sha512 = sha512(bytes);
|
||||
|
||||
if let Ok(f) = get_file_with_hash(user_id, &sha512) {
|
||||
return Ok(f);
|
||||
}
|
||||
|
||||
let file = NewFile {
|
||||
time_create: time() as i64,
|
||||
mime_type: mime_type.as_ref(),
|
||||
sha512: &sha512,
|
||||
file_size: bytes.len() as i32,
|
||||
file_name,
|
||||
user_id: user_id.0,
|
||||
};
|
||||
|
||||
s3_connection::upload_file(&file.file_path(), bytes).await?;
|
||||
|
||||
let res = diesel::insert_into(files::table)
|
||||
.values(&file)
|
||||
.get_result(&mut db()?)?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn get_file_with_hash(user_id: UserID, sha512: &str) -> anyhow::Result<File> {
|
||||
Ok(files::table
|
||||
.filter(
|
||||
files::dsl::sha512
|
||||
.eq(sha512)
|
||||
.and(files::dsl::user_id.eq(user_id.0)),
|
||||
)
|
||||
.first(&mut db()?)?)
|
||||
}
|
||||
|
||||
pub fn get_file_with_id(id: FileID) -> anyhow::Result<File> {
|
||||
Ok(files::table
|
||||
.filter(files::dsl::id.eq(id.0))
|
||||
.first(&mut db()?)?)
|
||||
}
|
||||
|
||||
pub async fn get_file_content_by_id(id: FileID) -> anyhow::Result<Vec<u8>> {
|
||||
let file = get_file_with_id(id)?;
|
||||
get_file_content(&file).await
|
||||
}
|
||||
|
||||
pub async fn get_file_content(file: &File) -> anyhow::Result<Vec<u8>> {
|
||||
s3_connection::get_file(&file.file_path()).await
|
||||
}
|
||||
|
||||
/// Delete the file if it is not referenced anymore in the database. Returns true
|
||||
/// if the file was actually deleted, false otherwise
|
||||
pub async fn delete_file_if_unused(id: FileID) -> anyhow::Result<bool> {
|
||||
let file = get_file_with_id(id)?;
|
||||
|
||||
let res = diesel::delete(files::dsl::files.filter(files::dsl::id.eq(file.id().0)))
|
||||
.execute(&mut db()?);
|
||||
|
||||
match res {
|
||||
Ok(_) => {
|
||||
s3_connection::delete_file_if_exists(&file.file_path()).await?;
|
||||
log::info!("File {:?} was deleted", file.id());
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
Err(e) => {
|
||||
log::info!(
|
||||
"File {:?} could not be deleted, it must be used somewhere: {e}",
|
||||
file.id()
|
||||
);
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the entire list of file
|
||||
pub async fn get_entire_list() -> anyhow::Result<Vec<File>> {
|
||||
Ok(files::table.get_results(&mut db()?)?)
|
||||
}
|
||||
|
||||
/// Remove unused files
|
||||
pub async fn run_garbage_collector() -> anyhow::Result<usize> {
|
||||
let mut count_deleted = 0;
|
||||
|
||||
for file in get_entire_list().await? {
|
||||
if delete_file_if_unused(file.id()).await? {
|
||||
count_deleted += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(count_deleted)
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
pub mod accounts_service;
|
||||
pub mod files_service;
|
||||
pub mod tokens_service;
|
||||
pub mod users_service;
|
||||
|
@ -17,7 +17,7 @@ pub struct NewTokenInfo {
|
||||
pub right_account: bool,
|
||||
pub right_movement: bool,
|
||||
pub right_inbox: bool,
|
||||
pub right_attachment: bool,
|
||||
pub right_file: bool,
|
||||
pub right_auth: bool,
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ pub async fn create(new_token: NewTokenInfo) -> anyhow::Result<Token> {
|
||||
right_account: new_token.right_account,
|
||||
right_movement: new_token.right_movement,
|
||||
right_inbox: new_token.right_inbox,
|
||||
right_attachment: new_token.right_attachment,
|
||||
right_file: new_token.right_file,
|
||||
};
|
||||
|
||||
let res = diesel::insert_into(tokens::table)
|
||||
|
9
moneymgr_backend/src/utils/crypt_utils.rs
Normal file
9
moneymgr_backend/src/utils/crypt_utils.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use sha2::{Digest, Sha512};
|
||||
|
||||
/// Compute hash of a slice of bytes (sha512)
|
||||
pub fn sha512(bytes: &[u8]) -> String {
|
||||
let mut hasher = Sha512::new();
|
||||
hasher.update(bytes);
|
||||
let h = hasher.finalize();
|
||||
format!("{:x}", h)
|
||||
}
|
@ -1,2 +1,3 @@
|
||||
pub mod crypt_utils;
|
||||
pub mod rand_utils;
|
||||
pub mod time_utils;
|
||||
|
@ -12,7 +12,7 @@ export interface Token {
|
||||
right_account: boolean;
|
||||
right_movement: boolean;
|
||||
right_inbox: boolean;
|
||||
right_attachment: boolean;
|
||||
right_file: boolean;
|
||||
right_auth: boolean;
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ export interface NewToken {
|
||||
right_account: boolean;
|
||||
right_movement: boolean;
|
||||
right_inbox: boolean;
|
||||
right_attachment: boolean;
|
||||
right_file: boolean;
|
||||
right_auth: boolean;
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ export function CreateTokenDialog(p: {
|
||||
max_inactivity: 3600 * 24 * 90,
|
||||
read_only: false,
|
||||
right_account: false,
|
||||
right_attachment: false,
|
||||
right_file: false,
|
||||
right_auth: false,
|
||||
right_inbox: false,
|
||||
right_movement: false,
|
||||
@ -74,7 +74,7 @@ export function CreateTokenDialog(p: {
|
||||
read_only: false,
|
||||
right_account: false,
|
||||
right_movement: false,
|
||||
right_attachment: true,
|
||||
right_file: true,
|
||||
right_auth: true,
|
||||
right_inbox: true,
|
||||
});
|
||||
@ -181,12 +181,12 @@ export function CreateTokenDialog(p: {
|
||||
<br />
|
||||
<CheckboxInput
|
||||
editable
|
||||
label="Right: attachment routes"
|
||||
checked={newToken.right_attachment}
|
||||
label="Right: file routes"
|
||||
checked={newToken.right_file}
|
||||
onValueChange={(v) => {
|
||||
setNewToken({
|
||||
...newToken,
|
||||
right_attachment: v,
|
||||
right_file: v,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
@ -180,8 +180,8 @@ function TokensRouteInner(p: {
|
||||
type: "boolean",
|
||||
},
|
||||
{
|
||||
field: "right_attachment",
|
||||
headerName: "Attachment",
|
||||
field: "right_file",
|
||||
headerName: "File",
|
||||
flex: 2,
|
||||
type: "boolean",
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user