Frontend: codernote-frontend
Backend: codernote-backend
go build
./codernote-backend
cd crawler
go run cmd/main.go
ルート以下のAPIサーバとcrawler/
以下のCrawlerは別モジュールになっています。
CrawlerはAPIサーバ側のdbパッケージに依存しているので、バージョン管理に注意してください。
例えば、DBの構成を変更したり、DBの接続先を変更してクローラーを実行する場合には、crawler/go.mod
に以下のように追記してローカルパッケージを用いる、といった対応をしてください。
replace github.com/tsushiy/codernote-backend => ../
以下のAPIから取得したデータを同じ形式にしてデータベースに格納します。
apiv1.codernote.tsushiy.com で呼べますが、codernote-frontend 以外から呼ばれることはあまり想定していません。
Crawlerで取得しておいたデータを用いて問題のバリデーションを行います。
認証が必要なAPIでは、Firebase Authenticationで得られたJWTの検証を行います。
問題の一覧を取得します
QueryString
example: /problems?domain=atcoder
[
{
"No": 1,
"Domain": "atcoder",
"ProblemID": "abc001_1",
"ContestID": "abc001",
"Title": "A. 積雪深差",
"Slug":"",
"FrontendID":"",
"Difficulty":"194.98182678222656"
}
]
コンテストの一覧を取得します
QueryString
example: /contests?domain=atcoder&order=-started
[
{
"No": 5,
"Domain": "atcoder",
"ContestID": "abc001",
"Title": "AtCoder Beginner Contest 001",
"StartTimeSeconds": 1381579200,
"DurationSeconds": 7200,
"ProblemNoList": [
1,
2,
3,
4
]
}
]
公開されている単一のノートを取得します
QueryString
example: /note?noteId=74b3ea1e-b296-4d62-bb9a-81fa5c39dd31
{
"ID": "74b3ea1e-b296-4d62-bb9a-81fa5c39dd31",
"CreatedAt": "2020-03-15T11:38:48.04207Z",
"UpdatedAt": "2020-03-15T11:41:43.371398Z",
"Text": "sample text.",
"Problem": {
"No": 1,
"Domain": "atcoder",
"ProblemID": "abc001_1",
"ContestID": "abc001",
"Title": "A. 積雪深差",
"Slug":"",
"FrontendID":"",
"Difficulty":"194.98182678222656"
},
"User": {
"UserID": "fgCE5ZcTeOT8hmEmNnXvBb4mhEg1",
"Name": "tsushiy",
"CreatedAt": "2020-03-15T10:36:11.273197Z",
"UpdatedAt": "2020-03-15T11:17:48.712348Z"
},
"Public": 2
}
公開されているノートの一覧を取得します
QueryString
example: /notes?domain=atcoder&userName=tsushiy&tag=tag1&limit=100&skip=0&order=-updated
{
"Count": 1, // Total # of notes matched to the query (domain, problemNo, contestId, userName, tag)
"Notes": [
{
"ID": "74b3ea1e-b296-4d62-bb9a-81fa5c39dd31",
"CreatedAt": "2020-03-15T11:38:48.04207Z",
"UpdatedAt": "2020-03-15T11:41:43.371398Z",
"Text": "sample text.",
"Problem": {
"No": 1,
"Domain": "atcoder",
"ProblemID": "abc001_1",
"ContestID": "abc001",
"Title": "A. 積雪深差",
"Slug":"",
"FrontendID":"",
"Difficulty":"194.98182678222656"
},
"User": {
"UserID": "fgCE5ZcTeOT8hmEmNnXvBb4mhEg1",
"Name": "tsushiy",
"CreatedAt": "2020-03-15T10:36:11.273197Z",
"UpdatedAt": "2020-03-15T11:17:48.712348Z"
},
"Public": 2
}
]
}
A JWT must be included in the header of the request.
{
"Authorization": "Bearer {JWT}"
}
ログインします。
初回ログイン時にはランダムなユーザネームで新しく登録されます。
{
"UserID": "fgCE5ZcTeOT8hmEmNnXvBb4mhEg1",
"Name": "tsushiy",
"CreatedAt": "2020-03-15T08:36:01.629775Z",
"UpdatedAt": "2020-03-15T08:37:57.083458Z"
}
ユーザ名を変更します。
ユーザ名は3文字から30文字の英数字である必要があります。
Request Body
{
"Name": "tsushiy" // must be between 3 and 30 alphanumeric characters
}
{
"UserID": "fgCE5ZcTeOT8hmEmNnXvBb4mhEg1",
"Name": "tsushiy",
"CreatedAt": "2020-03-15T08:36:01.629775Z",
"UpdatedAt": "2020-03-15T08:41:02.216072Z"
}
ユーザ設定を取得します。
{
"AtCoderID": "",
"CodeforcesID": "",
"YukicoderID": "",
"AOJID": "",
"LeetCodeID": "",
}
ユーザ設定を変更します。
Request Body
{
"AtCoderID": "",
"CodeforcesID": "",
"YukicoderID": "",
"AOJID": "",
"LeetCodeID": "",
}
{
"AtCoderID": "",
"CodeforcesID": "",
"YukicoderID": "",
"AOJID": "",
"LeetCodeID": "",
}
公開されている単一のノートを取得します
ログインしているユーザの作成したノートであれば、公開されていなくても取得します。
GET /note とほぼ同じです。ログインした状態であれば基本的にこっちを使えばいいです。
QueryString
example: /user/note?noteId=74b3ea1e-b296-4d62-bb9a-81fa5c39dd31
{
"ID": "74b3ea1e-b296-4d62-bb9a-81fa5c39dd31",
"CreatedAt": "2020-03-15T11:38:48.04207Z",
"UpdatedAt": "2020-03-15T11:41:43.371398Z",
"Text": "sample text.",
"Problem": {
"No": 1,
"Domain": "atcoder",
"ProblemID": "abc001_1",
"ContestID": "abc001",
"Title": "A. 積雪深差",
"Slug":"",
"FrontendID":"",
"Difficulty":"194.98182678222656"
},
"User": {
"UserID": "fgCE5ZcTeOT8hmEmNnXvBb4mhEg1",
"Name": "tsushiy",
"CreatedAt": "2020-03-15T10:36:11.273197Z",
"UpdatedAt": "2020-03-15T11:17:48.712348Z"
},
"Public": 2
}
ログインしているユーザの指定された単一のノートを取得します。
Path
example: /user/note/1
{
"ID": "74b3ea1e-b296-4d62-bb9a-81fa5c39dd31",
"CreatedAt": "2020-03-15T11:38:48.04207Z",
"UpdatedAt": "2020-03-15T11:39:50.905595Z",
"Text": "sample text.",
"Problem": {
"No": 1,
"Domain": "atcoder",
"ProblemID": "abc001_1",
"ContestID": "abc001",
"Title": "A. 積雪深差",
"Slug":"",
"FrontendID":"",
"Difficulty":"194.98182678222656"
},
"User": {
"UserID": "fgCE5ZcTeOT8hmEmNnXvBb4mhEg1",
"Name": "tsushiy",
"CreatedAt": "2020-03-15T10:36:11.273197Z",
"UpdatedAt": "2020-03-15T11:17:48.712348Z"
},
"Public": 2 // 1 if private, otherwise 2
}
ログインしているユーザで指定された単一のノートを投稿または更新します。
Path
Request Body
{
"Text": "sample text.", // required, must not be empty
"Public": true // false if empty
}
{
"ID": "74b3ea1e-b296-4d62-bb9a-81fa5c39dd31",
"CreatedAt": "2020-03-15T11:38:48.04207Z",
"UpdatedAt": "2020-03-15T11:39:50.905595Z",
"Text": "sample text.",
"Problem": {
"No": 1,
"Domain": "atcoder",
"ProblemID": "abc001_1",
"ContestID": "abc001",
"Title": "A. 積雪深差",
"Slug":"",
"FrontendID":"",
"Difficulty":"194.98182678222656"
},
"User": {
"UserID": "fgCE5ZcTeOT8hmEmNnXvBb4mhEg1",
"Name": "tsushiy",
"CreatedAt": "2020-03-15T10:36:11.273197Z",
"UpdatedAt": "2020-03-15T11:17:48.712348Z"
},
"Public": 2 // 1 if private, otherwise 2
}
ログインしているユーザの指定された単一のノートを削除します。
Path
ログインしているユーザのノートの一覧を取得します。
QueryString
example: /user/notes?domain=atcoder&tag=tag1&limit=100&skip=0&order=-updated
{
"Count": 1, // Total # of notes matched to the query (domain, contestId, tag)
"Notes": [
{
"ID": "74b3ea1e-b296-4d62-bb9a-81fa5c39dd31",
"CreatedAt": "2020-03-15T11:38:48.04207Z",
"UpdatedAt": "2020-03-15T11:41:43.371398Z",
"Text": "sample text.",
"Problem": {
"No": 1,
"Domain": "atcoder",
"ProblemID": "abc001_1",
"ContestID": "abc001",
"Title": "A. 積雪深差",
"Slug":"",
"FrontendID":"",
"Difficulty":"194.98182678222656"
},
"User": {
"UserID": "fgCE5ZcTeOT8hmEmNnXvBb4mhEg1",
"Name": "tsushiy",
"CreatedAt": "2020-03-15T10:36:11.273197Z",
"UpdatedAt": "2020-03-15T11:17:48.712348Z"
},
"Public": 2
}
]
}
ログインしているユーザの指定されたノートのタグ一覧を取得します。
Path
example: /user/note/1/tag
{
"Tags": [
"tag1",
"tag2",
"tag3",
]
}
ログインしているユーザの指定されたノートにタグを追加します。
Path
Request Body
{
"Tag": "tag1", // required
}
ログインしているユーザの指定されたノートからタグを削除します。
Path
Request Body
{
"Tag": "tag1", // required
}
User {
No int
UserID string
Name string
CreatedAt string (RFC 3339)
UpdatedAt string (RFC 3339)
}
UserDetail struct {
UserID string
AtCoderID string
CodeforcesID string
YukicoderID string
AOJID string
LeetCodeID string
}
Contest {
No int
Domain string
ContestID string
Title string
StartTimeSeconds int
DurationSeconds int
Rated string
ProblemNoList []int
}
Problem {
No int
Domain string
ProblemID string
ContestID string
Title string
Slug string
FrontendID string
Difficulty string
}
Note {
ID string
CreatedAt string (RFC 3339)
UpdatedAt string (RFC 3339)
Text string
ProblemNo int
Problem Problem
UserNo int
User User
Public int
}
Tag {
No int
Key string
}
TagMap {
No int
NoteID string
TagNo int
}