diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2f8f7382..73f8d1a2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,11 +1,17 @@
# Contribution Guide
-All contributions are welcome. If you're not sure about something, feel free to ask.
+All contributions are welcome _if_ they are in the scope of the project. If you're not sure about something, feel free to ask.
## Guidelines
-Note that you should try and make your changes against the **most active branch**, which is usually the `main` branch but
-may be different when a new minor version is being developed.
+- Make sure you are familiar with Go and React.
+- Your contributions must be small and focused. If you want to add a new feature that requires substantial changes or additions to the codebase, please contact the dev first.
+- Make sure your changes are in line with the project's goals (Create a feature request if you're unsure).
+- Make sure your changes are well tested and do not introduce any new issues or regressions.
+- You should try and make your changes against the **most active branch**, which is usually the `main` branch but
+ may be different when a new version is being developed.
+
+## How to contribute
1. Create an issue before starting work on a feature or a bug fix.
2. Fork the repository, clone it, and create a new branch.
diff --git a/DEVELOPMENT_AND_BUILD.md b/DEVELOPMENT_AND_BUILD.md
index 6ddaaf31..e3f02eb6 100644
--- a/DEVELOPMENT_AND_BUILD.md
+++ b/DEVELOPMENT_AND_BUILD.md
@@ -47,13 +47,13 @@ Note that the web interface should be built first before building the server.
# Development
-To get started, you will need to be familiar with Go and React.
+1. To get started, you **must be familiar with Go and React**.
-I recommend creating a dummy AniList account for testing purposes.
+2. I recommend creating a dummy AniList account to use for testing. This will prevent the tests from affecting your actual account.
## Server
-1. Choose a data directory for testing.
+1. Create/choose a dummy data directory for testing.
This will prevent the server from writing to your actual data directory.
2. Create a dummy `web` folder if you want to develop the web interface too or build the web interface first and move the contents to a `web` directory at the root of the project before running the server.
@@ -85,20 +85,24 @@ Either way, a `web` directory should be present at the root of the project.
During development, the web interface is served by the Next.js development server instead of the Go server,
leading to different ports.
-## Codegen
+## Handlers & Codegen
-1. All routes are declared in `internal/handlers/routes.go` where a `handler` method is passed to each route.
-2. Route handlers are defined in `internal/handlers`.
- - The comments above each route handler declaration is a form of documentation similar to OpenAPI
- - These comments allow the internal code generator (`codegen/main.go`) to generate endpoint objects & types for the client.
- - Types for the frontend are auto-generated in `seanime-web/api/generated/types.ts` based on the `@returns` directive and request body variables of each route handler. The code generator will analyze the entire Go codebase and convert _returned_ public structs into Typescript types.
+> The following points are very important for understanding the codebase.
+
+- All routes are declared in `internal/handlers/routes.go` where a `handler` method is passed to each route.
+- Route handlers are defined in `internal/handlers`.
+- The comments above each route handler declaration is a form of documentation similar to OpenAPI
+- These comments allow the internal code generator (`codegen/main.go`) to generate endpoint objects & types for the client.
+- Types for the frontend are auto-generated in `seanime-web/api/generated/types.ts`, `seanime-web/api/generated/endpoint.types.ts`, `seanime-web/api/generated/hooks_template.ts` based on the comments above route handlers and all public Go structs.
Run the `go generate` command at the top of `codegen/main.go` to generate the necessary types for the frontend.
-This should be done after making changes to the route handlers or structs returned by the route handlers.
+This should be done **each time you make changes** to the **route handlers** or **structs** that are used in the frontend.
## AniList GraphQL API
+> The following points are for understanding the AniList API integration.
+
Anilist queries are defined in `internal/anilist/queries/*.graphql` and generated using `gqlgenc`.
Run this when you make changes to the GraphQL schema.
@@ -119,11 +123,11 @@ cd ../../..
go mod tidy
```
-`gqlgenc` will generate the different queries, mutations and structs associated with the AniList API and the queries we defined.
-These are located in `internal/api/anilist/clieng_gen.go`.
+- `gqlgenc` will generate the different queries, mutations and structs associated with the AniList API and the queries we defined.
+These are located in `internal/api/anilist/client_gen.go`.
-In `internal/api/anilist/client.go`, we manually reimplements the different queries and mutations into a `ClientWrapper` struct and a `MockClientWrapper` for testing.
+- In `internal/api/anilist/client.go`, we manually reimplements the different queries and mutations into a `ClientWrapper` struct and a `MockClientWrapper` for testing.
This is done to avoid using the generated code directly in the business logic. It also allows us to mock the AniList API for testing.
## Tests
diff --git a/codegen/generated/handlers.json b/codegen/generated/handlers.json
index 27dcde6c..1836d65c 100644
--- a/codegen/generated/handlers.json
+++ b/codegen/generated/handlers.json
@@ -2327,7 +2327,7 @@
"",
"\t@summary returns the API documentation",
"\t@route /api/v1/internal/docs [GET]",
- "\t@returns docs.Docs",
+ "\t@returns []handlers.ApiDocsGroup",
""
],
"filepath": "internal/handlers/docs.go",
@@ -2341,9 +2341,9 @@
],
"params": [],
"bodyFields": [],
- "returns": "docs.Docs",
- "returnGoType": "docs.Docs",
- "returnTypescriptType": "INTERNAL_Docs"
+ "returns": "[]handlers.ApiDocsGroup",
+ "returnGoType": "handlers.ApiDocsGroup",
+ "returnTypescriptType": "Array\u003cApiDocsGroup\u003e"
}
},
{
@@ -2956,7 +2956,7 @@
"\t@summary returns the total size of cache files.",
"\t@desc The total size of the cache files is returned in human-readable format.",
"\t@route /api/v1/filecache/total-size [GET]",
- "\t@returns bool",
+ "\t@returns string",
""
],
"filepath": "internal/handlers/filecache.go",
@@ -2972,9 +2972,9 @@
],
"params": [],
"bodyFields": [],
- "returns": "bool",
- "returnGoType": "bool",
- "returnTypescriptType": "boolean"
+ "returns": "string",
+ "returnGoType": "string",
+ "returnTypescriptType": "string"
}
},
{
@@ -3028,7 +3028,7 @@
"\t@summary returns the total size of cached video file data.",
"\t@desc The total size of the cache video file data is returned in human-readable format.",
"\t@route /api/v1/filecache/mediastream/videofiles/total-size [GET]",
- "\t@returns bool",
+ "\t@returns string",
""
],
"filepath": "internal/handlers/filecache.go",
@@ -3044,9 +3044,9 @@
],
"params": [],
"bodyFields": [],
- "returns": "bool",
- "returnGoType": "bool",
- "returnTypescriptType": "boolean"
+ "returns": "string",
+ "returnGoType": "string",
+ "returnTypescriptType": "string"
}
},
{
diff --git a/codegen/generated/public_structs.json b/codegen/generated/public_structs.json
index 3a27fb75..151023a2 100644
--- a/codegen/generated/public_structs.json
+++ b/codegen/generated/public_structs.json
@@ -28290,6 +28290,16 @@
"public": false,
"comments": []
},
+ {
+ "name": "torrentRepository",
+ "jsonName": "torrentRepository",
+ "goType": "torrent.Repository",
+ "typescriptType": "Torrent_Repository",
+ "usedStructName": "torrent.Repository",
+ "required": false,
+ "public": false,
+ "comments": []
+ },
{
"name": "playbackManager",
"jsonName": "playbackManager",
@@ -28380,6 +28390,16 @@
"public": true,
"comments": []
},
+ {
+ "name": "TorrentRepository",
+ "jsonName": "TorrentRepository",
+ "goType": "torrent.Repository",
+ "typescriptType": "Torrent_Repository",
+ "usedStructName": "torrent.Repository",
+ "required": false,
+ "public": true,
+ "comments": []
+ },
{
"name": "PlaybackManager",
"jsonName": "PlaybackManager",
@@ -28614,6 +28634,15 @@
"required": true,
"public": true,
"comments": []
+ },
+ {
+ "name": "AutoSelect",
+ "jsonName": "AutoSelect",
+ "goType": "bool",
+ "typescriptType": "boolean",
+ "required": true,
+ "public": true,
+ "comments": []
}
],
"comments": []
@@ -29014,6 +29043,17 @@
"formattedName": "Debrid_TorrentInfo",
"package": "debrid",
"fields": [
+ {
+ "name": "ID",
+ "jsonName": "id",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": false,
+ "public": true,
+ "comments": [
+ " ID of the torrent if added to the debrid service"
+ ]
+ },
{
"name": "Name",
"jsonName": "name",
@@ -33271,6 +33311,281 @@
],
"comments": []
},
+ {
+ "filepath": "../internal/handlers/docs.go",
+ "filename": "docs.go",
+ "name": "ApiDocsGroup",
+ "formattedName": "ApiDocsGroup",
+ "package": "handlers",
+ "fields": [
+ {
+ "name": "Filename",
+ "jsonName": "filename",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "Name",
+ "jsonName": "name",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "Handlers",
+ "jsonName": "handlers",
+ "goType": "[]RouteHandler",
+ "typescriptType": "Array\u003cRouteHandler\u003e",
+ "usedStructName": "handlers.RouteHandler",
+ "required": false,
+ "public": true,
+ "comments": []
+ }
+ ],
+ "comments": []
+ },
+ {
+ "filepath": "../internal/handlers/docs.go",
+ "filename": "docs.go",
+ "name": "RouteHandler",
+ "formattedName": "RouteHandler",
+ "package": "handlers",
+ "fields": [
+ {
+ "name": "Name",
+ "jsonName": "name",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "TrimmedName",
+ "jsonName": "trimmedName",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "Comments",
+ "jsonName": "comments",
+ "goType": "[]string",
+ "typescriptType": "Array\u003cstring\u003e",
+ "required": false,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "Filepath",
+ "jsonName": "filepath",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "Filename",
+ "jsonName": "filename",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "Api",
+ "jsonName": "api",
+ "goType": "RouteHandlerApi",
+ "typescriptType": "RouteHandlerApi",
+ "usedStructName": "handlers.RouteHandlerApi",
+ "required": false,
+ "public": true,
+ "comments": []
+ }
+ ],
+ "comments": []
+ },
+ {
+ "filepath": "../internal/handlers/docs.go",
+ "filename": "docs.go",
+ "name": "RouteHandlerApi",
+ "formattedName": "RouteHandlerApi",
+ "package": "handlers",
+ "fields": [
+ {
+ "name": "Summary",
+ "jsonName": "summary",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "Descriptions",
+ "jsonName": "descriptions",
+ "goType": "[]string",
+ "typescriptType": "Array\u003cstring\u003e",
+ "required": false,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "Endpoint",
+ "jsonName": "endpoint",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "Methods",
+ "jsonName": "methods",
+ "goType": "[]string",
+ "typescriptType": "Array\u003cstring\u003e",
+ "required": false,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "Params",
+ "jsonName": "params",
+ "goType": "[]RouteHandlerParam",
+ "typescriptType": "Array\u003cRouteHandlerParam\u003e",
+ "usedStructName": "handlers.RouteHandlerParam",
+ "required": false,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "BodyFields",
+ "jsonName": "bodyFields",
+ "goType": "[]RouteHandlerParam",
+ "typescriptType": "Array\u003cRouteHandlerParam\u003e",
+ "usedStructName": "handlers.RouteHandlerParam",
+ "required": false,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "Returns",
+ "jsonName": "returns",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "ReturnGoType",
+ "jsonName": "returnGoType",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "ReturnTypescriptType",
+ "jsonName": "returnTypescriptType",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": []
+ }
+ ],
+ "comments": []
+ },
+ {
+ "filepath": "../internal/handlers/docs.go",
+ "filename": "docs.go",
+ "name": "RouteHandlerParam",
+ "formattedName": "RouteHandlerParam",
+ "package": "handlers",
+ "fields": [
+ {
+ "name": "Name",
+ "jsonName": "name",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "JsonName",
+ "jsonName": "jsonName",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "GoType",
+ "jsonName": "goType",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": [
+ " e.g., []models.User"
+ ]
+ },
+ {
+ "name": "UsedStructType",
+ "jsonName": "usedStructType",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": [
+ " e.g., models.User"
+ ]
+ },
+ {
+ "name": "TypescriptType",
+ "jsonName": "typescriptType",
+ "goType": "string",
+ "typescriptType": "string",
+ "required": true,
+ "public": true,
+ "comments": [
+ " e.g., Array\u003cUser\u003e"
+ ]
+ },
+ {
+ "name": "Required",
+ "jsonName": "required",
+ "goType": "bool",
+ "typescriptType": "boolean",
+ "required": true,
+ "public": true,
+ "comments": []
+ },
+ {
+ "name": "Descriptions",
+ "jsonName": "descriptions",
+ "goType": "[]string",
+ "typescriptType": "Array\u003cstring\u003e",
+ "required": false,
+ "public": true,
+ "comments": []
+ }
+ ],
+ "comments": []
+ },
{
"filepath": "../internal/handlers/download.go",
"filename": "download.go",
diff --git a/codegen/internal/generate_ts_endpoints.go b/codegen/internal/generate_ts_endpoints.go
index 3f4224e3..c4af92f6 100644
--- a/codegen/internal/generate_ts_endpoints.go
+++ b/codegen/internal/generate_ts_endpoints.go
@@ -21,9 +21,9 @@ var additionalStructNamesForEndpoints = []string{
"vendor_hibike_torrent.AnimeTorrent",
}
-func GenerateTypescriptEndpointsFile(docsPath string, structsPath string, outDir string) []string {
- handlers := LoadHandlers(docsPath)
- structs := LoadPublicStructs(structsPath)
+func GenerateTypescriptEndpointsFile(handlersJsonPath string, structsJsonPath string, outDir string) []string {
+ handlers := LoadHandlers(handlersJsonPath)
+ structs := LoadPublicStructs(structsJsonPath)
_ = os.MkdirAll(outDir, os.ModePerm)
f, err := os.Create(filepath.Join(outDir, typescriptEndpointsFileName))
diff --git a/internal/handlers/docs.go b/internal/handlers/docs.go
index d9310d18..6fb2a567 100644
--- a/internal/handlers/docs.go
+++ b/internal/handlers/docs.go
@@ -1,27 +1,96 @@
package handlers
+import (
+ "github.com/goccy/go-json"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+type (
+ ApiDocsGroup struct {
+ Filename string `json:"filename"`
+ Name string `json:"name"`
+ Handlers []*RouteHandler `json:"handlers"`
+ }
+
+ RouteHandler struct {
+ Name string `json:"name"`
+ TrimmedName string `json:"trimmedName"`
+ Comments []string `json:"comments"`
+ Filepath string `json:"filepath"`
+ Filename string `json:"filename"`
+ Api *RouteHandlerApi `json:"api"`
+ }
+
+ RouteHandlerApi struct {
+ Summary string `json:"summary"`
+ Descriptions []string `json:"descriptions"`
+ Endpoint string `json:"endpoint"`
+ Methods []string `json:"methods"`
+ Params []*RouteHandlerParam `json:"params"`
+ BodyFields []*RouteHandlerParam `json:"bodyFields"`
+ Returns string `json:"returns"`
+ ReturnGoType string `json:"returnGoType"`
+ ReturnTypescriptType string `json:"returnTypescriptType"`
+ }
+
+ RouteHandlerParam struct {
+ Name string `json:"name"`
+ JsonName string `json:"jsonName"`
+ GoType string `json:"goType"` // e.g., []models.User
+ UsedStructType string `json:"usedStructType"` // e.g., models.User
+ TypescriptType string `json:"typescriptType"` // e.g., Array
+
*/}
- {/*
*/} + {/* {route.name}*/} + {/*
*/} + {/**/} + {/* Used in: {route.filename.replace(".go", "")}.hooks.ts*/} + {/*
*/} + {!!route.api!.descriptions?.length &&{desc}
+ ))} +*/} - {/* {route.name.replace("Handle", "")}*/} - {/*
*/} - {/* {!!route.description &&{route.description}
}*/} + {!!route.api!.params?.length &&+ {param.name} + {param.required && *} +
+{param.typescriptType}
+ {param.descriptions?.map((desc, i) => ( +{desc}
+ ))} +*/} - {/* {param.name}*/} - {/* {param.required && *}*/} - {/*
*/} - {/*{param.type}
*/} - {/*{param.description}
*/} - {/*{field.jsonName} {field.required && + *}
+{field.typescriptType}
+ {field.descriptions?.map((desc, i) => ( +{desc}
+ ))} +{field.name}
*/} - {/*{field.type}
*/} - {/*{field.description}
*/} - {/*Returns
+{route.api!.returnTypescriptType}
+Returns
*/} - {/*{route.returns}
*/} - {/*