mirror of
https://github.com/lifegpc/pixiv_downloader.git
synced 2026-06-06 05:49:01 +08:00
Impl /auth/user/list
This commit is contained in:
@@ -35,6 +35,10 @@
|
||||
* 方法: `GET` 或 `POST`
|
||||
* RESTful: `GET /api/auth/user` 或 `GET /auth/user`
|
||||
* 鉴权: 需要(其他用户信息仅管理员)
|
||||
## 获取用户列表
|
||||
* 路径: `/api/auth/user/list`、 `/auth/user/list`
|
||||
* 方法: `GET` 或 `POST`
|
||||
* 鉴权: 需要(仅管理员)
|
||||
## 获取Token
|
||||
* 路径: `/api/auth/token/add`、 `/auth/token/add`
|
||||
* 方法: `GET` 或 `POST`
|
||||
|
||||
@@ -319,6 +319,38 @@ impl PixivDownloaderSqlite {
|
||||
.optional()?)
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
async fn _list_users(&self, offset: u64, limit: u64) -> Result<Vec<User>, SqliteError> {
|
||||
let con = self.db.lock().await;
|
||||
let mut stmt = con.prepare("SELECT * FROM users LIMIT ?, ?;")?;
|
||||
let mut rows = stmt.query([offset, limit])?;
|
||||
let mut users = Vec::new();
|
||||
while let Some(row) = rows.next()? {
|
||||
let password: Vec<u8> = row.get(3)?;
|
||||
let password: &[u8] = &password;
|
||||
users.push(User {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
username: row.get(2)?,
|
||||
password: BytesMut::from(password),
|
||||
is_admin: row.get(4)?,
|
||||
});
|
||||
}
|
||||
Ok(users)
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
async fn _list_users_id(&self, offset: u64, count: u64) -> Result<Vec<u64>, SqliteError> {
|
||||
let con = self.db.lock().await;
|
||||
let mut stmt = con.prepare("SELECT id FROM users LIMIT ?, ?;")?;
|
||||
let mut rows = stmt.query([offset, count])?;
|
||||
let mut ids = Vec::new();
|
||||
while let Some(row) = rows.next()? {
|
||||
ids.push(row.get(0)?);
|
||||
}
|
||||
Ok(ids)
|
||||
}
|
||||
|
||||
async fn _read_version(&self) -> Result<Option<[u8; 4]>, SqliteError> {
|
||||
let con = self.db.lock().await;
|
||||
let mut stmt = con.prepare("SELECT v1, v2, v3, v4 FROM version WHERE id='main';")?;
|
||||
@@ -596,6 +628,24 @@ impl PixivDownloaderDb for PixivDownloaderSqlite {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
async fn list_users(
|
||||
&self,
|
||||
offset: u64,
|
||||
limit: u64,
|
||||
) -> Result<Vec<User>, PixivDownloaderDbError> {
|
||||
Ok(self._list_users(offset, limit).await?)
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
async fn list_users_id(
|
||||
&self,
|
||||
offset: u64,
|
||||
count: u64,
|
||||
) -> Result<Vec<u64>, PixivDownloaderDbError> {
|
||||
Ok(self._list_users_id(offset, count).await?)
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
async fn revoke_expired_tokens(&self) -> Result<usize, PixivDownloaderDbError> {
|
||||
let mut db = self.db.lock().await;
|
||||
|
||||
@@ -78,6 +78,24 @@ pub trait PixivDownloaderDb {
|
||||
/// Initialize the database (create tables, migrate data, etc.)
|
||||
async fn init(&self) -> Result<(), PixivDownloaderDbError>;
|
||||
#[cfg(feature = "server")]
|
||||
/// List users
|
||||
/// * `offset` - The offset of the first user
|
||||
/// * `limit` - The maximum number of users to return
|
||||
async fn list_users(
|
||||
&self,
|
||||
offset: u64,
|
||||
limit: u64,
|
||||
) -> Result<Vec<User>, PixivDownloaderDbError>;
|
||||
#[cfg(feature = "server")]
|
||||
/// List users' id
|
||||
/// * `offset` - The offset of the list
|
||||
/// * `count` - The maximum count of the list
|
||||
async fn list_users_id(
|
||||
&self,
|
||||
offset: u64,
|
||||
count: u64,
|
||||
) -> Result<Vec<u64>, PixivDownloaderDbError>;
|
||||
#[cfg(feature = "server")]
|
||||
/// Remove all expired tokens
|
||||
/// Return the number of removed tokens
|
||||
async fn revoke_expired_tokens(&self) -> Result<usize, PixivDownloaderDbError>;
|
||||
|
||||
@@ -19,6 +19,8 @@ pub enum AuthUserAction {
|
||||
Delete,
|
||||
/// Get a user's information.
|
||||
GetInfo,
|
||||
/// List users.
|
||||
List,
|
||||
/// Update a existed user.
|
||||
Update,
|
||||
}
|
||||
@@ -276,6 +278,77 @@ impl AuthUserContext {
|
||||
}
|
||||
Ok(nuser.to_json2())
|
||||
}
|
||||
AuthUserAction::List => {
|
||||
if root_user.is_some() {
|
||||
if !user.as_ref().expect("User not found:").is_admin {
|
||||
return Err((9, gettext("Admin privileges required.")).into());
|
||||
}
|
||||
}
|
||||
let page = params
|
||||
.get_u64_mult(&["page", "p"])
|
||||
.try_err3(
|
||||
20,
|
||||
&gettext("Failed to parse <opt>:")
|
||||
.replace("<opt>", gettext("page number")),
|
||||
)?
|
||||
.unwrap_or(1);
|
||||
let page_count = params
|
||||
.get_u64_mult(&["page_count", "pc"])
|
||||
.try_err3(
|
||||
22,
|
||||
&gettext("Failed to parse <opt>:")
|
||||
.replace("<opt>", gettext("page count")),
|
||||
)?
|
||||
.unwrap_or(10);
|
||||
if page == 0 {
|
||||
return Err((
|
||||
21,
|
||||
&gettext("<sth> should be greater than <num>.")
|
||||
.replace("<sth>", gettext("Page number"))
|
||||
.replace("<num>", "0"),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
if page_count == 0 {
|
||||
return Err((
|
||||
23,
|
||||
&gettext("<sth> should be greater than <num>.")
|
||||
.replace("<sth>", gettext("Page count"))
|
||||
.replace("<num>", "0"),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
let id_only = params
|
||||
.get_bool("id_only")
|
||||
.try_err3(
|
||||
24,
|
||||
&gettext("Failed to parse <opt>:").replace("<opt>", "id_only"),
|
||||
)?
|
||||
.unwrap_or(false);
|
||||
let offset = (page - 1) * page_count;
|
||||
let data = if id_only {
|
||||
let users = self
|
||||
.ctx
|
||||
.db
|
||||
.list_users_id(offset, page_count)
|
||||
.await
|
||||
.try_err3(-1001, gettext("Failed to operate the database:"))?;
|
||||
json::from(users)
|
||||
} else {
|
||||
let users = self
|
||||
.ctx
|
||||
.db
|
||||
.list_users(offset, page_count)
|
||||
.await
|
||||
.try_err3(-1001, gettext("Failed to operate the database:"))?;
|
||||
let mut tmp = Vec::with_capacity(users.len());
|
||||
for user in users {
|
||||
tmp.push(user.to_json2());
|
||||
}
|
||||
json::from(tmp)
|
||||
};
|
||||
Ok(json::object! { "page": page, "page_count": page_count, "data": data })
|
||||
}
|
||||
AuthUserAction::Update => {
|
||||
if root_user.is_some() {
|
||||
if !user.as_ref().expect("User not found:").is_admin {
|
||||
@@ -423,7 +496,7 @@ impl AuthUserRoute {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
regex: Regex::new(
|
||||
r"^(/+api)?/+auth/+user(/+(add|update|delete|info|change/+(name|password)))?$",
|
||||
r"^(/+api)?/+auth/+user(/+(add|update|delete|info|list|change/+(name|password)))?$",
|
||||
)
|
||||
.unwrap(),
|
||||
}
|
||||
@@ -456,6 +529,7 @@ impl MatchRoute<Body, Body> for AuthUserRoute {
|
||||
"add" => Some(AuthUserAction::Add),
|
||||
"delete" => Some(AuthUserAction::Delete),
|
||||
"info" => Some(AuthUserAction::GetInfo),
|
||||
"list" => Some(AuthUserAction::List),
|
||||
"update" => Some(AuthUserAction::Update),
|
||||
_ => {
|
||||
if m.starts_with("change/") {
|
||||
|
||||
@@ -74,6 +74,24 @@ impl RequestParams {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get parameter and return it as [u64].
|
||||
/// * `name` - A list of the parameter name.
|
||||
/// # Note
|
||||
/// It will return the first existed parameter's value.
|
||||
pub fn get_u64_mult<S: AsRef<str> + ?Sized>(
|
||||
&self,
|
||||
name: &[&S],
|
||||
) -> Result<Option<u64>, PixivDownloaderError> {
|
||||
for name in name.iter() {
|
||||
match self.get_u64(name) {
|
||||
Ok(Some(v)) => return Ok(Some(v)),
|
||||
Ok(None) => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Get all parameters with same name.
|
||||
/// * `name` - Parameter name.
|
||||
pub fn get_all<S: AsRef<str> + ?Sized>(&self, name: &S) -> Option<&Vec<String>> {
|
||||
|
||||
@@ -426,6 +426,24 @@ pub async fn test(ctx: &UnitTestContext) -> Result<[(u64, Vec<u8>); 2], PixivDow
|
||||
"is_admin": false,
|
||||
}
|
||||
);
|
||||
let re = ctx
|
||||
.request_json2_sign(
|
||||
"/auth/user/list",
|
||||
&json::object! { "id_only": true },
|
||||
&token,
|
||||
token_id,
|
||||
)
|
||||
.await?
|
||||
.unwrap();
|
||||
let result = JSONResult::from_json(re)?.unwrap();
|
||||
assert_eq!(
|
||||
result,
|
||||
json::object! {
|
||||
"page": 1,
|
||||
"page_count": 10,
|
||||
"data": [0, 1],
|
||||
}
|
||||
);
|
||||
let mut password3 = BytesMut::with_capacity(64);
|
||||
password3.resize(64, 0);
|
||||
openssl::rand::rand_bytes(&mut password3)?;
|
||||
@@ -489,6 +507,24 @@ pub async fn test(ctx: &UnitTestContext) -> Result<[(u64, Vec<u8>); 2], PixivDow
|
||||
"is_admin": false,
|
||||
}
|
||||
);
|
||||
let re = ctx
|
||||
.request_json2_sign(
|
||||
"/auth/user/list",
|
||||
&json::object! { "id_only": true, "page_count": 2, "page": 2 },
|
||||
&token,
|
||||
token_id,
|
||||
)
|
||||
.await?
|
||||
.unwrap();
|
||||
let result = JSONResult::from_json(re)?.unwrap();
|
||||
assert_eq!(
|
||||
result,
|
||||
json::object! {
|
||||
"page": 2,
|
||||
"page_count": 2,
|
||||
"data": [2],
|
||||
}
|
||||
);
|
||||
let re = ctx
|
||||
.request_json2_sign(
|
||||
"/auth/user/delete",
|
||||
@@ -511,5 +547,49 @@ pub async fn test(ctx: &UnitTestContext) -> Result<[(u64, Vec<u8>); 2], PixivDow
|
||||
.unwrap();
|
||||
let result = JSONResult::from_json(re)?.unwrap();
|
||||
assert_eq!(result.as_bool(), Some(true));
|
||||
let re = ctx
|
||||
.request_json2_sign(
|
||||
"/auth/user/list",
|
||||
&json::object! { "id_only": true, "page_count": 2, "page": 1 },
|
||||
&token,
|
||||
token_id,
|
||||
)
|
||||
.await?
|
||||
.unwrap();
|
||||
let result = JSONResult::from_json(re)?.unwrap();
|
||||
assert_eq!(
|
||||
result,
|
||||
json::object! {
|
||||
"page": 1,
|
||||
"page_count": 2,
|
||||
"data": [0, 1],
|
||||
}
|
||||
);
|
||||
let re = ctx
|
||||
.request_json2_sign("/auth/user/list", &json::object! {}, &token, token_id)
|
||||
.await?
|
||||
.unwrap();
|
||||
let result = JSONResult::from_json(re)?.unwrap();
|
||||
assert_eq!(
|
||||
result,
|
||||
json::object! {
|
||||
"page": 1,
|
||||
"page_count": 10,
|
||||
"data": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "test",
|
||||
"username": "test",
|
||||
"is_admin": true,
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "sadiuqwed",
|
||||
"username": "test1",
|
||||
"is_admin": false,
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
Ok([(token_id, token), (token2_id, token2)])
|
||||
}
|
||||
|
||||
@@ -291,7 +291,9 @@ impl UgoiraFrames {
|
||||
pub fn append<T: ToCStr>(&mut self, file: T, delay: f32) -> Result<(), UgoiraError> {
|
||||
let f = file.to_cstr()?;
|
||||
if delay <= 0f32 {
|
||||
Err(gettext("Delay should be greater than 0."))?;
|
||||
Err(gettext("<sth> should be greater than <num>.")
|
||||
.replace("<sth>", gettext("Delay"))
|
||||
.replace("<num>", "0"))?;
|
||||
}
|
||||
let re = unsafe { _ugoira::new_ugoira_frame(f.as_ptr(), delay, self.tail) };
|
||||
if re.is_null() {
|
||||
|
||||
Reference in New Issue
Block a user