Skip to content

Commit

Permalink
update invitation feature
Browse files Browse the repository at this point in the history
  • Loading branch information
zmh-program committed Oct 24, 2023
1 parent 7b3880d commit ba3d930
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 15 deletions.
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,26 @@
- 📦 Cache system
8. 🎈 对话记忆功能
- 🎈 Conversation memorization
9. 🎁 图片生成功能
- 🎁 Image generation
10. 🔔 PWA 应用
9. 👋 对话分享
- 👋 Conversation sharing
10. 🎁 图片生成功能
- 🎁 Image generation
11. 🔔 PWA 应用
- 🔔 PWA application
11. ⚡ Token 计费系统
12. ⚡ Token 计费系统
- ⚡ Token billing system
12. 📚 逆向工程模型支持
13. 📚 逆向工程模型支持
- 📚 Reverse engineering model support
13. 🌏 国际化支持
14. 🌏 国际化支持
- 🌏 Internationalization support
- 🇨🇳 简体中文
- 🇺🇸 English
- 🇷🇺 Русский
14. 🍎 主题切换
15. 🍎 主题切换
- 🍎 Theme switching
15. 🥪 Key 中转服务
16. 🥪 Key 中转服务
- 🥪 Key relay service
16. 🔨 多模型支持
17. 🔨 多模型支持
- 🔨 Multi-model support


Expand Down
2 changes: 1 addition & 1 deletion app/src/conf.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import axios from "axios";
import { Model } from "./conversation/types.ts";

export const version = "3.5.6";
export const version = "3.5.7";
export const dev: boolean = window.location.hostname === "localhost";
export const deploy: boolean = true;
export let rest_api: string = "http://localhost:8094";
Expand Down
6 changes: 1 addition & 5 deletions app/src/conversation/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@ export async function loadConversation(
): Promise<ConversationInstance> {
const resp = await axios.get(`/conversation/load?id=${id}`);
if (resp.data.status) return resp.data.data as ConversationInstance;
return {
id,
name: "",
message: [{ role: "assistant", content: "load conversation failed" }],
};
return { id, name: "", message: [] };
}

export async function deleteConversation(
Expand Down
34 changes: 34 additions & 0 deletions auth/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package auth
import (
"chat/utils"
"github.com/gin-gonic/gin"
"strings"
)

type BuyForm struct {
Expand Down Expand Up @@ -141,3 +142,36 @@ func BuyAPI(c *gin.Context) {
})
}
}

func InviteAPI(c *gin.Context) {
user := GetUserByCtx(c)
if user == nil {
return
}

db := utils.GetDBFromContext(c)
code := strings.TrimSpace(c.Query("code"))
if len(code) == 0 {
c.JSON(200, gin.H{
"status": false,
"error": "invalid code",
"quota": 0.,
})
return
}

if quota, err := user.UseInvitation(db, code); err != nil {
c.JSON(200, gin.H{
"status": false,
"error": err.Error(),
"quota": 0.,
})
return
} else {
c.JSON(200, gin.H{
"status": true,
"error": "success",
"quota": quota,
})
}
}
107 changes: 107 additions & 0 deletions auth/invitation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package auth

import (
"chat/utils"
"database/sql"
"errors"
"fmt"
)

type Invitation struct {
Id int64 `json:"id"`
Code string `json:"code"`
Quota float32 `json:"quota"`
Type string `json:"type"`
Used bool `json:"used"`
UsedId int64 `json:"used_id"`
}

func GenerateCodes(db *sql.DB, num int, quota float32, t string) ([]string, error) {
arr := make([]string, 0)
idx := 0
for idx < num {
code := fmt.Sprintf("%s-%s", t, utils.GenerateChar(24))
if err := GenerateCode(db, code, quota, t); err != nil {
// unique constraint
if errors.Is(err, sql.ErrNoRows) {
continue
}
return nil, fmt.Errorf("failed to generate code: %w", err)
}
arr = append(arr, code)
idx++
}

return arr, nil
}

func GenerateCode(db *sql.DB, code string, quota float32, t string) error {
_, err := db.Exec(`
INSERT INTO invitation (code, quota, type)
VALUES (?, ?, ?)
`, code, quota, t)
return err
}

func GetInvitation(db *sql.DB, code string) (*Invitation, error) {
row := db.QueryRow(`
SELECT id, code, quota, type, used, used_id
FROM invitation
WHERE code = ?
`, code)
var invitation Invitation
err := row.Scan(&invitation.Id, &invitation.Code, &invitation.Quota, &invitation.Type, &invitation.Used, &invitation.UsedId)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, fmt.Errorf("invitation code not found")
}
return nil, fmt.Errorf("failed to get invitation: %w", err)
}
return &invitation, nil
}

func (i *Invitation) IsUsed() bool {
return i.Used
}

func (i *Invitation) Use(db *sql.DB, userId int64) error {
_, err := db.Exec(`
UPDATE invitation SET used = TRUE, used_id = ? WHERE id = ?
`, userId, i.Id)
return err
}

func (i *Invitation) GetQuota() float32 {
return i.Quota
}

func (i *Invitation) UseInvitation(db *sql.DB, user User) error {
if i.IsUsed() {
return fmt.Errorf("this invitation has been used")
}

if err := i.Use(db, user.GetID(db)); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return fmt.Errorf("invitation code not found")
}
return fmt.Errorf("failed to use invitation: %w", err)
}

if !user.IncreaseQuota(db, i.GetQuota()) {
return fmt.Errorf("failed to increase quota for user")
}

return nil
}

func (u *User) UseInvitation(db *sql.DB, code string) (float32, error) {
if invitation, err := GetInvitation(db, code); err != nil {
return 0, err
} else {
if err := invitation.UseInvitation(db, *u); err != nil {
return 0, fmt.Errorf("failed to use invitation: %w", err)
}

return invitation.GetQuota(), nil
}
}
1 change: 1 addition & 0 deletions auth/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ func Register(app *gin.Engine) {
app.POST("/buy", BuyAPI)
app.GET("/subscription", SubscriptionAPI)
app.POST("/subscribe", SubscribeAPI)
app.GET("/invite", InviteAPI)
}
21 changes: 21 additions & 0 deletions connection/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func ConnectMySQL() *sql.DB {
CreateQuotaTable(db)
CreateSubscriptionTable(db)
CreateApiKeyTable(db)
CreateInvitationTable(db)
return db
}

Expand Down Expand Up @@ -151,3 +152,23 @@ func CreateApiKeyTable(db *sql.DB) {
log.Fatal(err)
}
}

func CreateInvitationTable(db *sql.DB) {
_, err := db.Exec(`
CREATE TABLE IF NOT EXISTS invitation (
id INT PRIMARY KEY AUTO_INCREMENT,
code VARCHAR(255) UNIQUE,
quota DECIMAL(6, 4),
type VARCHAR(255),
used BOOLEAN DEFAULT FALSE,
used_id INT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY (used_id, type),
FOREIGN KEY (used_id) REFERENCES auth(id)
);
`)
if err != nil {
log.Fatal(err)
}
}
1 change: 1 addition & 0 deletions middleware/throttle.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var limits = map[string]Limiter{
"/subscription": {Duration: 1, Count: 2},
"/chat": {Duration: 1, Count: 5},
"/conversation": {Duration: 1, Count: 5},
"/invite": {Duration: 7200, Count: 20},
}

func GetPrefixMap[T comparable](s string, p map[string]T) *T {
Expand Down

0 comments on commit ba3d930

Please sign in to comment.