Skip to content

Commit 719fcd0

Browse files
feat: support replugged store (#456)
* feat: support replugged store * fix: install links support store * fix: prod url * Use API URL * Update src/main/ipc/installer.ts Co-authored-by: Alyxia Sother <[email protected]> --------- Co-authored-by: Alyxia Sother <[email protected]>
1 parent 683806c commit 719fcd0

File tree

7 files changed

+90
-62
lines changed

7 files changed

+90
-62
lines changed

src/main/ipc/installer.ts

+47-23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { ipcMain } from "electron";
22
import {
3-
AnyAddonManifest,
43
CheckResultFailure,
54
CheckResultSuccess,
65
InstallResultFailure,
@@ -13,7 +12,8 @@ import { CONFIG_PATH, CONFIG_PATHS } from "../../util.mjs";
1312
import { readFile, writeFile } from "fs/promises";
1413
import fetch from "node-fetch";
1514
import { join } from "path";
16-
import { RepluggedManifest, anyAddon } from "src/types/addon";
15+
import { AnyAddonManifestOrReplugged, anyAddonOrReplugged } from "src/types/addon";
16+
import { readTransaction } from "./settings";
1717

1818
const octokit = new Octokit();
1919

@@ -59,33 +59,21 @@ async function github(
5959
(manifestAsset) => manifestAsset.name === asset.name.replace(/\.asar$/, ".json"),
6060
);
6161

62-
if (!manifestAsset && identifier !== "replugged-org/replugged") {
62+
if (!manifestAsset) {
6363
return {
6464
success: false,
6565
error: "No manifest asset found",
6666
};
6767
}
6868

69-
let manifest: AnyAddonManifest | RepluggedManifest;
70-
if (manifestAsset) {
71-
try {
72-
const json = await fetch(manifestAsset.browser_download_url).then((res) => res.json());
73-
manifest = anyAddon.parse(json);
74-
} catch {
75-
return {
76-
success: false,
77-
error: "Failed to parse manifest",
78-
};
79-
}
80-
} else {
81-
// For Replugged itself
82-
manifest = {
83-
version: res.data.tag_name.replace(/^v/, ""),
84-
updater: {
85-
id: identifier,
86-
type: "github",
87-
},
88-
type: "replugged",
69+
let manifest: AnyAddonManifestOrReplugged;
70+
try {
71+
const json = await fetch(manifestAsset.browser_download_url).then((res) => res.json());
72+
manifest = anyAddonOrReplugged.parse(json);
73+
} catch {
74+
return {
75+
success: false,
76+
error: "Failed to parse manifest",
8977
};
9078
}
9179

@@ -98,11 +86,47 @@ async function github(
9886
};
9987
}
10088

89+
async function store(id: string): Promise<CheckResultSuccess | CheckResultFailure> {
90+
const apiUrl =
91+
((await readTransaction("dev.replugged.Settings", (settings) => settings.get("apiUrl"))) as
92+
| string
93+
| undefined) ?? "https://replugged.dev";
94+
const STORE_BASE_URL = `${apiUrl}/api/v1/store`;
95+
const manifestUrl = `${STORE_BASE_URL}/${id}`;
96+
const asarUrl = `${manifestUrl}.asar`;
97+
98+
const res = await fetch(manifestUrl);
99+
if (!res.ok) {
100+
return {
101+
success: false,
102+
error: "Failed to fetch manifest",
103+
};
104+
}
105+
106+
let manifest;
107+
try {
108+
manifest = anyAddonOrReplugged.parse(await res.json());
109+
} catch {
110+
return {
111+
success: false,
112+
error: "Failed to parse manifest",
113+
};
114+
}
115+
116+
return {
117+
success: true,
118+
manifest,
119+
name: `${id}.asar`,
120+
url: asarUrl,
121+
};
122+
}
123+
101124
const handlers: Record<
102125
string,
103126
(identifier: string, id?: string) => Promise<CheckResultSuccess | CheckResultFailure>
104127
> = {
105128
github,
129+
store,
106130
};
107131

108132
ipcMain.handle(

src/main/ipc/settings.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ async function transaction<T>(namespace: string, handler: TransactionHandler<T>)
3939
return result;
4040
}
4141

42-
async function readTransaction<T>(
42+
export async function readTransaction<T>(
4343
namespace: string,
4444
handler: SettingsTransactionHandler<T>,
4545
): Promise<T> {
@@ -49,7 +49,7 @@ async function readTransaction<T>(
4949
});
5050
}
5151

52-
async function writeTransaction<T>(
52+
export async function writeTransaction<T>(
5353
namespace: string,
5454
handler: SettingsTransactionHandler<T>,
5555
): Promise<T> {

src/preload.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ const RepluggedNative = {
4444
updater: {
4545
check: async (
4646
type: string,
47-
repo: string,
47+
identifier: string,
4848
id: string,
4949
): Promise<CheckResultSuccess | CheckResultFailure> =>
50-
ipcRenderer.invoke(RepluggedIpcChannels.GET_ADDON_INFO, type, repo, id),
50+
ipcRenderer.invoke(RepluggedIpcChannels.GET_ADDON_INFO, type, identifier, id),
5151
install: async (
5252
type: InstallerType | "replugged",
5353
path: string,

src/renderer/coremods/installer/util.ts

-7
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,6 @@ export async function getInfo(
2828
id?: string,
2929
): Promise<CheckResultSuccess | null> {
3030
source ??= INSTALLER_SOURCES[0];
31-
// TODO: remove this once store is supported
32-
// Need to make sure GitHub install links will have the type specified
33-
// so they won't break once store is available (since that will be the default)
34-
if (source === "store") {
35-
logger.error('Store installers are not supported yet. Please specify "github" as the type.');
36-
return null;
37-
}
3831

3932
const cacheIdentifier = `${source}:${identifier}:${id ?? ""}`;
4033
const cached = cache.get(cacheIdentifier);

src/renderer/managers/updater.ts

+23-18
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as pluginManager from "./plugins";
33
import * as themeManager from "./themes";
44
import { Logger } from "../modules/logger";
55
import type { RepluggedPlugin, RepluggedTheme } from "src/types";
6-
import type { RepluggedManifest } from "src/types/addon";
6+
import { RepluggedEntity } from "src/types/addon";
77

88
const logger = Logger.coremod("Updater");
99

@@ -16,10 +16,27 @@ export type UpdateSettings = {
1616
lastChecked: number;
1717
};
1818

19-
interface RepluggedEntity {
20-
manifest: RepluggedManifest;
21-
path: string;
22-
}
19+
const REPLUGGED_ID = "dev.replugged.Replugged";
20+
const REPLUGGED_ENTITY: RepluggedEntity = {
21+
manifest: {
22+
id: REPLUGGED_ID,
23+
name: "Replugged",
24+
description: "Replugged itself",
25+
author: {
26+
name: "replugged",
27+
discordID: "1000992611840049192",
28+
github: "replugged-org",
29+
},
30+
type: "replugged",
31+
updater: {
32+
type: "store",
33+
id: REPLUGGED_ID,
34+
},
35+
version: window.RepluggedNative.getVersion(),
36+
license: "MIT",
37+
},
38+
path: "replugged.asar",
39+
};
2340

2441
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
2542
type MainUpdaterSettings = {
@@ -28,8 +45,6 @@ type MainUpdaterSettings = {
2845
lastChecked?: number;
2946
};
3047

31-
const REPLUGGED_ID = "dev.replugged.Replugged";
32-
3348
const mainUpdaterDefaultSettings: Partial<MainUpdaterSettings> = {
3449
checkInterval: 1000 * 60 * 60,
3550
};
@@ -78,17 +93,7 @@ async function getAddonFromManager(
7893
if (id === REPLUGGED_ID) {
7994
const version = window.RepluggedNative.getVersion();
8095
if (version === "dev") return undefined;
81-
return {
82-
manifest: {
83-
version,
84-
updater: {
85-
type: "github",
86-
id: "replugged-org/replugged",
87-
},
88-
type: "replugged",
89-
},
90-
path: "replugged.asar",
91-
};
96+
return REPLUGGED_ENTITY;
9297
}
9398

9499
return pluginManager.plugins.get(id) || (await themeManager.get(id));

src/types/addon.ts

+15-9
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export const common = z.object({
2626
version: z.string(),
2727
updater: z
2828
.object({
29-
type: z.literal("github"),
29+
type: z.enum(["store", "github"]),
3030
id: z.string(),
3131
})
3232
.optional(),
@@ -35,6 +35,17 @@ export const common = z.object({
3535

3636
export type Common = z.infer<typeof common>;
3737

38+
export const repluggedManifest = common.extend({
39+
type: z.literal("replugged"),
40+
});
41+
42+
export type RepluggedManifest = z.infer<typeof repluggedManifest>;
43+
44+
export interface RepluggedEntity {
45+
manifest: RepluggedManifest;
46+
path: string;
47+
}
48+
3849
export const theme = common.extend({
3950
type: z.literal("replugged-theme"),
4051
main: z.string(),
@@ -56,14 +67,9 @@ export const anyAddon = z.discriminatedUnion("type", [theme, plugin]);
5667

5768
export type AnyAddonManifest = z.infer<typeof anyAddon>;
5869

59-
export interface RepluggedManifest {
60-
version: string;
61-
updater: {
62-
id: string;
63-
type: "github";
64-
};
65-
type: "replugged";
66-
}
70+
export const anyAddonOrReplugged = z.discriminatedUnion("type", [repluggedManifest, theme, plugin]);
71+
72+
export type AnyAddonManifestOrReplugged = z.infer<typeof anyAddonOrReplugged>;
6773

6874
export interface PluginExports {
6975
start?: () => Promisable<void>;

src/types/installer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export interface CheckResultSuccess {
77
manifest: AnyAddonManifest | RepluggedManifest;
88
name: string;
99
url: string;
10-
webUrl: string;
10+
webUrl?: string;
1111
}
1212

1313
export interface CheckResultFailure {

0 commit comments

Comments
 (0)