feat: add private and profile
This commit is contained in:
@@ -36,9 +36,12 @@ func (h *Handler) Routes(user, pass string) http.Handler {
|
||||
r.Use(middleware.BasicAuth("hakemsho-admin", map[string]string{user: pass}))
|
||||
r.Get("/", h.dashboard)
|
||||
r.Get("/shop", h.shop)
|
||||
r.Post("/shop/{kind}/add", h.addItem)
|
||||
r.Post("/shop/{kind}/delete", h.deleteItem)
|
||||
r.Post("/shop/{kind}", h.updateItem)
|
||||
r.Get("/users", h.users)
|
||||
r.Post("/users/coins", h.adjustCoins)
|
||||
r.Post("/users/grant", h.grantToUser)
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -82,6 +85,8 @@ func (h *Handler) shop(w http.ResponseWriter, r *http.Request) {
|
||||
"id", "title", "price_coins", "sort", "enabled"),
|
||||
"Boosters": h.rows(ctx, `SELECT id,title,multiplier,hours,price_toman,sort,enabled FROM boosters ORDER BY sort`,
|
||||
"id", "title", "multiplier", "hours", "price_toman", "sort", "enabled"),
|
||||
"VIP": h.rows(ctx, `SELECT id,title,months,price_toman,sort,enabled FROM vip_packages ORDER BY sort`,
|
||||
"id", "title", "months", "price_toman", "sort", "enabled"),
|
||||
"Tiers": h.rows(ctx, `SELECT id,title,hands,entry,prize,xp,trophy,sort,enabled FROM table_tiers ORDER BY sort`,
|
||||
"id", "title", "hands", "entry", "prize", "xp", "trophy", "sort", "enabled"),
|
||||
"Saved": r.URL.Query().Get("saved") == "1",
|
||||
@@ -118,6 +123,9 @@ func (h *Handler) updateItem(w http.ResponseWriter, r *http.Request) {
|
||||
case "booster":
|
||||
q = `UPDATE boosters SET title=?,multiplier=?,hours=?,price_toman=?,sort=?,enabled=? WHERE id=?`
|
||||
args = []any{r.FormValue("title"), f("multiplier"), f("hours"), f("price_toman"), f("sort"), en, id}
|
||||
case "vip":
|
||||
q = `UPDATE vip_packages SET title=?,months=?,price_toman=?,sort=?,enabled=? WHERE id=?`
|
||||
args = []any{r.FormValue("title"), f("months"), f("price_toman"), f("sort"), en, id}
|
||||
case "tier":
|
||||
q = `UPDATE table_tiers SET title=?,hands=?,entry=?,prize=?,xp=?,trophy=?,sort=?,enabled=? WHERE id=?`
|
||||
args = []any{r.FormValue("title"), f("hands"), f("entry"), f("prize"), f("xp"), f("trophy"), f("sort"), en, id}
|
||||
@@ -134,6 +142,82 @@ func (h *Handler) updateItem(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/admin/shop?saved=1", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// addItem یک ردیفِ جدید به کاتالوگ اضافه میکند (افزودن محصول).
|
||||
func (h *Handler) addItem(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "bad form", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
kind := chi.URLParam(r, "kind")
|
||||
id := r.FormValue("id")
|
||||
if id == "" {
|
||||
http.Error(w, "id required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
f := func(k string) int { v, _ := strconv.Atoi(r.FormValue(k)); return v }
|
||||
en := 0
|
||||
if r.FormValue("enabled") == "on" {
|
||||
en = 1
|
||||
}
|
||||
|
||||
var q string
|
||||
var args []any
|
||||
switch kind {
|
||||
case "coin":
|
||||
q = `INSERT INTO coin_packages (id,title,coins,vip_days,price_toman,bonus_pct,sort,enabled) VALUES (?,?,?,?,?,?,?,?)`
|
||||
args = []any{id, r.FormValue("title"), f("coins"), f("vip_days"), f("price_toman"), f("bonus_pct"), f("sort"), en}
|
||||
case "ticket":
|
||||
q = `INSERT INTO ticket_packages (id,title,tickets,price_toman,sort,enabled) VALUES (?,?,?,?,?,?)`
|
||||
args = []any{id, r.FormValue("title"), f("tickets"), f("price_toman"), f("sort"), en}
|
||||
case "card":
|
||||
q = `INSERT INTO card_skins (id,title,price_coins,sort,enabled) VALUES (?,?,?,?,?)`
|
||||
args = []any{id, r.FormValue("title"), f("price_coins"), f("sort"), en}
|
||||
case "booster":
|
||||
q = `INSERT INTO boosters (id,title,multiplier,hours,price_toman,sort,enabled) VALUES (?,?,?,?,?,?)`
|
||||
args = []any{id, r.FormValue("title"), f("multiplier"), f("hours"), f("price_toman"), f("sort"), en}
|
||||
case "vip":
|
||||
q = `INSERT INTO vip_packages (id,title,months,price_toman,sort,enabled) VALUES (?,?,?,?,?,?)`
|
||||
args = []any{id, r.FormValue("title"), f("months"), f("price_toman"), f("sort"), en}
|
||||
case "tier":
|
||||
q = `INSERT INTO table_tiers (id,title,hands,entry,prize,xp,trophy,sort,enabled) VALUES (?,?,?,?,?,?,?,?,?)`
|
||||
args = []any{id, r.FormValue("title"), f("hands"), f("entry"), f("prize"), f("xp"), f("trophy"), f("sort"), en}
|
||||
default:
|
||||
http.Error(w, "unknown kind", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := h.db.ExecContext(r.Context(), q, args...); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_ = h.eco.LoadCatalog(r.Context())
|
||||
http.Redirect(w, r, "/admin/shop?saved=1", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// deleteItem یک ردیفِ کاتالوگ را حذف میکند.
|
||||
func (h *Handler) deleteItem(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "bad form", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
tables := map[string]string{
|
||||
"coin": "coin_packages", "ticket": "ticket_packages", "card": "card_skins",
|
||||
"booster": "boosters", "vip": "vip_packages", "tier": "table_tiers",
|
||||
}
|
||||
tbl, ok := tables[chi.URLParam(r, "kind")]
|
||||
if !ok {
|
||||
http.Error(w, "unknown kind", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if _, err := h.db.ExecContext(r.Context(),
|
||||
"DELETE FROM "+tbl+" WHERE id = ?", r.FormValue("id")); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_ = h.eco.LoadCatalog(r.Context())
|
||||
http.Redirect(w, r, "/admin/shop?saved=1", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// --- کاربران ---
|
||||
|
||||
func (h *Handler) users(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -194,6 +278,42 @@ func (h *Handler) adjustCoins(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/admin/users?saved=1", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// grantToUser یک محصول (سکه/بلیط/VIP/کارت) را به کاربر اعطا میکند.
|
||||
func (h *Handler) grantToUser(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "bad form", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
userID, _ := strconv.ParseInt(r.FormValue("user_id"), 10, 64)
|
||||
kind := r.FormValue("kind")
|
||||
value := r.FormValue("value")
|
||||
ctx := r.Context()
|
||||
if userID <= 0 {
|
||||
http.Redirect(w, r, "/admin/users", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
switch kind {
|
||||
case "coins":
|
||||
amount, _ := strconv.ParseInt(value, 10, 64)
|
||||
err = h.eco.Adjust(ctx, userID, economy.CurrencyCoin, amount, "admin_grant", "")
|
||||
case "tickets":
|
||||
amount, _ := strconv.ParseInt(value, 10, 64)
|
||||
err = h.eco.Adjust(ctx, userID, economy.CurrencyTicket, amount, "admin_grant", "")
|
||||
case "vip":
|
||||
days, _ := strconv.Atoi(value)
|
||||
err = h.eco.GrantVIPDays(ctx, userID, days)
|
||||
case "card":
|
||||
err = h.eco.GrantCard(ctx, userID, value)
|
||||
}
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/admin/users?saved=1", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// rows یک کوئری را به []map[string]any تبدیل میکند (برای رندرِ عمومیِ جدولها).
|
||||
func (h *Handler) rows(ctx context.Context, query string, cols ...string) []map[string]any {
|
||||
rs, err := h.db.QueryContext(ctx, query)
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
<div class="wrap">
|
||||
<h1>فروشگاه</h1>
|
||||
{{if .Saved}}<div class="saved">تغییرات ذخیره شد ✓</div>{{end}}
|
||||
<p style="color:#888">برای ویرایش مقدارها «ذخیره»، برای حذف «حذف» و در ردیف آخر هر جدول محصول جدید «افزودن».</p>
|
||||
|
||||
<h2>بستههای سکه</h2>
|
||||
<table>
|
||||
<tr><th>شناسه</th><th>عنوان</th><th>سکه</th><th>VIP (روز)</th><th>قیمت (تومان)</th><th>بونوس٪</th><th>ترتیب</th><th>فعال</th><th></th></tr>
|
||||
<tr><th>شناسه</th><th>عنوان</th><th>سکه</th><th>VIP (روز)</th><th>قیمت (تومان)</th><th>بونوس٪</th><th>ترتیب</th><th>فعال</th><th colspan="2"></th></tr>
|
||||
{{range .Coins}}
|
||||
<tr><form method="post" action="/admin/shop/coin">
|
||||
<td>{{.id}}<input type="hidden" name="id" value="{{.id}}"></td>
|
||||
@@ -21,13 +22,25 @@
|
||||
<td><input name="sort" value="{{.sort}}"></td>
|
||||
<td><input type="checkbox" name="enabled" {{if .enabled}}checked{{end}}></td>
|
||||
<td><button>ذخیره</button></td>
|
||||
<td><button formaction="/admin/shop/coin/delete" formnovalidate class="del">حذف</button></td>
|
||||
</form></tr>
|
||||
{{end}}
|
||||
<tr class="addrow"><form method="post" action="/admin/shop/coin/add">
|
||||
<td><input name="id" placeholder="شناسه" required></td>
|
||||
<td><input name="title" placeholder="عنوان"></td>
|
||||
<td><input name="coins" value="0"></td>
|
||||
<td><input name="vip_days" value="0"></td>
|
||||
<td><input name="price_toman" value="0"></td>
|
||||
<td><input name="bonus_pct" value="0"></td>
|
||||
<td><input name="sort" value="0"></td>
|
||||
<td><input type="checkbox" name="enabled" checked></td>
|
||||
<td colspan="2"><button>افزودن</button></td>
|
||||
</form></tr>
|
||||
</table>
|
||||
|
||||
<h2>بستههای بلیط</h2>
|
||||
<table>
|
||||
<tr><th>شناسه</th><th>عنوان</th><th>بلیط</th><th>قیمت (تومان)</th><th>ترتیب</th><th>فعال</th><th></th></tr>
|
||||
<tr><th>شناسه</th><th>عنوان</th><th>بلیط</th><th>قیمت (تومان)</th><th>ترتیب</th><th>فعال</th><th colspan="2"></th></tr>
|
||||
{{range .Tickets}}
|
||||
<tr><form method="post" action="/admin/shop/ticket">
|
||||
<td>{{.id}}<input type="hidden" name="id" value="{{.id}}"></td>
|
||||
@@ -37,13 +50,23 @@
|
||||
<td><input name="sort" value="{{.sort}}"></td>
|
||||
<td><input type="checkbox" name="enabled" {{if .enabled}}checked{{end}}></td>
|
||||
<td><button>ذخیره</button></td>
|
||||
<td><button formaction="/admin/shop/ticket/delete" formnovalidate class="del">حذف</button></td>
|
||||
</form></tr>
|
||||
{{end}}
|
||||
<tr class="addrow"><form method="post" action="/admin/shop/ticket/add">
|
||||
<td><input name="id" placeholder="شناسه" required></td>
|
||||
<td><input name="title" placeholder="عنوان"></td>
|
||||
<td><input name="tickets" value="0"></td>
|
||||
<td><input name="price_toman" value="0"></td>
|
||||
<td><input name="sort" value="0"></td>
|
||||
<td><input type="checkbox" name="enabled" checked></td>
|
||||
<td colspan="2"><button>افزودن</button></td>
|
||||
</form></tr>
|
||||
</table>
|
||||
|
||||
<h2>اسکین کارتها</h2>
|
||||
<table>
|
||||
<tr><th>شناسه</th><th>عنوان</th><th>قیمت (سکه)</th><th>ترتیب</th><th>فعال</th><th></th></tr>
|
||||
<tr><th>شناسه</th><th>عنوان</th><th>قیمت (سکه)</th><th>ترتیب</th><th>فعال</th><th colspan="2"></th></tr>
|
||||
{{range .Cards}}
|
||||
<tr><form method="post" action="/admin/shop/card">
|
||||
<td>{{.id}}<input type="hidden" name="id" value="{{.id}}"></td>
|
||||
@@ -52,13 +75,22 @@
|
||||
<td><input name="sort" value="{{.sort}}"></td>
|
||||
<td><input type="checkbox" name="enabled" {{if .enabled}}checked{{end}}></td>
|
||||
<td><button>ذخیره</button></td>
|
||||
<td><button formaction="/admin/shop/card/delete" formnovalidate class="del">حذف</button></td>
|
||||
</form></tr>
|
||||
{{end}}
|
||||
<tr class="addrow"><form method="post" action="/admin/shop/card/add">
|
||||
<td><input name="id" placeholder="شناسه" required></td>
|
||||
<td><input name="title" placeholder="عنوان"></td>
|
||||
<td><input name="price_coins" value="0"></td>
|
||||
<td><input name="sort" value="0"></td>
|
||||
<td><input type="checkbox" name="enabled" checked></td>
|
||||
<td colspan="2"><button>افزودن</button></td>
|
||||
</form></tr>
|
||||
</table>
|
||||
|
||||
<h2>بوسترها</h2>
|
||||
<table>
|
||||
<tr><th>شناسه</th><th>عنوان</th><th>ضریب</th><th>ساعت</th><th>قیمت (تومان)</th><th>ترتیب</th><th>فعال</th><th></th></tr>
|
||||
<tr><th>شناسه</th><th>عنوان</th><th>ضریب</th><th>ساعت</th><th>قیمت (تومان)</th><th>ترتیب</th><th>فعال</th><th colspan="2"></th></tr>
|
||||
{{range .Boosters}}
|
||||
<tr><form method="post" action="/admin/shop/booster">
|
||||
<td>{{.id}}<input type="hidden" name="id" value="{{.id}}"></td>
|
||||
@@ -69,13 +101,50 @@
|
||||
<td><input name="sort" value="{{.sort}}"></td>
|
||||
<td><input type="checkbox" name="enabled" {{if .enabled}}checked{{end}}></td>
|
||||
<td><button>ذخیره</button></td>
|
||||
<td><button formaction="/admin/shop/booster/delete" formnovalidate class="del">حذف</button></td>
|
||||
</form></tr>
|
||||
{{end}}
|
||||
<tr class="addrow"><form method="post" action="/admin/shop/booster/add">
|
||||
<td><input name="id" placeholder="شناسه" required></td>
|
||||
<td><input name="title" placeholder="عنوان"></td>
|
||||
<td><input name="multiplier" value="2"></td>
|
||||
<td><input name="hours" value="24"></td>
|
||||
<td><input name="price_toman" value="0"></td>
|
||||
<td><input name="sort" value="0"></td>
|
||||
<td><input type="checkbox" name="enabled" checked></td>
|
||||
<td colspan="2"><button>افزودن</button></td>
|
||||
</form></tr>
|
||||
</table>
|
||||
|
||||
<h2>اشتراکهای VIP</h2>
|
||||
<table>
|
||||
<tr><th>شناسه</th><th>عنوان</th><th>ماه</th><th>قیمت (تومان)</th><th>ترتیب</th><th>فعال</th><th colspan="2"></th></tr>
|
||||
{{range .VIP}}
|
||||
<tr><form method="post" action="/admin/shop/vip">
|
||||
<td>{{.id}}<input type="hidden" name="id" value="{{.id}}"></td>
|
||||
<td><input type="text" name="title" value="{{.title}}"></td>
|
||||
<td><input name="months" value="{{.months}}"></td>
|
||||
<td><input name="price_toman" value="{{.price_toman}}"></td>
|
||||
<td><input name="sort" value="{{.sort}}"></td>
|
||||
<td><input type="checkbox" name="enabled" {{if .enabled}}checked{{end}}></td>
|
||||
<td><button>ذخیره</button></td>
|
||||
<td><button formaction="/admin/shop/vip/delete" formnovalidate class="del">حذف</button></td>
|
||||
</form></tr>
|
||||
{{end}}
|
||||
<tr class="addrow"><form method="post" action="/admin/shop/vip/add">
|
||||
<td><input name="id" placeholder="شناسه" required></td>
|
||||
<td><input name="title" placeholder="عنوان"></td>
|
||||
<td><input name="months" value="1"></td>
|
||||
<td><input name="price_toman" value="0"></td>
|
||||
<td><input name="sort" value="0"></td>
|
||||
<td><input type="checkbox" name="enabled" checked></td>
|
||||
<td colspan="2"><button>افزودن</button></td>
|
||||
</form></tr>
|
||||
</table>
|
||||
|
||||
<h2>انواع میز</h2>
|
||||
<table>
|
||||
<tr><th>شناسه</th><th>عنوان</th><th>دست</th><th>ورودی</th><th>جایزه</th><th>XP</th><th>جام</th><th>ترتیب</th><th>فعال</th><th></th></tr>
|
||||
<tr><th>شناسه</th><th>عنوان</th><th>دست</th><th>ورودی</th><th>جایزه</th><th>XP</th><th>جام</th><th>ترتیب</th><th>فعال</th><th colspan="2"></th></tr>
|
||||
{{range .Tiers}}
|
||||
<tr><form method="post" action="/admin/shop/tier">
|
||||
<td>{{.id}}<input type="hidden" name="id" value="{{.id}}"></td>
|
||||
@@ -88,8 +157,21 @@
|
||||
<td><input name="sort" value="{{.sort}}"></td>
|
||||
<td><input type="checkbox" name="enabled" {{if .enabled}}checked{{end}}></td>
|
||||
<td><button>ذخیره</button></td>
|
||||
<td><button formaction="/admin/shop/tier/delete" formnovalidate class="del">حذف</button></td>
|
||||
</form></tr>
|
||||
{{end}}
|
||||
<tr class="addrow"><form method="post" action="/admin/shop/tier/add">
|
||||
<td><input name="id" placeholder="شناسه" required></td>
|
||||
<td><input name="title" placeholder="عنوان"></td>
|
||||
<td><input name="hands" value="7"></td>
|
||||
<td><input name="entry" value="0"></td>
|
||||
<td><input name="prize" value="0"></td>
|
||||
<td><input name="xp" value="0"></td>
|
||||
<td><input name="trophy" value="0"></td>
|
||||
<td><input name="sort" value="0"></td>
|
||||
<td><input type="checkbox" name="enabled" checked></td>
|
||||
<td colspan="2"><button>افزودن</button></td>
|
||||
</form></tr>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</form>
|
||||
|
||||
<table>
|
||||
<tr><th>#</th><th>موبایل</th><th>سکه</th><th>بلیط</th><th>سطح</th><th>جام</th><th>ادمین</th><th>تغییر سکه (+/-)</th></tr>
|
||||
<tr><th>#</th><th>موبایل</th><th>سکه</th><th>بلیط</th><th>سطح</th><th>جام</th><th>ادمین</th><th>تغییر سکه (+/-)</th><th>اعطای محصول</th></tr>
|
||||
{{range .Users}}
|
||||
<tr>
|
||||
<td>{{.ID}}</td>
|
||||
@@ -30,6 +30,19 @@
|
||||
<button>اعمال</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form method="post" action="/admin/users/grant" style="display:flex;gap:6px">
|
||||
<input type="hidden" name="user_id" value="{{.ID}}">
|
||||
<select name="kind">
|
||||
<option value="coins">سکه</option>
|
||||
<option value="tickets">بلیط</option>
|
||||
<option value="vip">VIP (روز)</option>
|
||||
<option value="card">کارت (شناسه)</option>
|
||||
</select>
|
||||
<input name="value" placeholder="مقدار / شناسه">
|
||||
<button>اعطا</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
|
||||
Reference in New Issue
Block a user