diff --git a/Config/readme.md b/Config/readme.md
new file mode 100644
index 0000000..61543d9
--- /dev/null
+++ b/Config/readme.md
@@ -0,0 +1,25 @@
+### How to use this folder
+
+Welcome to the config folder, in this folder you can config your chat server with the Config cmds.
+
+Be notice that the main Config file for your server is .env file you can find a sample .env file in the root of this project
+
+For Create your configuration file, just make a `config.json` file here
+
+### CMDS (JSON)
+
+_ServerName -> information: your server name
+Server_Description -> information: your server description
+Server_Owner -> information: your server manager
+Server_Date_Format -> Full Date format or simple Date Format_
+
+### Example
+
+```json
+{
+ "Server_Name": "Sunsend1",
+ "Server_Description": "This is the Default Server",
+ "Server_Owner": "SunSend",
+ "Sever_Date_Format": "full" // full/normal/simple
+}
+```
diff --git a/Config/words.l b/Config/words.l
new file mode 100644
index 0000000..3220089
--- /dev/null
+++ b/Config/words.l
@@ -0,0 +1 @@
+nice
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 4dc0565..58259af 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,6 @@
+setup:
+ go run cmd/gen_key.go
+
run:
go run cmd/api/main.go
@@ -7,4 +10,9 @@ build:
doc:
touch doc
echo "welcome" > doc
- echo "Done."
\ No newline at end of file
+ echo "Done."
+
+config:
+ mkdir -p Config/
+ touch Config/config.json
+ echo "Done.""
\ No newline at end of file
diff --git a/Storage/SunSend.db b/Storage/SunSend.db
deleted file mode 100644
index 6938917..0000000
Binary files a/Storage/SunSend.db and /dev/null differ
diff --git a/TODO.md b/TODO.md
index 7266c36..85f3abd 100644
--- a/TODO.md
+++ b/TODO.md
@@ -4,3 +4,8 @@
- [ ] Convert Error Numbers to a model
- [x] Add Create Channel
- [x] Add Database to save messages
+- [ ] Add Server Route for more information about server
+- [ ] Add managment panel for config server
+- [ ] User Scripts for making small plugins to expand the server capability
+- [ ] Add API key feature for servers
+- [ ] Make a ConfigCore to manage config files
diff --git a/cmd/api/main.go b/cmd/api/main.go
index 3380ff6..8f6c591 100644
--- a/cmd/api/main.go
+++ b/cmd/api/main.go
@@ -1,23 +1,21 @@
package main
import (
+ "fmt"
"log"
- "os"
+ "sunsend/internals/CoreConfig"
"sunsend/internals/DB"
"sunsend/internals/Handlers"
"sunsend/internals/Renderer"
"text/template"
- "github.com/joho/godotenv"
"github.com/labstack/echo/v4"
)
func main() {
e := echo.New()
- err := godotenv.Load(".env")
- if err != nil {
- log.Fatal(err.Error())
- }
+ CoreConfig.UpdateConfigs() // load bouth .env configs and user configs
+ log.Println("Your API Key is: " + fmt.Sprintf("%s", CoreConfig.Configs.Server.Key))
// e.Use(middleware.Logger())
// e.Use(middleware.Recover())
e.Renderer = &Renderer.Template{
@@ -26,5 +24,5 @@ func main() {
Handlers.Handler(e)
DB.PrepairDBSystem()
- e.Logger.Fatal(e.Start(":" + os.Getenv("PORT")))
+ e.Logger.Fatal(e.Start(":" + CoreConfig.Configs.Dotenv["PORT"]))
}
diff --git a/cmd/gen_key.go b/cmd/gen_key.go
new file mode 100644
index 0000000..2fa6c93
--- /dev/null
+++ b/cmd/gen_key.go
@@ -0,0 +1,18 @@
+package main
+
+import (
+ "crypto/sha256"
+ "fmt"
+
+ "github.com/thanhpk/randstr"
+)
+
+func GenerateAPIKey() [32]byte {
+ token := randstr.Hex(16) // generate 128-bit hex string
+ return sha256.Sum256([]byte(token))
+}
+
+func main() {
+ res := GenerateAPIKey()
+ fmt.Println("Your API KEY is: ", fmt.Sprintf("%X", res))
+}
diff --git a/go.mod b/go.mod
index c153f0d..2bb2b02 100644
--- a/go.mod
+++ b/go.mod
@@ -17,6 +17,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
+ github.com/thanhpk/randstr v1.0.6 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/crypto v0.17.0 // indirect
diff --git a/go.sum b/go.sum
index 37cdc78..97780d1 100644
--- a/go.sum
+++ b/go.sum
@@ -24,6 +24,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/thanhpk/randstr v1.0.6 h1:psAOktJFD4vV9NEVb3qkhRSMvYh4ORRaj1+w/hn4B+o=
+github.com/thanhpk/randstr v1.0.6/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
diff --git a/internals/Base/B_channel.go b/internals/Base/BaseChannel.go
similarity index 100%
rename from internals/Base/B_channel.go
rename to internals/Base/BaseChannel.go
diff --git a/internals/Base/Messages.go b/internals/Base/Messages.go
index ecfdff5..8e1f1c0 100644
--- a/internals/Base/Messages.go
+++ b/internals/Base/Messages.go
@@ -3,19 +3,25 @@ package Base
import (
"fmt"
"log"
+ "strings"
"sunsend/internals/DB"
"sunsend/internals/Data"
)
func IsEqulTo(message string) bool {
- return message == "321"
+ for _, v := range *Data.LoadedWordList {
+ if message == v || strings.Contains(message, v) {
+ return true
+ }
+ }
+ return false
}
func CheckMessage(message string) int {
if len(message) > 30 {
- return 15
+ return 15 // response 15 -> length error
} else if IsEqulTo(message) {
- return 12
+ return 12 // response 12 -> Word error
}
return 0
}
diff --git a/internals/Base/key.go b/internals/Base/key.go
new file mode 100644
index 0000000..3a87054
--- /dev/null
+++ b/internals/Base/key.go
@@ -0,0 +1,40 @@
+package Base
+
+import (
+ "crypto/sha256"
+ "crypto/subtle"
+ "fmt"
+ "strings"
+ "sunsend/internals/CoreConfig"
+)
+
+// Doc: https://dev.to/caiorcferreira/implementing-a-safe-and-sound-api-key-authorization-middleware-in-go-3g2c
+
+func GenerateAPIKey(rawkey string) [32]byte {
+ // token := randstr.Hex(16) // generate 128-bit hex string
+ return sha256.Sum256([]byte(rawkey))
+}
+
+func BearerToken(headers map[string][]string) (string, int) {
+ fmt.Println(headers)
+ api_key_org, ok := headers["Api_key"]
+ if !ok {
+ return "", 17 // invalid API KE Y
+ }
+ if len(strings.SplitN(api_key_org[0], " ", 2)) > 2 {
+ return "", 17
+ }
+ return strings.TrimSpace(api_key_org[0]), 0
+
+}
+
+// apiKeyIsValid checks if the given API key is valid and returns the principal if it is.
+func ApiKeyIsValid(user_api_key string) int {
+ // hash := sha256.Sum256([]byte(user_api_key))
+ // key := hash[:]
+ if subtle.ConstantTimeCompare([]byte(CoreConfig.Configs.Server.Key), []byte(user_api_key)) == 1 {
+ return 0
+ }
+
+ return 17
+}
diff --git a/internals/CoreConfig/ConfigReader.go b/internals/CoreConfig/ConfigReader.go
new file mode 100644
index 0000000..c264423
--- /dev/null
+++ b/internals/CoreConfig/ConfigReader.go
@@ -0,0 +1,48 @@
+package CoreConfig
+
+import (
+ "errors"
+ "log"
+ "os"
+ "sunsend/internals/Data"
+
+ "github.com/joho/godotenv"
+)
+
+var Configs *Data.Config
+var rawapikey string
+
+func getEnvConfig() (map[string]string, error) {
+ err := godotenv.Load(".env")
+ if err != nil {
+ // log.Fatal(err.Error())
+ return nil, errors.New("invalid or can't read .env file")
+ }
+ // It can be better to list of all the needed Config and iterate thought them
+ ret_values := make(map[string]string)
+ ret_values["PORT"] = os.Getenv("PORT")
+ ret_values["KEY"] = os.Getenv("KEY")
+ // copy(rawapikey[:], ret_values["KEY"]) // fix fixing some invalid memory address - TODO: Fix in better way - nice
+ rawapikey = ret_values["KEY"]
+ return ret_values, nil
+}
+
+func UpdateConfigs() {
+ envconfigs, err := getEnvConfig()
+ if err != nil {
+ log.Fatal(err.Error())
+ }
+ // TODO: get the user configs here
+
+ Configs = &Data.Config{
+ Dotenv: envconfigs,
+ Uconfig: nil, // for now just a little cute nil ^^
+ Server: &Data.Server{ // TODO: will holds data from user config file
+ Name: "test",
+ Description: "test1",
+ Owner: "test",
+ Date: "test",
+ Key: rawapikey,
+ },
+ }
+}
diff --git a/internals/DB/db_structure.go b/internals/DB/db_structure.go
index 2bd3882..c6ff1b4 100644
--- a/internals/DB/db_structure.go
+++ b/internals/DB/db_structure.go
@@ -19,7 +19,7 @@ func createBaseTable() int {
db_work := getbasedb()
base_channels_table := `CREATE TABLE Channels (
- "ID" integer NOT NULL PRIMARY KEY,
+ "ID" integer NOT NULL PRIMARY KEY,
"Name" TEXT,
"Description" TEXT,
"Owner" TEXT
@@ -31,7 +31,12 @@ func createBaseTable() int {
fmt.Println("this is where I stoped the program hahahahaha")
log.Fatalln(err.Error())
}
- statement_channel.Exec() // Execute SQL Statements
+ res, err := statement_channel.Exec() // Execute SQL Statements
+ if err != nil {
+ // fmt.Println(err.Error(), res)
+ log.Fatal(err.Error(), res)
+ }
+
log.Println("Base Channels table created")
// CreateMessageTable()
diff --git a/internals/Data/Response.go b/internals/Data/Response.go
index 4c00d6d..d1a4270 100644
--- a/internals/Data/Response.go
+++ b/internals/Data/Response.go
@@ -28,6 +28,8 @@ func GetErrorByResult(res_code int) (string, int, string) {
return "Your Messages Length is more than 30 character", http.StatusBadRequest, "FAILD" // response Status Error - message length
case 16:
return "There is a problem in the system", http.StatusServiceUnavailable, "FAILD" // response Status Error - Server has problem (bug)
+ case 17:
+ return "Invalid API KEY", http.StatusBadRequest, "FAILD" // response Status Error - invalid api key
default:
return "There is a problem", http.StatusNotAcceptable, "FAILD" // Response Status Error - uknown Eror
}
diff --git a/internals/Data/config_struct.go b/internals/Data/config_struct.go
new file mode 100644
index 0000000..bd8a398
--- /dev/null
+++ b/internals/Data/config_struct.go
@@ -0,0 +1,7 @@
+package Data
+
+type Config struct {
+ Dotenv map[string]string // .env file configs
+ Uconfig map[string]string // usr `Config` folder configs
+ Server *Server
+}
diff --git a/internals/Data/server_struct.go b/internals/Data/server_struct.go
new file mode 100644
index 0000000..47fbe66
--- /dev/null
+++ b/internals/Data/server_struct.go
@@ -0,0 +1,9 @@
+package Data
+
+type Server struct {
+ Name string
+ Description string
+ Owner string
+ Date string
+ Key string
+}
diff --git a/internals/Data/words.go b/internals/Data/words.go
new file mode 100644
index 0000000..b6906b5
--- /dev/null
+++ b/internals/Data/words.go
@@ -0,0 +1,25 @@
+package Data
+
+/*
+ This file will holds the words that shouldn't be on chat or send as a data to the server
+ It is better to not change edit this file and instead make a words list on config/words.l
+ There words are default for each server, and you should not remove these.
+*/
+
+var LoadedWordList *[]string
+
+var defaultWords []string = []string{
+ "321",
+ "nice",
+ "yes",
+}
+
+func LoadWordsFromConfig() {
+ // Load Words From Config File
+ LoadedWordList = &[]string{"config"}
+ *LoadedWordList = append(*LoadedWordList, defaultWords...)
+}
+
+func GetAllWords() *[]string {
+ return LoadedWordList
+}
diff --git a/internals/Routes/chat.go b/internals/Routes/chat.go
index b659e6a..0cd1299 100644
--- a/internals/Routes/chat.go
+++ b/internals/Routes/chat.go
@@ -22,12 +22,14 @@ func GetChatPostAction(c echo.Context) error {
err := Base.CheckMessage(msg)
if err != 0 {
response, _ := Data.NewResponse(c, err, channel_id_user, nil)
- return c.JSON(http.StatusBadRequest, response) // should be same Statuscode as NewResponse
+ _, error_code_org, _ := Data.GetErrorByResult(err)
+ return c.JSON(error_code_org, response) // should be same Statuscode as NewResponse
}
res_exists_channel := Base.ChannelExists(channel_id_user)
if res_exists_channel != 0 {
response_org, _ := Data.NewResponse(c, res_exists_channel, channel_id_user, nil)
- return c.JSON(http.StatusBadRequest, response_org) // should be same Statuscode as NewResponse
+ _, error_code_org2, _ := Data.GetErrorByResult(res_exists_channel)
+ return c.JSON(error_code_org2, response_org) // should be same Statuscode as NewResponse
}
// fmt.Println(msg)
@@ -35,14 +37,16 @@ func GetChatPostAction(c echo.Context) error {
res := DB.InsertMsg(IChannel_id_ser, rand.Intn(999), user, msg, time.Now().String(), 0)
if res != 0 {
response, _ := Data.NewResponse(c, res, channel_id_user, nil)
- return c.JSON(http.StatusBadRequest, response)
+ _, error_code_org3, _ := Data.GetErrorByResult(res_exists_channel)
+ return c.JSON(error_code_org3, response)
}
response, _ := Data.NewResponse(c, 0, channel_id_user, msg)
- return c.JSON(http.StatusOK, response)
+ _, error_code_org4, _ := Data.GetErrorByResult(res_exists_channel)
+ return c.JSON(error_code_org4, response)
}
-func StreamResponseJSON(c echo.Context, chat_data []*Data.Chat) error {
+func StreamResponseJSON(c echo.Context, chat_data *Data.Response) error {
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
c.Response().WriteHeader(http.StatusOK)
return json.NewEncoder(c.Response()).Encode(chat_data)
@@ -70,10 +74,23 @@ func chatActionFunc(c echo.Context) error {
}
fmt.Println(len(chat_collection))
-
- // response, _ = Data.NewResponse(c, res, channel_id, chat_collection)
+ headers := c.Request().Header
+ apiKey, res_api_key := Base.BearerToken(headers)
+ if res != 0 {
+ response, _ = Data.NewResponse(c, res_api_key, channel_id, nil)
+ _, error_code_org, _ := Data.GetErrorByResult(res_api_key)
+ return c.JSON(error_code_org, response)
+ }
+ res_check_api := Base.ApiKeyIsValid(apiKey)
+ if res_check_api != 0 {
+ response, _ = Data.NewResponse(c, res_check_api, channel_id, nil)
+ _, error_code_org, _ := Data.GetErrorByResult(res_check_api)
+ return c.JSON(error_code_org, response)
+ }
+ fmt.Println("API KEY:", apiKey, "requested to server succsessfully")
+ response, _ = Data.NewResponse(c, res, channel_id, chat_collection)
// return c.JSON(http.StatusOK, response)
- return StreamResponseJSON(c, chat_collection) // Stream JSON File
+ return StreamResponseJSON(c, response) // Stream JSON File
// return c.JSON(http.StatusAccepted, map[string]interface{}{
// "asd": "nice",
// })