feat: add basic website

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
2026-03-07 19:46:13 +01:00
commit b439762877
71 changed files with 16576 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
syntax = "proto3";
package forest.v1;
import "google/protobuf/timestamp.proto";
message Organisation {
string organisation_id = 1;
string name = 2;
google.protobuf.Timestamp created_at = 3;
}
message CreateOrganisationRequest {
string name = 1;
}
message CreateOrganisationResponse {
string organisation_id = 1;
}
message GetOrganisationRequest {
oneof identifier {
string organisation_id = 1;
string name = 2;
}
}
message GetOrganisationResponse {
Organisation organisation = 1;
}
message SearchOrganisationsRequest {
string query = 1;
int32 page_size = 2;
string page_token = 3;
}
message SearchOrganisationsResponse {
repeated Organisation organisations = 1;
string next_page_token = 2;
int32 total_count = 3;
}
message ListMyOrganisationsRequest {
// Optional role filter (e.g. "admin"); empty means all roles
string role = 1;
}
message ListMyOrganisationsResponse {
repeated Organisation organisations = 1;
// The role the caller has in each organisation (parallel to organisations)
repeated string roles = 2;
}
// -- Members ------------------------------------------------------------------
message OrganisationMember {
string user_id = 1;
string username = 2;
string role = 3;
google.protobuf.Timestamp joined_at = 4;
}
message AddMemberRequest {
string organisation_id = 1;
string user_id = 2;
string role = 3;
}
message AddMemberResponse {
OrganisationMember member = 1;
}
message RemoveMemberRequest {
string organisation_id = 1;
string user_id = 2;
}
message RemoveMemberResponse {}
message UpdateMemberRoleRequest {
string organisation_id = 1;
string user_id = 2;
string role = 3;
}
message UpdateMemberRoleResponse {
OrganisationMember member = 1;
}
message ListMembersRequest {
string organisation_id = 1;
int32 page_size = 2;
string page_token = 3;
}
message ListMembersResponse {
repeated OrganisationMember members = 1;
string next_page_token = 2;
int32 total_count = 3;
}
service OrganisationService {
rpc CreateOrganisation(CreateOrganisationRequest) returns (CreateOrganisationResponse);
rpc GetOrganisation(GetOrganisationRequest) returns (GetOrganisationResponse);
rpc SearchOrganisations(SearchOrganisationsRequest) returns (SearchOrganisationsResponse);
rpc ListMyOrganisations(ListMyOrganisationsRequest) returns (ListMyOrganisationsResponse);
rpc AddMember(AddMemberRequest) returns (AddMemberResponse);
rpc RemoveMember(RemoveMemberRequest) returns (RemoveMemberResponse);
rpc UpdateMemberRole(UpdateMemberRoleRequest) returns (UpdateMemberRoleResponse);
rpc ListMembers(ListMembersRequest) returns (ListMembersResponse);
}

View File

@@ -0,0 +1,151 @@
syntax = "proto3";
package forest.v1;
message AnnotateReleaseRequest {
string artifact_id = 1;
map<string, string> metadata = 2;
Source source = 3;
ArtifactContext context = 4;
Project project = 5;
Ref ref = 6;
}
message AnnotateReleaseResponse {
Artifact artifact = 1;
}
message GetArtifactBySlugRequest {
string slug = 1;
}
message GetArtifactBySlugResponse {
Artifact artifact = 1;
}
message GetArtifactsByProjectRequest {
Project project = 1;
}
message GetArtifactsByProjectResponse {
repeated Artifact artifact = 1;
}
message ReleaseRequest {
string artifact_id = 1;
repeated string destinations = 2;
repeated string environments = 3;
}
message ReleaseResponse {
// List of release intents created (one per destination)
repeated ReleaseIntent intents = 1;
}
message ReleaseIntent {
string release_intent_id = 1;
string destination = 2;
string environment = 3;
}
message WaitReleaseRequest {
string release_intent_id = 1;
}
message WaitReleaseEvent {
oneof event {
ReleaseStatusUpdate status_update = 1;
ReleaseLogLine log_line = 2;
}
}
message ReleaseStatusUpdate {
string destination = 1;
string status = 2;
}
message ReleaseLogLine {
string destination = 1;
string line = 2;
string timestamp = 3;
LogChannel channel = 4;
}
enum LogChannel {
LOG_CHANNEL_UNSPECIFIED = 0;
LOG_CHANNEL_STDOUT = 1;
LOG_CHANNEL_STDERR = 2;
}
message GetOrganisationsRequest {}
message GetOrganisationsResponse {
repeated OrganisationRef organisations = 1;
}
message GetProjectsRequest {
oneof query {
OrganisationRef organisation = 1;
}
}
message GetProjectsResponse {
repeated string projects = 1;
}
service ReleaseService {
rpc AnnotateRelease(AnnotateReleaseRequest) returns (AnnotateReleaseResponse);
rpc Release(ReleaseRequest) returns (ReleaseResponse);
rpc WaitRelease(WaitReleaseRequest) returns (stream WaitReleaseEvent);
rpc GetArtifactBySlug(GetArtifactBySlugRequest) returns (GetArtifactBySlugResponse);
rpc GetArtifactsByProject(GetArtifactsByProjectRequest) returns (GetArtifactsByProjectResponse);
rpc GetOrganisations(GetOrganisationsRequest) returns (GetOrganisationsResponse);
rpc GetProjects(GetProjectsRequest) returns (GetProjectsResponse);
}
message Source {
optional string user = 1;
optional string email = 2;
optional string source_type = 3;
optional string run_url = 4;
}
message ArtifactContext {
string title = 1;
optional string description = 2;
optional string web = 3;
optional string pr = 4;
}
message Artifact {
string id = 1;
string artifact_id = 2;
string slug = 3;
map<string, string> metadata = 4;
Source source = 5;
ArtifactContext context = 6;
Project project = 7;
repeated ArtifactDestination destinations = 8;
string created_at = 9;
}
message ArtifactDestination {
string name = 1;
string environment = 2;
string type_organisation = 3;
string type_name = 4;
uint64 type_version = 5;
}
message Project {
string organisation = 1;
string project = 2;
}
message Ref {
string commit_sha = 1;
optional string branch = 2;
optional string commit_message = 3;
optional string version = 4;
optional string repo_url = 5;
}
message OrganisationRef {
string organisation = 1;
}

View File

@@ -0,0 +1,317 @@
syntax = "proto3";
package forest.v1;
import "google/protobuf/timestamp.proto";
// UsersService handles user management, authentication, and profile operations.
service UsersService {
// Authentication
rpc Register(RegisterRequest) returns (RegisterResponse);
rpc Login(LoginRequest) returns (LoginResponse);
rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse);
rpc Logout(LogoutRequest) returns (LogoutResponse);
// User CRUD
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse);
rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
// Password management
rpc ChangePassword(ChangePasswordRequest) returns (ChangePasswordResponse);
// Email management
rpc AddEmail(AddEmailRequest) returns (AddEmailResponse);
rpc VerifyEmail(VerifyEmailRequest) returns (VerifyEmailResponse);
rpc RemoveEmail(RemoveEmailRequest) returns (RemoveEmailResponse);
// Social / OAuth login
rpc OAuthLogin(OAuthLoginRequest) returns (OAuthLoginResponse);
rpc LinkOAuthProvider(LinkOAuthProviderRequest) returns (LinkOAuthProviderResponse);
rpc UnlinkOAuthProvider(UnlinkOAuthProviderRequest) returns (UnlinkOAuthProviderResponse);
// Personal access tokens
rpc CreatePersonalAccessToken(CreatePersonalAccessTokenRequest) returns (CreatePersonalAccessTokenResponse);
rpc ListPersonalAccessTokens(ListPersonalAccessTokensRequest) returns (ListPersonalAccessTokensResponse);
rpc DeletePersonalAccessToken(DeletePersonalAccessTokenRequest) returns (DeletePersonalAccessTokenResponse);
// Token introspection (requires valid access token)
rpc TokenInfo(TokenInfoRequest) returns (TokenInfoResponse);
// MFA
rpc SetupMfa(SetupMfaRequest) returns (SetupMfaResponse);
rpc VerifyMfa(VerifyMfaRequest) returns (VerifyMfaResponse);
rpc DisableMfa(DisableMfaRequest) returns (DisableMfaResponse);
}
// ─── Core types ──────────────────────────────────────────────────────
message User {
string user_id = 1; // UUID
string username = 2;
repeated UserEmail emails = 3;
repeated OAuthConnection oauth_connections = 4;
bool mfa_enabled = 5;
google.protobuf.Timestamp created_at = 6;
google.protobuf.Timestamp updated_at = 7;
}
message UserEmail {
string email = 1;
bool verified = 2;
}
enum OAuthProvider {
OAUTH_PROVIDER_UNSPECIFIED = 0;
OAUTH_PROVIDER_GITHUB = 1;
OAUTH_PROVIDER_GOOGLE = 2;
OAUTH_PROVIDER_GITLAB = 3;
OAUTH_PROVIDER_MICROSOFT = 4;
}
message OAuthConnection {
OAuthProvider provider = 1;
string provider_user_id = 2;
string provider_email = 3;
google.protobuf.Timestamp linked_at = 4;
}
// ─── Authentication ──────────────────────────────────────────────────
message RegisterRequest {
string username = 1;
string email = 2;
string password = 3;
}
message RegisterResponse {
User user = 1;
AuthTokens tokens = 2;
}
message LoginRequest {
// Login with either username or email
oneof identifier {
string username = 1;
string email = 2;
}
string password = 3;
}
message LoginResponse {
User user = 1;
AuthTokens tokens = 2;
}
message RefreshTokenRequest {
string refresh_token = 1;
}
message RefreshTokenResponse {
AuthTokens tokens = 1;
}
message LogoutRequest {
string refresh_token = 1;
}
message LogoutResponse {}
message AuthTokens {
string access_token = 1;
string refresh_token = 2;
int64 expires_in_seconds = 3;
}
// ─── Token introspection ─────────────────────────────────────────────
message TokenInfoRequest {}
message TokenInfoResponse {
string user_id = 1;
int64 expires_at = 2; // Unix timestamp (seconds)
}
// ─── User CRUD ───────────────────────────────────────────────────────
message GetUserRequest {
oneof identifier {
string user_id = 1;
string username = 2;
string email = 3;
}
}
message GetUserResponse {
User user = 1;
}
message UpdateUserRequest {
string user_id = 1;
optional string username = 2;
}
message UpdateUserResponse {
User user = 1;
}
message DeleteUserRequest {
string user_id = 1;
}
message DeleteUserResponse {}
message ListUsersRequest {
int32 page_size = 1;
string page_token = 2;
optional string search = 3; // search across username, email
}
message ListUsersResponse {
repeated User users = 1;
string next_page_token = 2;
int32 total_count = 3;
}
// ─── Password management ─────────────────────────────────────────────
message ChangePasswordRequest {
string user_id = 1;
string current_password = 2;
string new_password = 3;
}
message ChangePasswordResponse {}
// ─── Email management ────────────────────────────────────────────────
message AddEmailRequest {
string user_id = 1;
string email = 2;
}
message AddEmailResponse {
UserEmail email = 1;
}
message VerifyEmailRequest {
string user_id = 1;
string email = 2;
}
message VerifyEmailResponse {}
message RemoveEmailRequest {
string user_id = 1;
string email = 2;
}
message RemoveEmailResponse {}
// ─── OAuth / Social login ────────────────────────────────────────────
message OAuthLoginRequest {
OAuthProvider provider = 1;
string authorization_code = 2;
string redirect_uri = 3;
}
message OAuthLoginResponse {
User user = 1;
AuthTokens tokens = 2;
bool is_new_user = 3;
}
message LinkOAuthProviderRequest {
string user_id = 1;
OAuthProvider provider = 2;
string authorization_code = 3;
string redirect_uri = 4;
}
message LinkOAuthProviderResponse {
OAuthConnection connection = 1;
}
message UnlinkOAuthProviderRequest {
string user_id = 1;
OAuthProvider provider = 2;
}
message UnlinkOAuthProviderResponse {}
// ─── Personal access tokens ──────────────────────────────────────────
message PersonalAccessToken {
string token_id = 1; // UUID
string name = 2;
repeated string scopes = 3;
google.protobuf.Timestamp expires_at = 4;
google.protobuf.Timestamp last_used = 5;
google.protobuf.Timestamp created_at = 6;
}
message CreatePersonalAccessTokenRequest {
string user_id = 1;
string name = 2;
repeated string scopes = 3;
// Duration in seconds; 0 = no expiry
int64 expires_in_seconds = 4;
}
message CreatePersonalAccessTokenResponse {
PersonalAccessToken token = 1;
// The raw token value, only returned on creation
string raw_token = 2;
}
message ListPersonalAccessTokensRequest {
string user_id = 1;
}
message ListPersonalAccessTokensResponse {
repeated PersonalAccessToken tokens = 1;
}
message DeletePersonalAccessTokenRequest {
string token_id = 1;
}
message DeletePersonalAccessTokenResponse {}
// ─── MFA ─────────────────────────────────────────────────────────────
enum MfaType {
MFA_TYPE_UNSPECIFIED = 0;
MFA_TYPE_TOTP = 1;
}
message SetupMfaRequest {
string user_id = 1;
MfaType mfa_type = 2;
}
message SetupMfaResponse {
string mfa_id = 1; // UUID
// TOTP provisioning URI (otpauth://...)
string provisioning_uri = 2;
// Base32-encoded secret for manual entry
string secret = 3;
}
message VerifyMfaRequest {
string mfa_id = 1;
// The TOTP code to verify setup
string code = 2;
}
message VerifyMfaResponse {}
message DisableMfaRequest {
string user_id = 1;
// Current TOTP code to confirm disable
string code = 2;
}
message DisableMfaResponse {}