Skip to content

Commit 49b3cb8

Browse files
committed
feat: new generic and unified banner system
1 parent 378d480 commit 49b3cb8

12 files changed

Lines changed: 347 additions & 1 deletion

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@
1313
/dimbreath/
1414

1515
id_ed25519_sign
16+
.idea
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
CREATE TABLE IF NOT EXISTS gacha_banners
2+
(
3+
id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
4+
5+
-- Game id, 1 = HSR, 2 = ZZZ, 3 = Genshin, etc.
6+
game_id integer NOT NULL,
7+
8+
-- Banner gacha type, depends on the game (1 = hsr standard, 11 = hsr lightcone, 1001 = zzz standard, etc.)
9+
gacha_type integer,
10+
-- Banner id, optional, unique id of banner used by the game (only HSR and ZZZ have these?)
11+
banner_id integer,
12+
13+
title text,
14+
internal_name text,
15+
version text,
16+
17+
rate_up_5_stars int[] DEFAULT '{}' NOT NULL,
18+
rate_up_4_stars int[] DEFAULT '{}' NOT NULL,
19+
20+
start_time timestamp without time zone NOT NULL,
21+
end_time timestamp without time zone NOT NULL,
22+
timezone_dependant boolean NOT NULL DEFAULT FALSE,
23+
24+
disabled boolean NOT NULL DEFAULT FALSE,
25+
created_at timestamp WITH TIME ZONE DEFAULT timezone('UTC'::text, now()) NOT NULL
26+
);

sql/gacha_banners/create.sql

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
INSERT INTO gacha_banners (
2+
game_id,
3+
gacha_type,
4+
banner_id,
5+
title,
6+
internal_name,
7+
version,
8+
rate_up_5_stars,
9+
rate_up_4_stars,
10+
start_time,
11+
end_time,
12+
timezone_dependant,
13+
disabled
14+
) VALUES (
15+
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
16+
)
17+
RETURNING id;

sql/gacha_banners/delete_by_id.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
DELETE FROM gacha_banners
2+
WHERE id = $1;
3+

sql/gacha_banners/get_all.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
SELECT
2+
*
3+
FROM
4+
gacha_banners;
5+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SELECT * FROM gacha_banners
2+
WHERE game_id = $1
3+

sql/gacha_banners/get_by_id.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
SELECT
2+
*
3+
FROM
4+
gacha_banners
5+
WHERE
6+
id = $1;
7+

sql/gacha_banners/update.sql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
UPDATE gacha_banners
2+
SET
3+
game_id = $1,
4+
gacha_type = $2,
5+
banner_id = $3,
6+
title = $4,
7+
internal_name = $5,
8+
version = $6,
9+
rate_up_5_stars = $7,
10+
rate_up_4_stars = $8,
11+
start_time = $9,
12+
end_time = $10,
13+
timezone_dependant = $11,
14+
disabled = $12
15+
WHERE id = $13;

src/api/admin/gacha_banners/mod.rs

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
use actix_session::Session;
2+
use actix_web::{delete, get, post, put, web, HttpResponse, Responder};
3+
use sqlx::PgPool;
4+
use utoipa::{OpenApi, ToSchema};
5+
6+
use crate::{api::ApiResult, database};
7+
8+
#[derive(OpenApi)]
9+
#[openapi(
10+
tags((name = "admin/gacha_banners")),
11+
paths(get_gacha_banners, create_gacha_banner, update_gacha_banner, delete_gacha_banner),
12+
components(schemas(
13+
database::gacha_banners::DbGachaBanner
14+
))
15+
)]
16+
struct ApiDoc;
17+
18+
pub fn openapi() -> utoipa::openapi::OpenApi {
19+
ApiDoc::openapi()
20+
}
21+
22+
pub fn configure(cfg: &mut web::ServiceConfig) {
23+
cfg
24+
.service(get_gacha_banners)
25+
.service(create_gacha_banner)
26+
.service(update_gacha_banner)
27+
.service(delete_gacha_banner);
28+
}
29+
30+
#[utoipa::path(
31+
tag = "admin/gacha_banners",
32+
get,
33+
path = "/api/admin/gacha_banners",
34+
responses(
35+
(status = 200, description = "List of gacha banners", body = Vec<database::gacha_banners::DbGachaBanner>),
36+
),
37+
security(("admin" = []))
38+
)]
39+
#[get("/api/admin/gacha_banners")]
40+
async fn get_gacha_banners(session: Session, pool: web::Data<PgPool>) -> ApiResult<impl Responder> {
41+
let Ok(Some(username)) = session.get::<String>("username") else {
42+
return Ok(HttpResponse::BadRequest().finish());
43+
};
44+
45+
let admin = database::admins::exists(&username, &pool).await?;
46+
if !admin {
47+
return Ok(HttpResponse::Forbidden().finish());
48+
}
49+
50+
let banners = database::gacha_banners::get_all(&pool).await?;
51+
Ok(HttpResponse::Ok().json(banners))
52+
}
53+
54+
#[utoipa::path(
55+
tag = "admin/gacha_banners",
56+
post,
57+
path = "/api/admin/gacha_banners",
58+
request_body = database::gacha_banners::DbGachaBanner,
59+
responses(
60+
(status = 201, description = "Created gacha banner", body = database::gacha_banners::DbGachaBanner)
61+
),
62+
security(("admin" = []))
63+
)]
64+
#[post("/api/admin/gacha_banners")]
65+
async fn create_gacha_banner(
66+
session: Session,
67+
pool: web::Data<PgPool>,
68+
banner: web::Json<database::gacha_banners::DbGachaBanner>,
69+
) -> ApiResult<impl Responder> {
70+
let Ok(Some(username)) = session.get::<String>("username") else {
71+
return Ok(HttpResponse::BadRequest().finish());
72+
};
73+
74+
let admin = database::admins::exists(&username, &pool).await?;
75+
if !admin {
76+
return Ok(HttpResponse::Forbidden().finish());
77+
}
78+
79+
let created_banner = database::gacha_banners::create(&banner, &pool).await?;
80+
Ok(HttpResponse::Created().json(created_banner))
81+
}
82+
83+
#[utoipa::path(
84+
tag = "admin/gacha_banners",
85+
put,
86+
path = "/api/admin/gacha_banners/{id}",
87+
request_body = database::gacha_banners::DbGachaBanner,
88+
responses(
89+
(status = 200, description = "Updated gacha banner", body = database::gacha_banners::DbGachaBanner),
90+
(status = 404, description = "Gacha banner not found"),
91+
),
92+
security(("admin" = []))
93+
)]
94+
#[put("/api/admin/gacha_banners/{id}")]
95+
async fn update_gacha_banner(
96+
session: Session,
97+
pool: web::Data<PgPool>,
98+
id: web::Path<i32>,
99+
banner: web::Json<database::gacha_banners::DbGachaBanner>,
100+
) -> ApiResult<impl Responder> {
101+
let Ok(Some(username)) = session.get::<String>("username") else {
102+
return Ok(HttpResponse::BadRequest().finish());
103+
};
104+
105+
let admin = database::admins::exists(&username, &pool).await?;
106+
if !admin {
107+
return Ok(HttpResponse::Forbidden().finish());
108+
}
109+
110+
let mut banner = banner.into_inner();
111+
banner.id = *id;
112+
113+
let updated_banner = database::gacha_banners::update(&banner, &pool).await?;
114+
if updated_banner.is_none() {
115+
return Ok(HttpResponse::NotFound().finish());
116+
}
117+
118+
Ok(HttpResponse::Ok().json(updated_banner))
119+
}
120+
121+
#[utoipa::path(
122+
tag = "admin/gacha_banners",
123+
delete,
124+
path = "/api/admin/gacha_banners/{id}",
125+
responses(
126+
(status = 204, description = "Gacha banner deleted"),
127+
),
128+
security(("admin" = []))
129+
)]
130+
#[delete("/api/admin/gacha_banners/{id}")]
131+
async fn delete_gacha_banner(
132+
session: Session,
133+
pool: web::Data<PgPool>,
134+
id: web::Path<i32>,
135+
) -> ApiResult<impl Responder> {
136+
let Ok(Some(username)) = session.get::<String>("username") else {
137+
return Ok(HttpResponse::BadRequest().finish());
138+
};
139+
140+
let admin = database::admins::exists(&username, &pool).await?;
141+
if !admin {
142+
return Ok(HttpResponse::Forbidden().finish());
143+
}
144+
145+
database::gacha_banners::delete_by_id(*id, &pool).await?;
146+
Ok(HttpResponse::NoContent().finish())
147+
}

src/api/admin/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
mod delete_unofficial_signals;
22
mod delete_unofficial_warps;
33
mod delete_unofficial_wishes;
4+
mod gacha_banners;
45

56
use actix_web::web;
67

78
pub fn openapi() -> utoipa::openapi::OpenApi {
89
let mut openapi = delete_unofficial_signals::openapi();
910
openapi.merge(delete_unofficial_warps::openapi());
1011
openapi.merge(delete_unofficial_wishes::openapi());
12+
openapi.merge(gacha_banners::openapi());
1113
openapi
1214
}
1315

1416
pub fn configure(cfg: &mut web::ServiceConfig) {
1517
cfg.configure(delete_unofficial_signals::configure)
1618
.configure(delete_unofficial_warps::configure)
17-
.configure(delete_unofficial_wishes::configure);
19+
.configure(delete_unofficial_wishes::configure)
20+
.configure(gacha_banners::configure);
1821
}

0 commit comments

Comments
 (0)