Browse Source

change login

master
godo 3 weeks ago
parent
commit
9938f17701
  1. 2
      os/auto-imports.d.ts
  2. 16
      os/src/api/auth.ts
  3. 55
      os/src/api/captcha.ts
  4. 40
      os/src/components/auth/PasswordLogin.vue
  5. 44
      os/src/components/auth/SlideCaptcha.vue
  6. 36
      os/src/components/auth/UserRegister.vue
  7. 12
      os/src/stores/login.ts
  8. 5
      os/src/styles/login.scss
  9. 2
      os/src/utils/request.ts
  10. 19
      server/app/user/auth.go
  11. 21
      server/app/user/captcha.go
  12. 14
      server/app/user/login/const.go
  13. 22
      server/app/user/login/factory.go
  14. 86
      server/app/user/login/password.go
  15. 19
      server/app/user/login/sms.go
  16. 12
      server/common/cache.go
  17. 2
      server/middleware/serve.go
  18. 8
      server/model/auto_migrate.go
  19. 6
      server/model/user.go
  20. 2
      server/tmp/build-errors.log
  21. BIN
      server/tmp/main.exe

2
os/auto-imports.d.ts

@ -6,5 +6,5 @@
// biome-ignore lint: disable
export {}
declare global {
const ElMessage: typeof import('element-plus/es')['ElMessage']
}

16
os/src/api/auth.ts

@ -3,13 +3,7 @@ import { get, getToken, setToken,post } from '@/utils/request'
import { getClientId } from '@/utils/uuid'
import { errMsg } from '@/utils/msg';
export function loginIn(params: any) {
return fetch('user/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
}).then(res => res.json()).then(res => {
return post('user/login', params).then(res => {
if (res.success) {
setToken(res.data.token)
}
@ -72,11 +66,5 @@ export async function getSmsCode(phone: string) {
return await res.json()
}
export async function register(params: any) {
return fetch('/user/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
}).then(res => res.json())
return post('/user/register', params)
}

55
os/src/api/captcha.ts

@ -1,10 +1,10 @@
import {noticeMsg,successMsg} from "@/utils/msg";
import {get,post} from '@/utils/request'
import { noticeMsg} from "@/utils/msg";
import { get, post } from '@/utils/request'
import { onMounted, reactive, watch } from 'vue'
export const useHandler = (domRef:any) => {
const state = reactive({popoverVisible: false, type: "default"})
const cData = reactive({image: "", thumb: "", captKey: "", thumbX: 0, thumbY: 0, thumbWidth: 0, thumbHeight: 0})
export const useHandler = (domRef: any) => {
const state = reactive({ popoverVisible: false, type: "default" })
const cData = reactive({ image: "", thumb: "", captKey: "", thumbX: 0, thumbY: 0, thumbWidth: 0, thumbHeight: 0 })
const clickEvent = () => {
state.popoverVisible = true
@ -21,9 +21,9 @@ export const useHandler = (domRef:any) => {
const requestCaptchaData = () => {
domRef.value.clear && domRef.value.clear()
get('user/getcaptcha').then((response)=>{
console.log(response)
const {data = {}} = response;
get('user/getcaptcha').then((response) => {
//console.log(response)
const { data = {} } = response;
if (response.code === 0) {
cData.image = data['image_base64'] || ''
cData.thumb = data['tile_base64'] || ''
@ -31,11 +31,11 @@ export const useHandler = (domRef:any) => {
cData.thumbX = data['tile_x'] || 0
cData.thumbY = data['tile_y'] || 0
cData.thumbWidth = data['tile_width'] || 0
cData.thumbHeight =data['tile_height'] || 0
cData.thumbHeight = data['tile_height'] || 0
} else {
noticeMsg(`获取验证码失败`,'提示','error')
noticeMsg(`获取验证码失败`, '提示', 'error')
}
}).catch((e)=>{
}).catch((e) => {
console.warn(e)
})
}
@ -44,27 +44,28 @@ export const useHandler = (domRef:any) => {
requestCaptchaData()
}
const confirmEvent = (point:any, clear:any) => {
console.log(cData)
post('user/checkcaptcha',{
point: [point.x, point.y].join(','),
key: cData.captKey || ''
}).then((response)=>{
console.log(response)
if (response.success) {
successMsg(`check data success`)
const confirmEvent = (point: any, clear: any) => {
//console.log(cData)
post('user/checkcaptcha', {
point: [point.x, point.y].join(','),
key: cData.captKey || ''
}).then((response) => {
//console.log(response)
if (response.success) {
//successMsg(`check data success`)
state.popoverVisible = false
state.type = "success"
}else{
noticeMsg(`校验失败`,'提示','error')
//warningMsg(`check data failed`)
state.type = "error"
}
return;
} else {
noticeMsg(`校验失败`, '提示', 'error')
//warningMsg(`check data failed`)
state.type = "error"
}
setTimeout(() => {
requestCaptchaData()
}, 1000)
}).catch((e)=>{
}).catch((e) => {
console.warn(e)
})
}
@ -75,7 +76,7 @@ export const useHandler = (domRef:any) => {
}
})
onMounted(( ) => {
onMounted(() => {
requestCaptchaData()
})

40
os/src/components/auth/PasswordLogin.vue

@ -42,15 +42,16 @@ const submitForm = () => {
});
};
const showCaptcha = ref(false); //
const captchaPassed = ref(false); //
//const captchaPassed = ref(false); //
const handleCaptchaConfirm = (success: boolean) => {
if (success) {
captchaPassed.value = true;
//captchaPassed.value = true;
showCaptcha.value = false;
store.onLogin({
loginType: "password",
params: store.loginForm,
});
showCaptcha.value = false; //
//
} else {
ElMessage.error("验证码验证失败,请重试");
showCaptcha.value = false;
@ -81,12 +82,7 @@ const closeCaptcha = () => {
</el-form-item>
</el-form>
<teleport to="body">
<div v-if="showCaptcha" class="custom-captcha-dialog">
<div class="overlay" @click.self="closeCaptcha"></div>
<div class="dialog-content">
<SlideCaptcha ref="captcha" @onSuccess="handleCaptchaConfirm" />
</div>
</div>
<SlideCaptcha v-if="showCaptcha" @onSuccess="handleCaptchaConfirm" @onCancel="closeCaptcha" />
</teleport>
</template>
@ -110,31 +106,5 @@ const closeCaptcha = () => {
border-radius: 50px;
}
.custom-captcha-dialog {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 99;
display: flex;
align-items: center;
justify-content: center;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9998;
}
.dialog-content {
padding: 20px;
z-index: 9998;
width: 350px;
}
</style>

44
os/src/components/auth/SlideCaptcha.vue

@ -1,23 +1,27 @@
<template>
<div class="custom-captcha-dialog">
<div class="overlay" @click.self="close"></div>
<div class="dialog-content">
<gocaptcha-slide
:data="handler.data"
:scope="0"
:events="{
close: handler.closeEvent,
close: close,
refresh: handler.refreshEvent,
confirm: handler.confirmEvent,
}"
ref="domRef"
/>
</div>
</div>
</template>
<script setup lang="ts">
import {useHandler} from '@/api/captcha'
import {ref, watch} from 'vue'
const emit = defineEmits(['onSuccess'])
const emit = defineEmits(['onSuccess','onCancel'])
const domRef = ref(null)
const handler = useHandler(domRef)
@ -34,10 +38,42 @@
state.popoverVisible = true
}
const close = () => {
emit("onCancel",true)
state.popoverVisible = false
}
defineExpose({open, close})
</script>
<style></style>
<style>
.custom-captcha-dialog {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 99;
display: flex;
align-items: center;
justify-content: center;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
overflow: hidden;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9998;
}
.dialog-content {
padding: 20px;
z-index: 9998;
width: 350px;
}
</style>

36
os/src/components/auth/UserRegister.vue

@ -1,5 +1,6 @@
<script setup lang="ts">
import { useLoginStore } from "@/stores/login";
import { ElMessage } from "element-plus";
import { ref, Ref } from "vue";
const store = useLoginStore();
interface RegisterInfo {
@ -86,6 +87,36 @@ const rules = {
// },
// ],
};
const regFormRef = ref();
const showCaptcha = ref(false); //
const register = () => {
regFormRef.value.validate((valid:boolean) => {
if (valid) {
showCaptcha.value = true;
} else {
return false;
}
});
};
//const captchaPassed = ref(false); //
const handleCaptchaConfirm = (success: boolean) => {
if (success) {
//captchaPassed.value = true;
showCaptcha.value = false;
store.onRegister({
loginType: "password",
action: "register",
params: registerInfo.value,
})
//
} else {
ElMessage.error("验证码验证失败,请重试");
showCaptcha.value = false;
}
};
const closeCaptcha = () => {
showCaptcha.value = false;
};
</script>
<template>
<el-form label-position="left" label-width="0px" :model="registerInfo" ref="regFormRef" :rules="rules">
@ -127,9 +158,12 @@ const rules = {
</el-form-item>
<el-form-item class="button-center">
<el-button class="login-button" type="primary" size="large" @click="store.onRegister(registerInfo)">注册</el-button>
<el-button class="login-button" type="primary" size="large" @click="register">注册</el-button>
</el-form-item>
</el-form>
<teleport to="body">
<SlideCaptcha v-if="showCaptcha" @onSuccess="handleCaptchaConfirm" @onCancel="closeCaptcha" />
</teleport>
</template>
<style scoped>
.button-center {

12
os/src/stores/login.ts

@ -42,7 +42,7 @@ export const useLoginStore = defineStore('login', () => {
thirdPartyLoginMethod.value = 'password'
}
const onRegister = async (params: any) => {
delete params.confirmPassword
delete params.param.confirmPassword
const res = await auth.register(params)
if (res.success) {
@ -56,15 +56,16 @@ export const useLoginStore = defineStore('login', () => {
const postData = {
login_type: params.loginType,
client_id: getClientId(),
action: 'login',
param: params.params,
}
auth.loginIn(postData).then((res: any) => {
//console.log(res)
if (!params.params.rememberMe) {
loginForm.value.rememberMe = false
loginForm.value.username = ''
loginForm.value.password = ''
// loginForm.value.rememberMe = false
// loginForm.value.username = ''
// loginForm.value.password = ''
}
if (res.success) {
successMsg('登录成功')
@ -163,8 +164,7 @@ export const useLoginStore = defineStore('login', () => {
if (res.ok) {
const data = await res.json()
if (data.success) {
console.log(data.data)
; (window as any).WwLogin({
(window as any).WwLogin({
id: 'qywechat-qr-code',
appid: data.data.corp_id,
agentid: data.data.agent_id,

5
os/src/styles/login.scss

@ -1,5 +1,6 @@
.login-container {
height: 100vh;
height: 96vh;
overflow: hidden;
.lockscreen {
height: 100%;
width: 100%;
@ -192,4 +193,4 @@
width: 100%;
}
}
}

2
os/src/utils/request.ts

@ -84,7 +84,7 @@ request.interceptors.response.use(
(err) => {
// 处理错误信息
if (err) {
//console.log(err)
console.log(err)
if (err.response) {
const status = err.response.status;
const message = err.response.data;

19
server/app/user/auth.go

@ -28,21 +28,21 @@ func loginHandle(c *gin.Context) {
var req login.LoginRequest
// 解析请求体
if err := c.ShouldBindJSON(&req); err != nil {
libs.Error(c, "参数错误,登录失败")
libs.Error(c, "请求参数错误,登录失败")
return
}
fmt.Printf("request param: %+v\n", req)
//fmt.Printf("request param: %+v\n", req)
factory := &login.LoginHandlerFactory{}
handler, err := factory.GetHandler(req.LoginType)
req.Action = "login"
handler, err := factory.GetHandler(req)
if err != nil {
libs.Error(c, "登录失败:"+err.Error())
libs.Error(c, "适配失败:"+err.Error())
return
}
user, err := handler.Login(req.Param)
if err != nil {
user, err := handler.Login()
if err != nil || user == nil {
libs.Error(c, "登录失败:"+err.Error())
return
}
@ -197,13 +197,14 @@ func handleRegister(c *gin.Context) {
fmt.Printf("request param: %+v\n", req)
factory := &login.LoginHandlerFactory{}
handler, err := factory.GetHandler(req.LoginType)
req.Action = "register"
handler, err := factory.GetHandler(req)
if err != nil {
libs.Error(c, "适配失败:"+err.Error())
return
}
_, err = handler.Register(req.Param)
_, err = handler.Register()
if err != nil {
libs.Error(c, "注册失败:"+err.Error())
return

21
server/app/user/captcha.go

@ -109,7 +109,7 @@ func sendSmsCodeHandler(c *gin.Context) {
SendTime: time.Now().Unix(),
Err: err.Error(),
}
common.SetCache("cloudsms", record, 60*24*30)
common.SetCache("cloudsms", record, 60*time.Hour)
lastSenter = nil
libs.Error(c, "短信服务不可用")
return
@ -283,21 +283,12 @@ func checkSlideDataHandler(c *gin.Context) {
return
}
//log.Printf("cacheDataByte: %v", cacheDataByte)
var dataBytes []byte
switch v := cacheDataByte.(type) {
case string:
dataBytes = []byte(v)
case []byte:
dataBytes = v
default:
libs.Error(c, "invalid cache data type")
return
}
log.Printf("dataBytes: %v", dataBytes)
dataBytes := common.GetCacheVal(cacheDataByte)
//log.Printf("dataBytes: %v", dataBytes)
// 反序列化缓存中的数据
var dct *slide.Block
if err := json.Unmarshal(dataBytes, &dct); err != nil {
slog.Error("json unmarshal error", "err", err)
//slog.Error("json unmarshal error", "err", err)
libs.Error(c, "illegal key")
return
}
@ -308,13 +299,13 @@ func checkSlideDataHandler(c *gin.Context) {
if len(src) == 2 {
sx, err := strconv.ParseFloat(src[0], 64)
if err != nil {
slog.Error("parse point error", "err", err)
//slog.Error("parse point error", "err", err)
libs.Error(c, "invalid point formatr")
return
}
sy, err := strconv.ParseFloat(src[1], 64)
if err != nil {
slog.Error("parse point error", "err", err)
//slog.Error("parse point error", "err", err)
libs.Error(c, "invalid point format")
return
}

14
server/app/user/login/const.go

@ -1,6 +1,7 @@
package login
import (
"encoding/json"
"errors"
"godocms/model"
)
@ -28,16 +29,18 @@ const (
type LoginParam interface{}
type LoginHandler interface {
Login(param LoginParam) (*model.User, error)
Register(param LoginParam) (*model.User, error)
Init(req LoginRequest) error
Login() (*model.User, error)
Register() (*model.User, error)
}
var ErrNeedRegister = errors.New("need register")
type LoginRequest struct {
ClientId string `json:"client_id" binding:"required"`
LoginType string `json:"login_type" binding:"required"`
Param LoginParam `json:"param" binding:"required"`
ClientId string `json:"client_id" binding:"required"`
LoginType string `json:"login_type" binding:"required"`
Action string `json:"action" binding:"required"`
Param json.RawMessage `json:"param" binding:"required"`
}
const (
@ -49,6 +52,7 @@ const (
LoginPlatformMicrosoftADFS = "microsoft_adfs"
LoginPlatformMicrosoftOIDC = "microsoft_oidc"
LoginPlatformLDAP = "ldap"
LoginPlatformPassword = "password" // 密码登录
)
// 密码登录参数

22
server/app/user/login/factory.go

@ -1,16 +1,28 @@
package login
import "errors"
import (
"errors"
"fmt"
)
type LoginHandlerFactory struct{}
func (f *LoginHandlerFactory) GetHandler(loginType string) (LoginHandler, error) {
switch loginType {
// factory.go
func (f *LoginHandlerFactory) GetHandler(req LoginRequest) (LoginHandler, error) {
var handler LoginHandler
switch req.LoginType {
case LoginTypePassword:
return &PasswordLoginHandler{}, nil
handler = &PasswordLoginHandler{}
case LoginTypeSmsCode:
return &SmsCodeLoginHandler{}, nil
handler = &SmsCodeLoginHandler{}
default:
return nil, errors.New("unsupported login type")
}
if err := handler.Init(req); err != nil {
return nil, fmt.Errorf("参数初始化失败: %v", err)
}
return handler, nil
}

86
server/app/user/login/password.go

@ -1,22 +1,88 @@
package login
import (
"encoding/json"
"errors"
"godocms/common"
"godocms/model"
"log"
"godocms/pkg/db"
"godocms/utils"
"time"
"gorm.io/gorm"
)
type PasswordLoginHandler struct{}
type PasswordLoginHandler struct {
loginParam *PasswordLoginParam
}
func (h *PasswordLoginHandler) Login(param LoginParam) (*model.User, error) {
p := param.(PasswordLoginParam)
log.Printf("用户登录: %+v", p)
func (h *PasswordLoginHandler) Init(req LoginRequest) error {
//此处可以根据action处理不同的数据类型的解析
h.loginParam = new(PasswordLoginParam)
return json.Unmarshal(req.Param, h.loginParam)
}
func (h *PasswordLoginHandler) Login() (user *model.User, err error) {
if !common.LoginConf.UserNameLogin.Enable {
return nil, errors.New("登录已关闭")
}
//log.Printf("用户登录: %+v", h.loginParam.Password)
user = new(model.User)
if err := db.DB.Where("username = ?", h.loginParam.Username).First(&user).Error; err != nil {
return nil, errors.New("用户不存在")
}
if utils.HashPassword(h.loginParam.Password, user.Salt) != user.Password {
return nil, errors.New("密码错误")
}
// 实现短信验证码登录逻辑
return nil, nil
return user, nil
}
func (h *PasswordLoginHandler) Register(param LoginParam) (*model.User, error) {
p := param.(PasswordLoginParam)
log.Printf("用户登录: %+v", p)
func (h *PasswordLoginHandler) Register() (user *model.User, err error) {
//log.Printf("用户登录: %+v", h.loginParam)
// 检查用户名和邮箱是否已存在
var existingUser model.User
result := db.DB.Where("username = ?", h.loginParam.Username).First(&existingUser)
if result.RowsAffected > 0 {
return nil, errors.New("用户名或邮箱/手机号已存在")
}
salt, err := utils.GenerateSalt(16)
if err != nil {
return nil, err
}
hashedPassword := utils.HashPassword(h.loginParam.Password, salt)
user = &model.User{
Username: h.loginParam.Username,
Password: hashedPassword,
Salt: salt,
Email: "",
Nickname: h.loginParam.Username,
Phone: "",
RoleId: 1,
Status: 0,
DeptId: 1,
AddTime: int32(time.Now().Unix()),
UpTime: 0,
}
err = db.DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&user).Error; err != nil {
return err
}
thirdInfo := &model.UserThird{
UserID: user.ID,
Platform: LoginPlatformPassword,
ThirdUserID: h.loginParam.Username,
UnionID: h.loginParam.Username,
}
if err := tx.Create(thirdInfo).Error; err != nil {
return err
}
return nil, nil
return nil
})
if err != nil {
return user, err
}
return user, nil
}

19
server/app/user/login/sms.go

@ -1,22 +1,27 @@
package login
import (
"encoding/json"
"godocms/model"
"log"
)
type SmsCodeLoginHandler struct{}
type SmsCodeLoginHandler struct {
param *SmsCodeLoginParam
}
func (h *SmsCodeLoginHandler) Login(param LoginParam) (*model.User, error) {
p := param.(SmsCodeLoginParam)
log.Printf("手机号登录: %+v", p)
func (h *SmsCodeLoginHandler) Init(req LoginRequest) error {
h.param = new(SmsCodeLoginParam)
return json.Unmarshal(req.Param, h.param)
}
func (h *SmsCodeLoginHandler) Login() (*model.User, error) {
log.Printf("用户登录: %+v", h.param)
// 实现短信验证码登录逻辑
return nil, nil
}
func (h *SmsCodeLoginHandler) Register(param LoginParam) (*model.User, error) {
p := param.(SmsCodeLoginParam)
log.Printf("手机号登录: %+v", p)
func (h *SmsCodeLoginHandler) Register() (*model.User, error) {
log.Printf("用户登录: %+v", h.param)
// 实现短信验证码注册逻辑
return nil, nil
}

12
server/common/cache.go

@ -5,6 +5,18 @@ import "time"
func GetCache(k string) (interface{}, error) {
return Cache.Get(k)
}
func GetCacheVal(cacheDataByte interface{}) []byte {
var dataBytes []byte
switch v := cacheDataByte.(type) {
case string:
dataBytes = []byte(v)
case []byte:
dataBytes = v
default:
return dataBytes
}
return dataBytes
}
func SetCache(k string, v interface{}, t time.Duration) error {
return Cache.Set(k, v, t)
}

2
server/middleware/serve.go

@ -1,6 +1,7 @@
package middleware
import (
"godocms/libs"
"log"
"github.com/gin-gonic/gin"
@ -18,6 +19,7 @@ func Recover(c *gin.Context) {
if r := recover(); r != nil {
// 打印错误堆栈信息
log.Printf("panic: %v\n", r)
libs.Error(c, "服务器内部错误")
}
}()

8
server/model/auto_migrate.go

@ -10,15 +10,15 @@ import (
func AutoMigrate() error {
db.DB.AutoMigrate(&User{})
// 使用 GORM 的 CreateIndex 方法
createIndexIfNotExists(db.DB, &User{}, "username", "idx_user_username")
createIndexIfNotExists(db.DB, &User{}, "email", "idx_user_email")
createIndexIfNotExists(db.DB, &User{}, "phone", "idx_user_phone")
// createIndexIfNotExists(db.DB, &User{}, "username", "idx_user_username")
// createIndexIfNotExists(db.DB, &User{}, "email", "idx_user_email")
// createIndexIfNotExists(db.DB, &User{}, "phone", "idx_user_phone")
db.DB.AutoMigrate(&UserRole{})
db.DB.AutoMigrate(&UserDept{})
db.DB.AutoMigrate(&UserThird{})
return nil
}
func createIndexIfNotExists(Db *gorm.DB, model interface{}, field, indexName string) {
func CreateIndexIfNotExists(Db *gorm.DB, model interface{}, field, indexName string) {
if db.AppConfig.DBType != "mongodb" {
return
}

6
server/model/user.go

@ -6,13 +6,13 @@ const TableNameUser = "user"
type User struct {
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:主键" json:"id"`
GroupID int32 `gorm:"column:group_id;default:1;comment:租户ID" json:"group_id"`
Username string `gorm:"column:username;not null;unique;comment:用户姓名--例如张三" json:"username"` // 添加 unique
Username string `gorm:"column:username;not null;comment:用户姓名--例如张三" json:"username"` // 添加 unique
Nickname string `gorm:"column:nickname;not null;comment:用户真实姓名" json:"nickname"`
Sex int32 `gorm:"column:sex;comment:性别,0:男,1:女" json:"sex"`
Password string `gorm:"column:password;comment:登陆密码" json:"password"`
Salt string `gorm:"column:salt;not null;comment:salt校验" json:"salt"`
Email string `gorm:"column:email;unique;comment:电子邮箱" json:"email"` // 添加 unique
Phone string `gorm:"column:phone;unique;comment:手机号码" json:"phone"` // 添加 unique
Email string `gorm:"column:email;comment:电子邮箱" json:"email"` // 添加 unique
Phone string `gorm:"column:phone;comment:手机号码" json:"phone"` // 添加 unique
Status int32 `gorm:"column:status;comment:状态,0:正常,1:删除,2封禁" json:"status"`
Desc string `gorm:"column:desc;comment:用户描述信息" json:"desc"`
Remark string `gorm:"column:remark;comment:备注" json:"remark"`

2
server/tmp/build-errors.log

@ -1 +1 @@
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1

BIN
server/tmp/main.exe

Binary file not shown.
Loading…
Cancel
Save