start v2ray at launch

This commit is contained in:
yuanxue.yang 2018-10-24 15:07:55 +08:00
parent 6729f3373d
commit ebd370d962
15 changed files with 197 additions and 155 deletions

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array/>
</plist>

View File

@ -8,7 +8,6 @@
import Cocoa
import ServiceManagement
import os.log
let launcherAppIdentifier = "net.yanue.V2rayU.Launcher"
@ -16,11 +15,9 @@ let launcherAppIdentifier = "net.yanue.V2rayU.Launcher"
class AppDelegate: NSObject, NSApplicationDelegate {
// bar menu
@IBOutlet weak var statusMenu: NSMenu!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
os_log("v2rayu init.")
// Insert code here to initialize your application
let startedAtLogin = NSWorkspace.shared.runningApplications.contains {
$0.bundleIdentifier == launcherAppIdentifier
}
@ -30,9 +27,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
V2rayLaunch.generateLauchAgentPlist()
if UserDefaults.getBool(forKey: .v2rayTurnOn) {
V2rayLaunch.Start()
}
// auto check updates
if UserDefaults.getBool(forKey: .autoCheckVersion) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

View File

@ -8,14 +8,14 @@
import Cocoa
import WebKit
import os.log
class ConfigWindowController: NSWindowController,NSWindowDelegate {
var lastIndex:Int = 0
class ConfigWindowController: NSWindowController, NSWindowDelegate {
override var windowNibName: String? {
return "ConfigWindow" // no extension .xib here
}
let tableViewDragType: String = "ss.server.profile.data"
let tableViewDragType: String = "v2ray.item"
@IBOutlet weak var errTip: NSTextField!
@ -106,7 +106,7 @@ class ConfigWindowController: NSWindowController,NSWindowDelegate {
// save
V2rayServer.save(idx: self.serversTableView.selectedRow, jsonData: text)
print("save ok fater")
NSLog("save ok fater")
// refresh menu
menuController.showServers()
}
@ -118,17 +118,8 @@ class ConfigWindowController: NSWindowController,NSWindowDelegate {
NSApp.setActivationPolicy(.accessory)
}
@objc private func onDoubleClicked() {
print("onDoubleClicked row \(serversTableView.clickedRow), col \(serversTableView.clickedColumn) clicked")
}
@objc private func onItemClicked() {
print("row \(serversTableView.clickedRow), col \(serversTableView.clickedColumn) clicked")
}
func windowShouldClose(_ sender: NSWindow) -> Bool {
// NSApp.setActivationPolicy(.accessory)
print("close1")
self.close()
NSApp.terminate(self)
return true
@ -136,12 +127,12 @@ class ConfigWindowController: NSWindowController,NSWindowDelegate {
func windowWillClose(_ notification: Notification) {
// hide dock icon and close all opened windows
print("close")
NSLog("close")
// NSApp.setActivationPolicy(.accessory)
}
}
// NSTableViewDataSource
// NSv2rayItemListSource
extension ConfigWindowController: NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
@ -149,15 +140,15 @@ extension ConfigWindowController: NSTableViewDataSource {
}
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
let tableViewData = V2rayServer.list()
let v2rayItemList = V2rayServer.list()
// set cell data
return tableViewData[row].remark
return v2rayItemList[row].remark
}
// edit cell
func tableView(_ tableView: NSTableView, setObjectValue: Any?, for forTableColumn: NSTableColumn?, row: Int) {
guard let remark = setObjectValue as? String else {
os_log("remark is nil")
NSLog("remark is nil")
return
}
// edit

View File

@ -13,22 +13,11 @@ import Preferences
// menu controller
class MenuController:NSObject,NSMenuDelegate {
// when menu.xib loaded
override func awakeFromNib() {
// load server list
V2rayServer.loadConfig()
self.showServers()
statusMenu.delegate = self
if UserDefaults.getBool(forKey: .v2rayTurnOn) {
self.setStatusOn()
} else{
self.setStatusOff()
}
var configWindow: ConfigWindowController!
statusItem.menu = statusMenu
configWindow = ConfigWindowController()
}
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
var statusItemClicked: (() -> Void)?
let preferencesWindowController = PreferencesWindowController(
viewControllers: [
@ -36,19 +25,32 @@ class MenuController:NSObject,NSMenuDelegate {
]
)
var configWindow: ConfigWindowController!
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
var statusItemClicked: (() -> Void)?
@IBOutlet weak var statusMenu: NSMenu!
@IBOutlet weak var toggleV2rayItem: NSMenuItem!
@IBOutlet weak var v2rayStatusItem: NSMenuItem!
// bar menu
@IBOutlet weak var statusMenu: NSMenu!
// server list items
@IBOutlet weak var serverItems: NSMenuItem!
// when menu.xib loaded
override func awakeFromNib() {
statusMenu.delegate = self
NSLog("start menu")
// load server list
V2rayServer.loadConfig()
// show server list
self.showServers()
if UserDefaults.getBool(forKey: .v2rayTurnOn) {
// start
self.startV2rayCore()
} else{
self.setStatusOff()
}
statusItem.menu = statusMenu
configWindow = ConfigWindowController()
}
@IBAction func openLogs(_ sender: NSMenuItem) {
V2rayLaunch.OpenLogs()
}
@ -60,29 +62,27 @@ class MenuController:NSObject,NSMenuDelegate {
if let button = statusItem.button {
button.image = NSImage(named:NSImage.Name("IconOff"))
}
// set off
UserDefaults.setBool(forKey: .v2rayTurnOn, value: false)
}
func setStatusOn() {
v2rayStatusItem.title = "V2ray-Core: Off"
toggleV2rayItem.title = "Turn V2ray-Core On"
v2rayStatusItem.title = "V2ray-Core: On"
toggleV2rayItem.title = "Turn V2ray-Core Off"
if let button = statusItem.button {
button.image = NSImage(named:NSImage.Name("IconOn"))
}
// set on
UserDefaults.setBool(forKey: .v2rayTurnOn, value: true)
}
@IBAction func start(_ sender: NSMenuItem) {
// turn off
if UserDefaults.getBool(forKey: .v2rayTurnOn) {
// set off
UserDefaults.setBool(forKey: .v2rayTurnOn, value: false)
// set status
self.setStatusOff()
return
}
// start v2ray core
func startV2rayCore() {
NSLog("start v2ray-core begin")
guard let v2ray = V2rayServer.loadSelectedItem() else {
NSLog("v2ray config not fould")
return
@ -93,14 +93,27 @@ class MenuController:NSObject,NSMenuDelegate {
return
}
// set on
UserDefaults.setBool(forKey: .v2rayTurnOn, value: true)
// create json file
V2rayServer.createJsonFile(item: v2ray)
// set status
setStatusOn()
// launch
V2rayLaunch.Start()
NSLog("start v2ray-core end.")
}
@IBAction func start(_ sender: NSMenuItem) {
// turn off
if UserDefaults.getBool(forKey: .v2rayTurnOn) {
// set status
self.setStatusOff()
return
}
// start
self.startV2rayCore()
}
@IBAction func quitClicked(_ sender: NSMenuItem) {
@ -108,39 +121,25 @@ class MenuController:NSObject,NSMenuDelegate {
}
@IBAction func generateQRCode(_ sender: NSMenuItem) {
print("GenerateQRCode")
NSLog("GenerateQRCode")
}
@IBAction func scanQRCode(_ sender: NSMenuItem) {
print("ScanQRCode")
NSLog("ScanQRCode")
}
@IBAction func openPreference(_ sender: NSMenuItem) {
print("openPreference ")
// let app = NSApplication.shared.delegate as! AppDelegate
self.preferencesWindowController.showWindow()
}
// switch server
@IBAction func switchServer(_ sender: NSMenuItem) {
if let obj = sender.representedObject as? v2rayItem {
// path: /Application/V2rayU.app/Contents/Resources/config.json
guard let jsonFile = V2rayServer.getJsonFile() else {
print("unable get config file path")
return
}
let jsonText = obj.json
do {
try jsonText.write(to: URL.init(fileURLWithPath: jsonFile), atomically: true, encoding: String.Encoding.utf8)
} catch {
// failed to write file bad permissions, bad filename, missing permissions, or more likely it can't be converted to the encoding
return
}
UserDefaults.set(forKey: .v2rayCurrentServerName,value: obj.name)
guard let obj = sender.representedObject as? v2rayItem else {
NSLog("switchServer err")
return
}
UserDefaults.set(forKey: .v2rayCurrentServerName,value: obj.name)
}
// open config window
@ -162,6 +161,7 @@ class MenuController:NSObject,NSMenuDelegate {
serverItems.submenu?.removeAllItems()
let curSer = UserDefaults.get(forKey: .v2rayCurrentServerName)
// servers preferences...
let menuItem : NSMenuItem = NSMenuItem()
menuItem.title = "servers preferences..."
menuItem.action = #selector(self.openConfig(_:))
@ -169,6 +169,7 @@ class MenuController:NSObject,NSMenuDelegate {
menuItem.isEnabled = true
serverItems.submenu?.addItem(menuItem)
// separator
serverItems.submenu?.addItem(NSMenuItem.separator())
// add new
@ -184,7 +185,7 @@ class MenuController:NSObject,NSMenuDelegate {
menuItem.target = self
menuItem.isEnabled = true
if curSer == item.name {
if curSer == item.name || V2rayServer.count() == 1 {
menuItem.state = NSControl.StateValue.on
}

View File

@ -11,11 +11,17 @@ import Foundation
extension UserDefaults {
enum KEY: String {
// v2ray-core version
case v2rayCoreVersion
// v2ray server item list
case v2rayServerList
// current v2ray server name
case v2rayCurrentServerName
// v2ray-core turn on status
case v2rayTurnOn
// auth check version
case autoCheckVersion
// auto launch after login
case autoLaunch
}
@ -53,22 +59,13 @@ extension UserDefaults {
}
extension String {
// version compare
func versionToInt() -> [Int] {
return self.components(separatedBy: ".")
.map { Int.init($0) ?? 0 }
}
}
func getApplicationSupportDirectory() -> URL {
let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
return paths[0]
}
func getApplicationDirectory() -> URL {
let paths = FileManager.default.urls(for: .applicationDirectory, in: .userDomainMask)
return paths[0]
}
// run custom shell
// demo:
// shell("/bin/bash",["-c","ls"])

View File

@ -46,7 +46,7 @@ class V2rayCore {
//to get status code
if let status = response.response?.statusCode {
if status != 200 {
print("error with response status: \(status)")
NSLog("error with response status: ",status)
return
}
}
@ -57,20 +57,20 @@ class V2rayCore {
// get tag_name (verion)
guard let tag_name = JSON["tag_name"] else {
print("err: no tag_name")
NSLog("error: no tag_name")
return
}
// get prerelease and draft
guard let prerelease = JSON["prerelease"],let draft = JSON["draft"] else {
// get
print("err: get prerelease or draft")
NSLog("error: get prerelease or draft")
return
}
// not pre release or draft
if prerelease as! Bool == true || draft as! Bool == true {
print("this release is a prerelease or draft")
NSLog("this release is a prerelease or draft")
return
}
@ -122,11 +122,11 @@ class V2rayCore {
case .success(_):
break
case .failure(_):
print("error with response status:")
NSLog("error with response status:")
return
}
if let data = response.result.value {
if let _ = response.result.value {
// make unzip.sh execable
// chmod 777 unzip.sh
let execable = "cd "+AppResourcesPath+" && /bin/chmod 777 ./unzip.sh"
@ -137,7 +137,7 @@ class V2rayCore {
let sh = "cd "+AppResourcesPath+" && ./unzip.sh"
// exec shell
let res = shell(launchPath:"/bin/bash", arguments: ["-c",sh])
print("res",data,res!)
NSLog("res:",res!)
}
}
}

View File

@ -41,7 +41,7 @@ class V2rayLaunch: NSObject {
}
static func Start() {
// cmd: /bin/launchctl load -wF /Library/LaunchAgents/yanue.v2rayu.v2ray-core.plist
// cmd: /bin/launchctl load -wF /Users/xxx/Library/LaunchAgents/yanue.v2rayu.v2ray-core.plist
let task = Process.launchedProcess(launchPath: "/bin/launchctl", arguments: ["load" ,"-wF",launchAgentPlistFile])
task.waitUntilExit()
if task.terminationStatus == 0 {

View File

@ -22,12 +22,12 @@ class V2rayServer: NSObject {
}
// v2ray server list
static private var tableViewData:[v2rayItem] = []
static private var v2rayItemList:[v2rayItem] = []
// (init) load v2ray server list from UserDefaults
static func loadConfig() {
// static reset
self.tableViewData = []
self.v2rayItemList = []
// load name list from UserDefaults
var list = UserDefaults.getArray(forKey: .v2rayServerList)
@ -41,56 +41,55 @@ class V2rayServer: NSObject {
// load each v2rayItem
for item in list! {
guard let v2ray = v2rayItem.load(name:item) else {
guard let v2ray = v2rayItem.load(name: item) else {
// delete from UserDefaults
v2rayItem.remove(name: item)
continue
}
// append
self.tableViewData.append(v2ray)
self.v2rayItemList.append(v2ray)
}
}
// get list from v2ray server list
static func list() -> [v2rayItem] {
return self.tableViewData
return self.v2rayItemList
}
// get count from v2ray server list
static func count() -> Int {
return self.tableViewData.count
return self.v2rayItemList.count
}
static func edit(rowIndex:Int, remark:String) {
if !self.tableViewData.indices.contains(rowIndex) {
print("index out of range",rowIndex)
if !self.v2rayItemList.indices.contains(rowIndex) {
NSLog("index out of range",rowIndex)
return
}
// update list
self.tableViewData[rowIndex].remark = remark
self.v2rayItemList[rowIndex].remark = remark
// save
let v2ray = self.tableViewData[rowIndex]
let v2ray = self.v2rayItemList[rowIndex]
v2ray.remark = remark
v2ray.store()
}
// move item to new index
static func move(oldIndex:Int, newIndex:Int) {
if !V2rayServer.tableViewData.indices.contains(oldIndex) {
print("index out of range",oldIndex)
if !V2rayServer.v2rayItemList.indices.contains(oldIndex) {
NSLog("index out of range",oldIndex)
return
}
if !V2rayServer.tableViewData.indices.contains(newIndex) {
print("index out of range",newIndex)
if !V2rayServer.v2rayItemList.indices.contains(newIndex) {
NSLog("index out of range",newIndex)
return
}
let o = self.tableViewData[oldIndex]
self.tableViewData.remove(at: oldIndex)
self.tableViewData.insert(o, at: newIndex)
let o = self.v2rayItemList[oldIndex]
self.v2rayItemList.remove(at: oldIndex)
self.v2rayItemList.insert(o, at: newIndex)
// update server list UserDefaults
self.saveItemList()
@ -98,8 +97,8 @@ class V2rayServer: NSObject {
// add v2ray server (tmp)
static func add() {
if self.tableViewData.count > 20 {
print("over max len")
if self.v2rayItemList.count > 20 {
NSLog("over max len")
return
}
@ -116,7 +115,7 @@ class V2rayServer: NSObject {
v2ray.store()
// just add to mem
self.tableViewData.append(v2ray)
self.v2rayItemList.append(v2ray)
// update server list UserDefaults
self.saveItemList()
@ -124,21 +123,27 @@ class V2rayServer: NSObject {
// remove v2ray server (tmp and UserDefaults and config json file)
static func remove(idx: Int) {
if !V2rayServer.tableViewData.indices.contains(idx) {
print("index out of range",idx)
if !V2rayServer.v2rayItemList.indices.contains(idx) {
NSLog("index out of range",idx)
return
}
let v2ray = V2rayServer.tableViewData[idx]
let v2ray = V2rayServer.v2rayItemList[idx]
// delete from tmp
self.tableViewData.remove(at: idx)
self.v2rayItemList.remove(at: idx)
// delete from v2ray UserDefaults
v2rayItem.remove(name: v2ray.name)
// update server list UserDefaults
self.saveItemList()
// if cuerrent item is default
let curName = UserDefaults.get(forKey: .v2rayCurrentServerName)
if curName != nil && v2ray.name == curName {
UserDefaults.del(forKey: .v2rayCurrentServerName)
}
}
// update server list UserDefaults
@ -158,63 +163,98 @@ class V2rayServer: NSObject {
// load json file data
static func loadV2rayItem(idx:Int) -> v2rayItem? {
if !V2rayServer.tableViewData.indices.contains(idx) {
print("index out of range",idx)
if !V2rayServer.v2rayItemList.indices.contains(idx) {
NSLog("index out of range",idx)
return nil
}
return self.tableViewData[idx]
return self.v2rayItemList[idx]
}
// load selected v2ray item
static func loadSelectedItem() -> v2rayItem? {
guard let curName = UserDefaults.get(forKey: .v2rayCurrentServerName) else {
return nil
var v2ray:v2rayItem? = nil
if let curName = UserDefaults.get(forKey: .v2rayCurrentServerName) {
v2ray = v2rayItem.load(name: curName)
}
return v2rayItem.load(name: curName)
// if default server not fould
if v2ray == nil {
for item in self.v2rayItemList {
if item.usable {
v2ray = v2rayItem.load(name: item.name)
break
}
}
}
return v2ray
}
// save json data into local file
// create current v2ray server json file
static func createJsonFile(item:v2rayItem) {
let jsonText = item.json
// path: /Application/V2rayU.app/Contents/Resources/config.json
guard let jsonFile = V2rayServer.getJsonFile() else {
NSLog("unable get config file path")
return
}
do {
let jsonFilePath = URL.init(fileURLWithPath: jsonFile)
try! FileManager.default.removeItem(at: jsonFilePath)
try jsonText.write(to: jsonFilePath, atomically: true, encoding: String.Encoding.utf8)
} catch {
// failed to write file bad permissions, bad filename, missing permissions, or more likely it can't be converted to the encoding
NSLog("save json file fail.",item.remark)
}
}
// save json data
static func save(idx:Int, jsonData:String) {
if !self.tableViewData.indices.contains(idx) {
print("index out of range",idx)
if !self.v2rayItemList.indices.contains(idx) {
NSLog("index out of range",idx)
return
}
let v2ray = self.tableViewData[idx]
let v2ray = self.v2rayItemList[idx]
if v2ray.name == "" {
print("name is empty")
NSLog("name is empty")
return
}
guard let json = try? JSON(data: jsonData.data(using: String.Encoding.utf8, allowLossyConversion: false)!) else {
print("invalid json")
NSLog("invalid json")
return
}
if !json.exists(){
print("invalid json")
NSLog("invalid json")
return
}
if !json["dns"].exists() {
print("missing inbound")
NSLog("missing inbound")
return
}
if !json["inbound"].exists() {
print("missing inbound")
NSLog("missing inbound")
return
}
if !json["outbound"].exists() {
print("missing outbound")
NSLog("missing outbound")
return
}
if !json["routing"].exists() {
print("missing routing")
NSLog("missing routing")
return
}
@ -224,7 +264,21 @@ class V2rayServer: NSObject {
v2ray.store()
// update current
self.tableViewData[idx].usable = true
self.v2rayItemList[idx] = v2ray
// if just one usable server
// set as default server
var usableCount = 0
for item in v2rayItemList {
if item.usable {
usableCount += 1
}
}
// contain self
if usableCount <= 1 {
UserDefaults.set(forKey: .v2rayCurrentServerName, value: v2ray.name)
}
}
}
@ -232,11 +286,11 @@ class V2rayServer: NSObject {
class v2rayItem: NSObject, NSCoding {
var name: String
var remark: String
var usable: Bool
var json: String
var usable: Bool
// init
required init(name:String="", remark:String="", usable: Bool=false, json:String="") {
required init(name: String, remark: String, usable: Bool, json: String="") {
self.name = name
self.remark = remark
self.json = json
@ -248,7 +302,7 @@ class v2rayItem: NSObject, NSCoding {
self.name = decoder.decodeObject(forKey: "Name") as? String ?? ""
self.remark = decoder.decodeObject(forKey: "Remark") as? String ?? ""
self.json = decoder.decodeObject(forKey: "Json") as? String ?? ""
self.usable = decoder.decodeObject(forKey: "Usable") as? Bool ?? false
self.usable = decoder.decodeBool(forKey: "Usable")
}
// object encode

View File

@ -7,6 +7,7 @@
//
import Cocoa
import os.log
class V2rayULauncherApplication: NSApplication {
let strongDelegate = AppDelegate()
@ -55,7 +56,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
func applicationWillTerminate(_ aNotification: Notification) {
print("helper app terminated")
// Insert code here to tear down your application
}