Compare commits

..

16 commits
v1.0.0 ... main

6 changed files with 89 additions and 188 deletions

View file

@ -5,8 +5,19 @@ resourceDir="$path/../../resources/322dc186-04d2-4f69-89b5-403ab643cc1d"
rm -rf "$resourceDir" || exit 1
rm -rf "$path/../../services/data-tracker.fgs" || exit 1
cd "$path" || exit 1
printf "\033[1;35mBuilding data-tracker.fgs...\033[0m\n"
go build -o "$path/../../services/data-tracker.fgs" --buildmode=plugin -ldflags "-s -w" || exit 1
cd "$path/resources/wasm/oauth" || exit 1
GOOS=js GOARCH=wasm go build -o "$resourceDir/static/wasm/oauth.wasm" -ldflags "-s -w" || exit 1
find -L "$path/resources/wasm" -type f -name "main.go" | while read -r mainGo; do
buildDir=$(dirname "$mainGo")
baseName=$(basename "$buildDir")
printf "\033[1;34m\033[1;33mBuilding WASM object %s...\033[0m\n" "$baseName"
(cd "$buildDir" && GOOS=js GOARCH=wasm go build -o "$resourceDir/static/wasm/$(basename "$buildDir").wasm" -ldflags "-s -w") || {
printf "\033[1;31mError: %s failed.\033[0m\n" "$mainGo"
exit 1
}
done
printf "\033[1;34mCopying static files...\033[0m\n"
cp -r "$path/resources/static" "$resourceDir/" || exit 1
cp -r "$path/resources/templates" "$resourceDir/" || exit 1
printf "\033[1;36mdata-tracker.fgs has been built successfully!\033[0m\n"

8
go.mod
View file

@ -1,12 +1,12 @@
module git.ailur.dev/ailur/datatracker
go 1.23.1
go 1.23.3
require (
git.ailur.dev/ailur/fg-library/v2 v2.0.1
git.ailur.dev/ailur/fg-nucleus-library v1.0.0
git.ailur.dev/ailur/fg-library/v3 v3.6.2
git.ailur.dev/ailur/fg-nucleus-library v1.2.2
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/uuid v1.6.0
)
require github.com/go-chi/chi/v5 v5.1.0
require github.com/go-chi/chi/v5 v5.2.1 // indirect

14
go.sum
View file

@ -1,11 +1,9 @@
git.ailur.dev/ailur/fg-library/v2 v2.0.0 h1:NanDV52W+NBu96v/HPDPGqH8NOxLp6MRrRdXLPEsgYw=
git.ailur.dev/ailur/fg-library/v2 v2.0.0/go.mod h1:1jYbWhabGcIwp7CkhHqvRwC8eP+nHv5BrXPe9NX2HE8=
git.ailur.dev/ailur/fg-library/v2 v2.0.1 h1:ltPYXf/Om0hnMD8gr1K5bkYrfHqKPSbb0hxa0wtTnZ0=
git.ailur.dev/ailur/fg-library/v2 v2.0.1/go.mod h1:1jYbWhabGcIwp7CkhHqvRwC8eP+nHv5BrXPe9NX2HE8=
git.ailur.dev/ailur/fg-nucleus-library v1.0.0 h1:TT1V4cfka+uUpvV1zU7bc4KXFkgnsI/sIvaZDDxXk+k=
git.ailur.dev/ailur/fg-nucleus-library v1.0.0/go.mod h1:m4gNSEypfgrUV8bXaR8NLB8zchUM59y0ellV1wp/C+I=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
git.ailur.dev/ailur/fg-library/v3 v3.6.2 h1:PNJKxpvbel2iDeB9+/rpYRyMoim6JjRHOXPYFYky7Ng=
git.ailur.dev/ailur/fg-library/v3 v3.6.2/go.mod h1:ArNsafpqES2JuxQM5aM+bNe0FwHLIsL6pbjpiWvDwGs=
git.ailur.dev/ailur/fg-nucleus-library v1.2.2 h1:JbclmxGSoL+ByGZAl0W6PqWRoyBBGTrKrizWDJ7rdI0=
git.ailur.dev/ailur/fg-nucleus-library v1.2.2/go.mod h1:stxiTyMv3Fa7GzpyLbBUh3ahlb7110p0NnCl8ZTjwBs=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=

233
main.go
View file

@ -2,16 +2,14 @@ package main
import (
"errors"
"net/url"
"time"
"crypto/ed25519"
"database/sql"
"encoding/json"
library "git.ailur.dev/ailur/fg-library/v2"
library "git.ailur.dev/ailur/fg-library/v3"
authLibrary "git.ailur.dev/ailur/fg-nucleus-library"
"github.com/go-chi/chi/v5"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"html/template"
@ -24,6 +22,7 @@ var ServiceInformation = library.Service{
Permissions: library.Permissions{
Authenticate: true, // This service does require authentication
Database: true, // This service does require database access
Router: true, // This service does require a router
BlobStorage: false, // This service does not require blob storage
InterServiceCommunication: true, // This service does require inter-service communication
Resources: true, // This service does require its HTTP templates and static files
@ -31,18 +30,16 @@ var ServiceInformation = library.Service{
ServiceID: uuid.MustParse("322dc186-04d2-4f69-89b5-403ab643cc1d"),
}
func logFunc(message string, messageType uint64, information library.ServiceInitializationInformation) {
var (
loggerService = uuid.MustParse("00000000-0000-0000-0000-000000000002")
)
func logFunc(message string, messageType library.MessageCode, information *library.ServiceInitializationInformation) {
// Log the message to the logger service
information.Outbox <- library.InterServiceMessage{
ServiceID: ServiceInformation.ServiceID,
ForServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000002"), // Logger service
MessageType: messageType,
SentAt: time.Now(),
Message: message,
}
information.SendISMessage(loggerService, messageType, message)
}
func renderTemplate(statusCode int, w http.ResponseWriter, data map[string]interface{}, templatePath string, information library.ServiceInitializationInformation) {
func renderTemplate(statusCode int, w http.ResponseWriter, data map[string]interface{}, templatePath string, information *library.ServiceInitializationInformation) {
var err error
var requestedTemplate *template.Template
// Output ls of the resource directory
@ -60,7 +57,7 @@ func renderTemplate(statusCode int, w http.ResponseWriter, data map[string]inter
}
}
func renderString(statusCode int, w http.ResponseWriter, data string, information library.ServiceInitializationInformation) {
func renderString(statusCode int, w http.ResponseWriter, data string, information *library.ServiceInitializationInformation) {
w.WriteHeader(statusCode)
_, err := w.Write([]byte(data))
if err != nil {
@ -68,7 +65,7 @@ func renderString(statusCode int, w http.ResponseWriter, data string, informatio
}
}
func renderJSON(statusCode int, w http.ResponseWriter, data map[string]interface{}, information library.ServiceInitializationInformation) {
func renderJSON(statusCode int, w http.ResponseWriter, data map[string]interface{}, information *library.ServiceInitializationInformation) {
w.WriteHeader(statusCode)
err := json.NewEncoder(w).Encode(data)
if err != nil {
@ -105,6 +102,10 @@ func getUsername(token string, oauthHostName string, publicKey ed25519.PublicKey
Sub string `json:"sub"`
}
request, err := http.NewRequest("GET", oauthHostName+"/api/oauth/userinfo", nil)
if err != nil {
return "", "", err
}
request.Header.Set("Authorization", "Bearer "+token)
response, err := http.DefaultClient.Do(request)
if err != nil {
@ -161,174 +162,66 @@ func verifyJwt(token string, publicKey ed25519.PublicKey, conn library.Database)
return claims, true
}
func Main(information library.ServiceInitializationInformation) *chi.Mux {
func Main(information *library.ServiceInitializationInformation) {
var conn library.Database
hostName := information.Configuration["hostName"].(string)
go information.StartISProcessor()
// Initiate a connection to the database
// Call service ID 1 to get the database connection information
information.Outbox <- library.InterServiceMessage{
ServiceID: ServiceInformation.ServiceID,
ForServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"), // Service initialization service
MessageType: 1, // Request connection information
SentAt: time.Now(),
Message: nil,
}
// Wait for the response
response := <-information.Inbox
if response.MessageType == 2 {
// This is the connection information
// Set up the database connection
conn = response.Message.(library.Database)
if conn.DBType == library.Sqlite {
// Create the RFCs table
_, err := conn.DB.Exec("CREATE TABLE IF NOT EXISTS rfc (id INTEGER NOT NULL, year INTEGER NOT NULL, name TEXT NOT NULL, content TEXT NOT NULL, version TEXT NOT NULL, creator BLOB NOT NULL, UNIQUE(id, year))")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the users table
_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS users (id BLOB NOT NULL)")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the comments table
_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS comments (id BLOB NOT NULL, rfcId INTEGER NOT NULL, rfcYear INTEGER NOT NULL, content TEXT NOT NULL, creator BLOB NOT NULL, creatorName TEXT NOT NULL, created INTEGER NOT NULL)")
if err != nil {
logFunc(err.Error(), 3, information)
}
} else {
// Create the RFCs table
_, err := conn.DB.Exec("CREATE TABLE IF NOT EXISTS rfc (id SERIAL PRIMARY KEY, year INTEGER NOT NULL, name TEXT NOT NULL, content TEXT NOT NULL, version TEXT NOT NULL, creator BYTEA NOT NULL, UNIQUE(id, year))")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the users table
_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS users (id BYTEA NOT NULL)")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the comments table
_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS comments (id BYTEA NOT NULL, rfcId INTEGER NOT NULL, rfcYear INTEGER NOT NULL, content TEXT NOT NULL, creator BYTEA NOT NULL, creatorName TEXT NOT NULL, created INTEGER NOT NULL)")
if err != nil {
logFunc(err.Error(), 3, information)
}
}
} else {
// This is an error message
// Log the error message to the logger service
logFunc(response.Message.(error).Error(), 3, information)
}
// Ask the authentication service for the public key
information.Outbox <- library.InterServiceMessage{
ServiceID: ServiceInformation.ServiceID,
ForServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000004"), // Authentication service
MessageType: 2, // Request public key
SentAt: time.Now(),
Message: nil,
}
var publicKey ed25519.PublicKey = nil
// 3 second timeout
go func() {
time.Sleep(3 * time.Second)
if publicKey == nil {
logFunc("Timeout while waiting for the public key from the authentication service", 3, information)
}
}()
// Wait for the response
response = <-information.Inbox
if response.MessageType == 2 {
// This is the public key
publicKey = response.Message.(ed25519.PublicKey)
} else {
// This is an error message
// Log the error message to the logger service
logFunc(response.Message.(error).Error(), 3, information)
}
// Ask the authentication service for the OAuth host name
information.Outbox <- library.InterServiceMessage{
ServiceID: ServiceInformation.ServiceID,
ForServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000004"), // Authentication service
MessageType: 0, // Request OAuth host name
SentAt: time.Now(),
Message: nil,
}
var oauthHostName string
// 3 second timeout
go func() {
time.Sleep(3 * time.Second)
if oauthHostName == "" {
logFunc("Timeout while waiting for the OAuth host name from the authentication service", 3, information)
}
}()
// Wait for the response
response = <-information.Inbox
if response.MessageType == 0 {
// This is the OAuth host name
oauthHostName = response.Message.(string)
} else {
// This is an error message
// Log the error message to the logger service
logFunc(response.Message.(error).Error(), 3, information)
}
// Ask the authentication service to create a new OAuth2 client
urlPath, err := url.JoinPath(hostName, "/oauth")
conn, err := information.GetDatabase()
if err != nil {
logFunc(err.Error(), 3, information)
return
}
information.Outbox <- library.InterServiceMessage{
ServiceID: ServiceInformation.ServiceID,
ForServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000004"), // Authentication service
MessageType: 1, // Create OAuth2 client
SentAt: time.Now(),
Message: authLibrary.OAuthInformation{
Name: "Data Tracker",
RedirectUri: urlPath,
KeyShareUri: "",
Scopes: []string{"openid"},
},
}
oauthResponse := authLibrary.OAuthResponse{}
// 3 second timeout
go func() {
time.Sleep(3 * time.Second)
if oauthResponse == (authLibrary.OAuthResponse{}) {
logFunc("Timeout while waiting for the OAuth response from the authentication service", 3, information)
if conn.DBType == library.Sqlite {
// Create the RFCs table
_, err := conn.DB.Exec("CREATE TABLE IF NOT EXISTS rfc (id INTEGER NOT NULL, year INTEGER NOT NULL, name TEXT NOT NULL, content TEXT NOT NULL, version TEXT NOT NULL, creator BLOB NOT NULL, UNIQUE(id, year))")
if err != nil {
logFunc(err.Error(), 3, information)
}
}()
// Create the users table
_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS users (id BLOB NOT NULL)")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the comments table
_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS comments (id BLOB NOT NULL, rfcId INTEGER NOT NULL, rfcYear INTEGER NOT NULL, content TEXT NOT NULL, creator BLOB NOT NULL, creatorName TEXT NOT NULL, created INTEGER NOT NULL)")
if err != nil {
logFunc(err.Error(), 3, information)
}
} else {
// Create the RFCs table
_, err := conn.DB.Exec("CREATE TABLE IF NOT EXISTS rfc (id SERIAL PRIMARY KEY, year INTEGER NOT NULL, name TEXT NOT NULL, content TEXT NOT NULL, version TEXT NOT NULL, creator BYTEA NOT NULL, UNIQUE(id, year))")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the users table
_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS users (id BYTEA NOT NULL)")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the comments table
_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS comments (id BYTEA NOT NULL, rfcId INTEGER NOT NULL, rfcYear INTEGER NOT NULL, content TEXT NOT NULL, creator BYTEA NOT NULL, creatorName TEXT NOT NULL, created INTEGER NOT NULL)")
if err != nil {
logFunc(err.Error(), 3, information)
}
}
// Wait for the response
response = <-information.Inbox
switch response.MessageType {
case 0:
// Success, set the OAuth response
oauthResponse = response.Message.(authLibrary.OAuthResponse)
logFunc("Initialized with App ID: "+oauthResponse.AppID, 0, information)
case 1:
// An error which is their fault
logFunc(response.Message.(error).Error(), 3, information)
case 2:
// An error which is our fault
logFunc(response.Message.(error).Error(), 3, information)
default:
// An unknown error
logFunc("Unknown error", 3, information)
// Initialize the OAuth
oauthResponse, publicKey, oauthHostName, err := authLibrary.InitializeOAuth(authLibrary.OAuthInformation{
Name: "datatracker",
RedirectUri: hostName + "/oauth",
Scopes: []string{"openid"},
}, information)
if err != nil {
logFunc(err.Error(), 3, information)
return
}
// Set up the router
router := chi.NewRouter()
router := information.Router
// Set up the static routes
staticDir, err := fs.Sub(information.ResourceDir, "static")
@ -642,6 +535,4 @@ func Main(information library.ServiceInitializationInformation) *chi.Mux {
"AuthorizationUri": oauthHostName,
}, "oauth.html", information)
})
return router
}

View file

@ -59,8 +59,8 @@
"Content-Type": "application/json"
},
body: JSON.stringify({
year: year,
id: id,
year: parseInt(year),
id: parseInt(id),
token: localStorage.getItem("SECRET-token")
})
}).then(function(response) {

View file

@ -41,9 +41,7 @@ func randomChars(length int) (string, error) {
}
func main() {
// Clear local storage
localStorage := js.Global().Get("localStorage")
localStorage.Call("clear")
statusBox := js.Global().Get("document").Call("getElementById", "statusBox")
tryAgain := js.Global().Get("document").Call("getElementById", "tryAgain")
@ -175,6 +173,9 @@ func main() {
tryAgain.Set("style", "")
}
} else {
// Clear local storage
localStorage.Call("clear")
// Start the authorization process
verifier, err := randomChars(128)
if err != nil {