✨ Banner
This commit is contained in:
		@@ -1,6 +1,6 @@
 | 
			
		||||
// Code generated by protoc-gen-go. DO NOT EDIT.
 | 
			
		||||
// versions:
 | 
			
		||||
// 	protoc-gen-go v1.32.0
 | 
			
		||||
// 	protoc-gen-go v1.33.0
 | 
			
		||||
// 	protoc        v4.25.3
 | 
			
		||||
// source: auth.proto
 | 
			
		||||
 | 
			
		||||
@@ -30,7 +30,8 @@ type Userinfo struct {
 | 
			
		||||
	Nick        string  `protobuf:"bytes,3,opt,name=nick,proto3" json:"nick,omitempty"`
 | 
			
		||||
	Email       string  `protobuf:"bytes,4,opt,name=email,proto3" json:"email,omitempty"`
 | 
			
		||||
	Avatar      string  `protobuf:"bytes,5,opt,name=avatar,proto3" json:"avatar,omitempty"`
 | 
			
		||||
	Description *string `protobuf:"bytes,6,opt,name=description,proto3,oneof" json:"description,omitempty"`
 | 
			
		||||
	Banner      string  `protobuf:"bytes,6,opt,name=banner,proto3" json:"banner,omitempty"`
 | 
			
		||||
	Description *string `protobuf:"bytes,7,opt,name=description,proto3,oneof" json:"description,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *Userinfo) Reset() {
 | 
			
		||||
@@ -100,6 +101,13 @@ func (x *Userinfo) GetAvatar() string {
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *Userinfo) GetBanner() string {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.Banner
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *Userinfo) GetDescription() string {
 | 
			
		||||
	if x != nil && x.Description != nil {
 | 
			
		||||
		return *x.Description
 | 
			
		||||
@@ -237,43 +245,45 @@ var File_auth_proto protoreflect.FileDescriptor
 | 
			
		||||
 | 
			
		||||
var file_auth_proto_rawDesc = []byte{
 | 
			
		||||
	0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72,
 | 
			
		||||
	0x6f, 0x74, 0x6f, 0x22, 0xa7, 0x01, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f,
 | 
			
		||||
	0x6f, 0x74, 0x6f, 0x22, 0xbf, 0x01, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f,
 | 
			
		||||
	0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64,
 | 
			
		||||
	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
 | 
			
		||||
	0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x69, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01,
 | 
			
		||||
	0x28, 0x09, 0x52, 0x04, 0x6e, 0x69, 0x63, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69,
 | 
			
		||||
	0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x16,
 | 
			
		||||
	0x0a, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
 | 
			
		||||
	0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x25, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
 | 
			
		||||
	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x64,
 | 
			
		||||
	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a,
 | 
			
		||||
	0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x6c, 0x0a,
 | 
			
		||||
	0x0b, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c,
 | 
			
		||||
	0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01,
 | 
			
		||||
	0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12,
 | 
			
		||||
	0x28, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e,
 | 
			
		||||
	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73,
 | 
			
		||||
	0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65,
 | 
			
		||||
	0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xda, 0x01, 0x0a, 0x09,
 | 
			
		||||
	0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f,
 | 
			
		||||
	0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x56,
 | 
			
		||||
	0x61, 0x6c, 0x69, 0x64, 0x12, 0x26, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74,
 | 
			
		||||
	0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x63,
 | 
			
		||||
	0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x28, 0x0a, 0x0d,
 | 
			
		||||
	0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20,
 | 
			
		||||
	0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0c, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f,
 | 
			
		||||
	0x6b, 0x65, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x30, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e,
 | 
			
		||||
	0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 | 
			
		||||
	0x2e, 0x55, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x48, 0x02, 0x52, 0x08, 0x75, 0x73, 0x65,
 | 
			
		||||
	0x72, 0x69, 0x6e, 0x66, 0x6f, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x61, 0x63, 0x63,
 | 
			
		||||
	0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65,
 | 
			
		||||
	0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x5f,
 | 
			
		||||
	0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x32, 0x3e, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68,
 | 
			
		||||
	0x12, 0x36, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65,
 | 
			
		||||
	0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71,
 | 
			
		||||
	0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74,
 | 
			
		||||
	0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x3b, 0x70, 0x72,
 | 
			
		||||
	0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 | 
			
		||||
	0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72,
 | 
			
		||||
	0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x25,
 | 
			
		||||
	0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20,
 | 
			
		||||
	0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
 | 
			
		||||
	0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
 | 
			
		||||
	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x6c, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71,
 | 
			
		||||
	0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74,
 | 
			
		||||
	0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65,
 | 
			
		||||
	0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x28, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65,
 | 
			
		||||
	0x73, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00,
 | 
			
		||||
	0x52, 0x0c, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x88, 0x01,
 | 
			
		||||
	0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f,
 | 
			
		||||
	0x6b, 0x65, 0x6e, 0x22, 0xda, 0x01, 0x0a, 0x09, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x70, 0x6c,
 | 
			
		||||
	0x79, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20,
 | 
			
		||||
	0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x26, 0x0a, 0x0c,
 | 
			
		||||
	0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01,
 | 
			
		||||
	0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65,
 | 
			
		||||
	0x6e, 0x88, 0x01, 0x01, 0x12, 0x28, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f,
 | 
			
		||||
	0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0c, 0x72,
 | 
			
		||||
	0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x30,
 | 
			
		||||
	0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
 | 
			
		||||
	0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66,
 | 
			
		||||
	0x6f, 0x48, 0x02, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x88, 0x01, 0x01,
 | 
			
		||||
	0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65,
 | 
			
		||||
	0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f,
 | 
			
		||||
	0x6b, 0x65, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f,
 | 
			
		||||
	0x32, 0x3e, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, 0x36, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68,
 | 
			
		||||
	0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 | 
			
		||||
	0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x70,
 | 
			
		||||
	0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00,
 | 
			
		||||
	0x42, 0x09, 0x5a, 0x07, 0x2e, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
 | 
			
		||||
	0x74, 0x6f, 0x33,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,8 @@ message Userinfo {
 | 
			
		||||
  string nick = 3;
 | 
			
		||||
  string email = 4;
 | 
			
		||||
  string avatar = 5;
 | 
			
		||||
  optional string description = 6;
 | 
			
		||||
  string banner = 6;
 | 
			
		||||
  optional string description = 7;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message AuthRequest {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,17 @@
 | 
			
		||||
package grpc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
 | 
			
		||||
	"code.smartsheep.studio/hydrogen/identity/pkg/grpc/proto"
 | 
			
		||||
	"code.smartsheep.studio/hydrogen/identity/pkg/models"
 | 
			
		||||
	"code.smartsheep.studio/hydrogen/identity/pkg/services"
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/samber/lo"
 | 
			
		||||
	"github.com/spf13/viper"
 | 
			
		||||
	"google.golang.org/grpc"
 | 
			
		||||
	"google.golang.org/grpc/reflection"
 | 
			
		||||
	"net"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Server struct {
 | 
			
		||||
@@ -35,7 +36,8 @@ func (v *Server) Authenticate(_ context.Context, in *proto.AuthRequest) (*proto.
 | 
			
		||||
				Nick:        user.Nick,
 | 
			
		||||
				Email:       user.GetPrimaryEmail().Content,
 | 
			
		||||
				Avatar:      fmt.Sprintf("https://%s/api/avatar/%s", viper.GetString("domain"), user.Avatar),
 | 
			
		||||
				Description: nil,
 | 
			
		||||
				Banner:      fmt.Sprintf("https://%s/api/avatar/%s", viper.GetString("domain"), user.Banner),
 | 
			
		||||
				Description: &user.Description,
 | 
			
		||||
			},
 | 
			
		||||
		}, nil
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,11 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/samber/lo"
 | 
			
		||||
	"github.com/spf13/viper"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/samber/lo"
 | 
			
		||||
	"github.com/spf13/viper"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Account struct {
 | 
			
		||||
@@ -14,6 +15,7 @@ type Account struct {
 | 
			
		||||
	Nick              string                   `json:"nick"`
 | 
			
		||||
	Description       string                   `json:"description"`
 | 
			
		||||
	Avatar            string                   `json:"avatar"`
 | 
			
		||||
	Banner            string                   `json:"banner"`
 | 
			
		||||
	Profile           AccountProfile           `json:"profile"`
 | 
			
		||||
	Sessions          []AuthSession            `json:"sessions"`
 | 
			
		||||
	Challenges        []AuthChallenge          `json:"challenges"`
 | 
			
		||||
@@ -40,6 +42,11 @@ func (v Account) GetAvatarPath() string {
 | 
			
		||||
	return filepath.Join(basepath, v.Avatar)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v Account) GetBannerPath() string {
 | 
			
		||||
	basepath := viper.GetString("content")
 | 
			
		||||
	return filepath.Join(basepath, v.Banner)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AccountContactType = int8
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,14 @@
 | 
			
		||||
package server
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"code.smartsheep.studio/hydrogen/identity/pkg/database"
 | 
			
		||||
	"code.smartsheep.studio/hydrogen/identity/pkg/models"
 | 
			
		||||
	"github.com/gofiber/fiber/v2"
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"github.com/spf13/viper"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func getAvatar(c *fiber.Ctx) error {
 | 
			
		||||
@@ -23,12 +25,58 @@ func setAvatar(c *fiber.Ctx) error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var previous string
 | 
			
		||||
	if len(user.Avatar) > 0 {
 | 
			
		||||
		previous = user.GetAvatarPath()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	user.Avatar = uuid.NewString()
 | 
			
		||||
 | 
			
		||||
	if err := c.SaveFile(file, user.GetAvatarPath()); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	} else {
 | 
			
		||||
		database.C.Save(&user)
 | 
			
		||||
 | 
			
		||||
		// Clean up
 | 
			
		||||
		if len(previous) > 0 {
 | 
			
		||||
			basepath := viper.GetString("content")
 | 
			
		||||
			filepath := filepath.Join(basepath, previous)
 | 
			
		||||
			if info, err := os.Stat(filepath); err == nil && !info.IsDir() {
 | 
			
		||||
				os.Remove(filepath)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.SendStatus(fiber.StatusOK)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setBanner(c *fiber.Ctx) error {
 | 
			
		||||
	user := c.Locals("principal").(models.Account)
 | 
			
		||||
	file, err := c.FormFile("banner")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var previous string
 | 
			
		||||
	if len(user.Banner) > 0 {
 | 
			
		||||
		previous = user.GetBannerPath()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	user.Banner = uuid.NewString()
 | 
			
		||||
 | 
			
		||||
	if err := c.SaveFile(file, user.GetBannerPath()); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	} else {
 | 
			
		||||
		database.C.Save(&user)
 | 
			
		||||
 | 
			
		||||
		// Clean up
 | 
			
		||||
		if len(previous) > 0 {
 | 
			
		||||
			basepath := viper.GetString("content")
 | 
			
		||||
			filepath := filepath.Join(basepath, previous)
 | 
			
		||||
			if info, err := os.Stat(filepath); err == nil && !info.IsDir() {
 | 
			
		||||
				os.Remove(filepath)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.SendStatus(fiber.StatusOK)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,10 @@
 | 
			
		||||
package server
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"code.smartsheep.studio/hydrogen/identity/pkg/views"
 | 
			
		||||
	"github.com/gofiber/fiber/v2"
 | 
			
		||||
	"github.com/gofiber/fiber/v2/middleware/cache"
 | 
			
		||||
@@ -11,9 +15,6 @@ import (
 | 
			
		||||
	jsoniter "github.com/json-iterator/go"
 | 
			
		||||
	"github.com/rs/zerolog/log"
 | 
			
		||||
	"github.com/spf13/viper"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var A *fiber.App
 | 
			
		||||
@@ -58,21 +59,28 @@ func NewServer() {
 | 
			
		||||
	api := A.Group("/api").Name("API")
 | 
			
		||||
	{
 | 
			
		||||
		api.Get("/avatar/:avatarId", getAvatar)
 | 
			
		||||
		api.Put("/avatar", authMiddleware, setAvatar)
 | 
			
		||||
 | 
			
		||||
		api.Get("/notifications", authMiddleware, getNotifications)
 | 
			
		||||
		api.Put("/notifications/:notificationId/read", authMiddleware, markNotificationRead)
 | 
			
		||||
		api.Post("/notifications/subscribe", authMiddleware, addNotifySubscriber)
 | 
			
		||||
 | 
			
		||||
		api.Get("/users/me", authMiddleware, getUserinfo)
 | 
			
		||||
		api.Put("/users/me", authMiddleware, editUserinfo)
 | 
			
		||||
		api.Get("/users/me/events", authMiddleware, getEvents)
 | 
			
		||||
		api.Get("/users/me/challenges", authMiddleware, getChallenges)
 | 
			
		||||
		api.Get("/users/me/sessions", authMiddleware, getSessions)
 | 
			
		||||
		api.Delete("/users/me/sessions/:sessionId", authMiddleware, killSession)
 | 
			
		||||
		me := api.Group("/users/me").Name("Myself Operations")
 | 
			
		||||
		{
 | 
			
		||||
 | 
			
		||||
			me.Put("/avatar", authMiddleware, setAvatar)
 | 
			
		||||
			me.Put("/banner", authMiddleware, setBanner)
 | 
			
		||||
 | 
			
		||||
			me.Get("/", authMiddleware, getUserinfo)
 | 
			
		||||
			me.Put("/", authMiddleware, editUserinfo)
 | 
			
		||||
			me.Get("/events", authMiddleware, getEvents)
 | 
			
		||||
			me.Get("/challenges", authMiddleware, getChallenges)
 | 
			
		||||
			me.Get("/sessions", authMiddleware, getSessions)
 | 
			
		||||
			me.Delete("/sessions/:sessionId", authMiddleware, killSession)
 | 
			
		||||
 | 
			
		||||
			me.Post("/confirm", doRegisterConfirm)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		api.Post("/users", doRegister)
 | 
			
		||||
		api.Post("/users/me/confirm", doRegisterConfirm)
 | 
			
		||||
 | 
			
		||||
		api.Put("/auth", startChallenge)
 | 
			
		||||
		api.Post("/auth", doChallenge)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
  <v-app-bar height="64" color="primary" scroll-behavior="elevate" flat>
 | 
			
		||||
    <div class="max-md:px-5 md:px-12 flex flex-grow-1 items-center">
 | 
			
		||||
      <router-link :to="{ name: 'dashboard' }" class="flex gap-1">
 | 
			
		||||
        <img src="/favicon.png" width="24" height="24" class="icon-filter" />
 | 
			
		||||
        <img src="/favicon.png" width="27" height="24" class="icon-filter" />
 | 
			
		||||
        <h2 class="ml-2 text-lg font-500">Solarpass</h2>
 | 
			
		||||
      </router-link>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <v-container class="pt-6 px-6">
 | 
			
		||||
    <v-row>
 | 
			
		||||
      <v-col :xs="12" :sm="12" :md="4" :lg="3">
 | 
			
		||||
      <v-col :cols="12" :xs="12" :sm="12" :md="4" :lg="3">
 | 
			
		||||
        <v-card title="Navigation">
 | 
			
		||||
          <v-list density="comfortable">
 | 
			
		||||
            <v-list-item title="Dashboard" prepend-icon="mdi-view-dashboard" :to="{ name: 'dashboard' }" exact />
 | 
			
		||||
@@ -11,7 +11,7 @@
 | 
			
		||||
        </v-card>
 | 
			
		||||
      </v-col>
 | 
			
		||||
 | 
			
		||||
      <v-col :xs="12" :sm="12" :md="8" :lg="9">
 | 
			
		||||
      <v-col :cols="12" :xs="12" :sm="12" :md="8" :lg="9">
 | 
			
		||||
        <router-view />
 | 
			
		||||
      </v-col>
 | 
			
		||||
    </v-row>
 | 
			
		||||
 
 | 
			
		||||
@@ -70,29 +70,44 @@
 | 
			
		||||
    </v-card>
 | 
			
		||||
 | 
			
		||||
    <v-card>
 | 
			
		||||
      <template #text>
 | 
			
		||||
        <div class="flex items-center gap-3">
 | 
			
		||||
          <v-avatar
 | 
			
		||||
            color="grey-lighten-2"
 | 
			
		||||
            icon="mdi-account-circle"
 | 
			
		||||
            class="rounded-card"
 | 
			
		||||
            size="large"
 | 
			
		||||
            :image="'/api/avatar/' + id.userinfo.data.avatar"
 | 
			
		||||
          />
 | 
			
		||||
          <v-file-input
 | 
			
		||||
            clearable
 | 
			
		||||
            hide-details
 | 
			
		||||
            label="Upload another avatar"
 | 
			
		||||
            variant="outlined"
 | 
			
		||||
            density="comfortable"
 | 
			
		||||
            accept="image/*"
 | 
			
		||||
            prepend-icon=""
 | 
			
		||||
            append-icon="mdi-upload"
 | 
			
		||||
            v-model="avatar"
 | 
			
		||||
            @click:append="applyAvatar"
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      </template>
 | 
			
		||||
      <v-card-text class="flex items-center gap-3">
 | 
			
		||||
        <v-avatar
 | 
			
		||||
          color="grey-lighten-2"
 | 
			
		||||
          icon="mdi-account-circle"
 | 
			
		||||
          class="rounded-card"
 | 
			
		||||
          size="large"
 | 
			
		||||
          :image="'/api/avatar/' + id.userinfo.data.avatar"
 | 
			
		||||
        />
 | 
			
		||||
        <v-file-input
 | 
			
		||||
          clearable
 | 
			
		||||
          hide-details
 | 
			
		||||
          label="Upload another avatar"
 | 
			
		||||
          variant="outlined"
 | 
			
		||||
          density="comfortable"
 | 
			
		||||
          accept="image/*"
 | 
			
		||||
          prepend-icon=""
 | 
			
		||||
          append-icon="mdi-upload"
 | 
			
		||||
          v-model="avatar"
 | 
			
		||||
          @click:append="applyAvatar"
 | 
			
		||||
        />
 | 
			
		||||
      </v-card-text>
 | 
			
		||||
 | 
			
		||||
      <v-img cover class="bg-grey-lighten-2" :height="320" :src="'/api/avatar/' + id.userinfo.data.banner" />
 | 
			
		||||
 | 
			
		||||
      <v-card-text>
 | 
			
		||||
        <v-file-input
 | 
			
		||||
          clearable
 | 
			
		||||
          hide-details
 | 
			
		||||
          label="Update your banner"
 | 
			
		||||
          variant="outlined"
 | 
			
		||||
          density="compact"
 | 
			
		||||
          accept="image/*"
 | 
			
		||||
          prepend-icon=""
 | 
			
		||||
          append-icon="mdi-upload"
 | 
			
		||||
          v-model="banner"
 | 
			
		||||
          @click:append="applyBanner"
 | 
			
		||||
        />
 | 
			
		||||
      </v-card-text>
 | 
			
		||||
    </v-card>
 | 
			
		||||
 | 
			
		||||
    <v-snackbar v-model="done" :timeout="3000"> Your personal information has been updated. </v-snackbar>
 | 
			
		||||
@@ -115,6 +130,7 @@ const loading = ref(false)
 | 
			
		||||
 | 
			
		||||
const data = ref<any>({})
 | 
			
		||||
const avatar = ref<any>(null)
 | 
			
		||||
const banner = ref<any>(null)
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  id,
 | 
			
		||||
@@ -161,7 +177,7 @@ async function applyAvatar() {
 | 
			
		||||
  payload.set("avatar", avatar.value[0])
 | 
			
		||||
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  const res = await request("/api/avatar", {
 | 
			
		||||
  const res = await request("/api/users/me/avatar", {
 | 
			
		||||
    method: "PUT",
 | 
			
		||||
    headers: { Authorization: `Bearer ${getAtk()}` },
 | 
			
		||||
    body: payload,
 | 
			
		||||
@@ -176,6 +192,31 @@ async function applyAvatar() {
 | 
			
		||||
  }
 | 
			
		||||
  loading.value = false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function applyBanner() {
 | 
			
		||||
  if (!banner.value) return
 | 
			
		||||
 | 
			
		||||
  if (loading.value) return
 | 
			
		||||
 | 
			
		||||
  const payload = new FormData()
 | 
			
		||||
  payload.set("banner", banner.value[0])
 | 
			
		||||
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  const res = await request("/api/users/me/banner", {
 | 
			
		||||
    method: "PUT",
 | 
			
		||||
    headers: { Authorization: `Bearer ${getAtk()}` },
 | 
			
		||||
    body: payload,
 | 
			
		||||
  })
 | 
			
		||||
  if (res.status !== 200) {
 | 
			
		||||
    error.value = await res.text()
 | 
			
		||||
  } else {
 | 
			
		||||
    await id.readProfiles()
 | 
			
		||||
    done.value = true
 | 
			
		||||
    error.value = null
 | 
			
		||||
    banner.value = null
 | 
			
		||||
  }
 | 
			
		||||
  loading.value = false
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user