commit 517168dc297acc3c9fa87f84b6f5f6056fbbdf57
Author: rick <rick@gnous.eu>
Date:   Sun Oct 9 02:15:25 2022 +0200

    first files

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7d5ea64
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,70 @@
+.env
+
+# Created by https://www.toptal.com/developers/gitignore/api/vim,go,rust
+# Edit at https://www.toptal.com/developers/gitignore?templates=vim,go,rust
+
+### Go ###
+# If you prefer the allow list template instead of the deny list, see community template:
+# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
+#
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Dependency directories (remove the comment below to include it)
+# vendor/
+
+# Go workspace file
+go.work
+
+### Go Patch ###
+/vendor/
+/Godeps/
+
+### Rust ###
+# Generated by Cargo
+# will have compiled files and executables
+debug/
+target/
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
+
+# MSVC Windows builds of rustc generate these, which store debugging information
+*.pdb
+
+### Vim ###
+# Swap
+[._]*.s[a-v][a-z]
+!*.svg  # comment out if you don't need vector files
+[._]*.sw[a-p]
+[._]s[a-rt-v][a-z]
+[._]ss[a-gi-z]
+[._]sw[a-p]
+
+# Session
+Session.vim
+Sessionx.vim
+
+# Temporary
+.netrwhist
+*~
+# Auto-generated tag files
+tags
+# Persistent undo
+[._]*.un~
+
+# End of https://www.toptal.com/developers/gitignore/api/vim,go,rust
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d2063aa
--- /dev/null
+++ b/README.md
@@ -0,0 +1,17 @@
+# Site CDS
+
+## Structure du projet
+
+ * .env : fichier de configuration
+ * views : fichiers html dynamiques
+ * dao : fichiers pour faire des requetes en BDD
+ * models : structures utilisées pour manipuler le contenu de la base de données
+
+## Lancement
+
+Il faut mettre en place une base de données [MongoDB](https://www.mongodb.com/).
+Il est nécessaire d'avoir [une application Discord](https://discord.com/developers/) 
+et [une clé d'API Steam](https://steamcommunity.com/dev).
+Configurez ensuite le fichier `example.env` et renommez le en `.env`.
+
+Lancez le serveur avec `go run main.go`.
diff --git a/dao/connect.go b/dao/connect.go
new file mode 100644
index 0000000..7275a8b
--- /dev/null
+++ b/dao/connect.go
@@ -0,0 +1,28 @@
+package dao
+
+import (
+	"context"
+	"os"
+	"time"
+
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+)
+
+func get() (db *mongo.Database, err error) {
+	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
+	defer cancel()
+	uri := "mongodb://" + os.Getenv("MONGO_USER") + ":" + os.Getenv("MONGO_PWD") + "@" + os.Getenv("MONGO_CLUSTER")
+	client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri))
+	if err != nil {
+		return nil, err
+	}
+
+	return client.Database(os.Getenv("MONGO_DB")), nil
+}
+
+func disconnect(client *mongo.Client) {
+	if err := client.Disconnect(context.TODO()); err != nil {
+		panic(err)
+	}
+}
diff --git a/dao/users.go b/dao/users.go
new file mode 100644
index 0000000..cbfe33c
--- /dev/null
+++ b/dao/users.go
@@ -0,0 +1,113 @@
+package dao
+
+import (
+	"cds/models"
+	"context"
+	"fmt"
+
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+func GetByDiscord(id string) (user *models.User, err error) {
+	db, _ := get()
+	defer disconnect(db.Client())
+	coll := db.Collection("users")
+
+	var result models.User
+	err = coll.FindOne(context.TODO(), bson.D{{"userId", id}}).Decode(&result)
+	if err != nil {
+		return nil, err
+	} else {
+		return &result, nil
+	}
+}
+
+func GetBySteam(id string) (user *models.User, err error) {
+	db, _ := get()
+	defer disconnect(db.Client())
+	coll := db.Collection("users")
+
+	var result models.User
+	err = coll.FindOne(context.TODO(), bson.D{{"steamId", id}}).Decode(&result)
+	if err != nil {
+		return nil, err
+	} else {
+		return &result, nil
+	}
+}
+
+func GetById(id string) (user *models.User, err error) {
+	db, _ := get()
+	objectId, err := primitive.ObjectIDFromHex(id)
+	defer disconnect(db.Client())
+	coll := db.Collection("users")
+
+	var result models.User
+	err = coll.FindOne(context.TODO(), bson.D{{"_id", objectId}}).Decode(&result)
+	if err != nil {
+		return nil, err
+	} else {
+		return &result, nil
+	}
+}
+
+func CreateUser(user *models.User) (id string, err error) {
+	db, _ := get()
+	defer disconnect(db.Client())
+	coll := db.Collection("users")
+
+	idDb, err := coll.InsertOne(context.TODO(), user)
+	id = fmt.Sprint(idDb.InsertedID)
+	return id, err
+}
+
+func AddSteam(discord string, steam string) error {
+	db, _ := get()
+	defer disconnect(db.Client())
+	coll := db.Collection("users")
+
+	update := bson.D{{"$set", bson.D{{"steamId", steam}}}}
+
+	var result bson.M
+	err := coll.FindOneAndUpdate(context.TODO(), bson.D{{"userId", discord}}, update).Decode(&result)
+	return err
+}
+
+func AddDiscord(steam string, discordId string, discordName string) error {
+	db, _ := get()
+	defer disconnect(db.Client())
+	coll := db.Collection("users")
+
+	update := bson.D{{"$set", bson.D{{"userId", discordId}, {"userName", discordName}}}}
+
+	var result bson.M
+	err := coll.FindOneAndUpdate(context.TODO(), bson.D{{"steamId", steam}}, update).Decode(&result)
+	return err
+}
+
+func RemoveSteam(steam string) error {
+	return nil
+}
+
+func RemoveDiscord(discord string) error {
+	return nil
+}
+
+func DeleteBySteam(steam string) error {
+	db, _ := get()
+	defer disconnect(db.Client())
+	coll := db.Collection("users")
+
+	_, err := coll.DeleteOne(context.TODO(), bson.D{{"steamId", steam}})
+	return err
+}
+
+func DeleteByDiscord(discord string) error {
+	db, _ := get()
+	defer disconnect(db.Client())
+	coll := db.Collection("users")
+
+	_, err := coll.DeleteOne(context.TODO(), bson.D{{"userId", discord}})
+	return err
+}
diff --git a/example.env b/example.env
new file mode 100644
index 0000000..d10c35b
--- /dev/null
+++ b/example.env
@@ -0,0 +1,9 @@
+MONGO_USER: "user"
+MONGO_PWD: "123"
+MONGO_CLUSTER: "localhost:80800"
+MONGO_DB: "db"
+
+DISCORD_ID: "id"
+DISCORD_SECRET: "secret"
+
+STEAM_KEY: "steam"
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..5bebf92
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,54 @@
+module cds
+
+go 1.19
+
+require (
+	github.com/gofiber/fiber/v2 v2.38.1
+	github.com/gofiber/swagger v0.1.2
+	github.com/gofiber/template v1.7.1
+	github.com/joho/godotenv v1.4.0
+	github.com/markbates/goth v1.73.0
+	github.com/shareed2k/goth_fiber v0.2.8
+	github.com/swaggo/swag v1.8.5
+	go.mongodb.org/mongo-driver v1.10.3
+)
+
+require (
+	github.com/KyleBanks/depth v1.2.1 // indirect
+	github.com/PuerkitoBio/purell v1.1.1 // indirect
+	github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
+	github.com/andybalholm/brotli v1.0.4 // indirect
+	github.com/go-openapi/jsonpointer v0.19.5 // indirect
+	github.com/go-openapi/jsonreference v0.19.6 // indirect
+	github.com/go-openapi/spec v0.20.4 // indirect
+	github.com/go-openapi/swag v0.19.15 // indirect
+	github.com/golang/protobuf v1.5.2 // indirect
+	github.com/golang/snappy v0.0.3 // indirect
+	github.com/gorilla/context v1.1.1 // indirect
+	github.com/gorilla/mux v1.8.0 // indirect
+	github.com/gorilla/securecookie v1.1.1 // indirect
+	github.com/gorilla/sessions v1.1.1 // indirect
+	github.com/josharian/intern v1.0.0 // indirect
+	github.com/klauspost/compress v1.15.0 // indirect
+	github.com/mailru/easyjson v0.7.6 // indirect
+	github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 // indirect
+	github.com/valyala/bytebufferpool v1.0.0 // indirect
+	github.com/valyala/fasthttp v1.40.0 // indirect
+	github.com/valyala/tcplisten v1.0.0 // indirect
+	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
+	github.com/xdg-go/scram v1.1.1 // indirect
+	github.com/xdg-go/stringprep v1.0.3 // indirect
+	github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
+	golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
+	golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect
+	golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
+	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
+	golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
+	golang.org/x/text v0.3.7 // indirect
+	golang.org/x/tools v0.1.10 // indirect
+	google.golang.org/appengine v1.6.7 // indirect
+	google.golang.org/protobuf v1.27.1 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
+)
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..7f34c41
--- /dev/null
+++ b/main.go
@@ -0,0 +1,203 @@
+package main
+
+import (
+	"cds/dao"
+	"cds/models"
+	"fmt"
+	"os"
+	"time"
+
+	_ "cds/docs"
+
+	"github.com/gofiber/fiber/v2"
+	"github.com/gofiber/template/html"
+	"github.com/joho/godotenv"
+	"github.com/markbates/goth"
+	"github.com/markbates/goth/providers/discord"
+	"github.com/markbates/goth/providers/steam"
+	"github.com/shareed2k/goth_fiber"
+	"go.mongodb.org/mongo-driver/mongo"
+)
+
+//const DISCORD_API = "https://discord.com/api/v10/"
+
+func setupRoutes(app *fiber.App) {
+	app.Get("/", index)
+
+	//api := app.Group("/api")
+	//routes.AccountRoute(api.Group("/accounts"))
+	//routes.GroupeRoute(api.Group("/groups"))
+	//app.Get("/swagger/*", swagger.HandlerDefault)
+
+	app.Get("/login/:provider", goth_fiber.BeginAuthHandler)
+	app.Get("/auth/:provider", auth)
+}
+
+func checkDiscord(c *fiber.Ctx) error {
+	gothuser, err := goth_fiber.CompleteUserAuth(c)
+	if err != nil {
+		print("== ERROR ==")
+		panic(err)
+	}
+
+	/*
+		fmt.Println(gothuser.UserID)
+		fmt.Println(gothuser.Name)
+		fmt.Println(gothuser.RawData["discriminator"])
+		fmt.Println(gothuser.AccessToken)
+	*/
+
+	token := c.Cookies("token", "")
+	fmt.Println("Discord cookie:" + token)
+	id := gothuser.UserID
+	name := fmt.Sprint(gothuser.Name, "#", gothuser.RawData["discriminator"])
+
+	// on vérifie s'il existe déjà dans la db sinon on le créé
+	user, err := dao.GetByDiscord(id)
+	if user != nil && token == "" {
+		cookie := new(fiber.Cookie)
+		cookie.Name = "token"
+		cookie.Value = user.Id.Hex()
+		cookie.Expires = time.Now().Add(24 * time.Hour)
+		c.Cookie(cookie)
+	}
+
+	if err == mongo.ErrNoDocuments {
+		user = models.NewUserDiscord(id, name)
+		id, err = dao.CreateUser(user)
+		if err != nil {
+			fmt.Println(err)
+		}
+
+		if token == "" {
+			cookie := new(fiber.Cookie)
+			cookie.Name = "token"
+			cookie.Value = id
+			cookie.Expires = time.Now().Add(24 * time.Hour)
+			c.Cookie(cookie)
+		}
+	} else if err != nil {
+		fmt.Println(err)
+	}
+
+	c.Redirect("/")
+	return nil
+}
+
+func auth(c *fiber.Ctx) error {
+	provider, err := goth_fiber.GetProviderName(c)
+	if err != nil {
+		print("== ERROR ==")
+		panic(err)
+	}
+
+	if provider == "discord" {
+		return checkDiscord(c)
+	} else if provider == "steam" {
+		return checkSteam(c)
+	}
+
+	return nil
+}
+
+func checkSteam(c *fiber.Ctx) error {
+	token := c.Cookies("token", "")
+	gothuser, err := goth_fiber.CompleteUserAuth(c)
+	if err != nil {
+		print("== ERROR ==")
+		panic(err)
+	}
+
+	id := gothuser.UserID
+
+	user, err := dao.GetById(fmt.Sprint(token))
+
+	if user == nil {
+		user, err = dao.GetBySteam(id)
+		if user != nil && token == "" {
+			cookie := new(fiber.Cookie)
+			cookie.Name = "token"
+			cookie.Value = user.Id.Hex()
+			cookie.Expires = time.Now().Add(24 * time.Hour)
+			c.Cookie(cookie)
+		}
+
+		if err == mongo.ErrNoDocuments {
+			user = models.NewUserSteam(id)
+			id, err = dao.CreateUser(user)
+			if err != nil {
+				fmt.Println(err)
+			}
+
+			if token == "" {
+				cookie := new(fiber.Cookie)
+				cookie.Name = "token"
+				cookie.Value = id
+				cookie.Expires = time.Now().Add(24 * time.Hour)
+				c.Cookie(cookie)
+			}
+		} else if err != nil {
+			fmt.Println(err)
+		}
+	} else if user.SteamId == "" {
+		if user.DiscordId != "" {
+			dao.AddSteam(user.DiscordId, id)
+		}
+	}
+
+	c.Redirect("/")
+	return nil
+}
+
+func index(c *fiber.Ctx) error {
+	token := c.Cookies("token", "")
+	var discord string = ""
+	var steam string = ""
+	if token != "" {
+		user, _ := dao.GetById(fmt.Sprint(token))
+		if user != nil {
+			steam = user.SteamId
+			discord = user.DiscordName
+		}
+	}
+
+	return c.Render("index", fiber.Map{
+		"connected":   token != "",
+		"urlDiscord":  "/login/discord",
+		"urlSteam":    "/login/steam",
+		"discordName": discord,
+		"steamName":   steam,
+	})
+}
+
+// @title           CDS API
+// @version         1.0 (alpha)
+// @description     API du site web de CDS
+// @contact.name   La super équipe de dev
+// @license.name  AGPL 3.0
+// @license.url   https://www.gnu.org/licenses/agpl-3.0.html
+// @host      localhost:8080
+// @BasePath  /api
+func main() {
+	err := godotenv.Load()
+	if err != nil {
+		panic(err)
+	}
+
+	app := fiber.New(fiber.Config{Views: html.New("./views", ".html")})
+	goth.UseProviders(
+		discord.New(
+			os.Getenv("DISCORD_ID"),
+			os.Getenv("DISCORD_SECRET"),
+			"http://localhost:8080/auth/discord",
+			discord.ScopeIdentify, discord.ScopeGuilds),
+		steam.New(os.Getenv("STEAM_KEY"), "http://localhost:8080/auth/steam"),
+	)
+
+	setupRoutes(app)
+
+	err = app.Listen(":8080")
+	if err != nil {
+		panic(err)
+	}
+}
diff --git a/models/user.go b/models/user.go
new file mode 100644
index 0000000..6dffe04
--- /dev/null
+++ b/models/user.go
@@ -0,0 +1,34 @@
+package models
+
+import "go.mongodb.org/mongo-driver/bson/primitive"
+
+type User struct {
+	Id          primitive.ObjectID `bson:"_id", omitempty`
+	Banned      bool               `bson:banned`
+	BlackLister bool               `bson:"blacklisted"`
+	Experience  uint               `bson:"experience"`
+	//LastBuy     string `bson:"lastBuy,omitempty"`
+	Level       uint   `bson:"level"`
+	Money       uint   `bson:"money"`
+	MoneyLimit  uint   `bson:"moneyLimit"`
+	DiscordId   string `bson:"userId,omitempty"`
+	SteamId     string `bson:"steamId,omitempty"`
+	DiscordName string `bson:"username,omitempty"`
+}
+
+func NewUser() *User {
+	return &User{primitive.NewObjectID(), false, false, 0, 0, 0, 0, "", "", ""}
+}
+
+func NewUserDiscord(id string, name string) *User {
+	ret := NewUser()
+	ret.DiscordId = id
+	ret.DiscordName = name
+	return ret
+}
+
+func NewUserSteam(id string) *User {
+	ret := NewUser()
+	ret.SteamId = id
+	return ret
+}
diff --git a/views/index.html b/views/index.html
new file mode 100644
index 0000000..bbb596f
--- /dev/null
+++ b/views/index.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<head>
+</head>
+<html>
+    <body>
+        <h1>Chasseurs De Succès</h1>
+        <p>Site pour mieux réunir les chasseurs de succès sur les jeux multijoueurs</p>
+        <hr />
+
+        {{ if .error  }}
+        <div class="error">
+            <p>Erreur rencontrée: {{.error}}</p>
+        </div>
+        {{ end  }}
+
+        {{if .connected}}
+        <div>
+            <p>Bienvenue !!</p>
+            <p>Connexions actives : </p>
+            {{ if .discordName }}
+                <p>Discord : {{ .discordName }}</p>
+            {{ else }}
+                <a href="{{ .urlDiscord  }}">SE CONNECTER VIA DISCORD ICI</a>
+            {{ end }}
+
+            {{ if .steamName }}
+                <p>Steam : {{ .steamName }}</p>
+            {{ else }}
+                <a href="{{ .urlSteam  }}">SE CONNECTER VIA STEAM ICI</a>
+            {{ end }}
+        </div>
+        {{else}}
+        <a href="{{ .urlDiscord  }}">SE CONNECTER VIA DISCORD ICI</a>
+        <a href="{{ .urlSteam  }}">SE CONNECTER VIA STEAM ICI</a>
+        {{end}}
+    </body>
+</html>