diff --git a/package.json b/package.json
index f49df1a..296c5a7 100644
--- a/package.json
+++ b/package.json
@@ -74,7 +74,9 @@
"grpc",
"bg",
"str",
- "signup"
+ "signup",
+ "unary",
+ "invoker"
],
"skipIfMatch": [
"TODO\\(.+\\):",
diff --git a/src/App.tsx b/src/App.tsx
index b167f0c..14d137f 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,20 +1,34 @@
import React from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
-import { Projects, CreateProject, ProjectAPIKeys, Project, Documents, ProjectSettings, Login, Signup } from 'routes';
+import {
+ Login,
+ Signup,
+ PrivateRoute,
+ Projects,
+ CreateProject,
+ ProjectAPIKeys,
+ Project,
+ Documents,
+ ProjectSettings,
+} from 'routes';
function App() {
+ // TODO(hackerwins): If the user is already logged in, redirect to the
+ // projects page.
return (
} />
} />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
+ }>
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
);
diff --git a/src/api/admin.proto b/src/api/admin.proto
index 8c24549..4a98275 100644
--- a/src/api/admin.proto
+++ b/src/api/admin.proto
@@ -21,6 +21,9 @@ import "resources.proto";
// Admin is a service that provides a API for Admin.
service Admin {
+ rpc SignUp(SignUpRequest) returns (SignUpResponse) {}
+ rpc LogIn(LogInRequest) returns (LogInResponse) {}
+
rpc CreateProject(CreateProjectRequest) returns (CreateProjectResponse) {}
rpc ListProjects(ListProjectsRequest) returns (ListProjectsResponse) {}
rpc GetProject(GetProjectRequest) returns (GetProjectResponse) {}
@@ -34,6 +37,24 @@ service Admin {
rpc ListChanges (ListChangesRequest) returns (ListChangesResponse) {}
}
+message SignUpRequest {
+ string username = 1;
+ string password = 2;
+}
+
+message SignUpResponse {
+ User user = 1;
+}
+
+message LogInRequest {
+ string username = 1;
+ string password = 2;
+}
+
+message LogInResponse {
+ string token = 1;
+}
+
message CreateProjectRequest {
string name = 1;
}
diff --git a/src/api/admin_grpc_web_pb.d.ts b/src/api/admin_grpc_web_pb.d.ts
index f46aefb..a26ae56 100644
--- a/src/api/admin_grpc_web_pb.d.ts
+++ b/src/api/admin_grpc_web_pb.d.ts
@@ -8,6 +8,20 @@ export class AdminClient {
credentials?: null | { [index: string]: string; },
options?: null | { [index: string]: any; });
+ signUp(
+ request: admin_pb.SignUpRequest,
+ metadata: grpcWeb.Metadata | undefined,
+ callback: (err: grpcWeb.RpcError,
+ response: admin_pb.SignUpResponse) => void
+ ): grpcWeb.ClientReadableStream;
+
+ logIn(
+ request: admin_pb.LogInRequest,
+ metadata: grpcWeb.Metadata | undefined,
+ callback: (err: grpcWeb.RpcError,
+ response: admin_pb.LogInResponse) => void
+ ): grpcWeb.ClientReadableStream;
+
createProject(
request: admin_pb.CreateProjectRequest,
metadata: grpcWeb.Metadata | undefined,
@@ -78,6 +92,16 @@ export class AdminPromiseClient {
credentials?: null | { [index: string]: string; },
options?: null | { [index: string]: any; });
+ signUp(
+ request: admin_pb.SignUpRequest,
+ metadata?: grpcWeb.Metadata
+ ): Promise;
+
+ logIn(
+ request: admin_pb.LogInRequest,
+ metadata?: grpcWeb.Metadata
+ ): Promise;
+
createProject(
request: admin_pb.CreateProjectRequest,
metadata?: grpcWeb.Metadata
diff --git a/src/api/admin_grpc_web_pb.js b/src/api/admin_grpc_web_pb.js
index 26168b1..373a09a 100644
--- a/src/api/admin_grpc_web_pb.js
+++ b/src/api/admin_grpc_web_pb.js
@@ -72,6 +72,128 @@ proto.api.AdminPromiseClient =
};
+/**
+ * @const
+ * @type {!grpc.web.MethodDescriptor<
+ * !proto.api.SignUpRequest,
+ * !proto.api.SignUpResponse>}
+ */
+const methodDescriptor_Admin_SignUp = new grpc.web.MethodDescriptor(
+ '/api.Admin/SignUp',
+ grpc.web.MethodType.UNARY,
+ proto.api.SignUpRequest,
+ proto.api.SignUpResponse,
+ /**
+ * @param {!proto.api.SignUpRequest} request
+ * @return {!Uint8Array}
+ */
+ function(request) {
+ return request.serializeBinary();
+ },
+ proto.api.SignUpResponse.deserializeBinary
+);
+
+
+/**
+ * @param {!proto.api.SignUpRequest} request The
+ * request proto
+ * @param {?Object} metadata User defined
+ * call metadata
+ * @param {function(?grpc.web.RpcError, ?proto.api.SignUpResponse)}
+ * callback The callback function(error, response)
+ * @return {!grpc.web.ClientReadableStream|undefined}
+ * The XHR Node Readable Stream
+ */
+proto.api.AdminClient.prototype.signUp =
+ function(request, metadata, callback) {
+ return this.client_.rpcCall(this.hostname_ +
+ '/api.Admin/SignUp',
+ request,
+ metadata || {},
+ methodDescriptor_Admin_SignUp,
+ callback);
+};
+
+
+/**
+ * @param {!proto.api.SignUpRequest} request The
+ * request proto
+ * @param {?Object=} metadata User defined
+ * call metadata
+ * @return {!Promise}
+ * Promise that resolves to the response
+ */
+proto.api.AdminPromiseClient.prototype.signUp =
+ function(request, metadata) {
+ return this.client_.unaryCall(this.hostname_ +
+ '/api.Admin/SignUp',
+ request,
+ metadata || {},
+ methodDescriptor_Admin_SignUp);
+};
+
+
+/**
+ * @const
+ * @type {!grpc.web.MethodDescriptor<
+ * !proto.api.LogInRequest,
+ * !proto.api.LogInResponse>}
+ */
+const methodDescriptor_Admin_LogIn = new grpc.web.MethodDescriptor(
+ '/api.Admin/LogIn',
+ grpc.web.MethodType.UNARY,
+ proto.api.LogInRequest,
+ proto.api.LogInResponse,
+ /**
+ * @param {!proto.api.LogInRequest} request
+ * @return {!Uint8Array}
+ */
+ function(request) {
+ return request.serializeBinary();
+ },
+ proto.api.LogInResponse.deserializeBinary
+);
+
+
+/**
+ * @param {!proto.api.LogInRequest} request The
+ * request proto
+ * @param {?Object} metadata User defined
+ * call metadata
+ * @param {function(?grpc.web.RpcError, ?proto.api.LogInResponse)}
+ * callback The callback function(error, response)
+ * @return {!grpc.web.ClientReadableStream|undefined}
+ * The XHR Node Readable Stream
+ */
+proto.api.AdminClient.prototype.logIn =
+ function(request, metadata, callback) {
+ return this.client_.rpcCall(this.hostname_ +
+ '/api.Admin/LogIn',
+ request,
+ metadata || {},
+ methodDescriptor_Admin_LogIn,
+ callback);
+};
+
+
+/**
+ * @param {!proto.api.LogInRequest} request The
+ * request proto
+ * @param {?Object=} metadata User defined
+ * call metadata
+ * @return {!Promise}
+ * Promise that resolves to the response
+ */
+proto.api.AdminPromiseClient.prototype.logIn =
+ function(request, metadata) {
+ return this.client_.unaryCall(this.hostname_ +
+ '/api.Admin/LogIn',
+ request,
+ metadata || {},
+ methodDescriptor_Admin_LogIn);
+};
+
+
/**
* @const
* @type {!grpc.web.MethodDescriptor<
diff --git a/src/api/admin_pb.d.ts b/src/api/admin_pb.d.ts
index 7a823a1..9fbcba6 100644
--- a/src/api/admin_pb.d.ts
+++ b/src/api/admin_pb.d.ts
@@ -3,6 +3,88 @@ import * as jspb from 'google-protobuf'
import * as resources_pb from './resources_pb';
+export class SignUpRequest extends jspb.Message {
+ getUsername(): string;
+ setUsername(value: string): SignUpRequest;
+
+ getPassword(): string;
+ setPassword(value: string): SignUpRequest;
+
+ serializeBinary(): Uint8Array;
+ toObject(includeInstance?: boolean): SignUpRequest.AsObject;
+ static toObject(includeInstance: boolean, msg: SignUpRequest): SignUpRequest.AsObject;
+ static serializeBinaryToWriter(message: SignUpRequest, writer: jspb.BinaryWriter): void;
+ static deserializeBinary(bytes: Uint8Array): SignUpRequest;
+ static deserializeBinaryFromReader(message: SignUpRequest, reader: jspb.BinaryReader): SignUpRequest;
+}
+
+export namespace SignUpRequest {
+ export type AsObject = {
+ username: string,
+ password: string,
+ }
+}
+
+export class SignUpResponse extends jspb.Message {
+ getUser(): resources_pb.User | undefined;
+ setUser(value?: resources_pb.User): SignUpResponse;
+ hasUser(): boolean;
+ clearUser(): SignUpResponse;
+
+ serializeBinary(): Uint8Array;
+ toObject(includeInstance?: boolean): SignUpResponse.AsObject;
+ static toObject(includeInstance: boolean, msg: SignUpResponse): SignUpResponse.AsObject;
+ static serializeBinaryToWriter(message: SignUpResponse, writer: jspb.BinaryWriter): void;
+ static deserializeBinary(bytes: Uint8Array): SignUpResponse;
+ static deserializeBinaryFromReader(message: SignUpResponse, reader: jspb.BinaryReader): SignUpResponse;
+}
+
+export namespace SignUpResponse {
+ export type AsObject = {
+ user?: resources_pb.User.AsObject,
+ }
+}
+
+export class LogInRequest extends jspb.Message {
+ getUsername(): string;
+ setUsername(value: string): LogInRequest;
+
+ getPassword(): string;
+ setPassword(value: string): LogInRequest;
+
+ serializeBinary(): Uint8Array;
+ toObject(includeInstance?: boolean): LogInRequest.AsObject;
+ static toObject(includeInstance: boolean, msg: LogInRequest): LogInRequest.AsObject;
+ static serializeBinaryToWriter(message: LogInRequest, writer: jspb.BinaryWriter): void;
+ static deserializeBinary(bytes: Uint8Array): LogInRequest;
+ static deserializeBinaryFromReader(message: LogInRequest, reader: jspb.BinaryReader): LogInRequest;
+}
+
+export namespace LogInRequest {
+ export type AsObject = {
+ username: string,
+ password: string,
+ }
+}
+
+export class LogInResponse extends jspb.Message {
+ getToken(): string;
+ setToken(value: string): LogInResponse;
+
+ serializeBinary(): Uint8Array;
+ toObject(includeInstance?: boolean): LogInResponse.AsObject;
+ static toObject(includeInstance: boolean, msg: LogInResponse): LogInResponse.AsObject;
+ static serializeBinaryToWriter(message: LogInResponse, writer: jspb.BinaryWriter): void;
+ static deserializeBinary(bytes: Uint8Array): LogInResponse;
+ static deserializeBinaryFromReader(message: LogInResponse, reader: jspb.BinaryReader): LogInResponse;
+}
+
+export namespace LogInResponse {
+ export type AsObject = {
+ token: string,
+ }
+}
+
export class CreateProjectRequest extends jspb.Message {
getName(): string;
setName(value: string): CreateProjectRequest;
diff --git a/src/api/admin_pb.js b/src/api/admin_pb.js
index 65a7cfc..fe9180e 100644
--- a/src/api/admin_pb.js
+++ b/src/api/admin_pb.js
@@ -37,10 +37,98 @@ goog.exportSymbol('proto.api.ListDocumentsRequest', null, global);
goog.exportSymbol('proto.api.ListDocumentsResponse', null, global);
goog.exportSymbol('proto.api.ListProjectsRequest', null, global);
goog.exportSymbol('proto.api.ListProjectsResponse', null, global);
+goog.exportSymbol('proto.api.LogInRequest', null, global);
+goog.exportSymbol('proto.api.LogInResponse', null, global);
goog.exportSymbol('proto.api.SearchDocumentsRequest', null, global);
goog.exportSymbol('proto.api.SearchDocumentsResponse', null, global);
+goog.exportSymbol('proto.api.SignUpRequest', null, global);
+goog.exportSymbol('proto.api.SignUpResponse', null, global);
goog.exportSymbol('proto.api.UpdateProjectRequest', null, global);
goog.exportSymbol('proto.api.UpdateProjectResponse', null, global);
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.api.SignUpRequest = function(opt_data) {
+ jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.api.SignUpRequest, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+ /**
+ * @public
+ * @override
+ */
+ proto.api.SignUpRequest.displayName = 'proto.api.SignUpRequest';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.api.SignUpResponse = function(opt_data) {
+ jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.api.SignUpResponse, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+ /**
+ * @public
+ * @override
+ */
+ proto.api.SignUpResponse.displayName = 'proto.api.SignUpResponse';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.api.LogInRequest = function(opt_data) {
+ jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.api.LogInRequest, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+ /**
+ * @public
+ * @override
+ */
+ proto.api.LogInRequest.displayName = 'proto.api.LogInRequest';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.api.LogInResponse = function(opt_data) {
+ jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.api.LogInResponse, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+ /**
+ * @public
+ * @override
+ */
+ proto.api.LogInResponse.displayName = 'proto.api.LogInResponse';
+}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
@@ -422,6 +510,607 @@ if (goog.DEBUG && !COMPILED) {
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ * net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ * JSPB instance for transitional soy proto support:
+ * http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.api.SignUpRequest.prototype.toObject = function(opt_includeInstance) {
+ return proto.api.SignUpRequest.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ * the JSPB instance for transitional soy proto support:
+ * http://goto/soy-param-migration
+ * @param {!proto.api.SignUpRequest} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.api.SignUpRequest.toObject = function(includeInstance, msg) {
+ var f, obj = {
+ username: jspb.Message.getFieldWithDefault(msg, 1, ""),
+ password: jspb.Message.getFieldWithDefault(msg, 2, "")
+ };
+
+ if (includeInstance) {
+ obj.$jspbMessageInstance = msg;
+ }
+ return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.api.SignUpRequest}
+ */
+proto.api.SignUpRequest.deserializeBinary = function(bytes) {
+ var reader = new jspb.BinaryReader(bytes);
+ var msg = new proto.api.SignUpRequest;
+ return proto.api.SignUpRequest.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.api.SignUpRequest} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.api.SignUpRequest}
+ */
+proto.api.SignUpRequest.deserializeBinaryFromReader = function(msg, reader) {
+ while (reader.nextField()) {
+ if (reader.isEndGroup()) {
+ break;
+ }
+ var field = reader.getFieldNumber();
+ switch (field) {
+ case 1:
+ var value = /** @type {string} */ (reader.readString());
+ msg.setUsername(value);
+ break;
+ case 2:
+ var value = /** @type {string} */ (reader.readString());
+ msg.setPassword(value);
+ break;
+ default:
+ reader.skipField();
+ break;
+ }
+ }
+ return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.api.SignUpRequest.prototype.serializeBinary = function() {
+ var writer = new jspb.BinaryWriter();
+ proto.api.SignUpRequest.serializeBinaryToWriter(this, writer);
+ return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.api.SignUpRequest} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.api.SignUpRequest.serializeBinaryToWriter = function(message, writer) {
+ var f = undefined;
+ f = message.getUsername();
+ if (f.length > 0) {
+ writer.writeString(
+ 1,
+ f
+ );
+ }
+ f = message.getPassword();
+ if (f.length > 0) {
+ writer.writeString(
+ 2,
+ f
+ );
+ }
+};
+
+
+/**
+ * optional string username = 1;
+ * @return {string}
+ */
+proto.api.SignUpRequest.prototype.getUsername = function() {
+ return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.api.SignUpRequest} returns this
+ */
+proto.api.SignUpRequest.prototype.setUsername = function(value) {
+ return jspb.Message.setProto3StringField(this, 1, value);
+};
+
+
+/**
+ * optional string password = 2;
+ * @return {string}
+ */
+proto.api.SignUpRequest.prototype.getPassword = function() {
+ return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.api.SignUpRequest} returns this
+ */
+proto.api.SignUpRequest.prototype.setPassword = function(value) {
+ return jspb.Message.setProto3StringField(this, 2, value);
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ * net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ * JSPB instance for transitional soy proto support:
+ * http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.api.SignUpResponse.prototype.toObject = function(opt_includeInstance) {
+ return proto.api.SignUpResponse.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ * the JSPB instance for transitional soy proto support:
+ * http://goto/soy-param-migration
+ * @param {!proto.api.SignUpResponse} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.api.SignUpResponse.toObject = function(includeInstance, msg) {
+ var f, obj = {
+ user: (f = msg.getUser()) && resources_pb.User.toObject(includeInstance, f)
+ };
+
+ if (includeInstance) {
+ obj.$jspbMessageInstance = msg;
+ }
+ return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.api.SignUpResponse}
+ */
+proto.api.SignUpResponse.deserializeBinary = function(bytes) {
+ var reader = new jspb.BinaryReader(bytes);
+ var msg = new proto.api.SignUpResponse;
+ return proto.api.SignUpResponse.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.api.SignUpResponse} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.api.SignUpResponse}
+ */
+proto.api.SignUpResponse.deserializeBinaryFromReader = function(msg, reader) {
+ while (reader.nextField()) {
+ if (reader.isEndGroup()) {
+ break;
+ }
+ var field = reader.getFieldNumber();
+ switch (field) {
+ case 1:
+ var value = new resources_pb.User;
+ reader.readMessage(value,resources_pb.User.deserializeBinaryFromReader);
+ msg.setUser(value);
+ break;
+ default:
+ reader.skipField();
+ break;
+ }
+ }
+ return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.api.SignUpResponse.prototype.serializeBinary = function() {
+ var writer = new jspb.BinaryWriter();
+ proto.api.SignUpResponse.serializeBinaryToWriter(this, writer);
+ return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.api.SignUpResponse} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.api.SignUpResponse.serializeBinaryToWriter = function(message, writer) {
+ var f = undefined;
+ f = message.getUser();
+ if (f != null) {
+ writer.writeMessage(
+ 1,
+ f,
+ resources_pb.User.serializeBinaryToWriter
+ );
+ }
+};
+
+
+/**
+ * optional User user = 1;
+ * @return {?proto.api.User}
+ */
+proto.api.SignUpResponse.prototype.getUser = function() {
+ return /** @type{?proto.api.User} */ (
+ jspb.Message.getWrapperField(this, resources_pb.User, 1));
+};
+
+
+/**
+ * @param {?proto.api.User|undefined} value
+ * @return {!proto.api.SignUpResponse} returns this
+*/
+proto.api.SignUpResponse.prototype.setUser = function(value) {
+ return jspb.Message.setWrapperField(this, 1, value);
+};
+
+
+/**
+ * Clears the message field making it undefined.
+ * @return {!proto.api.SignUpResponse} returns this
+ */
+proto.api.SignUpResponse.prototype.clearUser = function() {
+ return this.setUser(undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.api.SignUpResponse.prototype.hasUser = function() {
+ return jspb.Message.getField(this, 1) != null;
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ * net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ * JSPB instance for transitional soy proto support:
+ * http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.api.LogInRequest.prototype.toObject = function(opt_includeInstance) {
+ return proto.api.LogInRequest.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ * the JSPB instance for transitional soy proto support:
+ * http://goto/soy-param-migration
+ * @param {!proto.api.LogInRequest} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.api.LogInRequest.toObject = function(includeInstance, msg) {
+ var f, obj = {
+ username: jspb.Message.getFieldWithDefault(msg, 1, ""),
+ password: jspb.Message.getFieldWithDefault(msg, 2, "")
+ };
+
+ if (includeInstance) {
+ obj.$jspbMessageInstance = msg;
+ }
+ return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.api.LogInRequest}
+ */
+proto.api.LogInRequest.deserializeBinary = function(bytes) {
+ var reader = new jspb.BinaryReader(bytes);
+ var msg = new proto.api.LogInRequest;
+ return proto.api.LogInRequest.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.api.LogInRequest} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.api.LogInRequest}
+ */
+proto.api.LogInRequest.deserializeBinaryFromReader = function(msg, reader) {
+ while (reader.nextField()) {
+ if (reader.isEndGroup()) {
+ break;
+ }
+ var field = reader.getFieldNumber();
+ switch (field) {
+ case 1:
+ var value = /** @type {string} */ (reader.readString());
+ msg.setUsername(value);
+ break;
+ case 2:
+ var value = /** @type {string} */ (reader.readString());
+ msg.setPassword(value);
+ break;
+ default:
+ reader.skipField();
+ break;
+ }
+ }
+ return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.api.LogInRequest.prototype.serializeBinary = function() {
+ var writer = new jspb.BinaryWriter();
+ proto.api.LogInRequest.serializeBinaryToWriter(this, writer);
+ return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.api.LogInRequest} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.api.LogInRequest.serializeBinaryToWriter = function(message, writer) {
+ var f = undefined;
+ f = message.getUsername();
+ if (f.length > 0) {
+ writer.writeString(
+ 1,
+ f
+ );
+ }
+ f = message.getPassword();
+ if (f.length > 0) {
+ writer.writeString(
+ 2,
+ f
+ );
+ }
+};
+
+
+/**
+ * optional string username = 1;
+ * @return {string}
+ */
+proto.api.LogInRequest.prototype.getUsername = function() {
+ return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.api.LogInRequest} returns this
+ */
+proto.api.LogInRequest.prototype.setUsername = function(value) {
+ return jspb.Message.setProto3StringField(this, 1, value);
+};
+
+
+/**
+ * optional string password = 2;
+ * @return {string}
+ */
+proto.api.LogInRequest.prototype.getPassword = function() {
+ return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.api.LogInRequest} returns this
+ */
+proto.api.LogInRequest.prototype.setPassword = function(value) {
+ return jspb.Message.setProto3StringField(this, 2, value);
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ * net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ * JSPB instance for transitional soy proto support:
+ * http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.api.LogInResponse.prototype.toObject = function(opt_includeInstance) {
+ return proto.api.LogInResponse.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ * the JSPB instance for transitional soy proto support:
+ * http://goto/soy-param-migration
+ * @param {!proto.api.LogInResponse} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.api.LogInResponse.toObject = function(includeInstance, msg) {
+ var f, obj = {
+ token: jspb.Message.getFieldWithDefault(msg, 1, "")
+ };
+
+ if (includeInstance) {
+ obj.$jspbMessageInstance = msg;
+ }
+ return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.api.LogInResponse}
+ */
+proto.api.LogInResponse.deserializeBinary = function(bytes) {
+ var reader = new jspb.BinaryReader(bytes);
+ var msg = new proto.api.LogInResponse;
+ return proto.api.LogInResponse.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.api.LogInResponse} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.api.LogInResponse}
+ */
+proto.api.LogInResponse.deserializeBinaryFromReader = function(msg, reader) {
+ while (reader.nextField()) {
+ if (reader.isEndGroup()) {
+ break;
+ }
+ var field = reader.getFieldNumber();
+ switch (field) {
+ case 1:
+ var value = /** @type {string} */ (reader.readString());
+ msg.setToken(value);
+ break;
+ default:
+ reader.skipField();
+ break;
+ }
+ }
+ return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.api.LogInResponse.prototype.serializeBinary = function() {
+ var writer = new jspb.BinaryWriter();
+ proto.api.LogInResponse.serializeBinaryToWriter(this, writer);
+ return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.api.LogInResponse} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.api.LogInResponse.serializeBinaryToWriter = function(message, writer) {
+ var f = undefined;
+ f = message.getToken();
+ if (f.length > 0) {
+ writer.writeString(
+ 1,
+ f
+ );
+ }
+};
+
+
+/**
+ * optional string token = 1;
+ * @return {string}
+ */
+proto.api.LogInResponse.prototype.getToken = function() {
+ return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.api.LogInResponse} returns this
+ */
+proto.api.LogInResponse.prototype.setToken = function(value) {
+ return jspb.Message.setProto3StringField(this, 1, value);
+};
+
+
+
+
+
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
diff --git a/src/api/auth_interceptor.ts b/src/api/auth_interceptor.ts
new file mode 100644
index 0000000..c8f30b8
--- /dev/null
+++ b/src/api/auth_interceptor.ts
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 The Yorkie Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * `AuthUnaryInterceptor` is a unary interceptor to add the Authorization header for each
+ * request.
+ */
+export class AuthUnaryInterceptor {
+ private token?: string;
+
+ constructor(token?: string) {
+ this.token = token;
+ }
+
+ /**
+ * `setToken` sets the token to the interceptor.
+ * @param token The token to set.
+ */
+ public setToken(token: string) {
+ this.token = token;
+ }
+
+ /**
+ * `intercept` intercepts the request and adds the token to the metadata.
+ */
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+ public intercept(request: any, invoker: any): any {
+ const metadata = request.getMetadata();
+ if (this.token) {
+ metadata['authorization'] = this.token;
+ }
+ return invoker(request);
+ }
+}
+
+/**
+ * `AuthStreamInterceptor` is a stream interceptor to add the Authorization header for each
+ * request.
+ */
+export class AuthStreamInterceptor {
+ private token?: string;
+
+ constructor(token?: string) {
+ this.token = token;
+ }
+
+ /**
+ * `setToken` sets the token to the interceptor.
+ * @param token The token to set.
+ */
+ public setToken(token: string) {
+ this.token = token;
+ }
+
+ /**
+ * `intercept` intercepts the request and adds the token to the metadata.
+ */
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+ public intercept(request: any, invoker: any): any {
+ const metadata = request.getMetadata();
+ if (this.token) {
+ metadata['authorization'] = this.token;
+ }
+ return invoker(request);
+ }
+}
diff --git a/src/api/converter.ts b/src/api/converter.ts
index 1252387..97e679a 100644
--- a/src/api/converter.ts
+++ b/src/api/converter.ts
@@ -1,7 +1,18 @@
import { Timestamp as PbTimestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
-import { Project, DocumentSummary, AuthWebhookMethod, ErrorWithDetails } from './types';
-import { Project as PbProject, DocumentSummary as PbDocumentSummary } from './resources_pb';
+import {
+ User,
+ Project,
+ DocumentSummary,
+ AuthWebhookMethod,
+ ErrorWithDetails,
+} from './types';
+
+import {
+ User as PbUser,
+ Project as PbProject,
+ DocumentSummary as PbDocumentSummary,
+} from './resources_pb';
import * as errorDetails from 'grpc-web-error-details';
@@ -9,6 +20,14 @@ export function fromTimestamp(pbTimestamp: PbTimestamp): number {
return pbTimestamp.getSeconds() + pbTimestamp.getNanos() / 1e9;
}
+export function fromUser(pbUser: PbUser): User {
+ return {
+ id: pbUser.getId(),
+ username: pbUser.getUsername(),
+ createdAt: fromTimestamp(pbUser.getCreatedAt()!),
+ };
+}
+
export function fromProject(pbProject: PbProject): Project {
return {
id: pbProject.getId(),
diff --git a/src/api/index.ts b/src/api/index.ts
index 94b5909..607cec4 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -1,5 +1,7 @@
import { AdminPromiseClient } from './admin_grpc_web_pb';
import {
+ LogInRequest,
+ SignUpRequest,
ListProjectsRequest,
ListDocumentsRequest,
GetProjectRequest,
@@ -10,6 +12,7 @@ import {
} from './admin_pb';
import * as errorDetails from 'grpc-web-error-details';
+import { AuthUnaryInterceptor, AuthStreamInterceptor } from './auth_interceptor';
import { UpdatableProjectFields as PbProjectFields } from './resources_pb';
import * as PbWrappers from 'google-protobuf/google/protobuf/wrappers_pb';
@@ -18,47 +21,58 @@ import * as converter from './converter';
export * from './types';
-const client = new AdminPromiseClient(`${process.env.REACT_APP_ADMIN_ADDR}`, null);
-
-// loginUser logins the user and returns a user.
-export async function loginUser(email: string, password: string): Promise {
- // TODO(hackerwins): Implement login and token saving logic.
- return Promise.resolve({
- email: 'dl_alto_dev@navercorp.com',
- createdAt: 0,
- });
+// TODO(hackerwins): Consider combining these two interceptors into one.
+const unaryInterceptor = new AuthUnaryInterceptor();
+const streamInterceptor = new AuthStreamInterceptor();
+const client = new AdminPromiseClient(`${process.env.REACT_APP_ADMIN_ADDR}`, null, {
+ unaryInterceptors: [unaryInterceptor],
+ streamInterceptors: [streamInterceptor],
+});
+
+// logIn logs in the user and returns a token.
+export async function logIn(username: string, password: string): Promise {
+ const req = new LogInRequest();
+ req.setUsername(username);
+ req.setPassword(password);
+ const res = await client.logIn(req);
+
+ // TODO(hackerwins): We should probably store the token in localStorage or cookies.
+ const token = res.getToken();
+ unaryInterceptor.setToken(token);
+ streamInterceptor.setToken(token);
+ return token;
}
-// signupUser creates a new user and returns a user.
-export async function signupUser(email: string, password: string): Promise {
- // TODO(hackerwins): Implement signup and token saving logic.
- return Promise.resolve({
- email: 'dl_alto_dev@navercorp.com',
- createdAt: 0,
- });
+// signUp signs up the user and returns a user.
+export async function signUp(username: string, password: string): Promise {
+ const req = new SignUpRequest();
+ req.setUsername(username);
+ req.setPassword(password);
+ const res = await client.signUp(req);
+ return converter.fromUser(res.getUser()!);
}
// createProject creates a new project.
export async function createProject(name: string): Promise {
const req = new CreateProjectRequest();
req.setName(name);
- const response = await client.createProject(req);
- return converter.fromProject(response.getProject()!);
+ const res = await client.createProject(req);
+ return converter.fromProject(res.getProject()!);
}
// listProjects fetches projects from the admin server.
export async function listProjects(): Promise> {
const req = new ListProjectsRequest();
- const response = await client.listProjects(req);
- return converter.fromProjects(response.getProjectsList());
+ const res = await client.listProjects(req);
+ return converter.fromProjects(res.getProjectsList());
}
// getProject fetch project from the admin server.
export async function getProject(name: string): Promise {
const req = new GetProjectRequest();
req.setName(name);
- const response = await client.getProject(req);
- return converter.fromProject(response.getProject()!);
+ const res = await client.getProject(req);
+ return converter.fromProject(res.getProject()!);
}
// UpdateProject updates a project info.
@@ -81,8 +95,8 @@ export async function updateProject(id: string, fields: UpdatableProjectFields):
req.setFields(pbFields);
try {
- const response = await client.updateProject(req);
- return converter.fromProject(response.getProject()!);
+ const res = await client.updateProject(req);
+ return converter.fromProject(res.getProject()!);
} catch (error) {
const [status, details] = errorDetails.statusFromError(error);
if (!status || !details) {
@@ -105,8 +119,8 @@ export async function listDocuments(
req.setPreviousId(previousID);
req.setPageSize(pageSize);
req.setIsForward(isForward);
- const response = await client.listDocuments(req);
- const summaries = converter.fromDocumentSummaries(response.getDocumentsList());
+ const res = await client.listDocuments(req);
+ const summaries = converter.fromDocumentSummaries(res.getDocumentsList());
if (isForward) {
summaries.reverse();
}
@@ -118,9 +132,9 @@ export async function getDocument(projectName: string, documentKey: string): Pro
const req = new GetDocumentRequest();
req.setProjectName(projectName);
req.setDocumentKey(documentKey);
- const response = await client.getDocument(req);
+ const res = await client.getDocument(req);
- const document = response.getDocument();
+ const document = res.getDocument();
return converter.fromDocumentSummary(document!);
}
@@ -137,10 +151,10 @@ export async function searchDocuments(
req.setProjectName(projectName);
req.setQuery(documentQuery);
req.setPageSize(pageSize);
- const response = await client.searchDocuments(req);
- const summaries = converter.fromDocumentSummaries(response.getDocumentsList());
+ const res = await client.searchDocuments(req);
+ const summaries = converter.fromDocumentSummaries(res.getDocumentsList());
return {
- totalCount: response.getTotalCount(),
+ totalCount: res.getTotalCount(),
documents: summaries,
};
}
diff --git a/src/api/resources.proto b/src/api/resources.proto
index 2c3f78a..62f6a3a 100644
--- a/src/api/resources.proto
+++ b/src/api/resources.proto
@@ -13,302 +13,307 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
- syntax = "proto3";
-
- import "google/protobuf/timestamp.proto";
- import "google/protobuf/wrappers.proto";
-
- package api;
-
- /////////////////////////////////////////
- // Messages for ChangePack //
- /////////////////////////////////////////
-
- message ChangePack {
- string document_key = 1;
- Checkpoint checkpoint = 2;
- bytes snapshot = 3;
- repeated Change changes = 4;
- TimeTicket min_synced_ticket = 5;
- }
-
- message Change {
- ChangeID id = 1;
- string message = 2;
- repeated Operation operations = 3;
- }
-
- message ChangeID {
- uint32 client_seq = 1;
- uint64 server_seq = 2 [jstype = JS_STRING];
- uint64 lamport = 3 [jstype = JS_STRING];
- bytes actor_id = 4;
- }
-
- message Operation {
- message Set {
- TimeTicket parent_created_at = 1;
- string key = 2;
- JSONElementSimple value = 3;
- TimeTicket executed_at = 4;
- }
- message Add {
- TimeTicket parent_created_at = 1;
- TimeTicket prev_created_at = 2;
- JSONElementSimple value = 3;
- TimeTicket executed_at = 4;
- }
- message Move {
- TimeTicket parent_created_at = 1;
- TimeTicket prev_created_at = 2;
- TimeTicket created_at = 3;
- TimeTicket executed_at = 4;
- }
- message Remove {
- TimeTicket parent_created_at = 1;
- TimeTicket created_at = 2;
- TimeTicket executed_at = 3;
- }
- message Edit {
- TimeTicket parent_created_at = 1;
- TextNodePos from = 2;
- TextNodePos to = 3;
- map created_at_map_by_actor = 4;
- string content = 5;
- TimeTicket executed_at = 6;
- }
- message Select {
- TimeTicket parent_created_at = 1;
- TextNodePos from = 2;
- TextNodePos to = 3;
- TimeTicket executed_at = 4;
- }
- message RichEdit {
- TimeTicket parent_created_at = 1;
- TextNodePos from = 2;
- TextNodePos to = 3;
- map created_at_map_by_actor = 4;
- string content = 5;
- map attributes = 6;
- TimeTicket executed_at = 7;
- }
- message Style {
- TimeTicket parent_created_at = 1;
- TextNodePos from = 2;
- TextNodePos to = 3;
- map attributes = 4;
- TimeTicket executed_at = 5;
- }
- message Increase {
- TimeTicket parent_created_at = 1;
- JSONElementSimple value = 2;
- TimeTicket executed_at = 3;
- }
-
- oneof body {
- Set set = 1;
- Add add = 2;
- Move move = 3;
- Remove remove = 4;
- Edit edit = 5;
- Select select = 6;
- RichEdit rich_edit = 7;
- Style style = 8;
- Increase increase = 9;
- }
- }
-
- message JSONElementSimple {
- TimeTicket created_at = 1;
- TimeTicket moved_at = 2;
- TimeTicket removed_at = 3;
- ValueType type = 4;
- bytes value = 5;
- }
-
- /////////////////////////////////////////
- // Messages for JSON //
- /////////////////////////////////////////
-
- message JSONElement {
- message JSONObject {
- repeated RHTNode nodes = 1;
- TimeTicket created_at = 2;
- TimeTicket moved_at = 3;
- TimeTicket removed_at = 4;
- }
- message JSONArray {
- repeated RGANode nodes = 1;
- TimeTicket created_at = 2;
- TimeTicket moved_at = 3;
- TimeTicket removed_at = 4;
- }
- message Primitive {
- ValueType type = 1;
- bytes value = 2;
- TimeTicket created_at = 3;
- TimeTicket moved_at = 4;
- TimeTicket removed_at = 5;
- }
- message Text {
- repeated TextNode nodes = 1;
- TimeTicket created_at = 2;
- TimeTicket moved_at = 3;
- TimeTicket removed_at = 4;
- }
- message RichText {
- repeated RichTextNode nodes = 1;
- TimeTicket created_at = 2;
- TimeTicket moved_at = 3;
- TimeTicket removed_at = 4;
- }
- message Counter {
- ValueType type = 1;
- bytes value = 2;
- TimeTicket created_at = 3;
- TimeTicket moved_at = 4;
- TimeTicket removed_at = 5;
- }
-
- oneof Body {
- JSONObject json_object = 1;
- JSONArray json_array = 2;
- Primitive primitive = 3;
- Text text = 4;
- RichText rich_text = 5;
- Counter counter = 6;
- }
- }
-
- message RHTNode {
- string key = 1;
- JSONElement element = 2;
- }
-
- message RGANode {
- RGANode next = 1;
- JSONElement element = 2;
- }
-
- message TextNode {
- TextNodeID id = 1;
- string value = 2;
- TimeTicket removed_at = 3;
- TextNodeID ins_prev_id = 4;
- }
-
- message RichTextNodeAttr {
- string key = 1;
- string value = 2;
- TimeTicket updated_at = 3;
- }
-
- message RichTextNode {
- TextNodeID id = 1;
- map attributes = 2;
- string value = 3;
- TimeTicket removed_at = 4;
- TextNodeID ins_prev_id = 5;
- }
-
- message TextNodeID {
- TimeTicket created_at = 1;
- int32 offset = 2;
- }
-
- /////////////////////////////////////////
- // Messages for Common //
- /////////////////////////////////////////
-
- message Project {
- string id = 1;
- string name = 2;
- string public_key = 3;
- string secret_key = 4;
- string auth_webhook_url = 5;
- repeated string auth_webhook_methods = 6;
- google.protobuf.Timestamp created_at = 7;
- google.protobuf.Timestamp updated_at = 8;
- }
-
- message UpdatableProjectFields {
- message AuthWebhookMethods {
- repeated string methods = 1;
- }
-
- google.protobuf.StringValue name = 1;
- google.protobuf.StringValue auth_webhook_url = 2;
- AuthWebhookMethods auth_webhook_methods = 3;
- }
-
- message DocumentSummary {
- string id = 1;
- string key = 2;
- string snapshot = 3;
- google.protobuf.Timestamp created_at = 4;
- google.protobuf.Timestamp accessed_at = 5;
- google.protobuf.Timestamp updated_at = 6;
- }
-
- message Presence {
- int32 clock = 1;
- map data = 2;
- }
-
- message Client {
- bytes id = 1;
- Presence presence = 2;
- }
-
- message Clients {
- repeated Client clients = 1;
- }
-
- message Checkpoint {
- uint64 server_seq = 1 [jstype = JS_STRING];
- uint32 client_seq = 2;
- }
-
- message TextNodePos {
- TimeTicket created_at = 1;
- int32 offset = 2;
- int32 relative_offset = 3;
- }
-
- message TimeTicket {
- uint64 lamport = 1 [jstype = JS_STRING];
- uint32 delimiter = 2;
- bytes actor_id = 3;
- }
-
- enum ValueType {
- NULL = 0;
- BOOLEAN = 1;
- INTEGER = 2;
- LONG = 3;
- DOUBLE = 4;
- STRING = 5;
- BYTES = 6;
- DATE = 7;
- JSON_OBJECT = 8;
- JSON_ARRAY = 9;
- TEXT = 10;
- RICH_TEXT = 11;
- INTEGER_CNT = 12;
- LONG_CNT = 13;
- DOUBLE_CNT = 14;
- }
-
- enum DocEventType {
- DOCUMENTS_CHANGED = 0;
- DOCUMENTS_WATCHED = 1;
- DOCUMENTS_UNWATCHED = 2;
- PRESENCE_CHANGED = 3;
- }
-
- message DocEvent {
- DocEventType type = 1;
- Client publisher = 2;
- repeated string document_keys = 3;
- }
-
\ No newline at end of file
+syntax = "proto3";
+
+import "google/protobuf/timestamp.proto";
+import "google/protobuf/wrappers.proto";
+
+package api;
+
+/////////////////////////////////////////
+// Messages for ChangePack //
+/////////////////////////////////////////
+
+message ChangePack {
+ string document_key = 1;
+ Checkpoint checkpoint = 2;
+ bytes snapshot = 3;
+ repeated Change changes = 4;
+ TimeTicket min_synced_ticket = 5;
+}
+
+message Change {
+ ChangeID id = 1;
+ string message = 2;
+ repeated Operation operations = 3;
+}
+
+message ChangeID {
+ uint32 client_seq = 1;
+ uint64 server_seq = 2 [jstype = JS_STRING];
+ uint64 lamport = 3 [jstype = JS_STRING];
+ bytes actor_id = 4;
+}
+
+message Operation {
+ message Set {
+ TimeTicket parent_created_at = 1;
+ string key = 2;
+ JSONElementSimple value = 3;
+ TimeTicket executed_at = 4;
+ }
+ message Add {
+ TimeTicket parent_created_at = 1;
+ TimeTicket prev_created_at = 2;
+ JSONElementSimple value = 3;
+ TimeTicket executed_at = 4;
+ }
+ message Move {
+ TimeTicket parent_created_at = 1;
+ TimeTicket prev_created_at = 2;
+ TimeTicket created_at = 3;
+ TimeTicket executed_at = 4;
+ }
+ message Remove {
+ TimeTicket parent_created_at = 1;
+ TimeTicket created_at = 2;
+ TimeTicket executed_at = 3;
+ }
+ message Edit {
+ TimeTicket parent_created_at = 1;
+ TextNodePos from = 2;
+ TextNodePos to = 3;
+ map created_at_map_by_actor = 4;
+ string content = 5;
+ TimeTicket executed_at = 6;
+ }
+ message Select {
+ TimeTicket parent_created_at = 1;
+ TextNodePos from = 2;
+ TextNodePos to = 3;
+ TimeTicket executed_at = 4;
+ }
+ message RichEdit {
+ TimeTicket parent_created_at = 1;
+ TextNodePos from = 2;
+ TextNodePos to = 3;
+ map created_at_map_by_actor = 4;
+ string content = 5;
+ map attributes = 6;
+ TimeTicket executed_at = 7;
+ }
+ message Style {
+ TimeTicket parent_created_at = 1;
+ TextNodePos from = 2;
+ TextNodePos to = 3;
+ map attributes = 4;
+ TimeTicket executed_at = 5;
+ }
+ message Increase {
+ TimeTicket parent_created_at = 1;
+ JSONElementSimple value = 2;
+ TimeTicket executed_at = 3;
+ }
+
+ oneof body {
+ Set set = 1;
+ Add add = 2;
+ Move move = 3;
+ Remove remove = 4;
+ Edit edit = 5;
+ Select select = 6;
+ RichEdit rich_edit = 7;
+ Style style = 8;
+ Increase increase = 9;
+ }
+}
+
+message JSONElementSimple {
+ TimeTicket created_at = 1;
+ TimeTicket moved_at = 2;
+ TimeTicket removed_at = 3;
+ ValueType type = 4;
+ bytes value = 5;
+}
+
+/////////////////////////////////////////
+// Messages for JSON //
+/////////////////////////////////////////
+
+message JSONElement {
+ message JSONObject {
+ repeated RHTNode nodes = 1;
+ TimeTicket created_at = 2;
+ TimeTicket moved_at = 3;
+ TimeTicket removed_at = 4;
+ }
+ message JSONArray {
+ repeated RGANode nodes = 1;
+ TimeTicket created_at = 2;
+ TimeTicket moved_at = 3;
+ TimeTicket removed_at = 4;
+ }
+ message Primitive {
+ ValueType type = 1;
+ bytes value = 2;
+ TimeTicket created_at = 3;
+ TimeTicket moved_at = 4;
+ TimeTicket removed_at = 5;
+ }
+ message Text {
+ repeated TextNode nodes = 1;
+ TimeTicket created_at = 2;
+ TimeTicket moved_at = 3;
+ TimeTicket removed_at = 4;
+ }
+ message RichText {
+ repeated RichTextNode nodes = 1;
+ TimeTicket created_at = 2;
+ TimeTicket moved_at = 3;
+ TimeTicket removed_at = 4;
+ }
+ message Counter {
+ ValueType type = 1;
+ bytes value = 2;
+ TimeTicket created_at = 3;
+ TimeTicket moved_at = 4;
+ TimeTicket removed_at = 5;
+ }
+
+ oneof Body {
+ JSONObject json_object = 1;
+ JSONArray json_array = 2;
+ Primitive primitive = 3;
+ Text text = 4;
+ RichText rich_text = 5;
+ Counter counter = 6;
+ }
+}
+
+message RHTNode {
+ string key = 1;
+ JSONElement element = 2;
+}
+
+message RGANode {
+ RGANode next = 1;
+ JSONElement element = 2;
+}
+
+message TextNode {
+ TextNodeID id = 1;
+ string value = 2;
+ TimeTicket removed_at = 3;
+ TextNodeID ins_prev_id = 4;
+}
+
+message RichTextNodeAttr {
+ string key = 1;
+ string value = 2;
+ TimeTicket updated_at = 3;
+}
+
+message RichTextNode {
+ TextNodeID id = 1;
+ map attributes = 2;
+ string value = 3;
+ TimeTicket removed_at = 4;
+ TextNodeID ins_prev_id = 5;
+}
+
+message TextNodeID {
+ TimeTicket created_at = 1;
+ int32 offset = 2;
+}
+
+/////////////////////////////////////////
+// Messages for Common //
+/////////////////////////////////////////
+
+message User {
+ string id = 1;
+ string username = 2;
+ google.protobuf.Timestamp created_at = 3;
+}
+
+message Project {
+ string id = 1;
+ string name = 2;
+ string public_key = 3;
+ string secret_key = 4;
+ string auth_webhook_url = 5;
+ repeated string auth_webhook_methods = 6;
+ google.protobuf.Timestamp created_at = 7;
+ google.protobuf.Timestamp updated_at = 8;
+}
+
+message UpdatableProjectFields {
+ message AuthWebhookMethods {
+ repeated string methods = 1;
+ }
+
+ google.protobuf.StringValue name = 1;
+ google.protobuf.StringValue auth_webhook_url = 2;
+ AuthWebhookMethods auth_webhook_methods = 3;
+}
+
+message DocumentSummary {
+ string id = 1;
+ string key = 2;
+ string snapshot = 3;
+ google.protobuf.Timestamp created_at = 4;
+ google.protobuf.Timestamp accessed_at = 5;
+ google.protobuf.Timestamp updated_at = 6;
+}
+
+message Presence {
+ int32 clock = 1;
+ map data = 2;
+}
+
+message Client {
+ bytes id = 1;
+ Presence presence = 2;
+}
+
+message Clients {
+ repeated Client clients = 1;
+}
+
+message Checkpoint {
+ uint64 server_seq = 1 [jstype = JS_STRING];
+ uint32 client_seq = 2;
+}
+
+message TextNodePos {
+ TimeTicket created_at = 1;
+ int32 offset = 2;
+ int32 relative_offset = 3;
+}
+
+message TimeTicket {
+ uint64 lamport = 1 [jstype = JS_STRING];
+ uint32 delimiter = 2;
+ bytes actor_id = 3;
+}
+
+enum ValueType {
+ NULL = 0;
+ BOOLEAN = 1;
+ INTEGER = 2;
+ LONG = 3;
+ DOUBLE = 4;
+ STRING = 5;
+ BYTES = 6;
+ DATE = 7;
+ JSON_OBJECT = 8;
+ JSON_ARRAY = 9;
+ TEXT = 10;
+ RICH_TEXT = 11;
+ INTEGER_CNT = 12;
+ LONG_CNT = 13;
+ DOUBLE_CNT = 14;
+}
+
+enum DocEventType {
+ DOCUMENTS_CHANGED = 0;
+ DOCUMENTS_WATCHED = 1;
+ DOCUMENTS_UNWATCHED = 2;
+ PRESENCE_CHANGED = 3;
+}
+
+message DocEvent {
+ DocEventType type = 1;
+ Client publisher = 2;
+ repeated string document_keys = 3;
+}
diff --git a/src/api/resources_pb.d.ts b/src/api/resources_pb.d.ts
index 527067d..52142d8 100644
--- a/src/api/resources_pb.d.ts
+++ b/src/api/resources_pb.d.ts
@@ -1076,6 +1076,34 @@ export namespace TextNodeID {
}
}
+export class User extends jspb.Message {
+ getId(): string;
+ setId(value: string): User;
+
+ getUsername(): string;
+ setUsername(value: string): User;
+
+ getCreatedAt(): google_protobuf_timestamp_pb.Timestamp | undefined;
+ setCreatedAt(value?: google_protobuf_timestamp_pb.Timestamp): User;
+ hasCreatedAt(): boolean;
+ clearCreatedAt(): User;
+
+ serializeBinary(): Uint8Array;
+ toObject(includeInstance?: boolean): User.AsObject;
+ static toObject(includeInstance: boolean, msg: User): User.AsObject;
+ static serializeBinaryToWriter(message: User, writer: jspb.BinaryWriter): void;
+ static deserializeBinary(bytes: Uint8Array): User;
+ static deserializeBinaryFromReader(message: User, reader: jspb.BinaryReader): User;
+}
+
+export namespace User {
+ export type AsObject = {
+ id: string,
+ username: string,
+ createdAt?: google_protobuf_timestamp_pb.Timestamp.AsObject,
+ }
+}
+
export class Project extends jspb.Message {
getId(): string;
setId(value: string): Project;
diff --git a/src/api/resources_pb.js b/src/api/resources_pb.js
index 4cf5de0..d68e565 100644
--- a/src/api/resources_pb.js
+++ b/src/api/resources_pb.js
@@ -66,6 +66,7 @@ goog.exportSymbol('proto.api.TextNodePos', null, global);
goog.exportSymbol('proto.api.TimeTicket', null, global);
goog.exportSymbol('proto.api.UpdatableProjectFields', null, global);
goog.exportSymbol('proto.api.UpdatableProjectFields.AuthWebhookMethods', null, global);
+goog.exportSymbol('proto.api.User', null, global);
goog.exportSymbol('proto.api.ValueType', null, global);
/**
* Generated by JsPbCodeGenerator.
@@ -634,6 +635,27 @@ if (goog.DEBUG && !COMPILED) {
*/
proto.api.TextNodeID.displayName = 'proto.api.TextNodeID';
}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.api.User = function(opt_data) {
+ jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.api.User, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+ /**
+ * @public
+ * @override
+ */
+ proto.api.User.displayName = 'proto.api.User';
+}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
@@ -9167,6 +9189,217 @@ proto.api.TextNodeID.prototype.setOffset = function(value) {
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ * net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ * JSPB instance for transitional soy proto support:
+ * http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.api.User.prototype.toObject = function(opt_includeInstance) {
+ return proto.api.User.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ * the JSPB instance for transitional soy proto support:
+ * http://goto/soy-param-migration
+ * @param {!proto.api.User} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.api.User.toObject = function(includeInstance, msg) {
+ var f, obj = {
+ id: jspb.Message.getFieldWithDefault(msg, 1, ""),
+ username: jspb.Message.getFieldWithDefault(msg, 2, ""),
+ createdAt: (f = msg.getCreatedAt()) && google_protobuf_timestamp_pb.Timestamp.toObject(includeInstance, f)
+ };
+
+ if (includeInstance) {
+ obj.$jspbMessageInstance = msg;
+ }
+ return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.api.User}
+ */
+proto.api.User.deserializeBinary = function(bytes) {
+ var reader = new jspb.BinaryReader(bytes);
+ var msg = new proto.api.User;
+ return proto.api.User.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.api.User} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.api.User}
+ */
+proto.api.User.deserializeBinaryFromReader = function(msg, reader) {
+ while (reader.nextField()) {
+ if (reader.isEndGroup()) {
+ break;
+ }
+ var field = reader.getFieldNumber();
+ switch (field) {
+ case 1:
+ var value = /** @type {string} */ (reader.readString());
+ msg.setId(value);
+ break;
+ case 2:
+ var value = /** @type {string} */ (reader.readString());
+ msg.setUsername(value);
+ break;
+ case 3:
+ var value = new google_protobuf_timestamp_pb.Timestamp;
+ reader.readMessage(value,google_protobuf_timestamp_pb.Timestamp.deserializeBinaryFromReader);
+ msg.setCreatedAt(value);
+ break;
+ default:
+ reader.skipField();
+ break;
+ }
+ }
+ return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.api.User.prototype.serializeBinary = function() {
+ var writer = new jspb.BinaryWriter();
+ proto.api.User.serializeBinaryToWriter(this, writer);
+ return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.api.User} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.api.User.serializeBinaryToWriter = function(message, writer) {
+ var f = undefined;
+ f = message.getId();
+ if (f.length > 0) {
+ writer.writeString(
+ 1,
+ f
+ );
+ }
+ f = message.getUsername();
+ if (f.length > 0) {
+ writer.writeString(
+ 2,
+ f
+ );
+ }
+ f = message.getCreatedAt();
+ if (f != null) {
+ writer.writeMessage(
+ 3,
+ f,
+ google_protobuf_timestamp_pb.Timestamp.serializeBinaryToWriter
+ );
+ }
+};
+
+
+/**
+ * optional string id = 1;
+ * @return {string}
+ */
+proto.api.User.prototype.getId = function() {
+ return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.api.User} returns this
+ */
+proto.api.User.prototype.setId = function(value) {
+ return jspb.Message.setProto3StringField(this, 1, value);
+};
+
+
+/**
+ * optional string username = 2;
+ * @return {string}
+ */
+proto.api.User.prototype.getUsername = function() {
+ return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.api.User} returns this
+ */
+proto.api.User.prototype.setUsername = function(value) {
+ return jspb.Message.setProto3StringField(this, 2, value);
+};
+
+
+/**
+ * optional google.protobuf.Timestamp created_at = 3;
+ * @return {?proto.google.protobuf.Timestamp}
+ */
+proto.api.User.prototype.getCreatedAt = function() {
+ return /** @type{?proto.google.protobuf.Timestamp} */ (
+ jspb.Message.getWrapperField(this, google_protobuf_timestamp_pb.Timestamp, 3));
+};
+
+
+/**
+ * @param {?proto.google.protobuf.Timestamp|undefined} value
+ * @return {!proto.api.User} returns this
+*/
+proto.api.User.prototype.setCreatedAt = function(value) {
+ return jspb.Message.setWrapperField(this, 3, value);
+};
+
+
+/**
+ * Clears the message field making it undefined.
+ * @return {!proto.api.User} returns this
+ */
+proto.api.User.prototype.clearCreatedAt = function() {
+ return this.setCreatedAt(undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.api.User.prototype.hasCreatedAt = function() {
+ return jspb.Message.getField(this, 3) != null;
+};
+
+
+
/**
* List of repeated fields within this message type.
* @private {!Array}
diff --git a/src/api/types.ts b/src/api/types.ts
index 70c7e95..6a9647b 100644
--- a/src/api/types.ts
+++ b/src/api/types.ts
@@ -8,7 +8,8 @@ export type DocumentSummary = {
};
export type User = {
- email: string;
+ id: string;
+ username: string;
createdAt: number;
};
diff --git a/src/features/users/LoginForm.tsx b/src/features/users/LoginForm.tsx
index 72cc41e..65b6119 100644
--- a/src/features/users/LoginForm.tsx
+++ b/src/features/users/LoginForm.tsx
@@ -9,14 +9,13 @@ export function LoginForm() {
const dispatch = useDispatch();
const navigate = useNavigate();
const { register, formState: { errors }, handleSubmit } = useForm();
- const { isSuccess } = useSelector(selectUsers);
+ const { login: { isSuccess } } = useSelector(selectUsers);
const onSubmit = (data: LoginFields) => {
dispatch(loginUser(data));
};
useEffect(() => {
- // TODO(hackerwins): Go to dashboard if the user is already logged in.
if (isSuccess) {
navigate('/');
}
@@ -29,10 +28,10 @@ export function LoginForm() {
>
Log in to Yorkie
-
- {errors.email && Email required.
}
+
+ {errors.username && Username required.
}