feat: Marketing site
Server Deploy / deploy (push) Successful in 5m51s

This commit is contained in:
2026-07-22 16:50:13 +01:00
parent 7a3b8cb700
commit 693d59a3e2
51 changed files with 10656 additions and 12 deletions
+54
View File
@@ -0,0 +1,54 @@
package models
import (
"time"
"go.mongodb.org/mongo-driver/v2/bson"
)
/*
Org and User mirror server/internal/models field for field, because sitesvc
writes into the same collections the control plane reads.
These two structs and the rules in internal/provision are the only places
sitesvc duplicates control-plane logic. If the control plane's shape changes,
these must change with it.
*/
type Org struct {
ID bson.ObjectID `bson:"_id,omitempty"`
OrgID string `bson:"org_id"`
Name string `bson:"name"`
Slug string `bson:"slug"`
CreatedAt time.Time `bson:"created_at"`
}
type User struct {
ID bson.ObjectID `bson:"_id,omitempty"`
UserID string `bson:"user_id"`
OrgID string `bson:"org_id"`
Email string `bson:"email"`
PasswordHash string `bson:"password_hash,omitempty"`
Role string `bson:"role"`
AuthSource string `bson:"auth_source"`
CreatedAt time.Time `bson:"created_at"`
LastLogin *time.Time `bson:"last_login,omitempty"`
}
// PendingSignup is sitesvc's own record, in its own collection. It holds a
// signup between the form being submitted and the email link being clicked.
//
// Nothing is written to orgs or users until verification succeeds, so an
// unverified address can never occupy an email, hold a slug, or sign in. The
// password is bcrypt-hashed here exactly as it would be in users, so the
// plaintext never rests anywhere.
type PendingSignup struct {
ID bson.ObjectID `bson:"_id,omitempty"`
PendingID string `bson:"pending_id"`
OrgName string `bson:"org_name"`
Email string `bson:"email"`
PasswordHash string `bson:"password_hash"`
TokenHash string `bson:"token_hash"`
CreatedAt time.Time `bson:"created_at"`
ExpiresAt time.Time `bson:"expires_at"`
}