Skip to content

Commit 8d02db8

Browse files
authored
add websocket endpoint and autogenerated docs (#523)
1 parent 6bbc0a2 commit 8d02db8

File tree

2 files changed

+76
-47
lines changed

2 files changed

+76
-47
lines changed

apps/server-new/src/domain/models.ts

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const InboxSenderAuthPolicy = Schema.Literal('requires_auth', 'anonymous'
2020
/**
2121
* Database entity schemas (Prisma-based)
2222
*/
23-
export const Account = Schema.Struct({
23+
export class Account extends Schema.Class<Account>('Account')({
2424
address: Schema.String,
2525
connectAddress: Schema.String,
2626
connectCiphertext: Schema.String,
@@ -30,9 +30,9 @@ export const Account = Schema.Struct({
3030
connectAccountProof: Schema.String,
3131
connectKeyProof: Schema.String,
3232
connectSignerAddress: Schema.String,
33-
});
33+
}) {}
3434

35-
export const AppIdentity = Schema.Struct({
35+
export class AppIdentity extends Schema.Class<AppIdentity>('AppIdentity')({
3636
address: Schema.String,
3737
ciphertext: Schema.String,
3838
signaturePublicKey: Schema.String,
@@ -42,34 +42,34 @@ export const AppIdentity = Schema.Struct({
4242
accountAddress: Schema.String,
4343
appId: Schema.String,
4444
sessionToken: Schema.String.pipe(Schema.NullOr),
45-
sessionTokenExpires: Schema.DateFromSelf.pipe(Schema.NullOr),
46-
});
45+
sessionTokenExpires: Schema.Date.pipe(Schema.NullOr),
46+
}) {}
4747

48-
export const Space = Schema.Struct({
48+
export class Space extends Schema.Class<Space>('Space')({
4949
id: Schema.String,
5050
name: Schema.String,
5151
infoContent: Schema.Uint8Array,
5252
infoAuthorAddress: Schema.String,
5353
infoSignatureHex: Schema.String,
5454
infoSignatureRecovery: Schema.Number,
55-
});
55+
}) {}
5656

57-
export const SpaceEvent = Schema.Struct({
57+
export class SpaceEvent extends Schema.Class<SpaceEvent>('SpaceEvent')({
5858
id: Schema.String,
5959
event: Schema.String,
6060
state: Schema.String,
6161
counter: Schema.Number,
6262
spaceId: Schema.String,
6363
createdAt: Schema.DateFromSelf,
64-
});
64+
}) {}
6565

66-
export const SpaceKey = Schema.Struct({
66+
export class SpaceKey extends Schema.Class<SpaceKey>('SpaceKey')({
6767
id: Schema.String,
6868
spaceId: Schema.String,
6969
createdAt: Schema.DateFromSelf,
70-
});
70+
}) {}
7171

72-
export const SpaceKeyBox = Schema.Struct({
72+
export class SpaceKeyBox extends Schema.Class<SpaceKeyBox>('SpaceKeyBox')({
7373
id: Schema.String,
7474
spaceKeyId: Schema.String,
7575
ciphertext: Schema.String,
@@ -78,19 +78,19 @@ export const SpaceKeyBox = Schema.Struct({
7878
accountAddress: Schema.String,
7979
appIdentityAddress: Schema.optional(Schema.String),
8080
createdAt: Schema.DateFromSelf,
81-
});
81+
}) {}
8282

83-
export const Update = Schema.Struct({
83+
export class Update extends Schema.Class<Update>('Update')({
8484
spaceId: Schema.String,
8585
clock: Schema.Number,
8686
content: Schema.Uint8Array,
8787
accountAddress: Schema.String,
8888
signatureHex: Schema.String,
8989
signatureRecovery: Schema.Number,
9090
updateId: Schema.String,
91-
});
91+
}) {}
9292

93-
export const SpaceInbox = Schema.Struct({
93+
export class SpaceInbox extends Schema.Class<SpaceInbox>('SpaceInbox')({
9494
id: Schema.String,
9595
spaceId: Schema.String,
9696
isPublic: Schema.Boolean,
@@ -99,19 +99,19 @@ export const SpaceInbox = Schema.Struct({
9999
encryptedSecretKey: Schema.String,
100100
spaceEventId: Schema.String,
101101
createdAt: Schema.DateFromSelf,
102-
});
102+
}) {}
103103

104-
export const SpaceInboxMessage = Schema.Struct({
104+
export class SpaceInboxMessage extends Schema.Class<SpaceInboxMessage>('SpaceInboxMessage')({
105105
id: Schema.String,
106106
spaceInboxId: Schema.String,
107107
ciphertext: Schema.String,
108108
signatureHex: Schema.optional(Schema.String),
109109
signatureRecovery: Schema.optional(Schema.Number),
110110
authorAccountAddress: Schema.optional(Schema.String),
111111
createdAt: Schema.DateFromSelf,
112-
});
112+
}) {}
113113

114-
export const AccountInbox = Schema.Struct({
114+
export class AccountInbox extends Schema.Class<AccountInbox>('AccountInbox')({
115115
id: Schema.String,
116116
accountAddress: Schema.String,
117117
isPublic: Schema.Boolean,
@@ -120,55 +120,55 @@ export const AccountInbox = Schema.Struct({
120120
signatureHex: Schema.String,
121121
signatureRecovery: Schema.Number,
122122
createdAt: Schema.DateFromSelf,
123-
});
123+
}) {}
124124

125-
export const AccountInboxMessage = Schema.Struct({
125+
export class AccountInboxMessage extends Schema.Class<AccountInboxMessage>('AccountInboxMessage')({
126126
id: Schema.String,
127127
accountInboxId: Schema.String,
128128
ciphertext: Schema.String,
129129
signatureHex: Schema.optional(Schema.String),
130130
signatureRecovery: Schema.optional(Schema.Number),
131131
authorAccountAddress: Schema.optional(Schema.String),
132132
createdAt: Schema.DateFromSelf,
133-
});
133+
}) {}
134134

135-
export const Invitation = Schema.Struct({
135+
export class Invitation extends Schema.Class<Invitation>('Invitation')({
136136
id: Schema.String,
137137
spaceId: Schema.String,
138138
accountAddress: Schema.String,
139139
inviteeAccountAddress: Schema.String,
140140
createdAt: Schema.DateFromSelf,
141-
});
141+
}) {}
142142

143-
export const InvitationTargetApp = Schema.Struct({
143+
export class InvitationTargetApp extends Schema.Class<InvitationTargetApp>('InvitationTargetApp')({
144144
id: Schema.String,
145145
invitationId: Schema.String,
146-
});
146+
}) {}
147147

148148
/**
149149
* API response schemas
150150
*/
151-
export const SpaceInboxPublic = Schema.Struct({
151+
export class SpaceInboxPublic extends Schema.Class<SpaceInboxPublic>('SpaceInboxPublic')({
152152
id: Schema.String,
153153
spaceId: Schema.String,
154154
isPublic: Schema.Boolean,
155155
authPolicy: InboxSenderAuthPolicy,
156156
encryptionPublicKey: Schema.String,
157-
});
157+
}) {}
158158

159-
export const AccountInboxPublic = Schema.Struct({
159+
export class AccountInboxPublic extends Schema.Class<AccountInboxPublic>('AccountInboxPublic')({
160160
id: Schema.String,
161161
accountAddress: Schema.String,
162162
isPublic: Schema.Boolean,
163163
authPolicy: InboxSenderAuthPolicy,
164164
encryptionPublicKey: Schema.String,
165-
});
165+
}) {}
166166

167-
export const PublicIdentity = Schema.Struct({
167+
export class PublicIdentity extends Schema.Class<PublicIdentity>('PublicIdentity')({
168168
accountAddress: Schema.String,
169169
signaturePublicKey: Schema.String,
170170
encryptionPublicKey: Schema.String,
171171
accountProof: Schema.String,
172172
keyProof: Schema.String,
173173
appId: Schema.optional(Schema.String),
174-
});
174+
}) {}

apps/server-new/src/server.ts

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,50 @@
11
import { createServer } from 'node:http';
2-
import { HttpApiBuilder, HttpServer } from '@effect/platform';
3-
import { NodeHttpServer } from '@effect/platform-node';
4-
import { Effect, Layer } from 'effect';
2+
import * as HttpApiScalar from '@effect/platform/HttpApiScalar';
3+
import * as HttpLayerRouter from '@effect/platform/HttpLayerRouter';
4+
import * as HttpMiddleware from '@effect/platform/HttpMiddleware';
5+
import * as HttpServerRequest from '@effect/platform/HttpServerRequest';
6+
import * as HttpServerResponse from '@effect/platform/HttpServerResponse';
7+
import * as NodeHttpServer from '@effect/platform-node/NodeHttpServer';
8+
import * as Effect from 'effect/Effect';
9+
import * as Layer from 'effect/Layer';
10+
import * as Schedule from 'effect/Schedule';
11+
import * as Stream from 'effect/Stream';
512
import { serverPortConfig } from './config/server.ts';
613
import { hypergraphApi } from './http/api.ts';
714
import { HandlersLive } from './http/handlers.ts';
815

9-
const apiLive = HttpApiBuilder.api(hypergraphApi).pipe(Layer.provide(HandlersLive));
16+
// Create scalar openapi browser layer at /docs.
17+
const DocsLayer = HttpApiScalar.layerHttpLayerRouter({
18+
api: hypergraphApi,
19+
path: '/docs',
20+
});
1021

11-
export const server = Layer.unwrapEffect(
12-
Effect.gen(function* () {
13-
const port = yield* serverPortConfig;
14-
return HttpApiBuilder.serve().pipe(
15-
Layer.provide(HttpApiBuilder.middlewareCors()),
16-
Layer.provide(apiLive),
17-
HttpServer.withLogAddress,
18-
Layer.provide(NodeHttpServer.layer(createServer, { port })),
19-
);
20-
}),
22+
// Create api layer with openapi.json documentation generated at /docs/openapi.json.
23+
const ApiLayer = HttpLayerRouter.addHttpApi(hypergraphApi, {
24+
openapiPath: '/docs/openapi.json',
25+
}).pipe(Layer.provide(HandlersLive));
26+
27+
// Create websocket layer at /ws.
28+
const WebSocketLayer = HttpLayerRouter.add(
29+
'GET',
30+
'/ws',
31+
// TODO: Implement actual websocket logic here.
32+
Stream.fromSchedule(Schedule.spaced(1000)).pipe(
33+
Stream.map(JSON.stringify),
34+
Stream.pipeThroughChannel(HttpServerRequest.upgradeChannel()),
35+
Stream.decodeText(),
36+
Stream.runForEach((_) => Effect.log(_)),
37+
Effect.as(HttpServerResponse.empty()),
38+
),
2139
);
40+
41+
// Merge router layers together and add the cors middleware layer.
42+
const CorsMiddleware = HttpLayerRouter.middleware(HttpMiddleware.cors());
43+
const AppLayer = Layer.mergeAll(ApiLayer, DocsLayer, WebSocketLayer).pipe(Layer.provide(CorsMiddleware.layer));
44+
45+
const HttpServerLayer = serverPortConfig.pipe(
46+
Effect.map((port) => NodeHttpServer.layer(createServer, { port })),
47+
Layer.unwrapEffect,
48+
);
49+
50+
export const server = HttpLayerRouter.serve(AppLayer).pipe(Layer.provide(HttpServerLayer));

0 commit comments

Comments
 (0)