feat: Create collection file for routes

This commit is contained in:
Björn Benouarets
2025-08-11 22:15:32 +02:00
commit f00ecb8155
14 changed files with 482 additions and 0 deletions

8
.idea/dictionaries/project.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<component name="ProjectDictionaryState">
<dictionary name="project">
<words>
<w>ossp</w>
<w>secnex</w>
</words>
</dictionary>
</component>

8
.idea/sqldialects.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/sql/secnex_support.sql" dialect="PostgreSQL" />
<file url="file://$PROJECT_DIR$/sql/tenante_admin.sql" dialect="PostgreSQL" />
<file url="PROJECT" dialect="PostgreSQL" />
</component>
</project>

4
.idea/tenante-api.iml generated Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="Go" enabled="true" />
</module>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

151
.idea/workspace.xml generated Normal file
View File

@@ -0,0 +1,151 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="ALL" />
</component>
<component name="ChangeListManager">
<list default="true" id="2d8b3225-34c3-4749-bb3b-3a6f310611b0" name="Changes" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Go File" />
</list>
</option>
</component>
<component name="GOROOT" url="file:///opt/homebrew/opt/go/libexec" />
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="KubernetesApiPersistence">{}</component>
<component name="KubernetesApiProvider">{
&quot;isMigrated&quot;: true
}</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 5
}</component>
<component name="ProjectId" id="310w7j0QRBrZfOep9zvchx4XLdf" />
<component name="ProjectLevelVcsManager">
<ConfirmationsSetting value="2" id="Add" />
</component>
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;ASKED_SHARE_PROJECT_CONFIGURATION_FILES&quot;: &quot;true&quot;,
&quot;DefaultGoTemplateProperty&quot;: &quot;Go File&quot;,
&quot;Go Build.go build github.com/tenante/api.executor&quot;: &quot;Run&quot;,
&quot;Go Build.go build main.go.executor&quot;: &quot;Run&quot;,
&quot;Go Build.go build tenante-api main.go.executor&quot;: &quot;Run&quot;,
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.formatter.settings.were.checked&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.migrated.go.modules.settings&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.modules.go.list.on.any.changes.was.set&quot;: &quot;true&quot;,
&quot;SHARE_PROJECT_CONFIGURATION_FILES&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;main&quot;,
&quot;go.import.settings.migrated&quot;: &quot;true&quot;,
&quot;go.sdk.automatically.set&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;/Users/bbenouarets/Projects/secnex/tenante-api&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;ts.external.directory.path&quot;: &quot;/Users/bbenouarets/Projects/secnex/tenante-api/node_modules/typescript/lib&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
},
&quot;keyToStringList&quot;: {
&quot;DatabaseDriversLRU&quot;: [
&quot;postgresql&quot;
]
}
}</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/server" />
<recent name="$PROJECT_DIR$/sql" />
</key>
</component>
<component name="RunManager">
<configuration default="true" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="tenante-api" />
<working_directory value="$PROJECT_DIR$" />
<go_parameters value="-i" />
<kind value="FILE" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
<configuration name="go build github.com/tenante/api" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
<module name="tenante-api" />
<working_directory value="$PROJECT_DIR$" />
<kind value="PACKAGE" />
<package value="github.com/tenante/api" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$/main.go" />
<method v="2" />
</configuration>
<configuration default="true" type="GoTestRunConfiguration" factoryName="Go Test">
<module name="tenante-api" />
<working_directory value="$PROJECT_DIR$" />
<go_parameters value="-i" />
<kind value="DIRECTORY" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<framework value="gotest" />
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Go Build.go build github.com/tenante/api" />
</list>
</recent_temporary>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-js-predefined-d6986cc7102b-e03c56caf84a-JavaScript-WS-252.23892.411" />
</set>
</attachedChunks>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="2d8b3225-34c3-4749-bb3b-3a6f310611b0" name="Changes" comment="" />
<created>1754674133452</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1754674133452</updated>
<workItem from="1754831940990" duration="393000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
</component>
<component name="VgoProject">
<settings-migrated>true</settings-migrated>
</component>
</project>

30
database/connection.go Normal file
View File

@@ -0,0 +1,30 @@
package database
import (
"database/sql"
"fmt"
_ "github.com/lib/pq"
)
type Connection struct {
Connection *sql.DB
}
func NewConnection(host string, port int, user string, password string, dbName string, sslMode string) *Connection {
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s", host, port, user, password, dbName, sslMode)
db, err := sql.Open("postgres", psqlInfo)
if err != nil {
panic(err)
}
return &Connection{Connection: db}
}
func (db *Connection) Close() error {
return db.Connection.Close()
}
func (db *Connection) Ping() bool {
fmt.Println("🔥 Pinging database...")
return db.Connection.Ping() == nil
}

5
go.mod Normal file
View File

@@ -0,0 +1,5 @@
module github.com/tenante/api
go 1.24.5
require github.com/lib/pq v1.10.9

2
go.sum Normal file
View File

@@ -0,0 +1,2 @@
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=

15
main.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import (
"github.com/tenante/api/database"
"github.com/tenante/api/server"
)
func main() {
db := database.NewConnection("localhost", 5432, "postgres", "postgres", "tenante_admin_PRD", "disable")
api := server.NewApiServer(8081, db)
err := api.Start()
if err != nil {
panic(err)
}
}

39
server/api.go Normal file
View File

@@ -0,0 +1,39 @@
package server
import (
"errors"
"fmt"
"net/http"
"time"
"github.com/tenante/api/database"
)
type ApiServer struct {
Port int
DatabaseConnection *database.Connection
}
func NewApiServer(port int, database *database.Connection) *ApiServer {
return &ApiServer{Port: port, DatabaseConnection: database}
}
func (api *ApiServer) Start() error {
mux := http.NewServeMux()
mux.HandleFunc("/_/health", api.HealthCheckRoute)
srv := &http.Server{
Addr: fmt.Sprintf(":%d", api.Port),
Handler: mux,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
fmt.Printf("🚀 Server starting on %s...\n", srv.Addr)
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
return err
}
return nil
}

13
server/response.go Normal file
View File

@@ -0,0 +1,13 @@
package server
type HealthCheckResponse struct {
Database bool `json:"database"`
API bool `json:"api"`
Status string `json:"status"`
}
type InternalServerErrorResponse struct {
Status string `json:"status"`
Code int `json:"code"`
Message string `json:"message"`
}

24
server/route.go Normal file
View File

@@ -0,0 +1,24 @@
package server
import (
"encoding/json"
"net/http"
)
func (api *ApiServer) HealthCheckRoute(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
response := HealthCheckResponse{
Database: api.DatabaseConnection.Ping(),
API: true,
Status: "OK",
}
err := json.NewEncoder(w).Encode(response)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, err := w.Write([]byte("Internal Server Error"))
if err != nil {
return
}
}
}

12
sql/secnex_support.sql Normal file
View File

@@ -0,0 +1,12 @@
-- CREATE DATABASE "secnex_PRD";
CREATE SCHEMA IF NOT EXISTS "support";
CREATE TABLE IF NOT EXISTS "public"."user" (
"id" SERIAL PRIMARY KEY,
"name" VARCHAR(255) NOT NULL,
"email" VARCHAR(255) NOT NULL,
"password" VARCHAR(255) NOT NULL,
"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

165
sql/tenante_admin.sql Normal file
View File

@@ -0,0 +1,165 @@
-- CREATE DATABASE "tenante_admin_PRD";
-- Drop all tables
DO $$
DECLARE
tabName text;
BEGIN
FOR tabName IN
SELECT tablename
FROM pg_tables
WHERE schemaname = 'public'
LOOP
EXECUTE 'DROP TABLE IF EXISTS public."' || tabName || '" CASCADE';
END LOOP;
END $$;
-- Install uuid extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Create the ORGANIZATION table
CREATE TABLE IF NOT EXISTS "organization" (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name TEXT NOT NULL UNIQUE,
slug TEXT NOT NULL UNIQUE,
"ssoConfigurationId" UUID,
"ssoDomain" UUID,
"ssoEnabled" BOOLEAN NOT NULL DEFAULT FALSE,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"deletedAt" TIMESTAMPTZ
);
-- Create the PROJECT table
CREATE TABLE IF NOT EXISTS "project" (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name TEXT NOT NULL,
slug TEXT NOT NULL,
description TEXT,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"deletedAt" TIMESTAMPTZ,
"organizationId" UUID REFERENCES organization(id) ON DELETE CASCADE,
CONSTRAINT unique_name_per_org UNIQUE ("organizationId", name),
CONSTRAINT unique_slug_per_org UNIQUE ("organizationId", slug)
);
-- Create the USER table
CREATE TABLE IF NOT EXISTS "user" (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
"firstName" TEXT NOT NULL,
"lastName" TEXT NOT NULL,
"displayName" TEXT NOT NULL,
"username" TEXT NOT NULL UNIQUE,
"email" TEXT NOT NULL UNIQUE,
"password" TEXT NOT NULL,
"verified" BOOLEAN NOT NULL DEFAULT FALSE,
"verifiedAt" TIMESTAMPTZ,
"enabled" BOOLEAN NOT NULL DEFAULT TRUE,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"deletedAt" TIMESTAMPTZ
);
-- Create the ACCOUNT table
CREATE TABLE IF NOT EXISTS "account" (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
"userId" UUID REFERENCES "user"(id) ON DELETE CASCADE,
"provider" TEXT NOT NULL,
"providerAccountId" TEXT NOT NULL,
"refreshToken" TEXT,
"accessToken" TEXT NOT NULL,
"accessTokenExpires" TIMESTAMPTZ NOT NULL,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"deletedAt" TIMESTAMPTZ,
CONSTRAINT unique_provider_per_user UNIQUE ("userId", "provider"),
CONSTRAINT unique_provider_account_per_user UNIQUE ("userId", "provider", "providerAccountId")
);
-- Create the MFA table to store MFA data
CREATE TABLE IF NOT EXISTS "mfa" (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
"userId" UUID REFERENCES "user"(id) ON DELETE CASCADE,
"secret" TEXT NOT NULL UNIQUE,
"enabled" BOOLEAN NOT NULL DEFAULT TRUE,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Create ENUM for user roles
DROP TYPE IF EXISTS "user_role" CASCADE;
CREATE TYPE "user_role" AS ENUM ('owner', 'admin', 'member', 'guest');
-- Create the PROJECT_USER table to link users to projects and assign roles
CREATE TABLE IF NOT EXISTS "project_user" (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
"userId" UUID REFERENCES "user"(id) ON DELETE CASCADE,
"projectId" UUID REFERENCES "project"(id) ON DELETE CASCADE,
"role" "user_role" NOT NULL DEFAULT 'guest',
"createdBy" UUID REFERENCES "user"(id) ON DELETE SET NULL,
"updatedBy" UUID REFERENCES "user"(id) ON DELETE SET NULL,
"deletedBy" UUID REFERENCES "user"(id) ON DELETE SET NULL,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"deletedAt" TIMESTAMPTZ,
CONSTRAINT unique_user_per_project UNIQUE ("userId", "projectId"),
CONSTRAINT unique_role_per_user_per_project UNIQUE ("userId", "projectId", "role")
);
-- Create the APP table to link apps to projects and assign roles
CREATE TABLE IF NOT EXISTS "app" (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name TEXT NOT NULL,
slug TEXT NOT NULL,
description TEXT,
"projectId" UUID REFERENCES "project"(id) ON DELETE CASCADE,
"createdBy" UUID REFERENCES "user"(id) ON DELETE SET NULL,
"updatedBy" UUID REFERENCES "user"(id) ON DELETE SET NULL,
"deletedBy" UUID REFERENCES "user"(id) ON DELETE SET NULL,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"deletedAt" TIMESTAMPTZ,
CONSTRAINT unique_name_per_project UNIQUE ("projectId", name),
CONSTRAINT unique_slug_per_project UNIQUE ("projectId", slug)
);
DROP TYPE IF EXISTS "user_audit_action" CASCADE;
CREATE TYPE "user_audit_action" AS ENUM ('login', 'password_change', 'reset_password', '2fa_enable', '2fa_disable', '2fa_generate_backup', 'request_deletion', 'stop_deletion');
-- Create USER_AUDIT table (to log any user changes and activities e.g., login attempts, password changes, 2FA changes
CREATE TABLE IF NOT EXISTS "user_audit" (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
"action" "user_audit_action" NOT NULL DEFAULT 'login',
"ipAddress" TEXT NOT NULL,
"userAgent" TEXT NOT NULL,
"appId" UUID REFERENCES "app"(id) ON DELETE SET NULL,
"userId" UUID REFERENCES "user"(id) ON DELETE CASCADE,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"deletedAt" TIMESTAMPTZ
);
-- Create the SESSION table to save session data
CREATE TABLE IF NOT EXISTS "session" (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
"userId" UUID REFERENCES "user"(id) ON DELETE CASCADE,
"ipAddress" TEXT NOT NULL,
"userAgent" TEXT NOT NULL,
"refreshToken" TEXT NOT NULL,
"appId" UUID REFERENCES "app"(id) ON DELETE SET NULL,
"expiresAt" TIMESTAMPTZ NOT NULL,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Create the EVENT table to save webhook data and more
CREATE TABLE IF NOT EXISTS "event" (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
type TEXT NOT NULL,
payload JSONB NOT NULL DEFAULT '{}',
"appId" UUID REFERENCES "app"(id) ON DELETE SET NULL,
"userId" UUID REFERENCES "user"(id) ON DELETE SET NULL,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW()
);