Merge branch 'Features/Backend/Posix-Lite-Persistence'
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -27,3 +27,5 @@ tmp/
|
|||||||
bin/
|
bin/
|
||||||
|
|
||||||
.cgcignore
|
.cgcignore
|
||||||
|
|
||||||
|
POSIX/
|
||||||
@@ -4,8 +4,11 @@ package bootstrap
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
@@ -41,7 +44,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
db *database.DB
|
db *database.DB
|
||||||
|
posixRoot string
|
||||||
}
|
}
|
||||||
|
|
||||||
type SaveInstanceInput struct {
|
type SaveInstanceInput struct {
|
||||||
@@ -172,8 +176,8 @@ type namedRecord struct {
|
|||||||
Slug string `json:"slug"`
|
Slug string `json:"slug"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(db *database.DB) *Service {
|
func NewService(db *database.DB, posixRoot string) *Service {
|
||||||
return &Service{db: db}
|
return &Service{db: db, posixRoot: strings.TrimSpace(posixRoot)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (service *Service) SaveInstance(ctx context.Context, input SaveInstanceInput) (InstallationRecord, error) {
|
func (service *Service) SaveInstance(ctx context.Context, input SaveInstanceInput) (InstallationRecord, error) {
|
||||||
@@ -405,6 +409,10 @@ func (service *Service) SaveStructure(ctx context.Context, input SaveStructureIn
|
|||||||
return StructureRecord{}, err
|
return StructureRecord{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := service.ensureBootstrapPOSIXSkeleton(installation, admin, organization, department, team, project); err != nil {
|
||||||
|
return StructureRecord{}, err
|
||||||
|
}
|
||||||
|
|
||||||
return StructureRecord{
|
return StructureRecord{
|
||||||
Installation: installation,
|
Installation: installation,
|
||||||
Organization: organization,
|
Organization: organization,
|
||||||
@@ -901,3 +909,173 @@ func personalHomeTitle(displayName string) string {
|
|||||||
|
|
||||||
return fmt.Sprintf("%s's Home", trimmedDisplayName)
|
return fmt.Sprintf("%s's Home", trimmedDisplayName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (service *Service) ensureBootstrapPOSIXSkeleton(
|
||||||
|
installation InstallationRecord,
|
||||||
|
admin AdminSummary,
|
||||||
|
organization namedRecord,
|
||||||
|
department namedRecord,
|
||||||
|
team namedRecord,
|
||||||
|
project namedRecord,
|
||||||
|
) error {
|
||||||
|
rootPath := strings.TrimSpace(service.posixRoot)
|
||||||
|
if rootPath == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(rootPath, 0o755); err != nil {
|
||||||
|
return fmt.Errorf("create POSIX root: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeJSONFile(filepath.Join(rootPath, "settings.json"), map[string]any{
|
||||||
|
"installation": map[string]any{
|
||||||
|
"id": installation.ID,
|
||||||
|
"name": installation.Name,
|
||||||
|
"mode": installation.Mode,
|
||||||
|
"access": installation.Access,
|
||||||
|
"protocol": installation.Protocol,
|
||||||
|
"host": installation.Host,
|
||||||
|
"isBootstrapped": installation.IsBootstrapped,
|
||||||
|
},
|
||||||
|
"organization": map[string]any{
|
||||||
|
"id": organization.ID,
|
||||||
|
"name": organization.Name,
|
||||||
|
"slug": organization.Slug,
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("write tenant settings.json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeJSONFile(filepath.Join(rootPath, "layout.json"), map[string]any{
|
||||||
|
"version": 1,
|
||||||
|
"type": "tenant-layout",
|
||||||
|
"home": map[string]any{
|
||||||
|
"defaultProjectSlug": project.Slug,
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("write tenant layout.json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(filepath.Join(rootPath, "catalog", "packs"), 0o755); err != nil {
|
||||||
|
return fmt.Errorf("create catalog packs root: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(filepath.Join(rootPath, "catalog", "standalone"), 0o755); err != nil {
|
||||||
|
return fmt.Errorf("create catalog standalone root: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
departmentPath := filepath.Join(rootPath, "departments", slugDir("department", department.Slug))
|
||||||
|
teamPath := filepath.Join(departmentPath, "teams", slugDir("team", team.Slug))
|
||||||
|
projectPath := filepath.Join(rootPath, "projects", slugDir("project", project.Slug))
|
||||||
|
usersPath := filepath.Join(rootPath, "users")
|
||||||
|
|
||||||
|
for _, dirPath := range []string{
|
||||||
|
departmentPath,
|
||||||
|
teamPath,
|
||||||
|
projectPath,
|
||||||
|
filepath.Join(projectPath, "tree"),
|
||||||
|
filepath.Join(usersPath, "personals"),
|
||||||
|
} {
|
||||||
|
if err := os.MkdirAll(dirPath, 0o755); err != nil {
|
||||||
|
return fmt.Errorf("create POSIX directory %s: %w", dirPath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeJSONFile(filepath.Join(departmentPath, "settings.json"), map[string]any{
|
||||||
|
"id": department.ID,
|
||||||
|
"name": department.Name,
|
||||||
|
"slug": department.Slug,
|
||||||
|
"type": "department",
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("write department settings.json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeJSONFile(filepath.Join(departmentPath, "users.json"), map[string]any{
|
||||||
|
"owners": []map[string]string{{
|
||||||
|
"id": admin.ID,
|
||||||
|
"email": admin.Email,
|
||||||
|
"displayName": admin.DisplayName,
|
||||||
|
}},
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("write department users.json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeJSONFile(filepath.Join(teamPath, "settings.json"), map[string]any{
|
||||||
|
"id": team.ID,
|
||||||
|
"name": team.Name,
|
||||||
|
"slug": team.Slug,
|
||||||
|
"type": "team",
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("write team settings.json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeJSONFile(filepath.Join(teamPath, "users.json"), map[string]any{
|
||||||
|
"owners": []map[string]string{{
|
||||||
|
"id": admin.ID,
|
||||||
|
"email": admin.Email,
|
||||||
|
"displayName": admin.DisplayName,
|
||||||
|
}},
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("write team users.json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeJSONFile(filepath.Join(projectPath, "settings.json"), map[string]any{
|
||||||
|
"id": project.ID,
|
||||||
|
"name": project.Name,
|
||||||
|
"slug": project.Slug,
|
||||||
|
"type": "project",
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("write project settings.json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeJSONFile(filepath.Join(projectPath, "home.json"), map[string]any{
|
||||||
|
"type": "project-home",
|
||||||
|
"project": project.Slug,
|
||||||
|
"widgets": []any{},
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("write project home.json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeJSONFile(filepath.Join(usersPath, "settings.json"), map[string]any{
|
||||||
|
"primaryAdminId": admin.ID,
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("write users settings.json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeJSONFile(filepath.Join(usersPath, "data.json"), map[string]any{
|
||||||
|
"users": []map[string]string{{
|
||||||
|
"id": admin.ID,
|
||||||
|
"email": admin.Email,
|
||||||
|
"displayName": admin.DisplayName,
|
||||||
|
}},
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("write users data.json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func slugDir(prefix, slug string) string {
|
||||||
|
trimmedSlug := strings.TrimSpace(slug)
|
||||||
|
if trimmedSlug == "" {
|
||||||
|
return prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s-%s", prefix, trimmedSlug)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeJSONFile(path string, payload any) error {
|
||||||
|
parentDir := filepath.Dir(path)
|
||||||
|
if err := os.MkdirAll(parentDir, 0o755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.MarshalIndent(payload, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data = append(data, '\n')
|
||||||
|
|
||||||
|
return os.WriteFile(path, data, 0o644)
|
||||||
|
}
|
||||||
|
|||||||
113
Backend/internal/bootstrap/service_test.go
Normal file
113
Backend/internal/bootstrap/service_test.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package bootstrap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEnsureBootstrapPOSIXSkeletonInitializesEmptyRoot(t *testing.T) {
|
||||||
|
rootPath := filepath.Join(t.TempDir(), "POSIX")
|
||||||
|
t.Setenv("POSIX_ROOT", rootPath)
|
||||||
|
|
||||||
|
if _, err := os.Stat(rootPath); !os.IsNotExist(err) {
|
||||||
|
t.Fatalf("expected isolated POSIX root to start absent, got err=%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
service := NewService(nil, os.Getenv("POSIX_ROOT"))
|
||||||
|
|
||||||
|
err := service.ensureBootstrapPOSIXSkeleton(
|
||||||
|
InstallationRecord{
|
||||||
|
ID: "installation-1",
|
||||||
|
Name: "MangoPig",
|
||||||
|
Mode: "personal",
|
||||||
|
Access: "local",
|
||||||
|
Protocol: "http",
|
||||||
|
Host: "localhost",
|
||||||
|
IsBootstrapped: true,
|
||||||
|
},
|
||||||
|
AdminSummary{
|
||||||
|
ID: "admin-1",
|
||||||
|
Email: "ronald@example.com",
|
||||||
|
DisplayName: "Ronald",
|
||||||
|
},
|
||||||
|
namedRecord{ID: "org-1", Name: "Primary Organization", Slug: "primary-organization"},
|
||||||
|
namedRecord{ID: "dept-1", Name: "Primary Department", Slug: "primary-department"},
|
||||||
|
namedRecord{ID: "team-1", Name: "Primary Team", Slug: "primary-team"},
|
||||||
|
namedRecord{ID: "project-1", Name: "Primary Project", Slug: "primary-project"},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ensure bootstrap POSIX skeleton: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
requiredPaths := []string{
|
||||||
|
filepath.Join(rootPath, "settings.json"),
|
||||||
|
filepath.Join(rootPath, "layout.json"),
|
||||||
|
filepath.Join(rootPath, "catalog", "packs"),
|
||||||
|
filepath.Join(rootPath, "catalog", "standalone"),
|
||||||
|
filepath.Join(rootPath, "departments", "department-primary-department", "settings.json"),
|
||||||
|
filepath.Join(rootPath, "departments", "department-primary-department", "users.json"),
|
||||||
|
filepath.Join(rootPath, "departments", "department-primary-department", "teams", "team-primary-team", "settings.json"),
|
||||||
|
filepath.Join(rootPath, "departments", "department-primary-department", "teams", "team-primary-team", "users.json"),
|
||||||
|
filepath.Join(rootPath, "projects", "project-primary-project", "settings.json"),
|
||||||
|
filepath.Join(rootPath, "projects", "project-primary-project", "home.json"),
|
||||||
|
filepath.Join(rootPath, "projects", "project-primary-project", "tree"),
|
||||||
|
filepath.Join(rootPath, "users", "settings.json"),
|
||||||
|
filepath.Join(rootPath, "users", "data.json"),
|
||||||
|
filepath.Join(rootPath, "users", "personals"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, path := range requiredPaths {
|
||||||
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
t.Fatalf("expected path to exist %s: %v", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsPayload := readJSONFileForTest[map[string]any](t, filepath.Join(rootPath, "settings.json"))
|
||||||
|
installationPayload, ok := settingsPayload["installation"].(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("settings.json missing installation object: %#v", settingsPayload)
|
||||||
|
}
|
||||||
|
if installationPayload["name"] != "MangoPig" {
|
||||||
|
t.Fatalf("expected installation name MangoPig, got %#v", installationPayload["name"])
|
||||||
|
}
|
||||||
|
if installationPayload["isBootstrapped"] != true {
|
||||||
|
t.Fatalf("expected installation to be bootstrapped, got %#v", installationPayload["isBootstrapped"])
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutPayload := readJSONFileForTest[map[string]any](t, filepath.Join(rootPath, "layout.json"))
|
||||||
|
homePayload, ok := layoutPayload["home"].(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("layout.json missing home object: %#v", layoutPayload)
|
||||||
|
}
|
||||||
|
if homePayload["defaultProjectSlug"] != "primary-project" {
|
||||||
|
t.Fatalf("expected default project slug primary-project, got %#v", homePayload["defaultProjectSlug"])
|
||||||
|
}
|
||||||
|
|
||||||
|
projectSettings := readJSONFileForTest[map[string]any](t, filepath.Join(rootPath, "projects", "project-primary-project", "settings.json"))
|
||||||
|
if projectSettings["type"] != "project" {
|
||||||
|
t.Fatalf("expected project settings type project, got %#v", projectSettings["type"])
|
||||||
|
}
|
||||||
|
|
||||||
|
usersSettings := readJSONFileForTest[map[string]any](t, filepath.Join(rootPath, "users", "settings.json"))
|
||||||
|
if usersSettings["primaryAdminId"] != "admin-1" {
|
||||||
|
t.Fatalf("expected primary admin id admin-1, got %#v", usersSettings["primaryAdminId"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readJSONFileForTest[T any](t *testing.T, path string) T {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("read %s: %v", path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload T
|
||||||
|
if err := json.Unmarshal(data, &payload); err != nil {
|
||||||
|
t.Fatalf("unmarshal %s: %v", path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ type Config struct {
|
|||||||
APIPort string
|
APIPort string
|
||||||
PostgresURL string
|
PostgresURL string
|
||||||
ValkeyURL string
|
ValkeyURL string
|
||||||
|
POSIXRoot string
|
||||||
ShutdownTimeout time.Duration
|
ShutdownTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ func Load() *Config {
|
|||||||
APIPort: getEnv("BACKEND_API_PORT", "8081"),
|
APIPort: getEnv("BACKEND_API_PORT", "8081"),
|
||||||
PostgresURL: getEnv("DATABASE_URL", "postgres://moku:moku_dev_password@localhost:5432/moku?sslmode=disable"),
|
PostgresURL: getEnv("DATABASE_URL", "postgres://moku:moku_dev_password@localhost:5432/moku?sslmode=disable"),
|
||||||
ValkeyURL: getEnv("VALKEY_URL", "redis://localhost:6379/0"),
|
ValkeyURL: getEnv("VALKEY_URL", "redis://localhost:6379/0"),
|
||||||
|
POSIXRoot: getEnv("POSIX_ROOT", "../POSIX"),
|
||||||
ShutdownTimeout: getDurationEnv("BACKEND_SHUTDOWN_TIMEOUT", 10*time.Second),
|
ShutdownTimeout: getDurationEnv("BACKEND_SHUTDOWN_TIMEOUT", 10*time.Second),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -341,7 +341,7 @@ func (routes apiRoutes) handleBootstrapStructureStep(w http.ResponseWriter, r *h
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (routes apiRoutes) bootstrapService() *bootstrapservice.Service {
|
func (routes apiRoutes) bootstrapService() *bootstrapservice.Service {
|
||||||
return bootstrapservice.NewService(routes.cfg.Database)
|
return bootstrapservice.NewService(routes.cfg.Database, routes.cfg.Config.POSIXRoot)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (routes apiRoutes) writeBootstrapStepResponse(w http.ResponseWriter, status int, step string, payload any) {
|
func (routes apiRoutes) writeBootstrapStepResponse(w http.ResponseWriter, status int, step string, payload any) {
|
||||||
|
|||||||
@@ -9,10 +9,14 @@ migrate-up:
|
|||||||
migrate-down:
|
migrate-down:
|
||||||
cd '{{backend_dir}}' && go run ./cmd/migrate down
|
cd '{{backend_dir}}' && go run ./cmd/migrate down
|
||||||
|
|
||||||
# Reset all embedded database migrations and reapply from scratch.
|
# Reset all embedded database migrations.
|
||||||
migrate-reset:
|
migrate-reset:
|
||||||
cd '{{backend_dir}}' && go run ./cmd/migrate reset
|
cd '{{backend_dir}}' && go run ./cmd/migrate reset
|
||||||
|
|
||||||
|
# Reset embedded database migrations and apply them again from scratch.
|
||||||
|
migrate-rebuild:
|
||||||
|
cd '{{backend_dir}}' && go run ./cmd/migrate reset && go run ./cmd/migrate up
|
||||||
|
|
||||||
# Show the embedded database migration status.
|
# Show the embedded database migration status.
|
||||||
migrate-status:
|
migrate-status:
|
||||||
cd '{{backend_dir}}' && go run ./cmd/migrate status
|
cd '{{backend_dir}}' && go run ./cmd/migrate status
|
||||||
@@ -20,7 +24,3 @@ migrate-status:
|
|||||||
# Format backend Go source files.
|
# Format backend Go source files.
|
||||||
fmt:
|
fmt:
|
||||||
cd '{{backend_dir}}' && gofmt -w ./cmd ./db ./internal
|
cd '{{backend_dir}}' && gofmt -w ./cmd ./db ./internal
|
||||||
|
|
||||||
# Run backend test suite.
|
|
||||||
test:
|
|
||||||
cd '{{backend_dir}}' && go test ./...
|
|
||||||
|
|||||||
11
Commands/Test/backend.just
Normal file
11
Commands/Test/backend.just
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
project_root := justfile_directory()
|
||||||
|
backend_dir := project_root + "/Backend"
|
||||||
|
|
||||||
|
# Run the full backend test suite.
|
||||||
|
[default]
|
||||||
|
all:
|
||||||
|
cd '{{backend_dir}}' && go test ./...
|
||||||
|
|
||||||
|
# Run the isolated POSIX bootstrap smoke test.
|
||||||
|
posix-bootstrap:
|
||||||
|
cd '{{backend_dir}}' && go test ./internal/bootstrap -run TestEnsureBootstrapPOSIXSkeletonInitializesEmptyRoot -count=1 -v
|
||||||
1
Commands/Test/mod.just
Normal file
1
Commands/Test/mod.just
Normal file
@@ -0,0 +1 @@
|
|||||||
|
mod backend
|
||||||
@@ -6,6 +6,7 @@ x-backend-service: &backend-service
|
|||||||
environment:
|
environment:
|
||||||
DATABASE_URL: postgres://moku:moku_dev_password@postgres:5432/moku?sslmode=disable
|
DATABASE_URL: postgres://moku:moku_dev_password@postgres:5432/moku?sslmode=disable
|
||||||
VALKEY_URL: redis://valkey:6379/0
|
VALKEY_URL: redis://valkey:6379/0
|
||||||
|
POSIX_ROOT: /posix
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@@ -13,6 +14,7 @@ x-backend-service: &backend-service
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
volumes:
|
volumes:
|
||||||
- ../Backend:/app
|
- ../Backend:/app
|
||||||
|
- ../POSIX:/posix
|
||||||
- moku_work_backend_go_pkg:/go/pkg/mod
|
- moku_work_backend_go_pkg:/go/pkg/mod
|
||||||
- moku_work_backend_go_build:/root/.cache/go-build
|
- moku_work_backend_go_build:/root/.cache/go-build
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,14 @@ x-backend-service: &backend-service
|
|||||||
environment:
|
environment:
|
||||||
DATABASE_URL: postgres://moku:moku_dev_password@postgres:5432/moku?sslmode=disable
|
DATABASE_URL: postgres://moku:moku_dev_password@postgres:5432/moku?sslmode=disable
|
||||||
VALKEY_URL: redis://valkey:6379/0
|
VALKEY_URL: redis://valkey:6379/0
|
||||||
|
POSIX_ROOT: /posix
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
valkey:
|
valkey:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
volumes:
|
||||||
|
- ../POSIX:/posix
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
|
|||||||
61
Documentation/POSIX-Structure.md
Normal file
61
Documentation/POSIX-Structure.md
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# POSIX Structure
|
||||||
|
|
||||||
|
``` markdown
|
||||||
|
Personal or Organization (server)/
|
||||||
|
├── settings.json
|
||||||
|
├── layout.json
|
||||||
|
├── catalog/
|
||||||
|
│ ├── packs/
|
||||||
|
│ │ └── pack-<slug>/
|
||||||
|
│ │ ├── manifest.json
|
||||||
|
│ │ └── entries/
|
||||||
|
│ │ └── app-<slug>/
|
||||||
|
│ │ └── manifest.json
|
||||||
|
│ └── standalone/
|
||||||
|
│ └── app-<slug>/
|
||||||
|
│ └── manifest.json
|
||||||
|
├── departments/
|
||||||
|
│ └── department-<slug>/
|
||||||
|
│ ├── settings.json
|
||||||
|
│ ├── users.json
|
||||||
|
│ └── teams/
|
||||||
|
│ └── team-<slug>/
|
||||||
|
│ ├── settings.json
|
||||||
|
│ └── users.json
|
||||||
|
├── projects/
|
||||||
|
│ └── project-<slug>/
|
||||||
|
│ ├── settings.json
|
||||||
|
│ ├── home.json
|
||||||
|
│ └── tree/
|
||||||
|
│ ├── item-<slug>/
|
||||||
|
│ │ ├── item.json
|
||||||
|
│ │ ├── schema.json
|
||||||
|
│ │ └── data.json
|
||||||
|
│ └── folder-<slug>/
|
||||||
|
│ ├── folder.json
|
||||||
|
│ └── item-<slug>/
|
||||||
|
│ ├── item.json
|
||||||
|
│ ├── schema.json
|
||||||
|
│ └── data.json
|
||||||
|
└── users/
|
||||||
|
├── settings.json
|
||||||
|
├── data.json
|
||||||
|
└── personals/
|
||||||
|
└── personal-<slug>/
|
||||||
|
├── layout.json
|
||||||
|
├── settings.json
|
||||||
|
├── home.json
|
||||||
|
└── tree/
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Responsibilities
|
||||||
|
|
||||||
|
- `settings.json` — Metadata and presentation config for the thing, such as display name, icon, description, and simple settings.
|
||||||
|
- `layout.json` — Layout configuration for the current server or personal space.
|
||||||
|
- `home.json` — Home surface configuration, such as widgets, sections, and how they are arranged.
|
||||||
|
- `folder.json` — Metadata for a folder node in a tree.
|
||||||
|
- `item.json` — Instance metadata for a created item, including what it is and how it should behave.
|
||||||
|
- `schema.json` — The structure expected by that item's data.
|
||||||
|
- `data.json` — The actual content or state data for that item.
|
||||||
|
- `manifest.json` — Catalog definition metadata, including versioning, description, and capabilities for reusable apps or entries.
|
||||||
|
- `users.json` — User membership or assignment data for departments and teams.
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
|
|
||||||
### Version 0.4.0
|
### Version 0.4.0
|
||||||
|
|
||||||
**Goal:** Introduce the POSIX-based file system drive direction with OnlyOffice + S3 blob storage
|
**Goal:** Introduce the POSIX-based file system drive direction with OnlyOffice + S3 blob storage + Per File Versioning
|
||||||
|
|
||||||
### Version 0.5.0
|
### Version 0.5.0
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ BACKEND_SHUTDOWN_TIMEOUT=10s
|
|||||||
|
|
||||||
DATABASE_URL=postgres://moku:moku_dev_password@localhost:5432/moku?sslmode=disable
|
DATABASE_URL=postgres://moku:moku_dev_password@localhost:5432/moku?sslmode=disable
|
||||||
VALKEY_URL=redis://localhost:6379/0
|
VALKEY_URL=redis://localhost:6379/0
|
||||||
|
POSIX_ROOT=../POSIX
|
||||||
|
|
||||||
VITE_API_BASE_URL=/v1
|
VITE_API_BASE_URL=/v1
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user